From 120ce15fac48f7aceac231ea3fcdcade26093e7b Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Mon, 24 Oct 2016 20:31:05 -0500 Subject: [PATCH] interrupt setup and handling --- idt.s | 210 +++++++++++++++++++++++++++++++++++++++++++ interrupt.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++ interrupt.h | 127 ++++++++++++++++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 idt.s create mode 100644 interrupt.c create mode 100644 interrupt.h diff --git a/idt.s b/idt.s new file mode 100644 index 0000000..10c95d5 --- /dev/null +++ b/idt.s @@ -0,0 +1,210 @@ +struc idt_entry +.handler_low: resw 1 +.selector: resw 1 +.ist: resb 1 +.flags: resb 1 +.handler_high: resw 1 +.handler_highest: resd 1 +.reserved: resd 1 +endstruc + +align 16 + +global set_idt_entry:function + +;void set_idt_entry( +; struct idt_entry *entry, rdi +; uintptr_t handler, rsi +; uint16_t selector, rdx +; uint8_t flags, rcx +; uint8_t ist r8 + +set_idt_entry: + mov [rdi + idt_entry.flags], cl + mov [rdi + idt_entry.ist], r8b + mov [rdi + idt_entry.selector], dx + mov [rdi + idt_entry.handler_low], si + shr rsi, 16 + mov [rdi + idt_entry.handler_high], si + shr rsi, 16 + mov [rdi + idt_entry.handler_highest], esi + mov [rdi + idt_entry.reserved], dword 0x0 + +global load_idt:function + +load_idt: +; size_t limit = sizeof(struct idt_entry) * length - 1; + shl rsi, 4 + sub rsi, 1 + + sub rsp, 10 + mov [rsp], si + mov [rsp + 2], rdi + lidt [rsp] + add rsp, 10 + ret + +section .text + +extern interrupt_handler +extern global_errno +extern printk + +%macro makeisr 2 +global isr%1:function +isr%1: + push %2 ; err_code + push %1 ; int_no + jmp interrupt_handler_prepare +%endmacro + +%macro makeisr 1 +global isr%1:function +isr%1: + ; error code pushed by processor + push %1 ; int_no + jmp interrupt_handler_prepare +%endmacro + +%macro makeirq 2 +global irq%1:function +irq%1: + push 0 ; err_code + push %2 ; int_no + jmp interrupt_handler_prepare +%endmacro + +; create isr 0-7, which need an error code pushed +%assign i 0 +%rep 8 + +makeisr i,0 + +%assign i i+1 +%endrep + +makeisr 8 +makeisr 9,0 +makeisr 10 +makeisr 11 +makeisr 12 +makeisr 13 +makeisr 14 + +; make isr 15-31 +%assign i 15 +%rep 17 +makeisr i,0 +%assign i i+1 +%endrep + +; make irqs +%assign i 32 +%assign j 0 +%rep 16 +makeirq j,i +%assign i i+1 +%assign j j+1 +%endrep + +; make remaining interrupts +%assign i 48 +%rep 208 +makeisr i,0 +%assign i i+1 +%endrep + +interrupt_handler_prepare: + + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + + ; CR2 for page faults, always push for consistent frame + mov rbp, cr2 + push rbp + + mov eax, [qword global_errno] + push rax + + mov rdi, rsp ; context stack frame pointer + mov rbx, rsp ; rbx is preserved by the abi, so save it + and rsp, 0xFFFFFFF0 ; should be sign extended + call interrupt_handler + mov rsp, rbx ; restore the stack + +load_interrupted_registers: + ; Restore the previous kernel errno. + pop rax + mov [qword global_errno], eax + + add rsp, 8 ; remove CR2 + + pop rdi + pop rsi + pop rbp + add rsp, 8 ; skip rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + ; Remove int_no and err_code + add rsp, 16 + + iretq +;.size interrupt_handler_prepare, . - interrupt_handler_prepare + +global interrupt_handler_null:function + +align 16 +interrupt_handler_null: + iretq +;.size interrupt_handler_null, . - interrupt_handler_null + +rfprintf: db 'rflags : %llx', 10, 0 +csprintf: db 'cs : %hx', 10, 0 +ripprintf: db 'rip : %llx', 10, 0 +rspprintf: db 'rsp : %llx', 10, 0 +ssprintf: db 'ss : %hx', 10, 0 +csvprintf: db 'gdt : %016llx', 10, 0 + +global interrupts_enabled:function +interrupts_enabled: + pushf + pop rax + shr rax, 9 + and rax, 0x1 + ret + +global enable_interrupts:function +enable_interrupts: + sti + ret + +global disable_interrupts:function +disable_interrupts: + cli + ret + diff --git a/interrupt.c b/interrupt.c new file mode 100644 index 0000000..7771f42 --- /dev/null +++ b/interrupt.c @@ -0,0 +1,254 @@ +#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; +} diff --git a/interrupt.h b/interrupt.h new file mode 100644 index 0000000..26922ef --- /dev/null +++ b/interrupt.h @@ -0,0 +1,127 @@ +#ifndef _SYS_INTERRUPT_H_ +#define _SYS_INTERRUPT_H_ + +#include + +struct interrupt_context { + /* pushed on the stack, before the handler is called */ + uint64_t kerrno; + uint64_t cr2; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t not_rsp; + uint64_t rbx; + uint64_t rdx; + uint64_t rcx; + uint64_t rax; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + /* assembly interrupt trampoline handles these */ + uint64_t int_no; + uint64_t err_code; + /* following pushed onto stack by CPU */ + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +}; + +struct interrupt_handler { + void (*handler)(struct interrupt_context *, void *); + void *context; + struct interrupt_handler *next; + struct interrupt_handler *prev; +}; + +void interrupt_handler_null(); +void isr128(); +void isr129(); +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 isr128(); +void isr130(); +void isr131(); + +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(); + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +void init_interrupts(); + +int interrupts_enabled(); +void enable_interrupts(); +void disable_interrupts(); +int interrupts_set(int enable); + +void interrupt_add_handler(unsigned int intno, struct interrupt_handler *handler); +void interrupt_remove_handler(unsigned int intno, struct interrupt_handler *handler); + +#endif -- 2.40.0