Each `LOG_X` call declared a rather huge variable on the stack, and using it to dump a whole structure often led to a page fault. Now the temporary variables are `static`, which is a bit wasteful on the kernel binary size, but easier to maintain. Additionally bootloader updated to load kernels bigger than 64K.
347 lines
6.7 KiB
NASM
347 lines
6.7 KiB
NASM
bits 16
|
|
org 0x8000
|
|
|
|
xor ax, ax
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov ss, ax
|
|
mov sp, 0x7000
|
|
|
|
FIRST_PARTITION_SECTOR equ 2048 ; TODO Read MBR.
|
|
STAGE_BUFFER_SEGMENT equ 0x0900
|
|
STAGE_BUFFER_OFFSET equ 0x0000
|
|
|
|
jmp prepare_kernel
|
|
|
|
dap:
|
|
.dap_size: db 16
|
|
.reserved: db 0
|
|
.src_sectors: dw 1
|
|
.dst_offset: dw 0
|
|
.dst_segment: dw 0
|
|
.src_lba: dq 0
|
|
dap_end:
|
|
|
|
bpb:
|
|
.bytes_per_sector: dw 0
|
|
.sectors_per_cluster: db 0
|
|
.reserved_sectors: dw 0
|
|
.fats_count: db 0
|
|
.root_entry_count: dw 0
|
|
.total_sectors_16: dw 0
|
|
.media_type: db 0
|
|
.sectors_per_fat: dw 0
|
|
bpb_end:
|
|
|
|
fat_start: dw 0
|
|
root_start: dw 0
|
|
root_size: dw 0
|
|
data_start: dw 0
|
|
|
|
kernel_filename: db 'KERNEL BIN'
|
|
kernel_cluster: dw 0
|
|
kernel_buffer_segment: dw 0x2000
|
|
kernel_buffer_offset: dw 0
|
|
|
|
read_sectors:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
mov [dap.src_lba], eax
|
|
mov [dap.src_sectors], cx
|
|
mov [dap.dst_segment], es
|
|
mov [dap.dst_offset], di
|
|
mov ah, 0x42
|
|
mov dl, 0x80
|
|
mov si, dap
|
|
int 0x13
|
|
jc disk_error
|
|
ret
|
|
|
|
disk_error:
|
|
mov ah, 0x0E
|
|
mov al, 'E'
|
|
int 0x10
|
|
jmp $
|
|
|
|
prepare_kernel:
|
|
load_bpb:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
mov eax, FIRST_PARTITION_SECTOR
|
|
mov cx, 1
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov es, bx
|
|
mov di, STAGE_BUFFER_OFFSET
|
|
call read_sectors
|
|
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov ds, bx
|
|
|
|
mov si, STAGE_BUFFER_OFFSET + 11
|
|
xor bx, bx
|
|
mov es, bx
|
|
mov di, bpb
|
|
mov cx, bpb_end - bpb
|
|
rep movsb
|
|
|
|
find_data_start:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
mov ax, FIRST_PARTITION_SECTOR
|
|
add ax, [bpb.reserved_sectors]
|
|
mov [fat_start], ax
|
|
|
|
xor ax, ax
|
|
mov al, [bpb.fats_count]
|
|
mul word [bpb.sectors_per_fat]
|
|
add ax, [fat_start]
|
|
mov [root_start], ax ; dx should be zero for sane FAT16 values
|
|
|
|
mov ax, [bpb.root_entry_count]
|
|
shl ax, 5 ; * 32
|
|
shr ax, 9 ; / 512 ; there should be at least 512 entries, so safe
|
|
mov [root_size], ax
|
|
|
|
mov ax, [root_start]
|
|
add ax, [root_size]
|
|
mov [data_start], ax
|
|
|
|
find_kernel:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
xor eax, eax
|
|
mov ax, [root_start]
|
|
mov cx, [root_size]
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov es, bx
|
|
mov di, STAGE_BUFFER_OFFSET
|
|
call read_sectors
|
|
|
|
mov cx, [bpb.root_entry_count]
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov ds, bx
|
|
mov si, STAGE_BUFFER_OFFSET
|
|
|
|
.check_entry:
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov ds, bx
|
|
|
|
cmp byte [si], 0x00
|
|
je .no_more_entries
|
|
cmp byte [si], 0xE5
|
|
je .continue
|
|
|
|
push si
|
|
push cx
|
|
xor bx, bx
|
|
mov es, bx
|
|
mov di, kernel_filename
|
|
mov cx, 11
|
|
rep cmpsb
|
|
pop cx
|
|
pop si
|
|
je .found
|
|
|
|
.continue:
|
|
add si, 32
|
|
loop .check_entry
|
|
|
|
.no_more_entries:
|
|
mov ah, 0x0E
|
|
mov al, 'K'
|
|
int 0x10
|
|
jmp $
|
|
|
|
.found:
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov ds, bx
|
|
|
|
mov ax, [si + 26]
|
|
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
mov [kernel_cluster], ax
|
|
|
|
load_fat:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
xor eax, eax
|
|
mov ax, [fat_start]
|
|
mov cx, [bpb.sectors_per_fat]
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov es, bx
|
|
mov di, STAGE_BUFFER_OFFSET
|
|
call read_sectors
|
|
|
|
load_kernel_cluster:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
xor eax, eax
|
|
mov ax, [kernel_cluster]
|
|
sub ax, 2
|
|
xor bx, bx
|
|
mov bl, [bpb.sectors_per_cluster]
|
|
mul bx
|
|
add ax, [data_start]
|
|
xor cx, cx
|
|
mov cl, [bpb.sectors_per_cluster]
|
|
mov bx, [kernel_buffer_segment]
|
|
mov es, bx
|
|
mov di, [kernel_buffer_offset]
|
|
call read_sectors
|
|
|
|
mov ax, [bpb.sectors_per_cluster]
|
|
shl ax, 9 ; * 512
|
|
add ax, [kernel_buffer_offset]
|
|
mov [kernel_buffer_offset], ax
|
|
jnc find_next_kernel_cluster
|
|
add word [kernel_buffer_segment], 0x1000
|
|
|
|
find_next_kernel_cluster:
|
|
xor bx, bx
|
|
mov ds, bx
|
|
|
|
mov si, [kernel_cluster]
|
|
shl si, 1 ; * 2
|
|
add si, STAGE_BUFFER_OFFSET
|
|
mov bx, STAGE_BUFFER_SEGMENT
|
|
mov ds, bx
|
|
mov ax, [si]
|
|
xor bx, bx
|
|
mov ds, bx
|
|
mov [kernel_cluster], ax
|
|
cmp ax, 0xFFF8
|
|
jb load_kernel_cluster
|
|
|
|
; ------------------------------------------------------------------------------
|
|
|
|
jmp load_gdt
|
|
|
|
gdt_start:
|
|
gdt_0: dq 0x0000000000000000
|
|
gdt_32_code:
|
|
.limit_low: dw 0xFFFF
|
|
.base_low: dw 0x0000
|
|
.base_middle: db 0x00
|
|
.access: db 10011010b ; present, ring 0, code, executable, readable
|
|
.flags: db 11001111b ; 32-bit, 4KB granularity
|
|
.base_high: db 0x00
|
|
gdt_32_code_end:
|
|
gdt_64_code:
|
|
.limit_low: dw 0xFFFF
|
|
.base_low: dw 0x0000
|
|
.base_middle: db 0x00
|
|
.access: db 10011010b ; present, ring 0, code, executable, readable
|
|
.flags: db 10101111b ; long mode (L bit)
|
|
.base_high: db 0x00
|
|
gdt_64_code_end:
|
|
gdt_data:
|
|
.limit_low: dw 0xFFFF
|
|
.base_low: dw 0x0000
|
|
.base_middle: db 0x00
|
|
.access: db 10010010b ; present, ring 0, data, writable
|
|
.flags: db 10000000b
|
|
.base_high: db 0x00
|
|
gdt_data_end:
|
|
gdt_end:
|
|
|
|
gdt_descriptor:
|
|
.size: dw gdt_end - gdt_start - 1
|
|
.offset: dd gdt_start
|
|
gdt_descriptor_end:
|
|
|
|
load_gdt:
|
|
cli
|
|
|
|
lgdt [gdt_descriptor]
|
|
|
|
mov eax, cr0
|
|
or eax, 0x00000001
|
|
mov cr0, eax
|
|
|
|
jmp 0x0008:protected_mode
|
|
|
|
; ------------------------------------------------------------------------------
|
|
|
|
bits 32
|
|
protected_mode:
|
|
mov ax, 0x0018 ; data segment is now third entry = 0x18
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov ss, ax
|
|
mov esp, 0x00090000
|
|
|
|
; zero page table memory
|
|
mov edi, 0x00010000
|
|
mov ecx, 256 * 5
|
|
xor eax, eax
|
|
rep stosd
|
|
|
|
; PML4[0] -> PDPT at 0x00011000
|
|
mov dword [0x00010000], 0x00011003
|
|
; PML4[511] -> PDPT at 0x00012000
|
|
mov dword [0x00010FF8], 0x00012003
|
|
; PML4[0].PDPT[0] -> PD at 0x00013000
|
|
mov dword [0x00011000], 0x00013003
|
|
; PML4[511].PDPT[510] -> PD at 0x00014000
|
|
mov dword [0x00012FF0], 0x00014003
|
|
; PD: fill 64 entries, each mapping 2MB
|
|
mov edi, 0x00013000
|
|
mov eax, 0x00000083 ; present, writable, huge page, base 0
|
|
mov ecx, 64
|
|
|
|
.fill_pd:
|
|
mov ebx, edi
|
|
add ebx, 0x1000
|
|
mov dword [edi], eax
|
|
mov dword [ebx], eax
|
|
add edi, 8
|
|
add eax, 0x00200000 ; next 2MB
|
|
loop .fill_pd
|
|
|
|
; load PML4 into CR3
|
|
mov eax, 0x00010000
|
|
mov cr3, eax
|
|
|
|
; enable PAE
|
|
mov eax, cr4
|
|
or eax, 0x00000020
|
|
mov cr4, eax
|
|
|
|
; enable long mode in EFER MSR
|
|
mov ecx, 0xC0000080
|
|
rdmsr
|
|
or eax, 0x00000100
|
|
wrmsr
|
|
|
|
; enable paging (and keep protection enabled)
|
|
mov eax, cr0
|
|
or eax, 0x80000000
|
|
mov cr0, eax
|
|
|
|
jmp 0x0010:long_mode ; 0x10 = second entry = 64-bit code segment
|
|
|
|
; ------------------------------------------------------------------------------
|
|
|
|
bits 64
|
|
long_mode:
|
|
mov ax, 0x0018 ; data segment
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov ss, ax
|
|
mov rsp, 0xFFFFFFFF80F00000
|
|
|
|
xor rax, rax
|
|
xor rbx, rbx
|
|
xor rcx, rcx
|
|
xor rdx, rdx
|
|
|
|
jmp 0xFFFFFFFF80020000
|