Page 1 of 1

Crash when loading GDT

Posted: Sun Oct 15, 2023 6:23 am
by tommasopeduzzi
Hi!

I am trying to introduce myself to the concept of the GDT. I am using this tutorial (http://www.jamesmolloy.co.uk/tutorial_h ... 20IDT.html) and the wiki as a foundation.
When I start my os though with the GDT, it crashes.

I am not quite sure how to read the QEMU output, but here's the last output I get:

Code: Select all

EAX=00100010 EBX=00102008 ECX=00004000 EDX=00000002
ESI=00000000 EDI=00001000 EBP=00103fc8 ESP=00103f9c
EIP=00100869 EFL=00000012 [----A--] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     c3c99010 0000c483
IDT=     00000000 000003ff
CR0=80000011 CR2=c3c99020 CR3=00021000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=0000000c CCD=00103fa4 CCO=SUBL
EFER=0000000000000000
check_exception old: 0x8 new 0xd
This is the Code I use to initialize the GDT:
GDT.h:

Code: Select all

#include <stdint.h>

struct Access {
    uint8_t accessed : 1;
    uint8_t read_write : 1;
    uint8_t direction_conforming : 1;
    uint8_t executable : 1;
    uint8_t descriptor_type : 1;
    uint8_t privilege_level : 2;
    uint8_t present : 1;
} __attribute__((packed));

struct Flags {
    uint8_t long_mode : 1;
    uint8_t size : 1;
    uint8_t granularity : 1;
} __attribute__((packed));

struct GlobalDescriptor {
    uint16_t limit_low : 16;
    uint16_t base_low : 16;
    uint8_t base_middle : 8;
    Access access;
    uint8_t limit_high : 4;
    uint8_t flags : 4;
    uint8_t base_high : 8;
} __attribute__((packed));

struct DescriptorTablePointer {
    uint16_t size : 16;
    uint32_t offset : 32;
} __attribute__((packed));

extern "C" void flush_gdt(uint32_t gdt);

class GDT
{
public:
    GDT();
private:
    uint32_t* gdt;
    const uint32_t GDT_SIZE = 5; 

    void create_descriptor(uint32_t index, uint32_t base, uint32_t limit, Access access, Flags flags);
    void load_gdt();
};
GDT.cpp:

Code: Select all

#include <stdint.h>
#include "GDT.h"
#include "kmalloc.h"

GDT::GDT()
{
    // kmalloc this in the future
    gdt = (uint32_t*)kmalloc(GDT_SIZE * sizeof(GlobalDescriptor));
    create_descriptor(0, 0, 0, {0}, {0}); // Null descriptor
    
    // Kernel code segment
    create_descriptor(1, 0, 0xFFFFFFFF, Access {
        .accessed = 0,
        .read_write = 1,
        .direction_conforming = 0,
        .executable = 1,
        .descriptor_type = 1,
        .privilege_level = 0,
        .present = 1
    }, Flags {
        .long_mode = 0,
        .size = 1,
        .granularity = 1
    });

    // Kernel data segment
    create_descriptor(2, 0, 0xFFFFFFF, Access {
        .accessed = 0,
        .read_write = 1,
        .direction_conforming = 0,
        .executable = 0,
        .descriptor_type = 1,
        .privilege_level = 0,
        .present = 1
    }, Flags {
        .long_mode = 0,
        .size = 1,
        .granularity = 1
    });

    // User code segment
    create_descriptor(3, 0, 0xFFFFFFFF, Access {
        .accessed = 0,
        .read_write = 1,
        .direction_conforming = 0,
        .executable = 1,
        .descriptor_type = 1,
        .privilege_level = 3,
        .present = 1
    }, Flags {
        .long_mode = 0,
        .size = 1,
        .granularity = 1
    });

    // User data segment
    create_descriptor(4, 0, 0xFFFFFFFF, Access {
        .accessed = 0,
        .read_write = 1,
        .direction_conforming = 0,
        .executable = 0,
        .descriptor_type = 1,
        .privilege_level = 3,
        .present = 1
    }, Flags {
        .long_mode = 0,
        .size = 1,
        .granularity = 1
    });

    load_gdt();
}

void GDT::create_descriptor(uint32_t index, uint32_t base, uint32_t limit, Access access, Flags flags)
{
    GlobalDescriptor descriptor {
        .limit_low = limit & 0xFFFF,
        .base_low = base & 0xFFFF,
        .base_middle = (base >> 16) & 0xFF,
        .access = access,
        .limit_high = (limit >> 16) & 0xF,
        .flags = (flags.granularity << 3) | (flags.size << 2) | (flags.long_mode << 1),
        .base_high = (base >> 24) & 0xFF
    };
    gdt[index*2] = *(uint32_t*)&descriptor & 0xFFFF;
    gdt[index*2 + 1] = (*(uint32_t*)&descriptor >> 32) & 0xFFFF;
}

void GDT::load_gdt()
{
    DescriptorTablePointer gdt_pointer {
        .size = GDT_SIZE * sizeof(GlobalDescriptor) - 1,
        .offset = (uint32_t)gdt
    };
    flush_gdt((uint32_t)&gdt_pointer);
}
flush_gdt.s:

Code: Select all

.text
.globl flush_gdt
.type flush_gdt, @function
flush_gdt:
    mov (%esp, 4), %ax // Get the pointer to the GDT, passed as a parameter.
    lgdt (%eax) // Load the new GDT pointer

    movw $0x10, %ax // 0x10 is the offset in the GDT to our data segment
    movw %ax, %ds   // Load all data segment selectors
    movw %ax, %es
    movw %ax, %fs
    movw %ax, %gs
    movw %ax, %ss
    jmp $0x08, $flush  // 0x08 is the offset to our code segment: Far jump!
flush:
    ret
If I return before the lgdt instruction it runs fine. So I assume there's a mistake in creating the gdt.

I am a real beginner in OSDev, so please excuse me if this is a stupid question. Happy to provide more information if needed.

Thanks in advance!

Re: Crash when loading GDT

Posted: Sun Oct 15, 2023 6:36 am
by quirck
In QEMU output,

Code: Select all

GDT=     c3c99010 0000c483
the first number is the address, and the second number is the limit. As the limit should be 8 * GDT_SIZE - 1, it is loaded with incorrect value.
tommasopeduzzi wrote:

Code: Select all

flush_gdt:
    mov (%esp, 4), %ax // Get the pointer to the GDT, passed as a parameter.
I think it should be

Code: Select all

    mov 4(%esp), %eax

Code: Select all

    gdt[index*2] = *(uint32_t*)&descriptor & 0xFFFF;
    gdt[index*2 + 1] = (*(uint32_t*)&descriptor >> 32) & 0xFFFF;
This is also suspicious. Why anding with 0xFFFF? What exactly is shifted by 32? Does it even have that many bits?
Maybe, just declare the GlobalDescriptor array instead of uint32_t* gdt?

Re: Crash when loading GDT

Posted: Sun Oct 15, 2023 9:54 am
by tommasopeduzzi
Thanks! The problem was in fact how I loaded the entries into the table. I have now fixed it. I really appreciate your help!