From 116bda997e4765eb2b8acbecb57fe367c87f8553 Mon Sep 17 00:00:00 2001 From: tkchia Date: Tue, 13 Sep 2022 17:01:49 +0800 Subject: [PATCH] [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 577c0f6226fde54a0018a8ebe7ff0ddbd0e16207 * Add degenerate MBR partition entry, to ease testing Co-authored-by: tkchia --- ape/ape.S | 67 +++++++++++++++++++++++------------- libc/intrin/mman.greg.c | 5 +-- libc/runtime/mman.internal.h | 18 +++++----- 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/ape/ape.S b/ape/ape.S index 0e92a729c..873c1b523 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -329,7 +329,7 @@ dsknfo: push %bx movpp %es,%ds xor %si,%si mov %si,%es - mov $0x1510,%si # mman::pc_drive_base_table + mov $0x1d10,%si # mman::pc_drive_base_table xchg %si,%di movsw #→ headunloadtime, headloadtime movsw #→ shutofftime, bytespersector @@ -382,11 +382,11 @@ pcread: push %ax add $512>>4,%si mov %si,%es inc %al # ++sector - cmp 0x151c,%al # mman::pc_drive_last_sector + cmp 0x1d1c,%al # mman::pc_drive_last_sector jbe 2f mov $1,%al inc %dh # ++head - cmp 0x1520,%dh # mman::pc_drive_last_head + cmp 0x1d20,%dh # mman::pc_drive_last_head jbe 2f xor %dh,%dh inc %cx # ++cylinder @@ -485,19 +485,19 @@ ape.mbrpad: ape_disk: .stub ape.diskid,quad .org 0x1be,0x00 - .macro .partn x - .stub ape.part\x\().status,byte # 0=absent / 0x80=present - .stub ape.part\x\().first.head,byte # in low 6 bits - .stub ape.part\x\().first.cylinder,byte - .stub ape.part\x\().first.sector,byte - .stub ape.part\x\().filesystem,byte - .stub ape.part\x\().last.head,byte - .stub ape.part\x\().last.cylinder,byte - .stub ape.part\x\().last.sector,byte - .stub ape.part\x\().lba,long # c₀*Cₙ + h₀*Hₙ + s₀*Sₙ - .stub ape.part\x\().sector.count,long # sectors are 512 bytes + .macro .partn x:req sta h0 s0 c0 fs h9 s9 c9 lba0 nsec + .stub ape.part\x\().status,byte,\sta # 0=non-boot / 0x80=active + .stub ape.part\x\().first.head,byte,\h0 + .stub ape.part\x\().first.sector,byte,\s0 # in low 6 bits + .stub ape.part\x\().first.cylinder,byte,\c0 + .stub ape.part\x\().filesystem,byte,\fs + .stub ape.part\x\().last.head,byte,\h9 + .stub ape.part\x\().last.sector,byte,\s9 + .stub ape.part\x\().last.cylinder,byte,\c9 + .stub ape.part\x\().lba,long,\lba0 # c₀*Cₙ + h₀*Hₙ + s₀*Sₙ + .stub ape.part\x\().sector.count,long,\nsec # sectors are 512 bytes .endm - .partn 1 + .partn 1,0x80,0,1,0,0x7f,0xff,0xff,0xff,0,0xffffffff .partn 2 .partn 3 .partn 4 @@ -1291,27 +1291,38 @@ lcheck: pushf # check for i8086 / i8088 / i80186 // Gets memory map from BIOS. e820: mov $0x0510>>4,%di # mman::e820 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 1: mov $0xE820,%eax # magic mov $8+8+4+4,%ecx # sizeof(struct SmapEntry) 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 jc 9f # cf = unsupported or abuse cmp %edx,%eax # more magic means success jne 9f test %cx,%cx # discard empty results 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 jb 4f - testb $1/*ignore*/,8+8+4/*SmapEntry::__acpi3*/(%di) - jnz 5f + testb $1/*don't ignore*/,8+8+4/*SmapEntry::acpi3*/(%di) + jz 5f 4: add $8+8+4+4,%di # keep entry 5: test %ebx,%ebx # last entry? jz 8f - cmp $0x1000,%di + cmp $(256-1)*(8+8+4+4),%di 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 call rldie .endfn e820 @@ -1381,9 +1392,16 @@ a20: cli // Initializes long mode paging. pinit: push %ds + push %es #define SEG 0x79000 mov $SEG>>4,%ax 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 # PDPT←PML4T (+) movl $0x7c000+PAGE_V+PAGE_RW,0x7e800-SEG # PDPT←PML4T (-) movl $0x7b000+PAGE_V+PAGE_RW,0x7d000-SEG # PDT←PDPT (+) @@ -1392,13 +1410,14 @@ pinit: push %ds movl $0x79000+PAGE_V+PAGE_RW,0x7a000-SEG # PD←PDT (-) mov $512,%cx # PD±2MB mov $PAGE_V+PAGE_RW,%eax - xor %si,%si -0: mov %eax,(%si) + xor %di,%di +0: stosl add $0x1000,%eax - add $8,%si + scasl # di += 4 loop 0b mov $0x7e000,%eax # PML4T←CR3 mov %eax,%cr3 + pop %es pop %ds ret .endfn pinit @@ -1407,7 +1426,7 @@ pinit: push %ds // // @see Intel Manual V3A §4.1.2 golong: cli - lidt 0x1522 # mman::bad_idt + lidt 0x1d22 # mman::bad_idt mov %cr4,%eax or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax mov %eax,%cr4 diff --git a/libc/intrin/mman.greg.c b/libc/intrin/mman.greg.c index fa0623e50..6e0cc8c47 100644 --- a/libc/intrin/mman.greg.c +++ b/libc/intrin/mman.greg.c @@ -74,6 +74,7 @@ noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t, if (!(*e & PAGE_V)) { if (!maketables) return NULL; if (!(p = __new_page(mm))) return NULL; + __clear_page(BANE + p); *e = p | PAGE_V | PAGE_RW; } 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; for (i = 0; i < mm->e820n; ++i) { 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); if (m && !(*m & PAGE_V)) { *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; m = MAX(m, v); } 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; } diff --git a/libc/runtime/mman.internal.h b/libc/runtime/mman.internal.h index 9863579c1..8c582a52d 100644 --- a/libc/runtime/mman.internal.h +++ b/libc/runtime/mman.internal.h @@ -7,16 +7,16 @@ COSMOPOLITAN_C_START_ struct mman { int64_t pdp; /* 0x0500 */ int32_t pdpi; /* 0x0508 */ - int32_t e820n; /* 0x050a */ + int32_t e820n; /* 0x050c */ struct SmapEntry e820[256]; /* 0x0510 */ - char pc_drive_base_table[11]; /* 0x1510 */ - unsigned char pc_drive_type; /* 0x151b */ - unsigned char pc_drive_last_sector; /* 0x151c */ - unsigned short pc_drive_last_cylinder; /* 0x151d */ - unsigned char pc_drives_attached; /* 0x151f */ - unsigned char pc_drive_last_head; /* 0x1520 */ - unsigned char pc_drive; /* 0x1521 */ - char bad_idt[6]; /* 0x1522 */ + char pc_drive_base_table[11]; /* 0x1d10 */ + unsigned char pc_drive_type; /* 0x1d1b */ + unsigned char pc_drive_last_sector; /* 0x1d1c */ + unsigned short pc_drive_last_cylinder; /* 0x1d1d */ + unsigned char pc_drives_attached; /* 0x1d1f */ + unsigned char pc_drive_last_head; /* 0x1d20 */ + unsigned char pc_drive; /* 0x1d21 */ + char bad_idt[6]; /* 0x1d22 */ }; COSMOPOLITAN_C_END_