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 }
|