]> pd.if.org Git - zos/commitdiff
interrupt setup and handling
authorNathan Wagner <nw@hydaspes.if.org>
Tue, 25 Oct 2016 01:31:05 +0000 (20:31 -0500)
committerNathan Wagner <nw@hydaspes.if.org>
Tue, 25 Oct 2016 01:40:32 +0000 (20:40 -0500)
idt.s [new file with mode: 0644]
interrupt.c [new file with mode: 0644]
interrupt.h [new file with mode: 0644]

diff --git a/idt.s b/idt.s
new file mode 100644 (file)
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 (file)
index 0000000..7771f42
--- /dev/null
@@ -0,0 +1,254 @@
+#include <stdint.h>
+#include <stddef.h>
+
+#include <string.h>
+
+#include "kernel.h"
+#include "interrupt.h"
+#include "cpu.h"
+
+#include "pic.h"
+
+#include <process.h>
+
+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 (file)
index 0000000..26922ef
--- /dev/null
@@ -0,0 +1,127 @@
+#ifndef _SYS_INTERRUPT_H_
+#define _SYS_INTERRUPT_H_
+
+#include <stddef.h>
+
+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