Page 1 of 1

(Another Post) Problems doing far jump after switch to PM

Posted: Tue Mar 12, 2019 6:48 am
by ZeroDependency
Hello,

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
The above code is to force the VM to be at a specific point I want. The code in this state doesn't reset Qemu. And I can do "info registers" to view the various registers (see below)

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
I can see the value 0x5574 is loaded into EAX. The EIP value seems sane, as does the value in GDT.

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?

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:18 am
by MichaelPetch
I see nothing wrong in this code (whether you use `jmp hang` or not). When it doesn't work are you sure the code you are assembling is what you are showing here?

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:20 am
by ZeroDependency
Amusingly I went away for a meeting with a client, came back, had an idea, and now it seems to work.

Immediately prior to my jump I now do this:

Code: Select all

    mov eax, 0
    mov ds, eax
    jmp 0x0008:flush
Setting the DS register to 0 seems to have allowed the jmp to execute the address I actually wanted it to do.

Code: Select all

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
    mov eax, 0x34567890
loop:
    jmp loop
Doing 'info registers' in Qemu now shows me that EAX is equal to 0x34567890 and there is no reboot loop.....

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:27 am
by MichaelPetch
There is no reason to do this:

Code: Select all

mov eax, 0
mov ds, eax
Are you sure if you remove those lines it fails?

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:33 am
by MichaelPetch
Using this exact file does it crash or not?

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
    ; Remove all instruction between turning on PE flag and the JMP
    jmp 0x0008:flush


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
TO assemble and run this in QEMU use:

Code: Select all

nasm -f bin boot.asm -o boot.bin
qemu-system-i386 -fda boot.bin

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:42 am
by ZeroDependency
Nope you're absolutely right, removing the bits I added means it's not crashing.

Building and running the code snippet you provided, results in no crashing....

I'm so confused....

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:43 am
by ZeroDependency
Is it purely because I had something between setting the Protected Mode bit in cr0 and the jump?

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:47 am
by MichaelPetch
If I didn't know any better and I had heard this story the first thing I would have asked is "When it fails, is it failing on real hardware and are you booting with USB Floppy Disk Emulation". I'm going to straight up ask... The failures you are seeing are strictly in QEMU and not on real hardware?

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:49 am
by ZeroDependency
It was crashing in both Qemu and VirtualBox VMs - I don't have an easy way to test on real hardware.

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:50 am
by ZeroDependency
Thank you for your assistance though. It has allowed me to get past this roadblock and can hopefully leave much of this ASM world behind soon and get back into a language I'm much more familiar with (C).

Re: (Another Post) Problems doing far jump after switch to P

Posted: Tue Mar 12, 2019 8:51 am
by MichaelPetch
The idea of using the JMP 0x0008:flush right after turning on the PE bit in CR0 is to ensure that the instruction prefetch queue is cleared (to ensure the instructions following are decoded properly) and that a 32-bit CS selector is set. To be honest the code you did have shouldn't have caused a problem. Maybe you weren't testing the code you thought you were in QEMU? (wrote one set of code, ran another by accident)