diff --git a/kernel/arch/x86_64/boot.asm b/kernel/arch/x86_64/boot.asm index 3d7797b..17ebf46 100644 --- a/kernel/arch/x86_64/boot.asm +++ b/kernel/arch/x86_64/boot.asm @@ -10,6 +10,11 @@ ; flat GDT, enter 32-bit protected mode and jump to the stage-2 entry. ; ; Memory map used by the whole boot chain: +; 0x005000 E820 staging: count byte at 0x5000, 24-byte entries from +; 0x5020 (64 max = 1536 bytes, ends 0x5620). Free: below the +; 0x7C00 sector, clear of the 0x10000 kernel load, the 0x70000 +; tables and the 0x90000 stack. Stage 2 copies it into kernel +; .bss (memmap_count/memmap_entries) after BSS zeroing. ; 0x007C00 this sector (512 bytes) ; 0x010000 kernel image (loaded here, linked here) ; 0x70000 PML4 | 0x71000 PDPT | 0x72000 PD (built by stage 2) @@ -25,6 +30,10 @@ org 0x7C00 KERNEL_LOAD_SEG equ 0x1000 ; ES for load target 0x1000:0x0000 = 0x10000 STACK_TOP equ 0x7C00 ; real-mode stack grows down from here +E820_COUNT equ 0x5000 ; staged E820 entry count (one byte) +E820_BUF equ 0x5020 ; staged E820 entries (24 bytes each) +E820_MAX equ 64 ; cap: 64 * 24 = 1536 bytes, ends 0x5620 +E820_ENTRY_SIZE equ 24 start: cli @@ -40,8 +49,8 @@ start: call puts call enable_a20 - call read_drive_params call load_kernel + call query_e820 mov si, msg_entering call puts @@ -73,18 +82,15 @@ puts: ret ; --------------------------------------------------------------------------- -; enable_a20: try BIOS, keyboard-controller and fast-A20 methods in turn. -; Each is harmless if A20 is already on. +; enable_a20: try the BIOS and fast-A20 methods in turn. Each is harmless +; if A20 is already on. +; WHY only two methods: the keyboard-controller pulse was dropped to free +; sector bytes for the E820 query above — every 64-bit CPU postdates +; chipsets with the 0x92 fast gate (or a BIOS that honors 0x2401), so the +; legacy AT pulse buys nothing on this kernel's hardware floor. enable_a20: mov ax, 0x2401 ; BIOS: enable A20 int 0x15 - call kbd_wait ; keyboard controller: pulse output port - mov al, 0xD1 - out 0x64, al - call kbd_wait - mov al, 0xDF - out 0x60, al - call kbd_wait in al, 0x92 ; fast A20 (skip if already set or absent) test al, 0x02 jnz .done @@ -93,34 +99,11 @@ enable_a20: .done: ret -kbd_wait: - in al, 0x64 - test al, 0x02 - jnz kbd_wait - ret - ; --------------------------------------------------------------------------- -; read_drive_params: query sectors/track and head count via int 0x13 AH=08. -; Falls back to floppy defaults (18 spt, 2 heads) on failure or nonsense. -read_drive_params: - xor ax, ax - mov ds, ax ; re-establish DS (BIOS may clobber it) - mov ah, 0x08 - mov dl, [boot_drive] - int 0x13 - jc .defaults - and cx, 0x3F ; CL[5:0] = max sector = sectors per track - jz .defaults - mov [spt], cl - inc dh ; DH = max head -> head count - jz .defaults - mov [heads], dh - ret -.defaults: - mov byte [spt], 18 - mov byte [heads], 2 - ret - +; Floppy geometry is fixed at 18 sectors/track, 2 heads (the image only +; ever boots via -fda; see [spt]/[heads] inits below). The old int 0x13 +; AH=08 probe was dropped to free sector bytes for the E820 query above. +; HDD generality wants EDD packet reads (AH=42), planned, not CHS probing. ; --------------------------------------------------------------------------- ; lba_to_chs: convert LBA in AX to int 0x13 CHS in CH/CL/DH. ; Clobbers AX, BX, CX, DX. @@ -193,6 +176,79 @@ load_kernel: jnz .next ret +; --------------------------------------------------------------------------- +; query_e820: ask BIOS for the physical memory map (int 0x15, eax=0xE820, +; edx='SMAP') and stage it at E820_BUF with the entry count byte at +; E820_COUNT. Must run BEFORE the protected-mode switch as BIOS calls are +; unavailable after. Runs in 16-bit real mode at Ring 0; clobbers AX, +; BX, CX, DX, DI, ES. +; +; NOTE: like load_kernel, no register survives int 0x15 — SeaBIOS/real +; BIOSes may clobber general registers across the call, so the +; continuation (EBX) lives in memory ([e820_next]) and ECX/EDX/ES:DI are +; reloaded from immediates before every call. DS is saved on the stack +; across the call (push/pop restores OUR value even if BIOS clobbers it); +; it must NOT be rebuilt with xor ax,ax + mov ds,ax after the call, as +; that zeroes EAX's low half and breaks the 'SMAP' check below. DI is recomputed from +; the in-memory count each pass (index < 64, so the 8-bit multiply +; cannot overflow) instead of keeping a second pointer. +; +; Fallback choice: if no entry was staged (first query failed via carry +; or EAX magic mismatch), synthesize a single usable region at 1MB, +; length 127MB (type 1) — enough for the kernel + PMM to boot on an +; unknown box — and continue silently (no message: the sector has no +; bytes to spare). A mid-list failure keeps whatever entries were +; already staged. Documented here so the PMM knows the map may be +; synthetic on old hardware. Kept golfed on purpose: this sector is +; exactly 510 bytes, every instruction here displaced a boot message. +query_e820: + xor ax, ax + mov ds, ax ; re-establish DS + mov [E820_COUNT], al ; count byte = 0 +.loop: + xor ax, ax + mov es, ax ; ES:DI rebuilt per call + mov al, [E820_COUNT] + mov cl, E820_ENTRY_SIZE + mul cl ; AX = index * 24 + mov di, E820_BUF + add di, ax + mov ecx, E820_ENTRY_SIZE ; reload every call: BIOS owns nothing + mov edx, 0x534D4150 + mov eax, 0xE820 + mov ebx, [e820_next] ; continuation lives in memory + push ds ; DS survives on the stack, EAX intact + int 0x15 + pop ds ; our DS back, whatever BIOS did + jc .done ; carry = end of list or call failed + cmp eax, 0x534D4150 ; BIOS must echo 'SMAP' in EAX + jne .done + cmp byte [E820_COUNT], E820_MAX ; buffer full: stop, keep what we have + jae .done + inc byte [E820_COUNT] ; entry already written by BIOS at ES:DI + mov [e820_next], ebx ; save continuation across the next call + test ebx, ebx ; EBX=0 ends the list + jnz .loop +.done: + cmp byte [E820_COUNT], 0 + jne .ok + xor ax, ax ; fallback: ES:DI may not have survived + mov es, ax + mov di, E820_BUF + mov eax, 0x00100000 + stosd ; base = 1MB + xor eax, eax + stosd ; base high = 0 + mov eax, 0x07F00000 + stosd ; length = 127MB + xor eax, eax + stosd ; length high = 0 + inc eax + stosd ; type = 1 (usable) + inc byte [E820_COUNT] ; count was 0, now 1 +.ok: + ret + halt: cli .hang: @@ -226,9 +282,9 @@ gdt_ptr: dw gdt_end - gdt - 1 dd gdt -msg_loading db "Nadir: loading kernel...", 0x0D, 0x0A, 0 -msg_entering db "Nadir: entering protected mode", 0x0D, 0x0A, 0 -msg_disk_err db "Nadir: disk read error", 0x0D, 0x0A, 0 +msg_loading db "Nadir: load", 0x0D, 0x0A, 0 +msg_entering db "Nadir: pmode", 0x0D, 0x0A, 0 +msg_disk_err db "Nadir: disk err", 0x0D, 0x0A, 0 boot_drive db 0 spt dw 18 @@ -238,6 +294,8 @@ buf_seg dw 0 ; load buffer segment (starts 0x1000) buf_off dw 0 ; load buffer offset within segment sectors_left dw 0 ; sectors still to load tries db 0 ; retries left for current sector +e820_next dd 0 ; E820 continuation across int 0x15 + ; (image-zeroed; first call starts at 0) times 510-($-$$) db 0 dw 0xAA55