#include #include #include #include "kernel.h" #include "interrupt.h" #include "cpu.h" #include "pic.h" #include struct idt_entry { uint16_t handler_low; uint16_t selector; uint8_t ist; uint8_t flags; uint16_t handler_high; uint32_t handler_highest; uint32_t reserved1; } __attribute__((packed)); /* in idt.s */ void set_idt_entry(struct idt_entry *entry, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist); void load_idt(struct idt_entry *table, size_t length); #define NUM_KNOWN_EXCEPTIONS 20 const char *exception_names[] = { "Divide by zero", /* 0, 0x0 */ "Debug", /* 1, 0x1 */ "Non maskable interrupt", /* 2, 0x2 */ "Breakpoint", /* 3, 0x3 */ "Into detected overflow", /* 4, 0x4 */ "Out of bounds", /* 5, 0x5 */ "Invalid opcode", /* 6, 0x6 */ "No coprocessor", /* 7, 0x7 */ "Double fault", /* 8, 0x8 */ "Coprocessor segment overrun", /* 9, 0x9 */ "Bad TSS", /* 10, 0xA */ "Segment not present", /* 11, 0xB */ "Stack fault", /* 12, 0xC */ "General protection fault", /* 13, 0xD */ "Page fault", /* 14, 0xE */ "Unknown interrupt", /* 15, 0xF */ "Coprocessor fault", /* 16, 0x10 */ "Alignment check", /* 17, 0x11 */ "Machine check", /* 18, 0x12 */ "SIMD Floating-Point", /* 19, 0x13 */ }; #define NUM_INTERRUPTS 256 static struct idt_entry interrupt_table[NUM_INTERRUPTS]; static struct interrupt_handler *handlers[NUM_INTERRUPTS]; /* index is the interrupt number */ void interrupt_add_handler(unsigned int index, struct interrupt_handler *handler) { struct interrupt_handler *h; int enabled; /* assert(index < NUM_INTERRUPTS); */ klog(0,"registering handler %llx\n", (uint64_t)handler->handler); handler->next = NULL; handler->prev = NULL; enabled = interrupts_set(0); h = handlers[index]; if (h) { while (h->next) { h = h->next; } /* TODO assembly xor eax,eax; lock cmpxchg [rdi], rsi */ h->next = handler; handler->prev = h; } else { handlers[index] = handler; } interrupts_set(enabled); } void interrupt_remove_handler(unsigned int index, struct interrupt_handler *handler) { /* assert(index < NUM_INTERRUPTS); */ int was_enabled = interrupts_set(0); if (handler->prev) { handler->prev->next = handler->next; } else { handlers[index] = handler->next; } if (handler->next) { handler->next->prev = handler->prev; } interrupts_set(was_enabled); } #define IDTFLAGS(type, rpl) ( (1 << 7) | type << 0 | rpl << 5 ) static void set_table_entry(unsigned int num, void (*handler)(void), int user, int trap) { uint8_t ist = 0, type, rpl; rpl = user ? 3 : 0; /* TODO what is the actual difference between these */ /* an interrupt clears the interrupt flag, a trap leaves it alone * thus a trap could be further interrupted */ type = trap ? 0xF : 0xE; /* 0x10 == kernel code segment */ set_idt_entry(&interrupt_table[num], (uintptr_t)handler, 0x10, IDTFLAGS(type,rpl), ist); } void init_interrupts() { int i; /* Initialize the pic and remap the IRQ table */ pic_init(); pic.reprogram(); /* Initialize the interrupt table entries to the null interrupt handler. */ memset(&interrupt_table, 0, sizeof interrupt_table); for (i = 0; i < NUM_INTERRUPTS; i++) { set_table_entry(i, interrupt_handler_null, 0, 0); } /* cpu internal handlers */ set_table_entry(0, isr0, 0, 0); set_table_entry(1, isr1, 0, 0); set_table_entry(2, isr2, 0, 0); set_table_entry(3, isr3, 0, 0); set_table_entry(4, isr4, 0, 0); set_table_entry(5, isr5, 0, 0); set_table_entry(6, isr6, 0, 0); set_table_entry(7, isr7, 0, 0); set_table_entry(8, isr8, 0, 0); set_table_entry(9, isr9, 0, 0); set_table_entry(10, isr10, 0, 0); set_table_entry(11, isr11, 0, 0); set_table_entry(12, isr12, 0, 0); set_table_entry(13, isr13, 0, 0); set_table_entry(14, isr14, 0, 0); set_table_entry(15, isr15, 0, 0); set_table_entry(16, isr16, 0, 0); set_table_entry(17, isr17, 0, 0); set_table_entry(18, isr18, 0, 0); set_table_entry(19, isr19, 0, 0); set_table_entry(20, isr20, 0, 0); set_table_entry(21, isr21, 0, 0); set_table_entry(22, isr22, 0, 0); set_table_entry(23, isr23, 0, 0); set_table_entry(24, isr24, 0, 0); set_table_entry(25, isr25, 0, 0); set_table_entry(26, isr26, 0, 0); set_table_entry(27, isr27, 0, 0); set_table_entry(28, isr28, 0, 0); set_table_entry(29, isr29, 0, 0); set_table_entry(30, isr30, 0, 0); set_table_entry(31, isr31, 0, 0); /* hardware interrupts from the pic */ set_table_entry(32, irq0, 0, 0); set_table_entry(33, irq1, 0, 0); set_table_entry(34, irq2, 0, 0); set_table_entry(35, irq3, 0, 0); set_table_entry(36, irq4, 0, 0); set_table_entry(37, irq5, 0, 0); set_table_entry(38, irq6, 0, 0); set_table_entry(39, irq7, 0, 0); set_table_entry(40, irq8, 0, 0); set_table_entry(41, irq9, 0, 0); set_table_entry(42, irq10, 0, 0); set_table_entry(43, irq11, 0, 0); set_table_entry(44, irq12, 0, 0); set_table_entry(45, irq13, 0, 0); set_table_entry(46, irq14, 0, 0); set_table_entry(47, irq15, 0, 0); /* software interrupt, could be used to force a schedule */ set_table_entry(128, isr128, 1, 0); set_table_entry(129, interrupt_handler_null, 0, 1); load_idt(interrupt_table, NUM_INTERRUPTS); enable_interrupts(); printk("interrupts enabled\n"); } const char *exceptionstr(int i) { if (i < NUM_KNOWN_EXCEPTIONS) { return exception_names[i]; } return "Unknown"; } void interrupt_handler(struct interrupt_context *intctx) { int crash; int in_kernel; struct interrupt_handler *h; unsigned int int_no = intctx->int_no; /* Ignore spurious IRQ 7 and 15 */ if ( int_no == IRQ7 && !(pic.readisr() & (1 << 7)) ) return; if ( int_no == IRQ15 && !(pic.readisr() & (1 << 15)) ) { pic.mastereoi(); return; } in_kernel = (intctx->cs & 0x3) == 0; crash = int_no < 32 && int_no != 7; /* I think 7 and 9 can't happen */ if (crash && in_kernel) { panic("kernel crash '%s' int %u, rip %llx, cr2 %llx, error %llx, rflags %llx\n", exceptionstr(intctx->int_no), intctx->int_no, intctx->rip, intctx->cr2, intctx->err_code, intctx->rflags); } if (crash && !in_kernel) { panic("user mode crash: interrupt %u\n", int_no); } /* run registered handlers */ for (h = handlers[int_no]; h; h = h->next ) { h->handler(intctx, h->context); } /* Send an end of interrupt signal to the PICs if we got an IRQ */ if (int_no >= IRQ0 && int_no <= IRQ15) { pic.eoi(int_no - IRQ0); } if (need_schedule()) { do_schedule(); } } int interrupts_set(int is_enabled) { int wasenabled = interrupts_enabled(); if (is_enabled) { enable_interrupts(); } else { disable_interrupts(); } return wasenabled; }