Crash when loading GDT
Posted: Sun Oct 15, 2023 6:23 am
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:
This is the Code I use to initialize the GDT:
GDT.h:
GDT.cpp:
flush_gdt.s:
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!
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
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();
};
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);
}
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
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!