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