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@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: /* offset used to remap IRQ numbers (+32) */ nuclear@11: #define IRQ_OFFSET 32 nuclear@11: /* conversion macros between IRQ and interrupt numbers */ nuclear@11: #define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) nuclear@11: #define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) nuclear@11: /* checks whether a particular interrupt is an remapped IRQ */ nuclear@11: #define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) 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: /* structure used to pass the interrupt stack frame from the nuclear@11: * entry points to the C dispatch function. nuclear@11: */ nuclear@11: struct intr_frame { nuclear@11: /* registers pushed by pusha in intr_entry_* */ nuclear@11: uint32_t edi, esi, ebp, esp; nuclear@11: uint32_t ebx, edx, ecx, eax; nuclear@11: /* interrupt number and error code pushed in intr_entry_* */ nuclear@11: uint32_t inum, err; nuclear@11: /* pushed by CPU during interrupt entry */ nuclear@11: uint32_t eip, cs, eflags; nuclear@11: }; 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: static void end_of_irq(int irq); 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@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@11: } nuclear@11: 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@11: if(intr_func[frm.inum]) { nuclear@11: intr_func[frm.inum](frm.inum, frm.err); 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@11: if(IS_IRQ(frm.inum)) { nuclear@11: end_of_irq(INTR_TO_IRQ(frm.inum)); 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@11: gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, 0, type); nuclear@11: } nuclear@11: nuclear@11: static void end_of_irq(int irq) nuclear@11: { nuclear@11: if(irq > 7) { nuclear@11: outb(OCW2_EOI, PIC2_CMD); nuclear@11: } nuclear@11: outb(OCW2_EOI, PIC1_CMD); nuclear@11: }