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