diff options
Diffstat (limited to 'kernel/main/gdt.c')
-rw-r--r-- | kernel/main/gdt.c | 129 |
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; +} |