mirror of
https://github.com/Plexi09/Nadir-Kernel.git
synced 2026-10-20 15:07:27 +02:00
159 lines
4.6 KiB
NASM
159 lines
4.6 KiB
NASM
; Nadir stage-2 entry: 32-bit protected mode -> 64-bit long mode -> C.
|
|
;
|
|
; Entered via a near jump to 0x10000 with a flat code segment (Ring 0,
|
|
; CPL 0 —still the privilege BIOS gave us; no userspace exists yet).
|
|
; Privileged instructions used: cpuid, mov cr4/cr0, rdmsr/wrmsr, lgdt.
|
|
; Fault behavior: no IDT is installed, so any exception here triple-faults
|
|
; and resets the machine. The only graceful failure is a CPU without long
|
|
; mode, which prints an error straight to VGA and halts.
|
|
;
|
|
; Steps: verify long mode exists, identity-map the first 1GB with 2MB
|
|
; pages, enable PAE + LME + paging, reload a GDT with a 64-bit code
|
|
; segment, zero BSS, copy the BIOS memory map staged by stage 1 into
|
|
; kernel .bss, then call the C entry point kmain (System V ABI).
|
|
; Interrupts stay disabled for the whole sequence.
|
|
;
|
|
; WHY copy after zeroing: the map globals live in .bss, so zeroing first
|
|
; then copying keeps the staged entries. Zeroing after would wipe them;
|
|
; copying before is impossible (BSS addresses are only valid once paging
|
|
; and the zeroed state the C code expects are both in place).
|
|
|
|
bits 32
|
|
section .boot32
|
|
global boot32_entry
|
|
extern kmain
|
|
extern __bss_start
|
|
extern __bss_end
|
|
extern memmap_count
|
|
extern memmap_entries
|
|
|
|
PML4 equ 0x70000
|
|
PDPT equ 0x71000
|
|
PD equ 0x72000
|
|
STACK64_TOP equ 0x90000
|
|
MEMMAP_COUNT_PHYS equ 0x5000 ; dword staged by stage 1
|
|
MEMMAP_BUF_PHYS equ 0x5020 ; 24-byte E820 entries staged by stage 1
|
|
E820_MAX equ 64
|
|
E820_ENTRY_SIZE equ 24
|
|
|
|
boot32_entry:
|
|
; --- CPUID: does this CPU do 64-bit long mode? ---
|
|
mov eax, 0x80000000
|
|
cpuid
|
|
cmp eax, 0x80000001
|
|
jb .no_longmode
|
|
mov eax, 0x80000001
|
|
cpuid
|
|
test edx, 1 << 29 ; LM bit
|
|
jz .no_longmode
|
|
|
|
; --- page tables: PML4 -> PDPT -> PD, 512 x 2MB identity pages ---
|
|
mov edi, PML4
|
|
xor eax, eax
|
|
mov ecx, 0x1000 ; clear 16KB (tables + slack)
|
|
rep stosd
|
|
mov eax, PDPT
|
|
or eax, 0x03 ; present + writable
|
|
mov [PML4], eax
|
|
mov eax, PD
|
|
or eax, 0x03
|
|
mov [PDPT], eax
|
|
mov ecx, 512
|
|
mov edi, PD
|
|
mov eax, 0x83 ; present + writable + 2MB page
|
|
.fill_pd:
|
|
mov [edi], eax
|
|
add eax, 0x200000
|
|
add edi, 8
|
|
loop .fill_pd
|
|
|
|
; --- enable long mode ---
|
|
mov eax, PML4 ; page-walk root (CR3 was 0 until now)
|
|
mov cr3, eax
|
|
mov eax, cr4
|
|
or eax, 1 << 5 ; PAE
|
|
mov cr4, eax
|
|
mov ecx, 0xC0000080 ; IA32_EFER
|
|
rdmsr
|
|
or eax, 1 << 8 ; LME
|
|
wrmsr
|
|
mov eax, cr0
|
|
or eax, 1 << 31 ; PG: paging on, long mode active
|
|
mov cr0, eax
|
|
|
|
lgdt [gdt64_ptr]
|
|
jmp 0x18:long_entry ; far jump loads the 64-bit code segment
|
|
|
|
.no_longmode:
|
|
mov edi, 0xB8000 ; VGA text buffer, line 0
|
|
mov esi, err_nolm
|
|
mov ah, 0x4F ; white on red
|
|
.print:
|
|
lodsb
|
|
test al, al
|
|
jz .die
|
|
stosw
|
|
jmp .print
|
|
.die:
|
|
cli
|
|
.hang:
|
|
hlt
|
|
jmp .hang
|
|
|
|
err_nolm db "ERR: CPU has no 64-bit long mode", 0
|
|
|
|
; Own GDT: the stage-1 GDT is behind us, so bring null/code32/data32 plus
|
|
; the 64-bit code+data segments this stage needs.
|
|
align 8
|
|
gdt64:
|
|
dq 0x0000000000000000
|
|
dq 0x00CF9A000000FFFF ; code32 (kept for completeness)
|
|
dq 0x00CF92000000FFFF ; data32
|
|
dq 0x00209A0000000000 ; code64: L=1, exec/read
|
|
dq 0x0000920000000000 ; data64: read/write
|
|
gdt64_end:
|
|
gdt64_ptr:
|
|
dw gdt64_end - gdt64 - 1
|
|
dd gdt64
|
|
|
|
; ---------------------------------------------------------------------------
|
|
bits 64
|
|
long_entry:
|
|
mov ax, 0x20 ; 64-bit data segment
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
mov ss, ax
|
|
mov rsp, STACK64_TOP
|
|
|
|
mov rdi, __bss_start ; zero BSS (uninitialized C statics)
|
|
mov rcx, __bss_end
|
|
sub rcx, rdi
|
|
xor eax, eax
|
|
rep stosb
|
|
|
|
; --- copy staged E820 map into .bss globals (identity map covers
|
|
; both 0x5000 and .bss, and paging is already on) ---
|
|
; WHY byte + clamp: stage 1 stages a single count byte (64 max fits);
|
|
; the clamp is belt-and-braces against a corrupt staging byte.
|
|
movzx eax, byte [abs MEMMAP_COUNT_PHYS]
|
|
cmp eax, E820_MAX
|
|
jbe .map_count_ok
|
|
mov eax, E820_MAX
|
|
.map_count_ok:
|
|
mov [rel memmap_count], eax
|
|
test eax, eax
|
|
jz .map_done
|
|
mov rsi, MEMMAP_BUF_PHYS
|
|
lea rdi, [rel memmap_entries]
|
|
mov ecx, eax
|
|
imul ecx, ecx, E820_ENTRY_SIZE ; bytes = count * 24
|
|
rep movsb
|
|
.map_done:
|
|
|
|
call kmain ; first C code — does not return
|
|
cli
|
|
.hang:
|
|
hlt
|
|
jmp .hang
|