1

I have a subroutine 'switch32' on my bootloader which is supposed to make the transition to 32 bit protected mode, however the lgdt instruction seems to be causing trouble. Here is the code for "switch32.asm":

gdt_start:

gdt_null:
    dq 0x0
gdt_code:
    dw 0xFFFF   ;Lower 16 bits of limit
    dw 0x0000   ;Lower 16 bits of base address
    db 0x00     ;Middle 8 bits of base address
    db 10011010b;Access byte: present=1,privilege=00,type=1,exec=1,comform=0,read=1,acc=0
    db 0100111b ;High 4 bits = flags, Low 4 bits = last nibble of limit (20 bits)
    db 0x00     ;Higher 8 bits of base address (32 bits)

gdt_data:
    dw 0xFFFF   ;Lower 16 bits of limit
    dw 0x0000   ;Lower 16 bits of base address
    db 0x00     ;Middle 8 bits of base address
    db 10010010b;Access byte: present=1,privilege=00,type=1,exec=0,dir=0,write=1,acc=0
    db 0100111b ;High 4 bits = flags, Low 4 bits = last nibble of limit (20 bits)
    db 0x00     ;Higher 8 bits of base address (32 bits)

gdt_end:        

gdtr_data:  ;The data to pass to the GDTR register (lower word=size of GDT, higher dword=address of GDT)
    dw gdt_end - gdt_start - 1 ;16 bit size of GDT
    dd gdt_start               ;Address to start of GDT

CODE_SEGMENT equ gdt_code - gdt_start   ;Index of code segment descriptor
DATA_SEGMENT equ gdt_data - gdt_start   ;Index of data segment descriptor

switch32:
    pusha
    cli ;Disable interrupts
    lgdt [gdtr_data]

    ;Set first bit (protected mode enable) in control register 0
    mov eax,cr0
    or eax,0x1
    mov cr0,eax

    jmp CODE_SEGMENT:post_flush       ; 'fake' far jump to trick CPU into flushing pipeline to prevent nasty exceptions
                                      ; according to intel manual, CS register is implicitly set to far JMP's selector
post_flush:
    [bits 32]
    mov eax,DATA_SEGMENT
    mov ds,eax 
    mov ss,eax 
    mov es,eax
    mov fs,eax
    mov gs,eax

    popa
    ret

I'm using qemu and whenever the subroutine is called, the screen starts blinking and nothing past the lgdt instruction is executed, I'm pretty sure that means there is a CPU error. The error is coming from the lgdt instruction but i'm clueless as to why. Am I passing the right data to the GDTR register? or is my entire GDT set up wrong?

I'm using the intel i386 programmer manual and the osdev wiki article on the Global Descriptor Table as resources and I set up my GDT according to what I've read.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
MottLx
  • 53
  • 1
  • 7
  • You're already using QEMU; attach a debugger to it so you can single-step your code. (Qemu can be a remote for GDB). Or use Bochs; its built-in debugger is designed for debugging problems switching to protected mode, and is aware of segmentation and can parse + dump the GDT for you, I think. The bad old days of having having the computer crash and just have to re-read your code carefully are long gone, except when real hardware / BIOSes are different from VMs / simulators. (e.g. with a BPB) – Peter Cordes Jun 06 '22 at 08:21
  • 2
    32-bit code uses `esp` and not `sp` so you'd want to make sure that the highest bits of `esp` are clear before switching to 32-bit code (e.g. `movzx esp,sp`). Also `pusha` in 16-bit code is probably a synonym for `pushaw` (and pushes 16-bit registers), and `popa` in 32-bit code is probably a synonym for `popad` (popping 32-bit registers); and a `call` in 16-bit code will push a 16-bit return address (like `push ip`) and a `ret` in 32-bit code expects to return to a 32-bit address (like `pop eip`). In other words; your code probably trashes the stack and then tries to return to garbage. – Brendan Jun 06 '22 at 08:35
  • I solved the issue, apparently many things were going wrong at the same time. First, my GDT set the size flag for both the data and code segments to 16 bit so when i was using 32 bit registers later on on my code the CPU lost it's mind. Second, as @Brendan pointed out, my code was indeed trashing the stack. I think there *might* have been a third reason but i'm not sure at all. All i know is that the assembly gods forgave me and my code works fine now somehow. – MottLx Jun 08 '22 at 00:01

0 Answers0