[WIP] Get bare metal working outside of an emulator (#609)

You can now run bare metal on bare metal!

* Fix handling of int 0x15 eax = 0xe820 memory map
* Fix some issues in initial page table creation
* hello4.com now works outside emulators
* Ensure area for identity page tables are zeroed first
* Simplify logic for creating page table entries, this partly
  reverts 577c0f6226
* Add degenerate MBR partition entry, to ease testing

Co-authored-by: tkchia <tkchia-cosmo@gmx.com>
This commit is contained in:
tkchia 2022-09-13 17:01:49 +08:00 committed by Justine Tunney
parent c03359c637
commit 116bda997e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
3 changed files with 55 additions and 35 deletions

View file

@ -329,7 +329,7 @@ dsknfo: push %bx
movpp %es,%ds movpp %es,%ds
xor %si,%si xor %si,%si
mov %si,%es mov %si,%es
mov $0x1510,%si # mman::pc_drive_base_table mov $0x1d10,%si # mman::pc_drive_base_table
xchg %si,%di xchg %si,%di
movsw # headunloadtime, headloadtime movsw # headunloadtime, headloadtime
movsw # shutofftime, bytespersector movsw # shutofftime, bytespersector
@ -382,11 +382,11 @@ pcread: push %ax
add $512>>4,%si add $512>>4,%si
mov %si,%es mov %si,%es
inc %al # ++sector inc %al # ++sector
cmp 0x151c,%al # mman::pc_drive_last_sector cmp 0x1d1c,%al # mman::pc_drive_last_sector
jbe 2f jbe 2f
mov $1,%al mov $1,%al
inc %dh # ++head inc %dh # ++head
cmp 0x1520,%dh # mman::pc_drive_last_head cmp 0x1d20,%dh # mman::pc_drive_last_head
jbe 2f jbe 2f
xor %dh,%dh xor %dh,%dh
inc %cx # ++cylinder inc %cx # ++cylinder
@ -485,19 +485,19 @@ ape.mbrpad:
ape_disk: ape_disk:
.stub ape.diskid,quad .stub ape.diskid,quad
.org 0x1be,0x00 .org 0x1be,0x00
.macro .partn x .macro .partn x:req sta h0 s0 c0 fs h9 s9 c9 lba0 nsec
.stub ape.part\x\().status,byte # 0=absent / 0x80=present .stub ape.part\x\().status,byte,\sta # 0=non-boot / 0x80=active
.stub ape.part\x\().first.head,byte # in low 6 bits .stub ape.part\x\().first.head,byte,\h0
.stub ape.part\x\().first.cylinder,byte .stub ape.part\x\().first.sector,byte,\s0 # in low 6 bits
.stub ape.part\x\().first.sector,byte .stub ape.part\x\().first.cylinder,byte,\c0
.stub ape.part\x\().filesystem,byte .stub ape.part\x\().filesystem,byte,\fs
.stub ape.part\x\().last.head,byte .stub ape.part\x\().last.head,byte,\h9
.stub ape.part\x\().last.cylinder,byte .stub ape.part\x\().last.sector,byte,\s9
.stub ape.part\x\().last.sector,byte .stub ape.part\x\().last.cylinder,byte,\c9
.stub ape.part\x\().lba,long # c*C + h*H + s*S .stub ape.part\x\().lba,long,\lba0 # c*C + h*H + s*S
.stub ape.part\x\().sector.count,long # sectors are 512 bytes .stub ape.part\x\().sector.count,long,\nsec # sectors are 512 bytes
.endm .endm
.partn 1 .partn 1,0x80,0,1,0,0x7f,0xff,0xff,0xff,0,0xffffffff
.partn 2 .partn 2
.partn 3 .partn 3
.partn 4 .partn 4
@ -1291,27 +1291,38 @@ lcheck: pushf # check for i8086 / i8088 / i80186
// Gets memory map from BIOS. // Gets memory map from BIOS.
e820: mov $0x0510>>4,%di # mman::e820 e820: mov $0x0510>>4,%di # mman::e820
mov %di,%es mov %di,%es
xor %edi,%edi # es:di is destination buffer xor %di,%di # es:di is destination buffer
xor %ebx,%ebx # ebx is an api state tracker xor %ebx,%ebx # ebx is an api state tracker
1: mov $0xE820,%eax # magic 1: mov $0xE820,%eax # magic
mov $8+8+4+4,%ecx # sizeof(struct SmapEntry) mov $8+8+4+4,%ecx # sizeof(struct SmapEntry)
mov $0x534d4150,%edx # magic number mov $0x534d4150,%edx # magic number
movl $1,8+8+4/*SmapEntry::acpi3*/(%di) # prefill ACPI attributes;
# apparently some buggy BIOSes say
# that they return this field, yet
# do not fill it correctly
int $0x15 # ax,bx,cx,dx,di ax,bx,cx int $0x15 # ax,bx,cx,dx,di ax,bx,cx
jc 9f # cf = unsupported or abuse jc 9f # cf = unsupported or abuse
cmp %edx,%eax # more magic means success cmp %edx,%eax # more magic means success
jne 9f jne 9f
test %cx,%cx # discard empty results test %cx,%cx # discard empty results
jz 5f jz 5f
mov 8/*LODWORD(SmapEntry::size)*/(%di),%eax
or 8+4/*HIDWORD(SmapEntry::size)*/(%di),%eax
jz 5f
cmp $8+8+4+1,%cx # discard if ignore flag cmp $8+8+4+1,%cx # discard if ignore flag
jb 4f jb 4f
testb $1/*ignore*/,8+8+4/*SmapEntry::__acpi3*/(%di) testb $1/*don't ignore*/,8+8+4/*SmapEntry::acpi3*/(%di)
jnz 5f jz 5f
4: add $8+8+4+4,%di # keep entry 4: add $8+8+4+4,%di # keep entry
5: test %ebx,%ebx # last entry? 5: test %ebx,%ebx # last entry?
jz 8f jz 8f
cmp $0x1000,%di cmp $(256-1)*(8+8+4+4),%di
jb 1b jb 1b
8: ret 8: xor %ax,%ax # add a blank sentinel entry
mov $(8+8)/2,%cx
cld
rep stosw
ret
9: mov $REAL(str.e820),%di 9: mov $REAL(str.e820),%di
call rldie call rldie
.endfn e820 .endfn e820
@ -1381,9 +1392,16 @@ a20: cli
// Initializes long mode paging. // Initializes long mode paging.
pinit: push %ds pinit: push %ds
push %es
#define SEG 0x79000 #define SEG 0x79000
mov $SEG>>4,%ax mov $SEG>>4,%ax
mov %ax,%ds mov %ax,%ds
mov %ax,%es
xor %di,%di
xor %ax,%ax
mov $(0x7f000-SEG)/2,%cx
cld
rep stosw
movl $0x7d000+PAGE_V+PAGE_RW,0x7e000-SEG # PDPTPML4T (+) movl $0x7d000+PAGE_V+PAGE_RW,0x7e000-SEG # PDPTPML4T (+)
movl $0x7c000+PAGE_V+PAGE_RW,0x7e800-SEG # PDPTPML4T (-) movl $0x7c000+PAGE_V+PAGE_RW,0x7e800-SEG # PDPTPML4T (-)
movl $0x7b000+PAGE_V+PAGE_RW,0x7d000-SEG # PDTPDPT (+) movl $0x7b000+PAGE_V+PAGE_RW,0x7d000-SEG # PDTPDPT (+)
@ -1392,13 +1410,14 @@ pinit: push %ds
movl $0x79000+PAGE_V+PAGE_RW,0x7a000-SEG # PDPDT (-) movl $0x79000+PAGE_V+PAGE_RW,0x7a000-SEG # PDPDT (-)
mov $512,%cx # PD±2MB mov $512,%cx # PD±2MB
mov $PAGE_V+PAGE_RW,%eax mov $PAGE_V+PAGE_RW,%eax
xor %si,%si xor %di,%di
0: mov %eax,(%si) 0: stosl
add $0x1000,%eax add $0x1000,%eax
add $8,%si scasl # di += 4
loop 0b loop 0b
mov $0x7e000,%eax # PML4TCR3 mov $0x7e000,%eax # PML4TCR3
mov %eax,%cr3 mov %eax,%cr3
pop %es
pop %ds pop %ds
ret ret
.endfn pinit .endfn pinit
@ -1407,7 +1426,7 @@ pinit: push %ds
// //
// @see Intel Manual V3A §4.1.2 // @see Intel Manual V3A §4.1.2
golong: cli golong: cli
lidt 0x1522 # mman::bad_idt lidt 0x1d22 # mman::bad_idt
mov %cr4,%eax mov %cr4,%eax
or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax
mov %eax,%cr4 mov %eax,%cr4

View file

@ -74,6 +74,7 @@ noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t,
if (!(*e & PAGE_V)) { if (!(*e & PAGE_V)) {
if (!maketables) return NULL; if (!maketables) return NULL;
if (!(p = __new_page(mm))) return NULL; if (!(p = __new_page(mm))) return NULL;
__clear_page(BANE + p);
*e = p | PAGE_V | PAGE_RW; *e = p | PAGE_V | PAGE_RW;
} }
t = (uint64_t *)(BANE + (*e & PAGE_TA)); t = (uint64_t *)(BANE + (*e & PAGE_TA));
@ -117,7 +118,7 @@ static noasan textreal void __invert_memory(struct mman *mm, uint64_t *pml4t) {
uint64_t i, j, *m, p, pe; uint64_t i, j, *m, p, pe;
for (i = 0; i < mm->e820n; ++i) { for (i = 0; i < mm->e820n; ++i) {
for (p = mm->e820[i].addr, pe = mm->e820[i].addr + mm->e820[i].size; for (p = mm->e820[i].addr, pe = mm->e820[i].addr + mm->e820[i].size;
p + 0x200000 < pe; p += 4096) { p != pe + 0x200000; p += 4096) {
m = __get_virtual(mm, pml4t, BANE + p, true); m = __get_virtual(mm, pml4t, BANE + p, true);
if (m && !(*m & PAGE_V)) { if (m && !(*m & PAGE_V)) {
*m = p | PAGE_V | PAGE_RW; *m = p | PAGE_V | PAGE_RW;
@ -150,7 +151,7 @@ noasan textreal void __map_phdrs(struct mman *mm, uint64_t *pml4t, uint64_t b) {
v = b + p->p_offset + i; v = b + p->p_offset + i;
m = MAX(m, v); m = MAX(m, v);
} else { } else {
v = __clear_page(__new_page(mm)); v = __clear_page(BANE + __new_page(mm));
} }
*__get_virtual(mm, pml4t, p->p_vaddr + i, true) = (v & PAGE_TA) | f; *__get_virtual(mm, pml4t, p->p_vaddr + i, true) = (v & PAGE_TA) | f;
} }

View file

@ -7,16 +7,16 @@ COSMOPOLITAN_C_START_
struct mman { struct mman {
int64_t pdp; /* 0x0500 */ int64_t pdp; /* 0x0500 */
int32_t pdpi; /* 0x0508 */ int32_t pdpi; /* 0x0508 */
int32_t e820n; /* 0x050a */ int32_t e820n; /* 0x050c */
struct SmapEntry e820[256]; /* 0x0510 */ struct SmapEntry e820[256]; /* 0x0510 */
char pc_drive_base_table[11]; /* 0x1510 */ char pc_drive_base_table[11]; /* 0x1d10 */
unsigned char pc_drive_type; /* 0x151b */ unsigned char pc_drive_type; /* 0x1d1b */
unsigned char pc_drive_last_sector; /* 0x151c */ unsigned char pc_drive_last_sector; /* 0x1d1c */
unsigned short pc_drive_last_cylinder; /* 0x151d */ unsigned short pc_drive_last_cylinder; /* 0x1d1d */
unsigned char pc_drives_attached; /* 0x151f */ unsigned char pc_drives_attached; /* 0x1d1f */
unsigned char pc_drive_last_head; /* 0x1520 */ unsigned char pc_drive_last_head; /* 0x1d20 */
unsigned char pc_drive; /* 0x1521 */ unsigned char pc_drive; /* 0x1d21 */
char bad_idt[6]; /* 0x1522 */ char bad_idt[6]; /* 0x1d22 */
}; };
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_