Issue
I compile my code using GCC with the -masm=intel
option. My kernel is loaded by a Multiboot loader like GRUB.
I want to load the address of my GDT and then I reload all segment registers but this causes a triple fault (virtual machine restarts). This code works if I use it in native assembly (in a .asm file).
gdt.c:
#include "gdt.h"
GDT gdtp;
uint64_t gdt[GDT_ENTRIES];
void set_gdt_entry(int x, unsigned int base, unsigned int limit, int flags) {
gdt[x] = limit & 0xffffLL;
gdt[x] |= (base & 0xffffffLL) << 16;
gdt[x] |= ((flags >> 4) & 0xffLL) << 40;
gdt[x] |= ((limit >> 16) & 0xfLL) << 48;
gdt[x] |= (flags & 0xfLL) << 52;
gdt[x] |= ((base >> 24) & 0xffLL) << 56;
}
void gdt_init() {
gdtp.limit = GDT_ENTRIES * 8 - 1;
gdtp.pointer = gdt;
set_gdt_entry(0, 0, 0, 0);
set_gdt_entry(1, 0, 0xFFFFFFFF, GDT_SIZE | GDT_EXECUTABLE | GDT_SEGMENT | GDT_PRESENT);
set_gdt_entry(2, 0, 0xFFFFFFFF, GDT_SIZE | GDT_SEGMENT | GDT_PRESENT);
set_gdt_entry(3, 0, 0xFFFFFFFF, GDT_SIZE | GDT_EXECUTABLE | GDT_SEGMENT | GDT_PRESENT | GDT_RING3);
set_gdt_entry(4, 0, 0xFFFFFFFF, GDT_SIZE | GDT_SEGMENT | GDT_PRESENT | GDT_RING3);
asm volatile(
"lgdt %0\n"
"mov eax, 0x10\n"
"mov ss, eax\n"
"mov es, eax\n"
"mov ds, eax\n"
"mov gs, eax\n"
"mov fs, eax\n"
"jmp 0x08:1f\n"
"1:\n"
: : "m" (gdtp) : "eax"
);
}
This is my gdt.h:
#include <stdint.h>
#define GDT_ENTRIES 7
typedef enum {
GDT_AVAILABLE = 0x1,
GDT_LONG_MODE = 0x2,
GDT_SIZE = 0x3,
GDT_GRANULARITY = 0x8,
GDT_ACCESSED = 0x010,
GDT_READ_WRITE = 0x020,
GDT_CONFORMING = 0x040,
GDT_EXECUTABLE = 0x080,
GDT_SEGMENT = 0x100,
GDT_RING1 = 0x200,
GDT_RING2 = 0x400,
GDT_RING3 = 0x600,
GDT_PRESENT = 0x800
} GDT_FLAGS;
typedef struct {
uint16_t limit;
void *pointer;
}__attribute__((packed)) GDT;
void set_gdt_entry(int, unsigned int, unsigned int, int);
void gdt_init();
What can I do to get it work?
Solution
The problem isn't in the inline assembly code, however there are the things I see wrong in the code snippets you added to the question:
This
GDT_FLAGS
entry:GDT_SIZE = 0x3
Should be:
GDT_SIZE = 0x4
You are using a Multiboot loader and you will be accessing memory above 0x100000. Your GDT entries do not have the
GDT_GRANULARITY
bit set so you are constrained to the lower 1MiB of memory. As well you haven't marked any of your descriptors with theGDT_READ_WRITE
bit. The GDT initialization should be:void gdt_init() { gdtp.limit = GDT_ENTRIES * 8 - 1; gdtp.pointer = gdt; set_gdt_entry(0, 0, 0, 0); set_gdt_entry(1, 0, 0xFFFFFFFF, GDT_GRANULARITY | GDT_READ_WRITE \ | GDT_SIZE | GDT_EXECUTABLE | GDT_SEGMENT | GDT_PRESENT); set_gdt_entry(2, 0, 0xFFFFFFFF, GDT_GRANULARITY | GDT_READ_WRITE \ | GDT_SIZE | GDT_SEGMENT | GDT_PRESENT); set_gdt_entry(3, 0, 0xFFFFFFFF, GDT_GRANULARITY | GDT_READ_WRITE \ | GDT_SIZE | GDT_EXECUTABLE | GDT_SEGMENT | GDT_PRESENT | GDT_RING3); set_gdt_entry(4, 0, 0xFFFFFFFF, GDT_GRANULARITY | GDT_READ_WRITE \ | GDT_SIZE | GDT_SEGMENT | GDT_PRESENT | GDT_RING3); asm volatile( "lgdt %0\n" "mov eax, 0x10\n" "mov ss, eax\n" "mov es, eax\n" "mov ds, eax\n" "mov gs, eax\n" "mov fs, eax\n" "jmp 0x08:1f\n" "1:\n" : : "m" (gdtp) : "eax", "memory" ); }
Recommendations
When debugging GDT code and interrupts early on in OS development, I find it useful to use the BOCHS emulator. It will dump processor state information when a problem occurs (ie. triple fault), and has
info gdt
andinfo idt
commands that will dump these tables to the console. To use BOCHS for OS development you can generate an ISO image and boot as a CD-ROM.You correctly coded your
set_gdt_entry
in such a way that only the lower 20 bits are used and the upper 12 bits are thrown away. To make things more readable, I recommend specifying the limit with a value between 0x00000 and 0xFFFFF (inclusive). When usingGDT_GRANULARITY
the limit value is shifted left 12 bits by the CPU and the lower 12 bits are set to 0xFFF. WhenGDT_GRANULARITY
is set the limit is treated as 4KiB pages rather than bytes.
When GDT_GRANULARITY
isn't set it the limit value is simply a 20-bit value between 0x00000 and 0xFFFFF specifying the limit as bytes rather than 4KiB pages.
Answered By - Michael Petch Answer Checked By - Terry (WPSolving Volunteer)