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