diff options
Diffstat (limited to 'src/gdt.cpp')
-rw-r--r-- | src/gdt.cpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/gdt.cpp b/src/gdt.cpp new file mode 100644 index 0000000..24d974b --- /dev/null +++ b/src/gdt.cpp @@ -0,0 +1,169 @@ +#include <array> +#include <cstdint> + +struct gdt_entry_bits { + std::uint32_t limit_low : 16; + std::uint32_t base_low : 24; + std::uint32_t accessed : 1; + std::uint32_t read_write : 1; // readable for code, writable for data + std::uint32_t conforming_expand_down : 1; // conforming for code, expand down for data + std::uint32_t code : 1; // 1 for code, 0 for data + std::uint32_t code_data_segment : 1; // should be 1 for everything but TSS and LDT + std::uint32_t DPL : 2; // privilege level + std::uint32_t present : 1; + std::uint32_t limit_high : 4; + std::uint32_t available : 1; // only used in software; has no effect on hardware + std::uint32_t long_mode : 1; + std::uint32_t big : 1; // 32-bit opcodes for code, uint32_t stack for data + std::uint32_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing + std::uint32_t base_high : 8; +} __attribute__((packed)); + +struct TSSEntry +{ + std::uint32_t prevTSS; + std::uint32_t esp0; + std::uint32_t ss0; + std::uint32_t unused[23] = {}; +} __attribute__((packed)); + +static TSSEntry tss = { + .prevTSS = 0, + .esp0 = 0, + .ss0 = 0x10 +}; + +static const std::array<gdt_entry_bits, 6> gdt {{ + {}, + /* kernel_code = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 1, + .code_data_segment = 1, + .DPL = 0, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* kernel_data = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 0, + .code_data_segment = 1, + .DPL = 0, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* user_code = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 1, + .code_data_segment = 1, + .DPL = 3, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* user_data = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 0, + .code_data_segment = 1, + .DPL = 3, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* tss = */ { + .limit_low = sizeof(TSSEntry), + .base_low = (std::uint32_t)&tss & 0xFFFFFF, + .accessed = 1, + .read_write = 0, + .conforming_expand_down = 0, + .code = 1, + .code_data_segment = 0, + .DPL = 0, + .present = 1, + .limit_high = 0, + .available = 0, + .long_mode = 0, + .big = 0, + .gran = 0, + .base_high = (std::uint32_t)&tss >> 24 + } +}}; + +void gdt_initialize() +{ + auto gdtr = reinterpret_cast<std::uint64_t>(gdt.data()); + gdtr <<= 16; + gdtr |= gdt.size() * sizeof(gdt[0]); + + asm volatile(R"( + lgdt %0 + pushl $0x8 + push $.setcs + ljmp *(%%esp) + .setcs: + add $8, %%esp + mov $0x10, %%eax + mov %%eax, %%ds + mov %%eax, %%es + mov %%eax, %%fs + mov %%eax, %%gs + mov %%eax, %%ss + + mov $0x28, %%ax + ltr %%ax + )" :: "m"(gdtr)); +} + +void enter_user_mode(void (*func)()) +{ + asm volatile("mov %%esp, %0" : "=r" (tss.esp0)); + + asm volatile(R"( + mov $0x23, %%ax + mov %%ax, %%ds + mov %%ax, %%es + mov %%ax, %%fs + mov %%ax, %%gs + mov %%esp, %%eax + push $0x23 + push %%esp + pushf + push $0x1b + push %0 + iret + )" :: "b"(func)); +} + |