kern

annotate src/intr.c @ 80:4db99a52863e

fixed the "endianess" of the text messages in the ATA identify info block. this is the first time I've seen wrong byteorder in ascii text, the ATA committee should be commended.
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 13:35:39 +0200
parents fa65b4f45366
children
rev   line source
nuclear@11 1 #include <stdio.h>
nuclear@11 2 #include "intr.h"
nuclear@11 3 #include "desc.h"
nuclear@11 4 #include "segm.h"
nuclear@11 5 #include "asmops.h"
nuclear@11 6 #include "panic.h"
nuclear@55 7 #include "syscall.h"
nuclear@11 8
nuclear@11 9 /* IDT gate descriptor bits */
nuclear@11 10 #define GATE_TASK (5 << 8)
nuclear@11 11 #define GATE_INTR (6 << 8)
nuclear@11 12 #define GATE_TRAP (7 << 8)
nuclear@11 13 #define GATE_DEFAULT (1 << 11)
nuclear@11 14 #define GATE_PRESENT (1 << 15)
nuclear@11 15
nuclear@11 16 /* PIC command and data ports */
nuclear@11 17 #define PIC1_CMD 0x20
nuclear@11 18 #define PIC1_DATA 0x21
nuclear@11 19 #define PIC2_CMD 0xa0
nuclear@11 20 #define PIC2_DATA 0xa1
nuclear@11 21
nuclear@11 22 /* PIC initialization command word 1 bits */
nuclear@11 23 #define ICW1_ICW4_NEEDED (1 << 0)
nuclear@11 24 #define ICW1_SINGLE (1 << 1)
nuclear@11 25 #define ICW1_INTERVAL4 (1 << 2)
nuclear@11 26 #define ICW1_LEVEL (1 << 3)
nuclear@11 27 #define ICW1_INIT (1 << 4)
nuclear@11 28 /* PIC initialization command word 4 bits */
nuclear@11 29 #define ICW4_8086 (1 << 0)
nuclear@11 30 #define ICW4_AUTO_EOI (1 << 1)
nuclear@11 31 #define ICW4_BUF_SLAVE (1 << 3) /* 1000 */
nuclear@11 32 #define ICW4_BUF_MASTER (3 << 2) /* 1100 */
nuclear@11 33 #define ICW4_SPECIAL (1 << 4)
nuclear@11 34
nuclear@11 35 /* PIC operation command word 2 bits */
nuclear@11 36 #define OCW2_EOI (1 << 5)
nuclear@11 37
nuclear@11 38
nuclear@11 39 static void init_pic(int offset);
nuclear@11 40 static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type);
nuclear@11 41 static void set_intr_entry(int num, void (*handler)(void));
nuclear@11 42
nuclear@11 43 /* defined in intr-asm.S */
nuclear@11 44 void set_idt(uint32_t addr, uint16_t limit);
nuclear@11 45 void intr_entry_default(void);
nuclear@11 46
nuclear@11 47 /* the IDT (interrupt descriptor table) */
nuclear@11 48 static desc_t idt[256];
nuclear@11 49 /* table of handler functions for all interrupts */
nuclear@11 50 static intr_func_t intr_func[256];
nuclear@11 51
nuclear@52 52 static struct intr_frame *cur_intr_frame;
nuclear@55 53 static int eoi_pending;
nuclear@52 54
nuclear@11 55
nuclear@11 56 void init_intr(void)
nuclear@11 57 {
nuclear@11 58 int i;
nuclear@11 59
nuclear@11 60 set_idt((uint32_t)idt, sizeof idt - 1);
nuclear@11 61
nuclear@11 62 /* initialize all entry points and interrupt handlers */
nuclear@11 63 for(i=0; i<256; i++) {
nuclear@11 64 set_intr_entry(i, intr_entry_default);
nuclear@11 65 interrupt(i, 0);
nuclear@11 66 }
nuclear@11 67
nuclear@11 68 /* by including interrupts.h here (without ASM being defined)
nuclear@11 69 * the series of INTR_ENTRY_* macros will be expanded to a series
nuclear@11 70 * of function prototypes for all interrupt entry points and the
nuclear@11 71 * corresponding calls to set_intr_entry to set up the IDT slots
nuclear@11 72 */
nuclear@11 73 #include "interrupts.h"
nuclear@11 74
nuclear@11 75 /* initialize the programmable interrupt controller
nuclear@11 76 * setting up the maping of IRQs [0, 15] to interrupts [32, 47]
nuclear@11 77 */
nuclear@11 78 init_pic(IRQ_OFFSET);
nuclear@55 79 eoi_pending = 0;
nuclear@11 80 }
nuclear@11 81
nuclear@52 82 /* retrieve the current interrupt frame.
nuclear@52 83 * returns 0 when called during kernel init.
nuclear@52 84 */
nuclear@52 85 struct intr_frame *get_intr_frame(void)
nuclear@52 86 {
nuclear@52 87 return cur_intr_frame;
nuclear@52 88 }
nuclear@52 89
nuclear@11 90 /* set an interrupt handler function for a particular interrupt */
nuclear@11 91 void interrupt(int intr_num, intr_func_t func)
nuclear@11 92 {
nuclear@11 93 intr_func[intr_num] = func;
nuclear@11 94 }
nuclear@11 95
nuclear@11 96 /* this function is called from all interrupt entry points
nuclear@11 97 * it calls the appropriate interrupt handlers if available and handles
nuclear@11 98 * sending an end-of-interrupt command to the PICs when finished.
nuclear@11 99 */
nuclear@11 100 void dispatch_intr(struct intr_frame frm)
nuclear@11 101 {
nuclear@52 102 cur_intr_frame = &frm;
nuclear@52 103
nuclear@55 104 if(IS_IRQ(frm.inum)) {
nuclear@55 105 eoi_pending = frm.inum;
nuclear@55 106 }
nuclear@55 107
nuclear@11 108 if(intr_func[frm.inum]) {
nuclear@52 109 intr_func[frm.inum](frm.inum);
nuclear@11 110 } else {
nuclear@11 111 if(frm.inum < 32) {
nuclear@11 112 panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err);
nuclear@11 113 }
nuclear@11 114 printf("unhandled interrupt %d\n", frm.inum);
nuclear@11 115 }
nuclear@11 116
nuclear@55 117 disable_intr();
nuclear@55 118 if(eoi_pending) {
nuclear@55 119 end_of_irq(INTR_TO_IRQ(eoi_pending));
nuclear@11 120 }
nuclear@11 121 }
nuclear@11 122
nuclear@11 123 static void init_pic(int offset)
nuclear@11 124 {
nuclear@11 125 /* send ICW1 saying we'll follow with ICW4 later on */
nuclear@11 126 outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC1_CMD);
nuclear@11 127 outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC2_CMD);
nuclear@11 128 /* send ICW2 with IRQ remapping */
nuclear@11 129 outb(offset, PIC1_DATA);
nuclear@11 130 outb(offset + 8, PIC2_DATA);
nuclear@11 131 /* send ICW3 to setup the master/slave relationship */
nuclear@11 132 /* ... set bit3 = 3rd interrupt input has a slave */
nuclear@11 133 outb(4, PIC1_DATA);
nuclear@11 134 /* ... set slave ID to 2 */
nuclear@11 135 outb(2, PIC2_DATA);
nuclear@11 136 /* send ICW4 to set 8086 mode (no calls generated) */
nuclear@11 137 outb(ICW4_8086, PIC1_DATA);
nuclear@11 138 outb(ICW4_8086, PIC2_DATA);
nuclear@11 139 /* done, just reset the data port to 0 */
nuclear@11 140 outb(0, PIC1_DATA);
nuclear@11 141 outb(0, PIC2_DATA);
nuclear@11 142 }
nuclear@11 143
nuclear@11 144 static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type)
nuclear@11 145 {
nuclear@11 146 /* first 16bit part is the low 16bits of the entry address */
nuclear@11 147 desc->d[0] = addr & 0xffff;
nuclear@11 148 /* second 16bit part is the segment selector for the entry code */
nuclear@11 149 desc->d[1] = sel;
nuclear@11 150 /* third 16bit part has the privilege level, type, and present bit */
nuclear@11 151 desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT;
nuclear@11 152 /* last 16bit part is the high 16bits of the entry address */
nuclear@11 153 desc->d[3] = (addr & 0xffff0000) >> 16;
nuclear@11 154 }
nuclear@11 155
nuclear@29 156 #define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n))
nuclear@11 157 static void set_intr_entry(int num, void (*handler)(void))
nuclear@11 158 {
nuclear@11 159 int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR;
nuclear@55 160
nuclear@55 161 /* the syscall interrupt has to have a dpl of 3 otherwise calling it from
nuclear@55 162 * user space will raise a general protection exception. All the rest should
nuclear@55 163 * have a dpl of 0 to disallow user programs to execute critical interrupt
nuclear@55 164 * handlers and possibly crashing the system.
nuclear@55 165 */
nuclear@55 166 int dpl = (num == SYSCALL_INT) ? 3 : 0;
nuclear@55 167
nuclear@55 168 gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, dpl, type);
nuclear@11 169 }
nuclear@11 170
nuclear@51 171 void end_of_irq(int irq)
nuclear@11 172 {
nuclear@55 173 int intr_state = get_intr_state();
nuclear@55 174 disable_intr();
nuclear@55 175
nuclear@55 176 if(!eoi_pending) {
nuclear@55 177 return;
nuclear@55 178 }
nuclear@55 179 eoi_pending = 0;
nuclear@55 180
nuclear@11 181 if(irq > 7) {
nuclear@11 182 outb(OCW2_EOI, PIC2_CMD);
nuclear@11 183 }
nuclear@11 184 outb(OCW2_EOI, PIC1_CMD);
nuclear@55 185
nuclear@55 186 set_intr_state(intr_state);
nuclear@11 187 }