# HG changeset patch # User John Tsiombikas # Date 1298083311 -7200 # Node ID cccaa40f5432afcb4aa1a30896cb8d18a6f8fead # Parent b11a86695493538e9706af9520ad520c81224a42 forgot to add a load of stuff diff -r b11a86695493 -r cccaa40f5432 src/desc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/desc.h Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,10 @@ +#ifndef DESC_H_ +#define DESC_H_ + +#include + +typedef struct { + uint16_t d[4]; +} desc_t; + +#endif /* DESC_H_ */ diff -r b11a86695493 -r cccaa40f5432 src/interrupts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/interrupts.h Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,54 @@ +#ifdef ASM +/* included from intr-asm.S */ +#define INTR_ENTRY_EC(n, name) ientry_err n, name +#define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name +#else +/* included from intr.c inside init_intr() */ +#define INTR_ENTRY_EC(n, name) \ + void intr_entry_##name(void); \ + set_intr_entry(n, intr_entry_##name); +#define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name) +#endif /* ASM */ + +/* faults/traps/aborts (plus NMI) */ +INTR_ENTRY_NOEC(0, div) +INTR_ENTRY_NOEC(1, debug) +INTR_ENTRY_NOEC(2, nmi) +INTR_ENTRY_NOEC(3, bp) +INTR_ENTRY_NOEC(4, overflow) +INTR_ENTRY_NOEC(5, bound) +INTR_ENTRY_NOEC(6, ill) +INTR_ENTRY_NOEC(7, nodev) +INTR_ENTRY_EC(8, dfault) +INTR_ENTRY_NOEC(9, copseg) +INTR_ENTRY_EC(10, tss) +INTR_ENTRY_EC(11, segpres) +INTR_ENTRY_EC(12, stack) +INTR_ENTRY_EC(13, prot) +INTR_ENTRY_EC(14, page) +INTR_ENTRY_NOEC(15, reserved) +INTR_ENTRY_NOEC(16, fpu) +INTR_ENTRY_EC(17, align) +INTR_ENTRY_NOEC(18, mce) +INTR_ENTRY_NOEC(19, sse) +/* redirected IRQs */ +INTR_ENTRY_NOEC(32, irq0) +INTR_ENTRY_NOEC(33, irq1) +INTR_ENTRY_NOEC(34, irq2) +INTR_ENTRY_NOEC(35, irq3) +INTR_ENTRY_NOEC(36, irq4) +INTR_ENTRY_NOEC(37, irq5) +INTR_ENTRY_NOEC(38, irq6) +INTR_ENTRY_NOEC(39, irq7) +INTR_ENTRY_NOEC(40, irq8) +INTR_ENTRY_NOEC(41, irq9) +INTR_ENTRY_NOEC(42, irq10) +INTR_ENTRY_NOEC(43, irq11) +INTR_ENTRY_NOEC(44, irq12) +INTR_ENTRY_NOEC(45, irq13) +INTR_ENTRY_NOEC(46, irq14) +INTR_ENTRY_NOEC(47, irq15) +/* system call interrupt */ +INTR_ENTRY_NOEC(128, syscall) +/* default interrupt */ +INTR_ENTRY_NOEC(255, default) diff -r b11a86695493 -r cccaa40f5432 src/intr-asm.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intr-asm.S Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,60 @@ + .data + .align 4 + .short 0 +/* memory reserved for set_idt */ +lim:.short 0 +addr:.long 0 + + .text +/* set_idt(uint32_t addr, uint16_t limit) + * loads the IDTR with the new address and limit for the IDT */ + .globl set_idt +set_idt: + movl 4(%esp), %eax + movl %eax, (addr) + movw 8(%esp), %ax + movw %ax, (lim) + lidt (lim) + ret + +/* interrupt entry with error code macro + * this macro generates an interrupt entry point for the + * exceptions which include error codes in the stack frame + */ + .macro ientry_err n name + .globl intr_entry_\name +intr_entry_\name: + pushl $\n + jmp intr_entry_common + .endm + +/* interrupt entry without error code macro + * this macro generates an interrupt entry point for the interrupts + * and exceptions which do not include error codes in the stack frame + * it pushes a dummy error code (0), to make the stack frame identical + */ + .macro ientry_noerr n name + .globl intr_entry_\name +intr_entry_\name: + pushl $0 + pushl $\n + jmp intr_entry_common + .endm + +/* common code used by all entry points. calls dispatch_intr() + * defined in intr.c + */ + .extern dispatch_intr +intr_entry_common: + pusha + call dispatch_intr + popa + /* remove error code and intr num from stack */ + add $8, %esp + iret + +/* by including interrupts.h with ASM defined, the macros above + * are expanded to generate all required interrupt entry points + */ +#define ASM +#include diff -r b11a86695493 -r cccaa40f5432 src/intr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intr.c Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,175 @@ +#include +#include "intr.h" +#include "desc.h" +#include "segm.h" +#include "asmops.h" +#include "panic.h" + +/* 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) + +/* offset used to remap IRQ numbers (+32) */ +#define IRQ_OFFSET 32 +/* conversion macros between IRQ and interrupt numbers */ +#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) +#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) +/* checks whether a particular interrupt is an remapped IRQ */ +#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) + +/* 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) + + +/* structure used to pass the interrupt stack frame from the + * entry points to the C dispatch function. + */ +struct intr_frame { + /* registers pushed by pusha in intr_entry_* */ + uint32_t edi, esi, ebp, esp; + uint32_t ebx, edx, ecx, eax; + /* interrupt number and error code pushed in intr_entry_* */ + uint32_t inum, err; + /* pushed by CPU during interrupt entry */ + uint32_t eip, cs, eflags; +}; + + +static void init_pic(int offset); +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type); +static void set_intr_entry(int num, void (*handler)(void)); +static void end_of_irq(int irq); + +/* 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]; +/* table of handler functions for all interrupts */ +static intr_func_t intr_func[256]; + + +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 interrupts.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 "interrupts.h" + + /* initialize the programmable interrupt controller + * setting up the maping of IRQs [0, 15] to interrupts [32, 47] + */ + init_pic(IRQ_OFFSET); + + /* we're done setting up, enable interrupts before returning */ + enable_intr(); +} + +/* 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) +{ + if(intr_func[frm.inum]) { + intr_func[frm.inum](frm.inum, frm.err); + } else { + if(frm.inum < 32) { + panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err); + } + printf("unhandled interrupt %d\n", frm.inum); + } + + if(IS_IRQ(frm.inum)) { + end_of_irq(INTR_TO_IRQ(frm.inum)); + } +} + +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) < 20 && (n) != 2) +static void set_intr_entry(int num, void (*handler)(void)) +{ + int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR; + gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, 0, type); +} + +static void end_of_irq(int irq) +{ + if(irq > 7) { + outb(OCW2_EOI, PIC2_CMD); + } + outb(OCW2_EOI, PIC1_CMD); +} diff -r b11a86695493 -r cccaa40f5432 src/intr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intr.h Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,13 @@ +#ifndef INTR_H_ +#define INTR_H_ + +#include + +typedef void (*intr_func_t)(int, uint32_t); + + +void init_intr(void); + +void interrupt(int intr_num, intr_func_t func); + +#endif /* INTR_H_ */ diff -r b11a86695493 -r cccaa40f5432 src/panic.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/panic.c Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,16 @@ +#include +#include +#include "asmops.h" + +void panic(const char *fmt, ...) +{ + va_list ap; + + printf("~~~~~ kernel panic ~~~~~\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + disable_intr(); + halt_cpu(); +} diff -r b11a86695493 -r cccaa40f5432 src/panic.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/panic.h Sat Feb 19 04:41:51 2011 +0200 @@ -0,0 +1,6 @@ +#ifndef PANIC_H_ +#define PANIC_H_ + +void panic(const char *fmt, ...); + +#endif /* PANIC_H_ */