Working on my bootloader and all seems to be going along nicely, I had it printing characters to the screen and what not. Now comes the time to switch to 32-bit Protected Mode.
From what I can tell, I seem to load the GDT correctly and set the Protected Mode flag in cr0. However, as soon as I attempt to do the far jump afterwards in order to flush the 16-bit commands in the pipeline I have issues. Qemu sits in a reboot loop and VirtualBox crashes out. Presumably, I'm not jumping to the location I expect to be jumping to either due to a misunderstanding of how CS/DS is used in a far jump, or my GDT table is incorrect. As a result, meaningless instructions are being executed and causing an exception of some variety causing the VM to crash.
Below is the full copy of my bootloader code, stripped down to the minimum code required to reproduce the issue. Presumably, I'm doing something dumb and should be a simple fix.
Code: Select all
BITS 16
ORG 0x7C00
entry:
xor ax, ax
mov ds, ax
mov ss, ax
mov sp, 0x7C00
; Enable A20 Gate (TODO: Check if A20 is already enabled)
enable_a20:
in al, 0x92
or al, 2
out 0x92, al
begin_protected_mode_switch:
cli
load_gdt:
lgdt [gdt_desc]
enable_protected_mode:
; Switch to 32-bit Protected Mode
mov eax, cr0
or eax, 1
mov cr0, eax
mov ax, 0x5574
; Temporary to examine registers in Qemu
jmp hang
jmp 0x0008:flush
hang:
jmp hang
BITS 32
flush:
; Data Selectors set to the gdt_data offset (16 bytes into GDT)
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; TODO: Switch to C code from here
loop:
jmp loop
; GDT Table
gdt:
; Null Descriptor
gdt_null:
dq 0
; Code GDT Entry
gdt_code:
dw 0xFFFF
dw 0
db 0
db 0x9A
db 11001111b
db 0
; Data GDT Entry
gdt_data:
dw 0xFFFF
dw 0
db 0
db 0x92
db 11001111b
db 0
gdt_end:
; GDT Descriptor
gdt_desc:
dw gdt_end - gdt - 1
dd gdt
; Magic Bytes to Make Bootable
times 510-($-$$) db 0
db 0x55
db 0xAA
Code: Select all
mov ax, 0x5574
; Temporary to examine registers in Qemu
jmp hang
Code: Select all
EAX=00005574 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00007c00
EIP=00007c29 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
CS =0000 00000000 0000ffff 00009b00 DPL=0 CS16 [-RA]
SS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
DS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
FS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
GS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c3b 00000017
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
However, if I remove the "jmp hang" and allow it to execute the far jump, Qemu then gets stuck in it's reboot loop and doing "info registers" provides meaningless data depending on where I manage to execute it during the reboot.
I have attempted to use GDB to step through the instructions but GDB seems to refuse to load my executable and therefore doesn't play nicely for me.
Can anyone see anything obvious I've done wrong?