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