summaryrefslogtreecommitdiff
path: root/src/arch/i386/gdt/gdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/i386/gdt/gdt.c')
-rw-r--r--src/arch/i386/gdt/gdt.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/arch/i386/gdt/gdt.c b/src/arch/i386/gdt/gdt.c
new file mode 100644
index 0000000..3e0647b
--- /dev/null
+++ b/src/arch/i386/gdt/gdt.c
@@ -0,0 +1,119 @@
+#include <arch/generic.h>
+#include <arch/i386/gdt.h>
+#include <kernel/util.h>
+#include <stdint.h>
+
+
+struct gdt_entry {
+ uint64_t limit_low : 16;
+ uint64_t base_low : 24;
+ uint64_t accessed : 1; // set by the processor
+ // CODE | DATA
+ uint64_t rw : 1; // readable? | writeable?
+ uint64_t conforming : 1; // conforming? | expands down?
+ uint64_t code : 1; // 1 | 0
+
+ uint64_t codeordata : 1; // 1 for everything other than TSS and LDT
+ uint64_t ring : 2;
+ uint64_t present : 1; // always 1
+ uint64_t limit_high : 4;
+ uint64_t available : 1; // ???
+ uint64_t long_mode : 1;
+ uint64_t x32 : 1;
+ uint64_t gran : 1; // 1 - 4kb, 0 - 1b
+ uint64_t base_high : 8;
+} __attribute__((packed));
+
+struct tss_entry {
+ uint32_t prev_tss; // unused
+ uint32_t esp0; // kernel mode stack pointer
+ uint32_t ss0; // kernel mode stack segment
+ // total size = 0x68 (?) - 3 * sizeof(uint32_t) = 5c
+ uint8_t _unused[0x5c];
+} __attribute__((packed));
+
+struct lgdt_arg {
+ uint16_t limit;
+ uint32_t base;
+} __attribute__((packed));
+
+static struct gdt_entry GDT[SEG_end];
+static struct tss_entry TSS;
+static struct lgdt_arg lgdt_arg; // probably doesn't need to be global
+
+static void gdt_prepare();
+static void gdt_load();
+
+
+static void gdt_prepare() {
+ GDT[SEG_null].present = 0;
+
+ GDT[SEG_r0data].limit_low = 0xFFFF;
+ GDT[SEG_r0data].limit_high = 0xF;
+ GDT[SEG_r0data].gran = 1; // 4KB * 0xFFFFF = (almost) 4GB
+ GDT[SEG_r0data].base_low = 0;
+ GDT[SEG_r0data].base_high = 0;
+ GDT[SEG_r0data].accessed = 0;
+ GDT[SEG_r0data].rw = 1;
+ GDT[SEG_r0data].conforming = 0;
+ GDT[SEG_r0data].code = 0;
+ GDT[SEG_r0data].codeordata = 1;
+ GDT[SEG_r0data].ring = 0;
+ GDT[SEG_r0data].present = 1;
+ GDT[SEG_r0data].long_mode = 0; // ???
+ GDT[SEG_r0data].available = 1; // ???
+ GDT[SEG_r0data].x32 = 1;
+
+ // copy to r0 code
+ GDT[SEG_r0code] = GDT[SEG_r0data];
+ GDT[SEG_r0code].code = 1;
+
+ GDT[SEG_r3data] = GDT[SEG_r0data];
+ GDT[SEG_r3data].ring = 3;
+ GDT[SEG_r3code] = GDT[SEG_r0code];
+ GDT[SEG_r3code].ring = 3;
+
+ // tss
+ memset(&TSS, 0, sizeof(TSS));
+ TSS.ss0 = SEG_r0data << 3; // kernel data segment
+ TSS.esp0 = (uint32_t) &stack_top;
+
+ GDT[SEG_TSS].limit_low = sizeof(TSS);
+ GDT[SEG_TSS].base_low = (uint32_t) &TSS;
+ GDT[SEG_TSS].accessed = 1; // 1 for TSS
+ GDT[SEG_TSS].rw = 0; // 1 busy / 0 not busy
+ GDT[SEG_TSS].conforming = 0; // 0 for TSS
+ GDT[SEG_TSS].code = 1; // 32bit
+ GDT[SEG_TSS].codeordata = 0; // is a system entry
+ GDT[SEG_TSS].ring = 3;
+ GDT[SEG_TSS].present = 1;
+ GDT[SEG_TSS].limit_high = (sizeof(TSS) >> 16) & 0xf;
+ GDT[SEG_TSS].available = 0; // 0 for TSS
+ GDT[SEG_TSS].long_mode = 0;
+ GDT[SEG_TSS].x32 = 0; // idk
+ GDT[SEG_TSS].gran = 0;
+ GDT[SEG_TSS].base_high = (((uint32_t) &TSS) >> 24) & 0xff;
+}
+
+static void gdt_load() {
+ lgdt_arg.limit = sizeof(GDT) - 1;
+ lgdt_arg.base = (uint32_t) &GDT;
+ asm("lgdt (%0)"
+ : : "r" (&lgdt_arg) : "memory");
+ asm("ltr %%ax"
+ : : "a" (SEG_TSS << 3 | 3) : "memory");
+
+ // update all segment registers
+ gdt_farjump(SEG_r0code << 3);
+ asm("mov %0, %%ds;"
+ "mov %0, %%ss;"
+ "mov %0, %%es;"
+ "mov %0, %%fs;"
+ "mov %0, %%gs;"
+ : : "r" (SEG_r0data << 3) : "memory");
+}
+
+void gdt_init() {
+ gdt_prepare();
+ gdt_load();
+}