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