// // Created by rick on 19-03-21. // #include #include #include #include typedef struct gdt { uint32_t base; uint32_t limit; union { uint8_t access_byte; struct { bool accessed: 1; bool read_write: 1; // for executable, if reading is allowed. For Non-exec if writing is allowed // data sector: direction // grows up(false) or down(true). // executable sector: conforming // if true higher privilege levels can execute this code (e.g. ring 3 can exec ring 0 code) the privilege level it self is not changed. // if false, only the specified ring can execute the code bool direction_conforming: 1; bool executable: 1; bool is_sector: 1; uint8_t privilege: 2; bool present: 1; } att_packed; }; union { uint8_t flags_byte: 4; struct { uint8_t unused: 2; bool size: 1; // false 16bit, true 32bit bool granularity: 1; // false 1 byte, true 4KiB } att_packed; }; } gdt_t; struct tss_ring { uint32_t esp; uint16_t ss; uint16_t: 16; // skipped } att_packed; typedef struct tss { uint16_t link; uint16_t: 16; // skipped struct tss_ring r0; struct tss_ring r1; struct tss_ring r2; struct { uint32_t cr3; uint32_t eip; uint32_t eflags; uint32_t eax; uint32_t ecx; uint32_t edx; uint32_t ebx; uint32_t esp; uint32_t ebp; uint32_t esi; uint32_t edi; uint16_t es; uint16_t: 16; uint16_t cs; uint16_t: 16; uint16_t ss; uint16_t: 16; uint16_t ds; uint16_t: 16; uint16_t fs; uint16_t: 16; uint16_t gs; uint16_t: 16; uint16_t ldtr; uint16_t: 16; } att_packed regs; uint16_t: 16; uint16_t iopb_offset; } att_packed tss_t; _Static_assert(sizeof(tss_t) == 104, "TSS incorrect size"); typedef struct gdtr { uint16_t size; uint32_t offset; } att_packed gdtr_t; #define num_gdts 5 extern void _gdt_switch(); tss_t tss = { .iopb_offset = sizeof(tss_t), .r0.ss = 0x10, }; gdt_t gdts[num_gdts] = { { // 0x08 kernel code .present = true, .base = 0, .limit = 0xFFFFFFFF, .is_sector = true, .size = true, .granularity = true, .executable = true, .read_write = true, .privilege = 0, }, { // 0x10 kernel data .present = true, .base = 0, .limit = 0xFFFFFFFF, .is_sector = true, .size = true, .granularity = true, .executable = false, .read_write = true, }, { // 0x18 user code .present = true, .base = 0, .limit = 0xFFFFFFFF, .privilege = 3, .is_sector = true, .size = true, .granularity = true, .executable = true, .read_write = true, }, { // 0x20 user data .present = true, .base = 0, .limit = 0xFFFFFFFF, .privilege = 3, .is_sector = true, .size = true, .granularity = true, .executable = true, .read_write = true, }, { // 0x28 tss .base = (uint32_t) &tss, .limit = sizeof(tss_t), .present = true, .executable = true, .accessed = true, .size = true, } }; uint64_t gdt_values[num_gdts + 1] = {0}; gdtr_t gdt_ptr = { .size = sizeof(uint64_t) * (num_gdts + 1) - 1, .offset = (uint32_t) &gdt_values, }; _Static_assert(sizeof(gdt_ptr) == 6, "GDT PTR incorrect size"); uint64_t gdt_encode(gdt_t *gdt) { // high limit must be full 4k uint64_t limit = gdt->limit; if (limit > 65536 && (limit & 0xFFF) != 0xFFF && !gdt->granularity) { return 0; } else { limit >>= 12; } uint64_t value = 0 | (limit & 0xFFFF) | ((gdt->base & 0xFFFF) << 16) | ((uint64_t) ((gdt->base >> 16) & 0xFF) << 32) | ((uint64_t) (gdt->access_byte) << 40) | ((limit >> 16) << 48) | ((uint64_t) (gdt->flags_byte) << 52) | ((uint64_t) (gdt->base >> 24) << 56); return value; } void gdt_activate() { gdt_values[0] = 0; // first is zero for (int i = 0; i < num_gdts; ++i) { gdt_values[i + 1] = gdt_encode(&gdts[i]); if (gdt_values[i + 1] == 0) { // todo failed to encode k_panics("Failed to encode GDT\n"); } } _gdt_switch(); } void gdt_init() { // Replace the early gdt with a new one // this one will be extensible and support stuff like TSS tss.r0.esp = 0; // todo gdt_activate(); }