I am having a problem with a bootloader I made. Mostly used code snippets from wiki.osdev.org, and screeck (on Github and Youtube) . The issue is: the bootloader cannot jump farther than 0xFFFFF, and crashes (used QEMU to test, the system restarts infinitely or triple faults). This is a VBR. I don't think the MBR is needed (or affects anything) here. It jumps to a kernel, but i don't think the kernel is an issue either.
It stops with check_exception old: 0x8 new 0xd .
Can be assembled using nasm: nasm -fbin input.asm -o output.bin
For adding a kernel file, i used dd. To make a full binary:
dd if=bootloader.bin >> disk.img (or bin extension)
dd if=kernel.bin >> disk.img
dd if=/dev/zero bs=512 count=32 >> disk.img (add zeros because i am reading 32 sectors)
I used my own kernel, but just for testing i used a simple:
[org 0x100000]
[bits 64]
start:
cli
hlt
times 512 - ($ - $$) db 0
Assembled just like the bootloader.
Ran using qemu -hda disk.img -d int
Here is the bootloader code:
[BITS 16]
[ORG 0x7C00]
CODE_OFFSET equ 0x8
DATA_OFFSET equ 0x10
CR0_PAGING equ 1 << 31
PML4T_ADDR equ 0x1000
SIZEOF_PAGE_TABLE equ 4096
PDPT_ADDR equ 0x2000
PDT_ADDR equ 0x3000
PT_ADDR equ 0x4000
PT_ADDR_MASK equ 0xffffffffff000
PT_PRESENT equ 1
PT_READABLE equ 2
ENTRIES_PER_PT equ 512
SIZEOF_PT_ENTRY equ 8
PAGE_SIZE equ 0x1000
CR4_PAE_ENABLE equ 1 << 5
EFER_MSR equ 0xC0000080
EFER_LM_ENABLE equ 1 << 8
CR0_PM_ENABLE equ 1 << 0
CR0_PG_ENABLE equ 1 << 31
PRESENT equ 1 << 7
NOT_SYS equ 1 << 4
EXEC equ 1 << 3
DC equ 1 << 2
RW equ 1 << 1
ACCESSED equ 1 << 0
GRAN_4K equ 1 << 7
SZ_32 equ 1 << 6
LONG_MODE equ 1 << 5
VGA_TEXT_BUFFER_ADDR equ 0xb8000
COLS equ 80
ROWS equ 25
VGA_TEXT_BUFFER_SIZE equ COLS * ROWS
start:
cli
mov ax, 0x00
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
sti
load_kernel:
mov ah, 0x42
mov si, DAP
mov dl, 0x80
int 0x13
jnc load_protected
hlt
load_protected:
cli
lgdt [gdt_desc]
mov eax, cr0
or al, 1
mov cr0, eax
jmp CODE_OFFSET:protected_main
gdt_start:
dd 0x0
dd 0x0
dw 0xFFFF
dw 0x0000
db 0x00
db 10011010b
db 11001111b
db 0x00
dw 0xFFFF
dw 0x0000
db 0x00
db 10010010b
db 11001111b
db 0x00
gdt_end:
gdt_desc:
dw gdt_end - gdt_start - 1
dd gdt_start
gdt_start_new:
.Null: equ $ - gdt_start_new
dq 0
.Code: equ $ - gdt_start_new
.Code.limit_lo: dw 0xffff
.Code.base_lo: dw 0
.Code.base_mid: db 0
.Code.access: db PRESENT | NOT_SYS | EXEC | RW
.Code.flags: db GRAN_4K | LONG_MODE | 0xF
.Code.base_hi: db 0
.Data: equ $ - gdt_start_new
.Data.limit_lo: dw 0xffff
.Data.base_lo: dw 0
.Data.base_mid: db 0
.Data.access: db PRESENT | NOT_SYS | RW
.Data.Flags: db GRAN_4K | SZ_32 | 0xF
.Data.base_hi: db 0
gdt_end_new:
gdt_desc_new:
dw gdt_end_new - gdt_start_new - 1
dd gdt_start_new
[BITS 32]
protected_main:
mov ax, DATA_OFFSET
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov gs, ax
mov ebp, 0x9C00
mov esp, ebp
in al, 0x92
or al, 2
out 0x92, al
no_paging_32:
mov eax, cr0
and eax, ~CR0_PAGING
mov cr0, eax
clear_tables:
mov edi, PML4T_ADDR
mov cr3, edi
xor eax, eax
mov ecx, SIZEOF_PAGE_TABLE
rep stosd
mov edi, cr3
link_entries:
mov DWORD [edi], PDPT_ADDR & PT_ADDR_MASK | PT_PRESENT | PT_READABLE
mov edi, PDPT_ADDR
mov DWORD [edi], PDT_ADDR & PT_ADDR_MASK | PT_PRESENT | PT_READABLE
mov edi, PDT_ADDR
mov DWORD [edi], PT_ADDR & PT_ADDR_MASK | PT_PRESENT | PT_READABLE
fill_table:
mov edi, PT_ADDR
mov ebx, PT_PRESENT | PT_READABLE
mov ecx, ENTRIES_PER_PT
set_entry:
mov DWORD [edi], ebx
add ebx, PAGE_SIZE
add edi, SIZEOF_PT_ENTRY
loop set_entry
set_pae:
mov eax, cr4
or eax, CR4_PAE_ENABLE
mov cr4, eax
comp_main:
mov ecx, EFER_MSR
rdmsr
or eax, EFER_LM_ENABLE
wrmsr
enable_paging:
mov eax, cr0
or eax, CR0_PG_ENABLE | CR0_PM_ENABLE
mov cr0, eax
load_gdt:
lgdt [gdt_desc_new]
jmp gdt_start_new.Code:long_main
[BITS 64]
long_main:
cli
mov ax, gdt_start_new.Data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov rdi, VGA_TEXT_BUFFER_ADDR + 160
mov rcx, 11
mov rbx, 0
.print:
mov ah, 0x07
mov al, [success + rbx]
stosw
add rbx, 1
dec rcx
cmp rcx, 0
jnz .print
jump_kernel:
jmp 0x100000
DAP:
.size db 0x10
.reserved db 0
.numSectors dw 0x0020
.bufferSeg dd 0x100000
.LBAAddress dq 2
success db "VBR Loaded", 0
times 510 - ($ - $$) db 0
dw 0xAA55
According to OSDev, the 32-bit buffer address in the disk address packet (DAP) is actually a real mode seg:ofs far pointer. So your dd 0x100000 is actually 0x10:0000, which is physical address 0x100, not 0x100000. Thus your kernel is not loaded where you think it is, and you're presumably jumping to some garbage code. (In my test I get CR2 = RAX, which could be consistent with executing add [rax], al whose encoding is zeros.)
You can't load disk data outside the low 1MB of memory with this int 0x13 function. OSDev mentions an extended form that accepts a 64-bit flat address, but says it's not widely supported; I don't know whether QEMU supports it.
The usual solution here would be to load your kernel somewhere in the low 1MB, and either execute it there, or manually copy it to higher memory.