Loading the GDT with LGDT throws a General Protection Fault

1

Ive been developing a Bootloader and when I attempt to load the GDT with the LGDT instruction it throws a General Protection Fault and I am unsure how to deal with it.

bootloader/gdt.asm

gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
gdt_null:
    dq 0


; GDT for code segment. base = 0x00000000, length = 0xfffff
gdt_code: 
    dw 0xffff    ; segment length, bits 0-15
    dw 0x0       ; segment base, bits 0-15
    db 0x0       ; segment base, bits 16-23
    db 10011010b ; flags (8 bits)
    db 11001111b ; flags (4 bits) + segment length, bits 16-19
    db 0x0       ; segment base, bits 24-31

; GDT for data segment. base and length identical to code segment
gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0

gdt_end:

; GDT descriptor
gdt_descriptor:
    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
    dd gdt_start ; address (32 bit)

; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

[bits 16]
switch_to_pm:
    cli 
    lgdt [gdt_descriptor]  ; THIS is where the general protection fault occours 
    mov eax,0xff
    mov [0xa006],eax
    mov eax, cr0
    or eax, 0x1 ; 3. set 32-bit mode bit in cr0
    mov cr0, eax
    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment

; somehow forgot to copy this in
[bits 32]
init_pm: ; we are now using 32-bit instructions
    mov ax, DATA_SEG ; 5. update the segment registers
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov eax,0xee
    mov [0xa000],eax

    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
    mov esp, ebp

    call ready ; 7. Call a well-known label with useful code

bootloader.asm

[BITS 16]
[ORG 0x7c00]
    jmp 0:start

%include "bootloader/gdt.asm"
[BITS 16]

start:
    ; setup video mode
    mov ah,00h
    mov al,13h
    int 10h
    mov dx,0
    call print_dbg_x
    ; LOAD the system
    mov ah,02h
    mov al,20
    mov ch,0
    mov cl,02h
    mov dh,0
    mov dl,0
    mov ebx,PROGRAM
    int 13h
    call print_dbg_x
    ; next setup the gdt
    call print_dbg_x
    cli
    mov ax, 0x2401
    int 0x15 ; enable A20 bit

    call switch_to_pm

load_kern_err:
    mov ah,0Ch
    mov al,0xC
    xor bh,bh
    inc cx
    int 10h
    hlt
print_dbg_x:
    mov ah,0Ch
    mov al,0xF
    xor bh,bh
    inc cx
    int 10h
    ret

[BITS 32]
ready:
    mov eax,0xff
    mov [0xa000],eax

    mov esp, 090000h

    mov ax, DATA_SEG 
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ebp, 0x90000
    mov esp, ebp

    call PROGRAM
    jmp $

PROGRAM equ 0x1000

times 510 - ($ - $$) db 0
dw 0AA55h

switch_to_pm is jumped to by the bootloader when it needs to load the GDT and switch to pmode. Before that it loads the kernel from the FDD and puts it into address 0x1000. I dont load the IDT yet because the kernel in pmode will.

I use nasm and this is compiled into a binary (its an include on my main bootloader).

Bochs reports this before restarting as an exception, possibly the source of the #GPF

(0).[14992474] [0x000000007c40] 0000:0000000000007c40 (unk. ctxt): jmpf 0x0008:7c45 ; ea457c0800

Apparently the CPU gets into protected mode, so its something wrong with the jumpf.

00014992474i[CPU0  ] CPU is in protected mode (active)
00014992474i[CPU0  ] CS.mode = 16 bit
00014992474i[CPU0  ] SS.mode = 16 bit
00014992474i[CPU0  ] EFER   = 0x00000000
00014992474i[CPU0  ] | EAX=60000011  EBX=00000000  ECX=00090004  EDX=00000000
00014992474i[CPU0  ] | ESP=0000ffd4  EBP=00000000  ESI=000e0000  EDI=0000ffac
00014992474i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00014992474i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00014992474i[CPU0  ] |  CS:0000( 0004| 0|  0) 00000000 0000ffff 0 0
00014992474i[CPU0  ] |  DS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  SS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  ES:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  FS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  GS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] | EIP=00007c40 (00007c40)
00014992474i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00014992474i[CPU0  ] | CR3=0x00000000 CR4=0x00000000

This is the code that the bootloader will load from the second sector, kernel is a C external linked with this code on compile time.

kentry.asm

[BITS 32]

global _start
_start:
    mov ebp, 0x90000
    mov esp, ebp
    extern kernel
    call kernel
    ret

global idt_load
extern idtp
idt_load:
    lidt [idtp]
    ret

I put this code all together by compiling the bootloader then compiling the kernel, then I use cat to put the bootloader before the kernel in a binary file.

assembly
x86
bootloader
osdev
gdt
asked on Stack Overflow Aug 11, 2020 by nucke • edited Aug 12, 2020 by nucke

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0