gccx86inline-assemblyosdevgdt

Why does my inline assembly code cause a triple fault?


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:


    Recommendations

    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.