aboutsummaryrefslogtreecommitdiff
path: root/kernel/main/gdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/main/gdt.c')
-rw-r--r--kernel/main/gdt.c129
1 files changed, 129 insertions, 0 deletions
diff --git a/kernel/main/gdt.c b/kernel/main/gdt.c
new file mode 100644
index 0000000..9bc8282
--- /dev/null
+++ b/kernel/main/gdt.c
@@ -0,0 +1,129 @@
+#include "main/gdt.h"
+#include "globals.h"
+
+#include "util/debug.h"
+#include "util/printf.h"
+#include "util/string.h"
+
+typedef struct gdt_entry
+{
+ uint16_t ge_limitlo;
+ uint16_t ge_baselo;
+ uint8_t ge_basemid;
+ uint8_t ge_access;
+ uint8_t ge_flags;
+ uint8_t ge_basehi;
+} packed gdt_entry_t;
+
+static gdt_entry_t gdt[GDT_COUNT] CORE_SPECIFIC_DATA;
+
+typedef struct tss_entry
+{
+ uint32_t ts_reserved1;
+ uint64_t ts_rsp0;
+ uint64_t ts_rsp1;
+ uint64_t ts_rsp2;
+ uint64_t ts_reserved2;
+ uint64_t ts_ist1;
+ uint64_t ts_ist2;
+ uint64_t ts_ist3;
+ uint64_t ts_ist4;
+ uint64_t ts_ist5;
+ uint64_t ts_ist6;
+ uint64_t ts_ist7;
+ uint64_t ts_reserved3;
+ uint16_t ts_iopb;
+ uint16_t ts_reserved4;
+} packed tss_entry_t;
+
+typedef struct gdt_location
+{
+ uint16_t gl_size;
+ uint64_t gl_offset;
+} packed gdt_location_t;
+
+static gdt_location_t gdtl = {.gl_size = GDT_COUNT * sizeof(gdt_entry_t),
+ .gl_offset = (uint64_t)&gdt};
+
+static tss_entry_t tss CORE_SPECIFIC_DATA;
+
+void gdt_init(void)
+{
+ memset(gdt, 0, sizeof(gdt));
+ gdt_set_entry(GDT_KERNEL_TEXT, 0x0, 0xFFFFF, 0, 1, 0, 1);
+ gdt_set_entry(GDT_KERNEL_DATA, 0x0, 0xFFFFF, 0, 0, 0, 1);
+ gdt_set_entry(GDT_USER_TEXT, 0x0, 0xFFFFF, 3, 1, 0, 1);
+ gdt_set_entry(GDT_USER_DATA, 0x0, 0xFFFFF, 3, 0, 0, 1);
+
+ uintptr_t tss_pointer = (uintptr_t)&tss;
+ gdt_set_entry(GDT_TSS, (uint32_t)tss_pointer, sizeof(tss), 0, 1, 0, 0);
+ gdt[GDT_TSS / 8].ge_access &= ~(0b10000);
+ gdt[GDT_TSS / 8].ge_access |= 0b1;
+ gdt[GDT_TSS / 8].ge_flags &= ~(0b10000000);
+
+ uint64_t tss_higher_half = ((uint64_t)tss_pointer) >> 32;
+ memcpy(&gdt[GDT_TSS / 8 + 1], &tss_higher_half, 8);
+
+ memset(&tss, 0, sizeof(tss));
+ tss.ts_iopb = sizeof(tss);
+
+ gdt_location_t *data = &gdtl;
+ int segment = GDT_TSS;
+
+ dbg(DBG_CORE, "Installing GDT and TR\n");
+ __asm__ volatile("lgdt (%0); ltr %1" ::"p"(data), "m"(segment));
+}
+
+void gdt_set_kernel_stack(void *addr) { tss.ts_rsp0 = (uint64_t)addr; }
+
+void gdt_set_entry(uint32_t segment, uint32_t base, uint32_t limit,
+ uint8_t ring, int exec, int dir, int rw)
+{
+ KASSERT(segment < GDT_COUNT * 8 && 0 == segment % 8);
+ KASSERT(ring <= 3);
+ KASSERT(limit <= 0xFFFFF);
+
+ int index = segment / 8;
+ gdt[index].ge_limitlo = (uint16_t)limit;
+ gdt[index].ge_baselo = (uint16_t)base;
+ gdt[index].ge_basemid = (uint8_t)(base >> 16);
+ gdt[index].ge_basehi = (uint8_t)(base >> 24);
+
+ // For x86-64, set the L bit to indicate a 64-bit descriptor and clear Sz
+ // Having both L and Sz set is reserved for future use
+ gdt[index].ge_flags = (uint8_t)(0b10100000 | (limit >> 16));
+
+ gdt[index].ge_access = 0b10000000;
+ gdt[index].ge_access |= (ring << 5);
+ gdt[index].ge_access |= 0b10000;
+ if (exec)
+ {
+ gdt[index].ge_access |= 0b1000;
+ }
+ if (dir)
+ {
+ gdt[index].ge_access |= 0b100;
+ }
+ if (rw)
+ {
+ gdt[index].ge_access |= 0b10;
+ }
+}
+
+void gdt_clear(uint32_t segment)
+{
+ KASSERT(segment < GDT_COUNT * 8 && 0 == segment % 8);
+ memset(&gdt[segment / 8], 0, sizeof(gdt[segment / 8]));
+}
+
+size_t gdt_tss_info(const void *arg, char *buf, size_t osize)
+{
+ size_t size = osize;
+
+ KASSERT(NULL == arg);
+
+ iprintf(&buf, &size, "TSS:\n");
+ iprintf(&buf, &size, "kstack: 0x%p\n", (void *)tss.ts_rsp0);
+
+ return size;
+}