Page 1 of 1

Creating the GDT causes crash

Posted: Tue Oct 22, 2024 12:29 am
by BenjaminMickler
Hi, I have been putting together a very simple 64 bit UEFI kernel to learn about OS dev. I have tried to set up the GDT but it crashes at the lgdt instruction (commenting it out is enough to prevent it from crashing). Can anyone with more experience see anything wrong that I'm missing?

C:

Code: Select all

#include <stdio.h>
#include <stdint.h>

struct gdt_entry_t {
    uint16_t limit_low;
    uint16_t base_low;
    uint8_t base_middle;
    uint8_t access;
    uint8_t granularity;
    uint8_t base_high;
} __attribute__((packed, aligned(8)));


struct gdt_ptr_t {
    uint16_t limit;
    uint64_t base;
}__attribute__((packed));

extern void load_gdt();

struct gdt_entry_t gdt_entries[5];
struct gdt_ptr_t gdt_ptr;
struct gdt_ptr_t* gp;

static void gdt_set_gate(int32_t entry, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {
    gdt_entries[entry].base_low = (base & 0xFFFF);
    gdt_entries[entry].base_middle = (base >> 16) & 0xFF;
    gdt_entries[entry].base_high = (base >> 24) & 0xFF;
    gdt_entries[entry].limit_low = (limit & 0xFFFF);
    gdt_entries[entry].granularity = (limit >> 16) & 0x0F;
    gdt_entries[entry].granularity |= gran & 0xF0;
    gdt_entries[entry].access = access;
}

void gdt_init() {
    gdt_ptr.limit = (sizeof(struct gdt_entry_t)*5) - 1;
    gdt_ptr.base = (uint64_t)&gdt_entries;
    gp = &gdt_ptr;

    gdt_set_gate(0, 0, 0, 0, 0); // Null segment
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Code segment
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Data segment
    gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User mode code segment
    gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User mode data segment

    load_gdt();
}
assembly:

Code: Select all

bits 64

global load_gdt
extern gp

load_gdt:
    CLI
    LGDT  [rel gp]
    PUSH 0x08                 ; Push code segment to stack, 0x08 is a stand-in for your code segment
    LEA RAX, [rel .reload_CS] ; Load address of .reload_CS into RAX
    PUSH RAX                  ; Push this value to the stack
    RETFQ                     ; Perform a far return, RETFQ or LRETQ depending on syntax

.reload_CS:
    ; Reload data segment registers
    MOV   AX, 0x10 ; 0x10 is a stand-in for your data segment
    MOV   DS, AX
    MOV   ES, AX
    MOV   FS, AX
    MOV   GS, AX
    MOV   SS, AX
    RET

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 3:55 am
by MichaelPetch
`gp` is a pointer to a `struct gdt_ptr_t`not the address itself. You'd have to dereference it first. Something like `mov rax, [rel gp]` then `lgdt [rax]` . Another easy way to fix this is to just do `LGDT [rel gdt_ptr]`

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 4:43 am
by BenjaminMickler
Thanks for your reply. That's how I had initially done it and I thought it was wrong. I just changed it as you suggested and qemu still seems to be triple faulting.

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 5:15 am
by MichaelPetch
Probably because you have:

Code: Select all

    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Code segment
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Data segment
    gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User mode code segment
    gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User mode data segment
and you probably wanted:

Code: Select all

    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xAF); //Code segment
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Data segment
    gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xAF); //User mode code segment
    gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User mode data segment
I think the way you had it created 32-bit code segments.

Note: In 64-bit mode there are no limit checks, so the limit doesn't matter. You set it to 0xffffffff which doesn't hurt anything but it could have been easily set to 0 as well.

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 8:32 am
by nullplan
MichaelPetch wrote: Tue Oct 22, 2024 5:15 am Note: In 64-bit mode there are no limit checks, so the limit doesn't matter. You set it to 0xffffffff which doesn't hurt anything but it could have been easily set to 0 as well.
There was a thread a couple of years ago where someone did have issues setting the limits to zero using some kind of hypervisor. You are correct that it should not matter, but it also doesn't cost anything to set the limit to 4G. You have to fill it in anyway, so why not fill it in with something sensible?

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 3:05 pm
by BenjaminMickler
That seems to have fixed the GDT. Thanks for your help, I really should have seen that myself. Now printing to the UEFI console crashes it but I suspect that setting a new GDT probably disables those kinds of UEFI services. I won't be using them anyway, I plan to write my own drivers.

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 3:54 pm
by Octocontrabass
You need to call ExitBootServices() before you load your GDT.

Re: Creating the GDT causes crash

Posted: Tue Oct 22, 2024 4:38 pm
by BenjaminMickler
Yeah, thanks. I'm reading up on that now.