Move user exception support into userprog.
[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
12 /* Number of x86 interrupts. */
13 #define INTR_CNT 256
14
15 /* The Interrupt Descriptor Table (IDT).  The format is fixed by
16    the CPU.  See [IA32-v3] sections 5.10, 5.11, 5.12.1.2. */
17 static uint64_t idt[INTR_CNT];
18
19 /* Interrupt handler functions for each interrupt. */
20 static intr_handler_func *intr_handlers[INTR_CNT];
21
22 /* Names for each interrupt, for debugging purposes. */
23 static const char *intr_names[INTR_CNT];
24
25 /* External interrupts are those generated by devices outside the
26    CPU, such as the timer.  External interrupts run with
27    interrupts turned off, so they never nest, nor are they ever
28    pre-empted.  Handlers for external interrupts also may not
29    sleep, although they may invoke intr_yield_on_return() to
30    request that a new process be scheduled just before the
31    interrupt returns. */
32 static bool in_external_intr;   /* Are we processing an external interrupt? */
33 static bool yield_on_return;    /* Should we yield on interrupt return? */
34
35 /* Programmable Interrupt Controller helpers. */
36 static void pic_init (void);
37 static void pic_end_of_interrupt (int irq);
38
39 /* Interrupt Descriptor Table helpers. */
40 static uint64_t make_intr_gate (void (*) (void), int dpl);
41 static uint64_t make_trap_gate (void (*) (void), int dpl);
42 static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
43
44 /* Interrupt handlers. */
45 void intr_handler (struct intr_frame *args);
46 \f
47 /* Returns the current interrupt status. */
48 enum intr_level
49 intr_get_level (void) 
50 {
51   uint32_t flags;
52   
53   asm ("pushfl; popl %0" : "=g" (flags));
54
55   return flags & FLAG_IF ? INTR_ON : INTR_OFF;
56 }
57
58 /* Enables or disables interrupts as specified by LEVEL and
59    returns the previous interrupt status. */
60 enum intr_level
61 intr_set_level (enum intr_level level) 
62 {
63   return level == INTR_ON ? intr_enable () : intr_disable ();
64 }
65
66 /* Enables interrupts and returns the previous interrupt status. */
67 enum intr_level
68 intr_enable (void) 
69 {
70   enum intr_level old_level = intr_get_level ();
71   asm volatile ("sti");
72   return old_level;
73 }
74
75 /* Disables interrupts and returns the previous interrupt status. */
76 enum intr_level
77 intr_disable (void) 
78 {
79   enum intr_level old_level = intr_get_level ();
80   asm volatile ("cli");
81   return old_level;
82 }
83 \f
84 /* Initializes the interrupt system. */
85 void
86 intr_init (void)
87 {
88   uint64_t idtr_operand;
89   int i;
90
91   /* Initialize interrupt controller. */
92   pic_init ();
93
94   /* Load IDT register. */
95   idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
96   asm volatile ("lidt %0" :: "m" (idtr_operand));
97
98   /* Initialize intr_names. */
99   for (i = 0; i < INTR_CNT; i++)
100     intr_names[i] = "unknown";
101   intr_names[0] = "#DE Divide Error";
102   intr_names[1] = "#DB Debug Exception";
103   intr_names[2] = "NMI Interrupt";
104   intr_names[3] = "#BP Breakpoint Exception";
105   intr_names[4] = "#OF Overflow Exception";
106   intr_names[5] = "#BR BOUND Range Exceeded Exception";
107   intr_names[6] = "#UD Invalid Opcode Exception";
108   intr_names[7] = "#NM Device Not Available Exception";
109   intr_names[8] = "#DF Double Fault Exception";
110   intr_names[9] = "Coprocessor Segment Overrun";
111   intr_names[10] = "#TS Invalid TSS Exception";
112   intr_names[11] = "#NP Segment Not Present";
113   intr_names[12] = "#SS Stack Fault Exception";
114   intr_names[13] = "#GP General Protection Exception";
115   intr_names[16] = "#MF x87 FPU Floating-Point Error";
116   intr_names[17] = "#AC Alignment Check Exception";
117   intr_names[18] = "#MC Machine-Check Exception";
118   intr_names[19] = "#XF SIMD Floating-Point Exception";
119 }
120
121 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
122    NAME for debugging purposes.  The interrupt handler will be
123    invoked with interrupt status set to LEVEL.
124
125    The handler will have descriptor privilege level DPL, meaning
126    that it can be invoked intentionally when the processor is in
127    the DPL or lower-numbered ring.  In practice, DPL==3 allows
128    user mode to invoke the interrupts and DPL==0 prevents such
129    invocation.  Faults and exceptions that occur in user mode
130    still cause interrupts with DPL==0 to be invoked.  See
131    [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
132 void
133 intr_register (uint8_t vec_no, int dpl, enum intr_level level,
134                intr_handler_func *handler,
135                const char *name) 
136 {
137   /* Make sure this handler isn't already registered to someone
138      else. */
139   ASSERT (intr_handlers[vec_no] == NULL);
140
141   /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
142      0x2f) should specify INTR_OFF for LEVEL.  Otherwise a timer
143      interrupt could cause a task switch during interrupt
144      handling.  Most other interrupts can and should be handled
145      with interrupts enabled. */
146   ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
147
148   if (level == INTR_ON)
149     idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
150   else
151     idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
152   intr_handlers[vec_no] = handler;
153   intr_names[vec_no] = name;
154 }
155
156 /* Returns true during processing of an external interrupt
157    and false at all other times. */
158 bool
159 intr_context (void) 
160 {
161   return in_external_intr;
162 }
163
164 /* During processing of an external interrupt, directs the
165    interrupt handler to yield to a new process just before
166    returning from the interrupt.  May not be called at any other
167    time. */
168 void
169 intr_yield_on_return (void) 
170 {
171   ASSERT (intr_context ());
172   yield_on_return = true;
173 }
174 \f
175 /* 8259A Programmable Interrupt Controller. */
176
177 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
178    chips.  One is a "master" accessible at ports 0x20 and 0x21.
179    The other is a "slave" cascaded onto the master's IRQ 2 line
180    and accessible at ports 0xa0 and 0xa1.  Accesses to port 0x20
181    set the A0 line to 0 and accesses to 0x21 set the A1 line to
182    1.  The situation is similar for the slave PIC.
183
184    By default, interrupts 0...15 delivered by the PICs will go to
185    interrupt vectors 0...15.  Unfortunately, those vectors are
186    also used for CPU traps and exceptions.  We reprogram the PICs
187    so that interrupts 0...15 are delivered to interrupt vectors
188    32...47 (0x20...0x2f) instead. */
189
190 /* Initializes the PICs.  Refer to [8259A] for details. */
191 static void
192 pic_init (void)
193 {
194   /* Mask all interrupts on both PICs. */
195   outb (0x21, 0xff);
196   outb (0xa1, 0xff);
197
198   /* Initialize master. */
199   outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
200   outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
201   outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
202   outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
203
204   /* Initialize slave. */
205   outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
206   outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
207   outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
208   outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
209
210   /* Unmask all interrupts. */
211   outb (0x21, 0x00);
212   outb (0xa1, 0x00);
213 }
214
215 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
216    If we don't acknowledge the IRQ, it will never be delivered to
217    us again, so this is important.  */
218 static void
219 pic_end_of_interrupt (int irq) 
220 {
221   ASSERT (irq >= 0x20 && irq < 0x30);
222
223   /* Acknowledge master PIC. */
224   outb (0x20, 0x20);
225
226   /* Acknowledge slave PIC if this is a slave interrupt. */
227   if (irq >= 0x28)
228     outb (0xa0, 0x20);
229 }
230 \f
231 /* Creates an gate that invokes FUNCTION.
232
233    The gate has descriptor privilege level DPL, meaning that it
234    can be invoked intentionally when the processor is in the DPL
235    or lower-numbered ring.  In practice, DPL==3 allows user mode
236    to call into the gate and DPL==0 prevents such calls.  Faults
237    and exceptions that occur in user mode still cause gates with
238    DPL==0 to be invoked.  See [IA32-v3] sections 4.5 and 4.8.1.1
239    for further discussion.
240
241    TYPE must be either 14 (for an interrupt gate) or 15 (for a
242    trap gate).  The difference is that entering an interrupt gate
243    disables interrupts, but entering a trap gate does not.  See
244    [IA32-v3] section 5.12.1.2 for discussion. */
245 static uint64_t
246 make_gate (void (*function) (void), int dpl, int type)
247 {
248   uint32_t e0, e1;
249
250   ASSERT (function != NULL);
251   ASSERT (dpl >= 0 && dpl <= 3);
252   ASSERT (type >= 0 && type <= 15);
253
254   e0 = (((uint32_t) function & 0xffff)     /* Offset 15:0. */
255         | (SEL_KCSEG << 16));              /* Target code segment. */
256
257   e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
258         | (1 << 15)                        /* Present. */
259         | ((uint32_t) dpl << 13)           /* Descriptor privilege level. */
260         | (0 << 12)                        /* System. */
261         | ((uint32_t) type << 8));         /* Gate type. */
262
263   return e0 | ((uint64_t) e1 << 32);
264 }
265
266 /* Creates an interrupt gate that invokes FUNCTION with the given
267    DPL. */
268 static uint64_t
269 make_intr_gate (void (*function) (void), int dpl)
270 {
271   return make_gate (function, dpl, 14);
272 }
273
274 /* Creates a trap gate that invokes FUNCTION with the given
275    DPL. */
276 static uint64_t
277 make_trap_gate (void (*function) (void), int dpl)
278 {
279   return make_gate (function, dpl, 15);
280 }
281
282 /* Returns a descriptor that yields the given LIMIT and BASE when
283    used as an operand for the LIDT instruction. */
284 static inline uint64_t
285 make_idtr_operand (uint16_t limit, void *base)
286 {
287   return limit | ((uint64_t) (uint32_t) base << 16);
288 }
289 \f
290 /* Interrupt handlers. */
291
292 /* Handler for all interrupts, faults, and exceptions.  This
293    function is called by the assembly language interrupt stubs in
294    intr-stubs.S (see intr-stubs.pl).  FRAME describes the
295    interrupt and the interrupted thread's registers. */
296 void
297 intr_handler (struct intr_frame *frame) 
298 {
299   bool external;
300   intr_handler_func *handler;
301
302   external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
303   if (external) 
304     {
305       ASSERT (intr_get_level () == INTR_OFF);
306       ASSERT (!intr_context ());
307
308       in_external_intr = true;
309       yield_on_return = false;
310     }
311
312   /* Invoke the interrupt's handler.
313      If there is no handler, invoke the unexpected interrupt
314      handler. */
315   handler = intr_handlers[frame->vec_no];
316   if (handler == NULL)
317     {
318       intr_dump_frame (frame);
319       PANIC ("Unexpected interrupt");
320     }
321   handler (frame);
322
323   /* Complete the processing of an external interrupt. */
324   if (external) 
325     {
326       ASSERT (intr_get_level () == INTR_OFF);
327       ASSERT (intr_context ());
328
329       in_external_intr = false;
330       pic_end_of_interrupt (frame->vec_no); 
331
332       if (yield_on_return) 
333         thread_yield (); 
334     }
335 }
336
337 /* Dumps interrupt frame F to the console, for debugging. */
338 void
339 intr_dump_frame (const struct intr_frame *f) 
340 {
341   uint32_t cr2, ss;
342   asm ("movl %%cr2, %0" : "=r" (cr2));
343   asm ("movl %%ss, %0" : "=r" (ss));
344
345   printk ("Interrupt %#04x (%s) at eip=%p\n",
346           f->vec_no, intr_names[f->vec_no], f->eip);
347   printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
348   printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
349           f->eax, f->ebx, f->ecx, f->edx);
350   printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
351           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
352   printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
353           f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
354 }
355
356 /* Returns the name of interrupt VEC. */
357 const char *
358 intr_name (uint8_t vec) 
359 {
360   return intr_names[vec];
361 }