diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 5ecf3ea..0f49e37 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -8,10 +8,20 @@ #define STR_(x) #x #define STR(x) STR_(x) +// TODO: remove +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + /** * GDT Setup * ========= */ +#define GDT_LIMIT_MAX 0xFFFFF + +struct gdt_table_entry { + uint8_t data[8]; +}; + enum gdt_segment_descriptor_index : size_t { GDT_SDESC_BASE_0 = 2, GDT_SDESC_BASE_1 = 3, @@ -33,8 +43,13 @@ enum gdt_access_flag : uint8_t { GDT_ACCESS_DC = (1<<2), GDT_ACCESS_EXEC = (1<<3), GDT_ACCESS_DESCRIPTOR = (1<<4), - GDT_ACCESS_DPL = (1<<5), - GDT_ACCESS_PRESENT = (1<<6), + + GDT_ACCESS_DPL_0 = (0<<5), + GDT_ACCESS_DPL_1 = (1<<5), + GDT_ACCESS_DPL_2 = (2<<5), + GDT_ACCESS_DPL_3 = (3<<5), + + GDT_ACCESS_PRESENT = (1<<7), }; enum gdt_flags : uint8_t { @@ -42,59 +57,170 @@ enum gdt_flags : uint8_t { GDT_LONG = (1<<1), GDT_SIZE = (1<<2), GDT_GRANULARITY = (1<<3), -} - -struct gdt { - uint32_t base; - uint32_t limit; - uint8_t access_byte; - uint8_t flags; }; -static void gdt_encode_entry(uint8_t *target, struct GDT source) +static inline uint16_t gdt_segment_selector(uint16_t index, uint16_t table_indicator, uint16_t priv_level) { -#define GDT_LIMIT_MAX 0xFFFFF - if (source.limit > GDT_LIMIT_MAX) + return (index << 3) + | ((table_indicator << 2) & 0b100) + | ((priv_level << 0) & 0b011); +} + +struct gdt_entry_content { + uint32_t base; + uint32_t limit; + uint8_t access_byte; + uint8_t flags; +}; + +static void gdt_encode_entry(struct gdt_table_entry* target, struct gdt_entry_content content) +{ + if (content.limit > GDT_LIMIT_MAX) panic(str_attach("GDT cannot encode limits larger than " STR(GDT_LIMIT_MAX))); // encode the limit - target[GDT_SDESC_LIMIT_0] = (source.limit >> 0) & 0xFF; - target[GDT_SDESC_LIMIT_1] = (source.limit >> 8) & 0xFF; - target[GDT_SDESC_LIMIT_2] = (source.limit >> 16) & 0xFF; + target->data[GDT_SDESC_LIMIT_0] = (content.limit >> 0) & 0xFF; + target->data[GDT_SDESC_LIMIT_1] = (content.limit >> 8) & 0xFF; + target->data[GDT_SDESC_LIMIT_2] = (content.limit >> 16) & 0xFF; // encode the base - target[GDT_SDESC_BASE_0] = (source.base >> 0) & 0xFF; - target[GDT_SDESC_BASE_1] = (source.base >> 8) & 0xFF; - target[GDT_SDESC_BASE_2] = (source.base >> 16) & 0xFF; - target[GDT_SDESC_BASE_3] = (source.base >> 24) & 0xFF; + target->data[GDT_SDESC_BASE_0] = (content.base >> 0) & 0xFF; + target->data[GDT_SDESC_BASE_1] = (content.base >> 8) & 0xFF; + target->data[GDT_SDESC_BASE_2] = (content.base >> 16) & 0xFF; + target->data[GDT_SDESC_BASE_3] = (content.base >> 24) & 0xFF; // encode the access byte - target[GDT_SDESC_ACCESSBYTE] = source.access_byte; + target->data[GDT_SDESC_ACCESSBYTE] = content.access_byte; // encode the flags - target[GDT_SDESC_FLAGS] |= (source.flags << 4); + target->data[GDT_SDESC_FLAGS] |= (content.flags << 4); } -void gdt_set(uint16_t limit, uint32_t base) +static void gdt_set(uint16_t limit, uint32_t base, uint32_t offset) { + base += offset; + /* the lgdt instruction requires a packed alignment */ struct __attribute__((packed)) gdtr { uint16_t limit; uint32_t base; }; - struct gdtr gdtr = { - .limit = limit, - .base = base, - }; + static struct gdtr gdtr; + gdtr.limit = limit; + gdtr.base = base; __asm__ volatile ( - "lgdt %0" + "lgdt %[gdtr]" : - : "m" (gdtr)); + : [gdtr] "m"(gdtr) + ); } -void gdt_segment_reload() +// only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro +static inline void gdt_reload(uint32_t code, + uint32_t data, + uint32_t extra_segment, + uint32_t general_segment_1, + uint32_t general_segment_2, + uint32_t stack_segment) +{ + __asm__ volatile ( + // Far jump to reload the CS register + "jmp %[cs], $reload_CS\n" + "reload_CS:\n" + // Load data segment into AX and then move it to all segment registers + //"movw %[ds], %%ax\n" + "movw %[ds], %%ds\n" + "movw %[es], %%es\n" + "movw %[fs], %%fs\n" + "movw %[gs], %%gs\n" + "movw %[ss], %%ss\n" + : // No output operands + : [cs] "i"(code), + [ds] "i"(data), + [es] "i"(extra_segment), + [fs] "i"(general_segment_1), + [gs] "i"(general_segment_2), + [ss] "i"(stack_segment) + : "ax" // Clobbered register + ); +} + +// sets a flat model +void test_gdt() +{ + // disable interupts + __asm__ volatile ("cli"); + + static struct gdt_table_entry gdt_table[6]; + size_t i=0; + _Static_assert(sizeof(gdt_table) == 0x30); + + // Null Descriptor + gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){0}); + + // Kernel mode code segment + gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){ + .base = 0, + .limit = GDT_LIMIT_MAX, + .access_byte = GDT_ACCESS_RW + | GDT_ACCESS_EXEC + | GDT_ACCESS_DESCRIPTOR + | GDT_ACCESS_DPL_0 + | GDT_ACCESS_PRESENT, + .flags = GDT_SIZE + | GDT_GRANULARITY}); + + // Kernel mode data segment + gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){ + .base = 0, + .limit = GDT_LIMIT_MAX, + .access_byte = GDT_ACCESS_RW + | GDT_ACCESS_DESCRIPTOR + | GDT_ACCESS_DPL_0 + | GDT_ACCESS_PRESENT, + .flags = GDT_SIZE + | GDT_GRANULARITY}); + + // User mode code segment + gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){ + .base = 0, + .limit = GDT_LIMIT_MAX, + .access_byte = GDT_ACCESS_RW + | GDT_ACCESS_EXEC + | GDT_ACCESS_DESCRIPTOR + | GDT_ACCESS_DPL_3 + | GDT_ACCESS_PRESENT, + .flags = GDT_SIZE + | GDT_GRANULARITY}); + + // User mode data segment + gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){ + .base = 0, + .limit = GDT_LIMIT_MAX, + .access_byte = GDT_ACCESS_RW + | GDT_ACCESS_DESCRIPTOR + | GDT_ACCESS_DPL_3 + | GDT_ACCESS_PRESENT, + .flags = GDT_SIZE + | GDT_GRANULARITY}); + + // Task state segment + gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){ + .base = &tss, + .limit = sizeof(tss)-1, + .access_byte = GDT_ACCESS_ACCESSED + | GDT_ACCESS_EXEC + | GDT_ACCESS_DPL_0 + | GDT_ACCESS_PRESENT, + .flags = 0}); + + gdt_set(0, 0, 0); + const uint16_t code_segment = 0x8; // placeholder + const uint16_t data_segment = 0x10; // placeholder + gdt_reload(code_segment, data_segment); +} /** * Kernel entrypoint @@ -109,3 +235,6 @@ void kernel_main(void) } } + +// TODO: remove +#pragma GCC diagnostic pop