nuclear@11: #include nuclear@11: #include "intr.h" nuclear@11: #include "desc.h" nuclear@11: #include "segm.h" nuclear@11: #include "asmops.h" nuclear@11: #include "panic.h" nuclear@55: #include "syscall.h" nuclear@11: nuclear@11: /* IDT gate descriptor bits */ nuclear@11: #define GATE_TASK (5 << 8) nuclear@11: #define GATE_INTR (6 << 8) nuclear@11: #define GATE_TRAP (7 << 8) nuclear@11: #define GATE_DEFAULT (1 << 11) nuclear@11: #define GATE_PRESENT (1 << 15) nuclear@11: nuclear@11: /* PIC command and data ports */ nuclear@11: #define PIC1_CMD 0x20 nuclear@11: #define PIC1_DATA 0x21 nuclear@11: #define PIC2_CMD 0xa0 nuclear@11: #define PIC2_DATA 0xa1 nuclear@11: nuclear@11: /* PIC initialization command word 1 bits */ nuclear@11: #define ICW1_ICW4_NEEDED (1 << 0) nuclear@11: #define ICW1_SINGLE (1 << 1) nuclear@11: #define ICW1_INTERVAL4 (1 << 2) nuclear@11: #define ICW1_LEVEL (1 << 3) nuclear@11: #define ICW1_INIT (1 << 4) nuclear@11: /* PIC initialization command word 4 bits */ nuclear@11: #define ICW4_8086 (1 << 0) nuclear@11: #define ICW4_AUTO_EOI (1 << 1) nuclear@11: #define ICW4_BUF_SLAVE (1 << 3) /* 1000 */ nuclear@11: #define ICW4_BUF_MASTER (3 << 2) /* 1100 */ nuclear@11: #define ICW4_SPECIAL (1 << 4) nuclear@11: nuclear@11: /* PIC operation command word 2 bits */ nuclear@11: #define OCW2_EOI (1 << 5) nuclear@11: nuclear@11: nuclear@11: static void init_pic(int offset); nuclear@11: static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type); nuclear@11: static void set_intr_entry(int num, void (*handler)(void)); nuclear@11: nuclear@11: /* defined in intr-asm.S */ nuclear@11: void set_idt(uint32_t addr, uint16_t limit); nuclear@11: void intr_entry_default(void); nuclear@11: nuclear@11: /* the IDT (interrupt descriptor table) */ nuclear@11: static desc_t idt[256]; nuclear@11: /* table of handler functions for all interrupts */ nuclear@11: static intr_func_t intr_func[256]; nuclear@11: nuclear@52: static struct intr_frame *cur_intr_frame; nuclear@55: static int eoi_pending; nuclear@52: nuclear@11: nuclear@11: void init_intr(void) nuclear@11: { nuclear@11: int i; nuclear@11: nuclear@11: set_idt((uint32_t)idt, sizeof idt - 1); nuclear@11: nuclear@11: /* initialize all entry points and interrupt handlers */ nuclear@11: for(i=0; i<256; i++) { nuclear@11: set_intr_entry(i, intr_entry_default); nuclear@11: interrupt(i, 0); nuclear@11: } nuclear@11: nuclear@11: /* by including interrupts.h here (without ASM being defined) nuclear@11: * the series of INTR_ENTRY_* macros will be expanded to a series nuclear@11: * of function prototypes for all interrupt entry points and the nuclear@11: * corresponding calls to set_intr_entry to set up the IDT slots nuclear@11: */ nuclear@11: #include "interrupts.h" nuclear@11: nuclear@11: /* initialize the programmable interrupt controller nuclear@11: * setting up the maping of IRQs [0, 15] to interrupts [32, 47] nuclear@11: */ nuclear@11: init_pic(IRQ_OFFSET); nuclear@55: eoi_pending = 0; nuclear@11: } nuclear@11: nuclear@52: /* retrieve the current interrupt frame. nuclear@52: * returns 0 when called during kernel init. nuclear@52: */ nuclear@52: struct intr_frame *get_intr_frame(void) nuclear@52: { nuclear@52: return cur_intr_frame; nuclear@52: } nuclear@52: nuclear@11: /* set an interrupt handler function for a particular interrupt */ nuclear@11: void interrupt(int intr_num, intr_func_t func) nuclear@11: { nuclear@11: intr_func[intr_num] = func; nuclear@11: } nuclear@11: nuclear@11: /* this function is called from all interrupt entry points nuclear@11: * it calls the appropriate interrupt handlers if available and handles nuclear@11: * sending an end-of-interrupt command to the PICs when finished. nuclear@11: */ nuclear@11: void dispatch_intr(struct intr_frame frm) nuclear@11: { nuclear@52: cur_intr_frame = &frm; nuclear@52: nuclear@55: if(IS_IRQ(frm.inum)) { nuclear@55: eoi_pending = frm.inum; nuclear@55: } nuclear@55: nuclear@11: if(intr_func[frm.inum]) { nuclear@52: intr_func[frm.inum](frm.inum); nuclear@11: } else { nuclear@11: if(frm.inum < 32) { nuclear@11: panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err); nuclear@11: } nuclear@11: printf("unhandled interrupt %d\n", frm.inum); nuclear@11: } nuclear@11: nuclear@55: disable_intr(); nuclear@55: if(eoi_pending) { nuclear@55: end_of_irq(INTR_TO_IRQ(eoi_pending)); nuclear@11: } nuclear@11: } nuclear@11: nuclear@11: static void init_pic(int offset) nuclear@11: { nuclear@11: /* send ICW1 saying we'll follow with ICW4 later on */ nuclear@11: outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC1_CMD); nuclear@11: outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC2_CMD); nuclear@11: /* send ICW2 with IRQ remapping */ nuclear@11: outb(offset, PIC1_DATA); nuclear@11: outb(offset + 8, PIC2_DATA); nuclear@11: /* send ICW3 to setup the master/slave relationship */ nuclear@11: /* ... set bit3 = 3rd interrupt input has a slave */ nuclear@11: outb(4, PIC1_DATA); nuclear@11: /* ... set slave ID to 2 */ nuclear@11: outb(2, PIC2_DATA); nuclear@11: /* send ICW4 to set 8086 mode (no calls generated) */ nuclear@11: outb(ICW4_8086, PIC1_DATA); nuclear@11: outb(ICW4_8086, PIC2_DATA); nuclear@11: /* done, just reset the data port to 0 */ nuclear@11: outb(0, PIC1_DATA); nuclear@11: outb(0, PIC2_DATA); nuclear@11: } nuclear@11: nuclear@11: static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type) nuclear@11: { nuclear@11: /* first 16bit part is the low 16bits of the entry address */ nuclear@11: desc->d[0] = addr & 0xffff; nuclear@11: /* second 16bit part is the segment selector for the entry code */ nuclear@11: desc->d[1] = sel; nuclear@11: /* third 16bit part has the privilege level, type, and present bit */ nuclear@11: desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT; nuclear@11: /* last 16bit part is the high 16bits of the entry address */ nuclear@11: desc->d[3] = (addr & 0xffff0000) >> 16; nuclear@11: } nuclear@11: nuclear@29: #define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n)) nuclear@11: static void set_intr_entry(int num, void (*handler)(void)) nuclear@11: { nuclear@11: int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR; nuclear@55: nuclear@55: /* the syscall interrupt has to have a dpl of 3 otherwise calling it from nuclear@55: * user space will raise a general protection exception. All the rest should nuclear@55: * have a dpl of 0 to disallow user programs to execute critical interrupt nuclear@55: * handlers and possibly crashing the system. nuclear@55: */ nuclear@55: int dpl = (num == SYSCALL_INT) ? 3 : 0; nuclear@55: nuclear@55: gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, dpl, type); nuclear@11: } nuclear@11: nuclear@51: void end_of_irq(int irq) nuclear@11: { nuclear@55: int intr_state = get_intr_state(); nuclear@55: disable_intr(); nuclear@55: nuclear@55: if(!eoi_pending) { nuclear@55: return; nuclear@55: } nuclear@55: eoi_pending = 0; nuclear@55: nuclear@11: if(irq > 7) { nuclear@11: outb(OCW2_EOI, PIC2_CMD); nuclear@11: } nuclear@11: outb(OCW2_EOI, PIC1_CMD); nuclear@55: nuclear@55: set_intr_state(intr_state); nuclear@11: }