diff options
Diffstat (limited to 'src/kernel/core/idt.c')
-rw-r--r-- | src/kernel/core/idt.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/kernel/core/idt.c b/src/kernel/core/idt.c new file mode 100644 index 0000000..2f244e3 --- /dev/null +++ b/src/kernel/core/idt.c @@ -0,0 +1,253 @@ +#include <idt.h> +#include <gdt.h> +#include <sys.h> +#include <string.h> +#include <dbglog.h> + +struct idt_entry { + uint16_t base_lo; //Low part of address to jump to + uint16_t sel; //Kernel segment selector + uint8_t always0; + uint8_t type_attr; //Type + uint16_t base_hi; //High part of address to jump to +} __attribute__((packed)); +typedef struct idt_entry idt_entry_t; + +struct idt_ptr { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); +typedef struct idt_ptr idt_ptr_t; + +#define GATE_TYPE_INTERRUPT 14 // IF is cleared on interrupt +#define GATE_TYPE_TRAP 15 // IF stays as is + +#define GATE_PRESENT (1<<7) +#define GATE_DPL_SHIFT 5 + + +void isr0(); +void isr1(); +void isr2(); +void isr3(); +void isr4(); +void isr5(); +void isr6(); +void isr7(); +void isr8(); +void isr9(); +void isr10(); +void isr11(); +void isr12(); +void isr13(); +void isr14(); +void isr15(); +void isr16(); +void isr17(); +void isr18(); +void isr19(); +void isr20(); +void isr21(); +void isr22(); +void isr23(); +void isr24(); +void isr25(); +void isr26(); +void isr27(); +void isr28(); +void isr29(); +void isr30(); +void isr31(); + +void irq0(); +void irq1(); +void irq2(); +void irq3(); +void irq4(); +void irq5(); +void irq6(); +void irq7(); +void irq8(); +void irq9(); +void irq10(); +void irq11(); +void irq12(); +void irq13(); +void irq14(); +void irq15(); + +void syscall64(); + +// ************************************************************ +// Handler code + +static idt_entry_t idt_entries[256]; +static idt_ptr_t idt_ptr; + +static isr_handler_t irq_handlers[16] = {0}; +static isr_handler_t ex_handlers[32] = {0}; + +/* Called in interrupt.s when an exception fires (interrupt 0 to 31) */ +void idt_exHandler(registers_t *regs) { + if (ex_handlers[regs->int_no] != 0) { + ex_handlers[regs->int_no](regs); + } else { + //TODO: make sure all exceptions happenning in userspace do not cause kernel panic... + dbg_printf("Unhandled exception: %i\n", regs->int_no); + dbg_dump_registers(regs); + PANIC("Unhandled exception"); + } +} + +/* Called in interrupt.s when an IRQ fires (interrupt 32 to 47) */ +void idt_irqHandler(registers_t *regs) { + if (regs->err_code > 7) { + outb(0xA0, 0x20); + } + outb(0x20, 0x20); + + dbg_printf("IRQ %i\n", regs->err_code); + if (irq_handlers[regs->err_code] != 0) { + irq_handlers[regs->err_code](regs); + } +} + +/* Caled in interrupt.s when a syscall is called */ +void idt_syscallHandler(registers_t *regs) { + dbg_printf("Syscall %i\n", regs->int_no); + // do nothing, yet. +} + +/* For internal use only. Sets up an entry of the IDT with given parameters. */ +static void idt_set_gate(uint8_t num, void (*fun)(), uint8_t type) { + uint32_t base = (uint32_t)fun; + + idt_entries[num].base_lo = base & 0xFFFF; + idt_entries[num].base_hi = (base >> 16) & 0xFFFF; + + idt_entries[num].sel = K_CODE_SEGMENT; + idt_entries[num].always0 = 0; + idt_entries[num].type_attr = GATE_PRESENT + | (3 << GATE_DPL_SHIFT) // accessible from user mode + | type; +} + +static const struct { + uint8_t num; + void (*fun)(); + uint8_t type; +} gates[] = { + // Most processor exceptions are traps and handling them + // should be preemptible + { 0, isr0, GATE_TYPE_TRAP }, + { 1, isr1, GATE_TYPE_TRAP }, + { 2, isr2, GATE_TYPE_TRAP }, + { 3, isr3, GATE_TYPE_TRAP }, + { 4, isr4, GATE_TYPE_TRAP }, + { 5, isr5, GATE_TYPE_TRAP }, + { 6, isr6, GATE_TYPE_TRAP }, + { 7, isr7, GATE_TYPE_TRAP }, + { 8, isr8, GATE_TYPE_TRAP }, + { 9, isr9, GATE_TYPE_TRAP }, + { 10, isr10, GATE_TYPE_TRAP }, + { 11, isr11, GATE_TYPE_TRAP }, + { 12, isr12, GATE_TYPE_TRAP }, + { 13, isr13, GATE_TYPE_TRAP }, + { 14, isr14, GATE_TYPE_INTERRUPT }, // reenables interrupts later on + { 15, isr15, GATE_TYPE_TRAP }, + { 16, isr16, GATE_TYPE_TRAP }, + { 17, isr17, GATE_TYPE_TRAP }, + { 18, isr18, GATE_TYPE_TRAP }, + { 19, isr19, GATE_TYPE_TRAP }, + { 20, isr20, GATE_TYPE_TRAP }, + { 21, isr21, GATE_TYPE_TRAP }, + { 22, isr22, GATE_TYPE_TRAP }, + { 23, isr23, GATE_TYPE_TRAP }, + { 24, isr24, GATE_TYPE_TRAP }, + { 25, isr25, GATE_TYPE_TRAP }, + { 26, isr26, GATE_TYPE_TRAP }, + { 27, isr27, GATE_TYPE_TRAP }, + { 28, isr28, GATE_TYPE_TRAP }, + { 29, isr29, GATE_TYPE_TRAP }, + { 30, isr30, GATE_TYPE_TRAP }, + { 31, isr31, GATE_TYPE_TRAP }, + + // IRQs are not preemptible ; an IRQ handler should do the bare minimum + // (communication with the hardware), and then pass a message to a worker + // process in order to do further processing + { 32, irq0, GATE_TYPE_INTERRUPT }, + { 33, irq1, GATE_TYPE_INTERRUPT }, + { 34, irq2, GATE_TYPE_INTERRUPT }, + { 35, irq3, GATE_TYPE_INTERRUPT }, + { 36, irq4, GATE_TYPE_INTERRUPT }, + { 37, irq5, GATE_TYPE_INTERRUPT }, + { 38, irq6, GATE_TYPE_INTERRUPT }, + { 39, irq7, GATE_TYPE_INTERRUPT }, + { 40, irq8, GATE_TYPE_INTERRUPT }, + { 41, irq9, GATE_TYPE_INTERRUPT }, + { 42, irq10, GATE_TYPE_INTERRUPT }, + { 43, irq11, GATE_TYPE_INTERRUPT }, + { 44, irq12, GATE_TYPE_INTERRUPT }, + { 45, irq13, GATE_TYPE_INTERRUPT }, + { 46, irq14, GATE_TYPE_INTERRUPT }, + { 47, irq15, GATE_TYPE_INTERRUPT }, + + // Of course, syscalls are preemptible + { 64, syscall64, GATE_TYPE_TRAP }, + + { 0, 0, 0 } +}; + +/* Remaps the IRQs. Sets up the IDT. */ +void idt_init() { + memset((uint8_t*)&idt_entries, 0, sizeof(idt_entry_t) * 256); + + //Remap the IRQ table + outb(0x20, 0x11); + outb(0xA0, 0x11); + outb(0x21, 0x20); + outb(0xA1, 0x28); + outb(0x21, 0x04); + outb(0xA1, 0x02); + outb(0x21, 0x01); + outb(0xA1, 0x01); + outb(0x21, 0x0); + outb(0xA1, 0x0); + + for (int i = 0; gates[i].type != 0; i++) { + idt_set_gate(gates[i].num, gates[i].fun, gates[i].type); + } + + idt_ptr.limit = (sizeof(idt_entry_t) * 256) - 1; + idt_ptr.base = (uint32_t)&idt_entries; + + asm volatile ("lidt %0"::"m"(idt_ptr):"memory"); + + // Some setup calls that come later on are not preemptible, + // so we wait until then to enable interrupts. +} + +/* Sets up an IRQ handler for given IRQ. */ +void idt_set_irq_handler(int number, isr_handler_t func) { + if (number < 16 && number >= 0) { + irq_handlers[number] = func; + } +} + +/* Sets up a handler for a processor exception */ +void idt_set_ex_handler(int number, isr_handler_t func) { + if (number >= 0 && number < 32) { + ex_handlers[number] = func; + } +} + +void dbg_dump_registers(registers_t *regs) { + dbg_printf("/ Exception %i\n", regs->int_no); + dbg_printf("| EAX: 0x%p EBX: 0x%p ECX: 0x%p EDX: 0x%p\n", regs->eax, regs->ebx, regs->ecx, regs->edx); + dbg_printf("| EDI: 0x%p ESI: 0x%p ESP: 0x%p EBP: 0x%p\n", regs->edi, regs->esi, regs->esp, regs->ebp); + dbg_printf("| EIP: 0x%p CS : 0x%p DS : 0x%p SS : 0x%p\n", regs->eip, regs->cs, regs->ds, regs->ss); + dbg_printf("\\ EFl: 0x%p I# : 0x%p Err: 0x%p\n", regs->eflags, regs->int_no, regs->err_code); +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ + |