Creating the GDT causes crash

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
BenjaminMickler
Posts: 4
Joined: Tue Oct 22, 2024 12:22 am

Creating the GDT causes crash

Post 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
MichaelPetch
Member
Member
Posts: 774
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Creating the GDT causes crash

Post 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]`
BenjaminMickler
Posts: 4
Joined: Tue Oct 22, 2024 12:22 am

Re: Creating the GDT causes crash

Post 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.
MichaelPetch
Member
Member
Posts: 774
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Creating the GDT causes crash

Post 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.
nullplan
Member
Member
Posts: 1760
Joined: Wed Aug 30, 2017 8:24 am

Re: Creating the GDT causes crash

Post 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?
Carpe diem!
BenjaminMickler
Posts: 4
Joined: Tue Oct 22, 2024 12:22 am

Re: Creating the GDT causes crash

Post 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.
Octocontrabass
Member
Member
Posts: 5501
Joined: Mon Mar 25, 2013 7:01 pm

Re: Creating the GDT causes crash

Post by Octocontrabass »

You need to call ExitBootServices() before you load your GDT.
BenjaminMickler
Posts: 4
Joined: Tue Oct 22, 2024 12:22 am

Re: Creating the GDT causes crash

Post by BenjaminMickler »

Yeah, thanks. I'm reading up on that now.
Post Reply