Fix wild bit in interrupt gate addresses. Make some things static.
[pintos-anon] / src / threads / interrupt.c
1 #include "interrupt.h"
2 #include <inttypes.h>
3 #include <stdint.h>
4 #include "intr-stubs.h"
5 #include "debug.h"
6 #include "io.h"
7 #include "lib.h"
8 #include "mmu.h"
9 #include "thread.h"
10 #include "timer.h"
11 \f
12 enum if_level
13 intr_get_level (void) 
14 {
15   uint32_t flags;
16   
17   asm ("pushfl; popl %0" : "=g" (flags));
18
19   return flags & (1 << 9) ? IF_ON : IF_OFF;
20 }
21
22 enum if_level
23 intr_set_level (enum if_level level) 
24 {
25   enum if_level old_level = intr_get_level ();
26   if (level == IF_ON)
27     intr_enable ();
28   else
29     intr_disable ();
30   return old_level;
31 }
32
33 enum if_level
34 intr_enable (void) 
35 {
36   enum if_level old_level = intr_get_level ();
37   asm volatile ("sti");
38   return old_level;
39 }
40
41 enum if_level
42 intr_disable (void) 
43 {
44   enum if_level old_level = intr_get_level ();
45   asm volatile ("cli");
46   return old_level;
47 }
48 \f
49 static void
50 pic_init (void)
51 {
52   /* Every PC has two 8259A Programmable Interrupt Controller
53      (PIC) chips.  One is a "master" accessible at ports 0x20 and
54      0x21.  The other is a "slave" cascaded onto the master's IRQ
55      2 line and accessible at ports 0xa0 and 0xa1.  Accesses to
56      port 0x20 set the A0 line to 0 and accesses to 0x21 set the
57      A1 line to 1.  The situation is similar for the slave PIC.
58      Refer to the 8259A datasheet for details.
59
60      By default, interrupts 0...15 delivered by the PICs will go
61      to interrupt vectors 0...15.  Unfortunately, those vectors
62      are also used for CPU traps and exceptions.  We reprogram
63      the PICs so that interrupts 0...15 are delivered to
64      interrupt vectors 32...47 instead. */
65
66   /* Mask all interrupts on both PICs. */
67   outb (0x21, 0xff);
68   outb (0xa1, 0xff);
69
70   /* Initialize master. */
71   outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
72   outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
73   outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
74   outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
75
76   /* Initialize slave. */
77   outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
78   outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
79   outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
80   outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
81
82   /* Unmask all interrupts. */
83   outb (0x21, 0x00);
84   outb (0xa1, 0x00);
85 }
86
87 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
88    If we don't acknowledge the IRQ, we'll never get it again, so
89    this is important.  */
90 static void
91 pic_eoi (void) 
92 {
93   /* FIXME?  The Linux code is much more complicated. */
94   outb (0x20, 0x20);
95 }
96 \f
97 #define INTR_CNT 256
98
99 static uint64_t idt[INTR_CNT];
100 static intr_handler_func *intr_handlers[INTR_CNT];
101 static const char *intr_names[INTR_CNT];
102
103 void intr_handler (struct intr_frame *args);
104
105 static bool intr_in_progress;
106 static bool yield_on_return;
107
108 const char *
109 intr_name (int vec) 
110 {
111   if (vec < 0 || vec >= INTR_CNT || intr_names[vec] == NULL)
112     return "unknown";
113   else
114     return intr_names[vec];
115 }
116
117 void
118 intr_handler (struct intr_frame *args) 
119 {
120   bool external;
121   
122   yield_on_return = false;
123
124   external = args->vec_no >= 0x20 && args->vec_no < 0x30;
125   if (external) 
126     {
127       ASSERT (intr_get_level () == IF_OFF);
128       ASSERT (!intr_context ());
129       intr_in_progress = true;
130     }
131
132   intr_handlers[args->vec_no] (args);
133
134   if (external) 
135     {
136       ASSERT (intr_get_level () == IF_OFF);
137       ASSERT (intr_context ());
138       intr_in_progress = false;
139       pic_eoi (); 
140     }
141
142   if (yield_on_return) 
143     thread_yield (); 
144 }
145
146 bool
147 intr_context (void) 
148 {
149   return intr_in_progress;
150 }
151
152 void
153 intr_yield_on_return (void) 
154 {
155   yield_on_return = true;
156 }
157
158 intr_handler_func intr_panic NO_RETURN;
159 intr_handler_func intr_kill NO_RETURN;
160
161 static uint64_t
162 make_gate (void (*target) (void), int dpl, enum seg_type type)
163 {
164   uint32_t offset = (uint32_t) target;
165   uint32_t e0 = ((offset & 0xffff)            /* Offset 15:0. */
166                  | (SEL_KCSEG << 16));        /* Target code segment. */
167   uint32_t e1 = ((offset & 0xffff0000)        /* Offset 31:16. */
168                  | (1 << 15)                  /* Present. */
169                  | ((uint32_t) dpl << 13)     /* Descriptor privilege. */
170                  | (SYS_SYSTEM << 12)         /* System. */
171                  | ((uint32_t) type << 8));   /* Gate type. */
172   return e0 | ((uint64_t) e1 << 32);
173 }
174
175 static uint64_t
176 make_intr_gate (void (*target) (void), int dpl)
177 {
178   return make_gate (target, dpl, TYPE_INT_32);
179 }
180
181 static uint64_t
182 make_trap_gate (void (*target) (void), int dpl)
183 {
184   return make_gate (target, dpl, TYPE_TRAP_32);
185 }
186
187 /* We don't support nested interrupts generated by external
188    hardware, so these interrupts (vec_no 0x20...0x2f) should
189    specify IF_OFF for LEVEL.  Otherwise a timer interrupt could
190    cause a task switch during interrupt handling.  Most other
191    interrupts can and should be handled with interrupts
192    enabled. */
193 void
194 intr_register (uint8_t vec_no, int dpl, enum if_level level,
195                intr_handler_func *handler,
196                const char *name) 
197 {
198   if (level == IF_ON)
199     idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
200   else
201     idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
202   intr_handlers[vec_no] = handler;
203   intr_names[vec_no] = name;
204 }
205
206 void
207 intr_init (void)
208 {
209   uint64_t idtr_operand;
210   int i;
211
212   pic_init ();
213
214   /* Install default handlers. */
215   for (i = 0; i < 256; i++)
216     intr_register (i, 0, IF_OFF, intr_panic, NULL);
217
218   /* Most exceptions require ring 0.
219      Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
220   intr_register (0, 0, IF_ON, intr_kill, "#DE Divide Error");
221   intr_register (1, 0, IF_ON, intr_kill, "#DB Debug Exception");
222   intr_register (2, 0, IF_ON, intr_panic, "NMI Interrupt");
223   intr_register (3, 3, IF_ON, intr_kill, "#BP Breakpoint Exception");
224   intr_register (4, 3, IF_ON, intr_kill, "#OF Overflow Exception");
225   intr_register (5, 3, IF_ON, intr_kill, "#BR BOUND Range Exceeded Exception");
226   intr_register (6, 0, IF_ON, intr_kill, "#UD Invalid Opcode Exception");
227   intr_register (7, 0, IF_ON, intr_kill, "#NM Device Not Available Exception");
228   intr_register (8, 0, IF_ON, intr_panic, "#DF Double Fault Exception");
229   intr_register (9, 0, IF_ON, intr_panic, "Coprocessor Segment Overrun");
230   intr_register (10, 0, IF_ON, intr_panic, "#TS Invalid TSS Exception");
231   intr_register (11, 0, IF_ON, intr_kill, "#NP Segment Not Present");
232   intr_register (12, 0, IF_ON, intr_kill, "#SS Stack Fault Exception");
233   intr_register (13, 0, IF_ON, intr_kill, "#GP General Protection Exception");
234   intr_register (16, 0, IF_ON, intr_kill, "#MF x87 FPU Floating-Point Error");
235   intr_register (17, 0, IF_ON, intr_panic, "#AC Alignment Check Exception");
236   intr_register (18, 0, IF_ON, intr_panic, "#MC Machine-Check Exception");
237   intr_register (19, 0, IF_ON, intr_kill, "#XF SIMD Floating-Point Exception");
238
239   /* Most exceptions can be handled with interrupts turned on.
240      We need to disable interrupts for page faults because the
241      fault address is stored in CR2 and needs to be preserved. */
242   intr_register (14, 0, IF_OFF, intr_kill, "#PF Page-Fault Exception");
243
244   idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
245   asm volatile ("lidt %0" :: "m" (idtr_operand));
246 }
247
248 static void
249 dump_intr_frame (struct intr_frame *f) 
250 {
251   uint32_t cr2, ss;
252   asm ("movl %%cr2, %0" : "=r" (cr2));
253   asm ("movl %%ss, %0" : "=r" (ss));
254
255   printk ("Interrupt %#04x (%s) at eip=%p\n",
256           f->vec_no, intr_name (f->vec_no), f->eip);
257   printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
258   printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
259           f->eax, f->ebx, f->ecx, f->edx);
260   printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
261           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
262   printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
263           f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
264 }
265
266 void
267 intr_panic (struct intr_frame *regs) 
268 {
269   dump_intr_frame (regs);
270   panic ("Panic!");
271 }
272
273 void
274 intr_kill (struct intr_frame *f) 
275 {
276   switch (f->cs)
277     {
278     case SEL_UCSEG:
279       printk ("[%p] Interrupt %#04x (%s), killing process.\n",
280               thread_current (), f->vec_no, intr_name (f->vec_no));
281       thread_exit (); 
282
283     case SEL_KCSEG:
284       printk ("Kernel bug - unexpected interrupt in kernel context\n");
285       intr_panic (f);
286
287     default:
288       printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
289              f->vec_no, intr_name (f->vec_no), f->cs);
290       thread_exit ();
291     }
292 }
293
294