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