diff --git a/src/kernel/gdt.cpp b/src/kernel/gdt.cpp new file mode 100644 index 0000000..52def38 --- /dev/null +++ b/src/kernel/gdt.cpp @@ -0,0 +1,259 @@ +#include + +#include "kutil/assert.h" +#include "kutil/enum_bitfields.h" +#include "kutil/memory.h" +#include "console.h" +#include "log.h" + + +enum class gdt_type : uint8_t +{ + accessed = 0x01, + read_write = 0x02, + conforming = 0x04, + execute = 0x08, + system = 0x10, + ring1 = 0x20, + ring2 = 0x40, + ring3 = 0x60, + present = 0x80 +}; +IS_BITFIELD(gdt_type); + +struct gdt_descriptor +{ + uint16_t limit_low; + uint16_t base_low; + uint8_t base_mid; + gdt_type type; + uint8_t size; + uint8_t base_high; +} __attribute__ ((packed)); + +struct tss_descriptor +{ + uint16_t limit_low; + uint16_t base_00; + uint8_t base_16; + gdt_type type; + uint8_t size; + uint8_t base_24; + uint32_t base_32; + uint32_t reserved; +} __attribute__ ((packed)); + +struct tss_entry +{ + uint32_t reserved0; + + uint64_t rsp[3]; // stack pointers for CPL 0-2 + uint64_t ist[8]; // ist[0] is reserved + + uint64_t reserved1; + uint16_t reserved2; + uint16_t iomap_offset; +}; + +struct idt_descriptor +{ + uint16_t base_low; + uint16_t selector; + uint8_t ist; + uint8_t flags; + uint16_t base_mid; + uint32_t base_high; + uint32_t reserved; // must be zero +} __attribute__ ((packed)); + +struct table_ptr +{ + uint16_t limit; + uint64_t base; +} __attribute__ ((packed)); + + +gdt_descriptor g_gdt_table[10]; +idt_descriptor g_idt_table[256]; +table_ptr g_gdtr; +table_ptr g_idtr; +tss_entry g_tss; + + +extern "C" { + void idt_write(); + void idt_load(); + + void gdt_write(uint16_t cs, uint16_t ds, uint16_t tr); + void gdt_load(); +} + +void +gdt_set_entry(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type) +{ + g_gdt_table[i].limit_low = limit & 0xffff; + g_gdt_table[i].size = (limit >> 16) & 0xf; + g_gdt_table[i].size |= (is64 ? 0xa0 : 0xc0); + + g_gdt_table[i].base_low = base & 0xffff; + g_gdt_table[i].base_mid = (base >> 16) & 0xff; + g_gdt_table[i].base_high = (base >> 24) & 0xff; + + g_gdt_table[i].type = type | gdt_type::system | gdt_type::present; +} + +void +tss_set_entry(uint8_t i, uint64_t base, uint64_t limit) +{ + tss_descriptor tssd; + tssd.limit_low = limit & 0xffff; + tssd.size = (limit >> 16) & 0xf; + + tssd.base_00 = base & 0xffff; + tssd.base_16 = (base >> 16) & 0xff; + tssd.base_24 = (base >> 24) & 0xff; + tssd.base_32 = (base >> 32) & 0xffffffff; + + tssd.type = + gdt_type::accessed | + gdt_type::execute | + gdt_type::ring3 | + gdt_type::present; + kutil::memcpy(&g_gdt_table[i], &tssd, sizeof(tss_descriptor)); +} + +void +idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags) +{ + g_idt_table[i].base_low = addr & 0xffff; + g_idt_table[i].base_mid = (addr >> 16) & 0xffff; + g_idt_table[i].base_high = (addr >> 32) & 0xffffffff; + g_idt_table[i].selector = selector; + g_idt_table[i].flags = flags; + g_idt_table[i].ist = 0; + g_idt_table[i].reserved = 0; +} + +void +tss_set_stack(int ring, addr_t rsp) +{ + kassert(ring < 3, "Bad ring passed to set_tss_stack."); + g_tss.rsp[ring] = rsp; +} + +void +gdt_init() +{ + kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table)); + kutil::memset(&g_idt_table, 0, sizeof(g_idt_table)); + + g_gdtr.limit = sizeof(g_gdt_table) - 1; + g_gdtr.base = reinterpret_cast(&g_gdt_table); + + gdt_set_entry(1, 0, 0xfffff, true, gdt_type::read_write | gdt_type::execute); + gdt_set_entry(2, 0, 0xfffff, true, gdt_type::read_write); + gdt_set_entry(3, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute); + gdt_set_entry(4, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write); + + kutil::memset(&g_tss, 0, sizeof(tss_entry)); + addr_t tss_base = reinterpret_cast(&g_tss); + + // Note that this takes TWO GDT entries + tss_set_entry(6, tss_base, sizeof(tss_entry)); + + gdt_write(1 << 3, 2 << 3, 6 << 3); + + g_idtr.limit = sizeof(g_idt_table) - 1; + g_idtr.base = reinterpret_cast(&g_idt_table); + + idt_write(); +} + +void +gdt_dump() +{ + const table_ptr &table = g_gdtr; + + console *cons = console::get(); + cons->printf(" GDT: loc:%lx size:%d\n", table.base, table.limit+1); + + int count = (table.limit + 1) / sizeof(gdt_descriptor); + const gdt_descriptor *gdt = + reinterpret_cast(table.base); + + for (int i = 0; i < count; ++i) { + uint32_t base = + (gdt[i].base_high << 24) | + (gdt[i].base_mid << 16) | + gdt[i].base_low; + + uint32_t limit = + static_cast(gdt[i].size & 0x0f) << 16 | + gdt[i].limit_low; + + cons->printf(" %02d:", i); + if (! bitfield_has(gdt[i].type, gdt_type::present)) { + cons->puts(" Not Present\n"); + continue; + } + + cons->printf(" Base %08x limit %05x ", base, limit); + + switch (gdt[i].type & gdt_type::ring3) { + case gdt_type::ring3: cons->puts("ring3"); break; + case gdt_type::ring2: cons->puts("ring2"); break; + case gdt_type::ring1: cons->puts("ring1"); break; + default: cons->puts("ring0"); break; + } + + cons->printf(" %s %s %s %s %s %s %s\n", + bitfield_has(gdt[i].type, gdt_type::accessed) ? "A" : " ", + bitfield_has(gdt[i].type, gdt_type::read_write) ? "RW" : " ", + bitfield_has(gdt[i].type, gdt_type::conforming) ? "C" : " ", + bitfield_has(gdt[i].type, gdt_type::execute) ? "EX" : " ", + bitfield_has(gdt[i].type, gdt_type::system) ? "S" : " ", + (gdt[i].size & 0x80) ? "KB" : " B", + (gdt[i].size & 0x60) == 0x20 ? "64" : + (gdt[i].size & 0x60) == 0x40 ? "32" : "16"); + } +} + +void +idt_dump() +{ + const table_ptr &table = g_idtr; + + log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1); + + int count = (table.limit + 1) / sizeof(idt_descriptor); + const idt_descriptor *idt = + reinterpret_cast(table.base); + + for (int i = 0; i < count; ++i) { + uint64_t base = + (static_cast(idt[i].base_high) << 32) | + (static_cast(idt[i].base_mid) << 16) | + idt[i].base_low; + + char const *type; + switch (idt[i].flags & 0xf) { + case 0x5: type = " 32tsk "; break; + case 0x6: type = " 16int "; break; + case 0x7: type = " 16trp "; break; + case 0xe: type = " 32int "; break; + case 0xf: type = " 32trp "; break; + default: type = " ????? "; break; + } + + if (idt[i].flags & 0x80) { + log::debug(logs::boot, + " Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base, + (idt[i].selector & 0x3), + ((idt[i].selector & 0x4) >> 2), + (idt[i].selector >> 3), + idt[i].ist, + type, + ((idt[i].flags >> 5) & 0x3)); + } + } +} diff --git a/src/kernel/gdt.h b/src/kernel/gdt.h new file mode 100644 index 0000000..20c22a5 --- /dev/null +++ b/src/kernel/gdt.h @@ -0,0 +1,27 @@ +#pragma once +/// \file gdt.h +/// Definitions relating to system descriptor tables: GDT, IDT, TSS +#include +#include "kutil/memory.h" + +/// Set up the GDT and TSS, and switch segment registers to point +/// to them. +void gdt_init(); + +/// Set an entry in the IDT +/// \arg i Index in the IDT (vector of the interrupt this handles) +/// \arg addr Address of the handler +/// \arg selector GDT selector to set when invoking this handler +/// \arg flags Descriptor flags to set +void idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags); + +/// Set the stack pointer for a given ring in the TSS +/// \arg ring Ring to set for (0-2) +/// \arg rsp Stack pointer to set +void tss_set_stack(int ring, addr_t rsp); + +/// Dump information about the current GDT to the screen +void gdt_dump(); + +/// Dump information about the current IDT to the screen +void idt_dump(); diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 18ae760..49d78eb 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -1,94 +1,15 @@ -#include #include -#include "kutil/enum_bitfields.h" -#include "kutil/memory.h" #include "console.h" #include "device_manager.h" +#include "gdt.h" #include "interrupts.h" #include "io.h" #include "log.h" -enum class gdt_type : uint8_t -{ - accessed = 0x01, - read_write = 0x02, - conforming = 0x04, - execute = 0x08, - system = 0x10, - ring1 = 0x20, - ring2 = 0x40, - ring3 = 0x60, - present = 0x80 -}; -IS_BITFIELD(gdt_type); - -struct gdt_descriptor -{ - uint16_t limit_low; - uint16_t base_low; - uint8_t base_mid; - gdt_type type; - uint8_t size; - uint8_t base_high; -} __attribute__ ((packed)); - -struct idt_descriptor -{ - uint16_t base_low; - uint16_t selector; - uint8_t ist; - uint8_t flags; - uint16_t base_mid; - uint32_t base_high; - uint32_t reserved; // must be zero -} __attribute__ ((packed)); - -struct tss_descriptor -{ - uint16_t limit_low; - uint16_t base_00; - uint8_t base_16; - gdt_type type; - uint8_t size; - uint8_t base_24; - uint32_t base_32; - uint32_t reserved; -} __attribute__ ((packed)); - -struct tss_entry -{ - uint32_t reserved0; - - uint64_t rsp[3]; // stack pointers for CPL 0-2 - uint64_t ist[8]; // ist[0] is reserved - - uint64_t reserved1; - uint16_t reserved2; - uint16_t iomap_offset; -}; - -struct table_ptr -{ - uint16_t limit; - uint64_t base; -} __attribute__ ((packed)); - -gdt_descriptor g_gdt_table[10]; -idt_descriptor g_idt_table[256]; -table_ptr g_gdtr; -table_ptr g_idtr; -tss_entry g_tss; - struct registers; extern "C" { - void idt_write(); - void idt_load(); - - void gdt_write(uint16_t cs, uint16_t ds, uint16_t tr); - void gdt_load(); - void isr_handler(registers); void irq_handler(registers); @@ -101,9 +22,6 @@ extern "C" { #undef ISR } -void idt_dump(const table_ptr &table); -void gdt_dump(const table_ptr &table); - isr operator+(const isr &lhs, int rhs) { @@ -127,52 +45,6 @@ get_irq(unsigned vector) } } -void -set_gdt_entry(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type) -{ - g_gdt_table[i].limit_low = limit & 0xffff; - g_gdt_table[i].size = (limit >> 16) & 0xf; - g_gdt_table[i].size |= (is64 ? 0xa0 : 0xc0); - - g_gdt_table[i].base_low = base & 0xffff; - g_gdt_table[i].base_mid = (base >> 16) & 0xff; - g_gdt_table[i].base_high = (base >> 24) & 0xff; - - g_gdt_table[i].type = type | gdt_type::system | gdt_type::present; -} - -void -set_tss_entry(uint8_t i, uint64_t base, uint64_t limit) -{ - tss_descriptor tssd; - tssd.limit_low = limit & 0xffff; - tssd.size = (limit >> 16) & 0xf; - - tssd.base_00 = base & 0xffff; - tssd.base_16 = (base >> 16) & 0xff; - tssd.base_24 = (base >> 24) & 0xff; - tssd.base_32 = (base >> 32) & 0xffffffff; - - tssd.type = - gdt_type::accessed | - gdt_type::execute | - gdt_type::ring3 | - gdt_type::present; - kutil::memcpy(&g_gdt_table[i], &tssd, sizeof(tss_descriptor)); -} - -void -set_idt_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags) -{ - g_idt_table[i].base_low = addr & 0xffff; - g_idt_table[i].base_mid = (addr >> 16) & 0xffff; - g_idt_table[i].base_high = (addr >> 32) & 0xffffffff; - g_idt_table[i].selector = selector; - g_idt_table[i].flags = flags; - g_idt_table[i].ist = 0; - g_idt_table[i].reserved = 0; -} - static void disable_legacy_pic() { @@ -207,39 +79,14 @@ enable_serial_interrupts() void interrupts_init() { - kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table)); - kutil::memset(&g_idt_table, 0, sizeof(g_idt_table)); - - g_gdtr.limit = sizeof(g_gdt_table) - 1; - g_gdtr.base = reinterpret_cast(&g_gdt_table); - - set_gdt_entry(1, 0, 0xfffff, true, gdt_type::read_write | gdt_type::execute); - set_gdt_entry(2, 0, 0xfffff, true, gdt_type::read_write); - set_gdt_entry(3, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute); - set_gdt_entry(4, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write); - - kutil::memset(&g_tss, 0, sizeof(tss_entry)); - addr_t tss_base = reinterpret_cast(&g_tss); - - // Note that this takes TWO GDT entries - set_tss_entry(6, tss_base, sizeof(tss_entry)); - // TODO: Set up TSS stack pointers! - - gdt_write(1 << 3, 2 << 3, 6 << 3); - - g_idtr.limit = sizeof(g_idt_table) - 1; - g_idtr.base = reinterpret_cast(&g_idt_table); - -#define ISR(i, name) set_idt_entry(i, reinterpret_cast(& name), 0x08, 0x8e); -#define EISR(i, name) set_idt_entry(i, reinterpret_cast(& name), 0x08, 0x8e); -#define IRQ(i, q, name) set_idt_entry(i, reinterpret_cast(& name), 0x08, 0x8e); +#define ISR(i, name) idt_set_entry(i, reinterpret_cast(& name), 0x08, 0x8e); +#define EISR(i, name) idt_set_entry(i, reinterpret_cast(& name), 0x08, 0x8e); +#define IRQ(i, q, name) idt_set_entry(i, reinterpret_cast(& name), 0x08, 0x8e); #include "interrupt_isrs.inc" #undef IRQ #undef EISR #undef ISR - idt_write(); - disable_legacy_pic(); enable_serial_interrupts(); @@ -316,13 +163,13 @@ isr_handler(registers regs) switch (regs.errorcode & 0x06) { case 0: cons->printf(" GDT[%d]\n", index); - gdt_dump(g_gdtr); + gdt_dump(); break; case 1: case 3: cons->printf(" IDT[%d]\n", index); - idt_dump(g_idtr); + idt_dump(); break; default: @@ -479,88 +326,3 @@ irq_handler(registers regs) *reinterpret_cast(0xffffff80fee000b0) = 0; } - -void -gdt_dump(const table_ptr &table) -{ - console *cons = console::get(); - cons->printf(" GDT: loc:%lx size:%d\n", table.base, table.limit+1); - - int count = (table.limit + 1) / sizeof(gdt_descriptor); - const gdt_descriptor *gdt = - reinterpret_cast(table.base); - - for (int i = 0; i < count; ++i) { - uint32_t base = - (gdt[i].base_high << 24) | - (gdt[i].base_mid << 16) | - gdt[i].base_low; - - uint32_t limit = - static_cast(gdt[i].size & 0x0f) << 16 | - gdt[i].limit_low; - - cons->printf(" %02d:", i); - if (! bitfield_has(gdt[i].type, gdt_type::present)) { - cons->puts(" Not Present\n"); - continue; - } - - cons->printf(" Base %08x limit %05x ", base, limit); - - switch (gdt[i].type & gdt_type::ring3) { - case gdt_type::ring3: cons->puts("ring3"); break; - case gdt_type::ring2: cons->puts("ring2"); break; - case gdt_type::ring1: cons->puts("ring1"); break; - default: cons->puts("ring0"); break; - } - - cons->printf(" %s %s %s %s %s %s %s\n", - bitfield_has(gdt[i].type, gdt_type::accessed) ? "A" : " ", - bitfield_has(gdt[i].type, gdt_type::read_write) ? "RW" : " ", - bitfield_has(gdt[i].type, gdt_type::conforming) ? "C" : " ", - bitfield_has(gdt[i].type, gdt_type::execute) ? "EX" : " ", - bitfield_has(gdt[i].type, gdt_type::system) ? "S" : " ", - (gdt[i].size & 0x80) ? "KB" : " B", - (gdt[i].size & 0x60) == 0x20 ? "64" : - (gdt[i].size & 0x60) == 0x40 ? "32" : "16"); - } -} - -void -idt_dump(const table_ptr &table) -{ - log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1); - - int count = (table.limit + 1) / sizeof(idt_descriptor); - const idt_descriptor *idt = - reinterpret_cast(table.base); - - for (int i = 0; i < count; ++i) { - uint64_t base = - (static_cast(idt[i].base_high) << 32) | - (static_cast(idt[i].base_mid) << 16) | - idt[i].base_low; - - char const *type; - switch (idt[i].flags & 0xf) { - case 0x5: type = " 32tsk "; break; - case 0x6: type = " 16int "; break; - case 0x7: type = " 16trp "; break; - case 0xe: type = " 32int "; break; - case 0xf: type = " 32trp "; break; - default: type = " ????? "; break; - } - - if (idt[i].flags & 0x80) { - log::debug(logs::boot, - " Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base, - (idt[i].selector & 0x3), - ((idt[i].selector & 0x4) >> 2), - (idt[i].selector >> 3), - idt[i].ist, - type, - ((idt[i].flags >> 5) & 0x3)); - } - } -} diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index fe033fe..9a3fa3d 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -17,11 +17,17 @@ enum class isr : uint8_t _zero = 0 }; +/// Helper operator to add an offset to an isr vector isr operator+(const isr &lhs, int rhs); extern "C" { + /// Set the CPU interrupt enable flag (sti) void interrupts_enable(); + + /// Set the CPU interrupt disable flag (cli) void interrupts_disable(); } +/// Fill the IDT with our ISRs, and disable the legacy +/// PIC interrupts. void interrupts_init(); diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 2e87e99..80a6c16 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -8,6 +8,7 @@ #include "cpu.h" #include "device_manager.h" #include "font.h" +#include "gdt.h" #include "interrupts.h" #include "io.h" #include "kernel_data.h" @@ -66,6 +67,9 @@ kernel_main(popcorn_data *header) { kutil::assert_set_callback(__kernel_assert); + gdt_init(); + interrupts_init(); + page_manager *pager = new (&g_page_manager) page_manager; memory_initialize( @@ -80,7 +84,6 @@ kernel_main(popcorn_data *header) init_console(header); // pager->dump_blocks(); - interrupts_init(); device_manager *devices = new (&device_manager::get()) device_manager(header->acpi_table);