Wording improvements, from "Valentin I. Spitkovsky"
[pintos.git] / src / threads / interrupt.c
blob075962f5081cc09b4295ff87a35b8d9f057148e3
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/thread.h"
10 #include "threads/vaddr.h"
11 #include "devices/timer.h"
13 /* Number of x86 interrupts. */
14 #define INTR_CNT 256
16 /* The Interrupt Descriptor Table (IDT). The format is fixed by
17 the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor
18 Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
19 Exception- or Interrupt-Handler Procedure". */
20 static uint64_t idt[INTR_CNT];
22 /* Interrupt handler functions for each interrupt. */
23 static intr_handler_func *intr_handlers[INTR_CNT];
25 /* Names for each interrupt, for debugging purposes. */
26 static const char *intr_names[INTR_CNT];
28 /* External interrupts are those generated by devices outside the
29 CPU, such as the timer. External interrupts run with
30 interrupts turned off, so they never nest, nor are they ever
31 pre-empted. Handlers for external interrupts also may not
32 sleep, although they may invoke intr_yield_on_return() to
33 request that a new process be scheduled just before the
34 interrupt returns. */
35 static bool in_external_intr; /* Are we processing an external interrupt? */
36 static bool yield_on_return; /* Should we yield on interrupt return? */
38 /* Programmable Interrupt Controller helpers. */
39 static void pic_init (void);
40 static void pic_end_of_interrupt (int irq);
42 /* Interrupt Descriptor Table helpers. */
43 static uint64_t make_intr_gate (void (*) (void), int dpl);
44 static uint64_t make_trap_gate (void (*) (void), int dpl);
45 static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
47 /* Interrupt handlers. */
48 void intr_handler (struct intr_frame *args);
50 /* Returns the current interrupt status. */
51 enum intr_level
52 intr_get_level (void)
54 uint32_t flags;
56 /* Push the flags register on the processor stack, then pop the
57 value off the stack into `flags'. See [IA32-v2b] "PUSHF"
58 and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
59 Interrupts". */
60 asm volatile ("pushfl; popl %0" : "=g" (flags));
62 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
65 /* Enables or disables interrupts as specified by LEVEL and
66 returns the previous interrupt status. */
67 enum intr_level
68 intr_set_level (enum intr_level level)
70 return level == INTR_ON ? intr_enable () : intr_disable ();
73 /* Enables interrupts and returns the previous interrupt status. */
74 enum intr_level
75 intr_enable (void)
77 enum intr_level old_level = intr_get_level ();
78 ASSERT (!intr_context ());
80 /* Enable interrupts by setting the interrupt flag.
82 See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
83 Hardware Interrupts". */
84 asm volatile ("sti");
86 return old_level;
89 /* Disables interrupts and returns the previous interrupt status. */
90 enum intr_level
91 intr_disable (void)
93 enum intr_level old_level = intr_get_level ();
95 /* Disable interrupts by clearing the interrupt flag.
96 See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
97 Hardware Interrupts". */
98 asm volatile ("cli" : : : "memory");
100 return old_level;
103 /* Initializes the interrupt system. */
104 void
105 intr_init (void)
107 uint64_t idtr_operand;
108 int i;
110 /* Initialize interrupt controller. */
111 pic_init ();
113 /* Initialize IDT. */
114 for (i = 0; i < INTR_CNT; i++)
115 idt[i] = make_intr_gate (intr_stubs[i], 0);
117 /* Load IDT register.
118 See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
119 Descriptor Table (IDT)". */
120 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
121 asm volatile ("lidt %0" : : "m" (idtr_operand));
123 /* Initialize intr_names. */
124 for (i = 0; i < INTR_CNT; i++)
125 intr_names[i] = "unknown";
126 intr_names[0] = "#DE Divide Error";
127 intr_names[1] = "#DB Debug Exception";
128 intr_names[2] = "NMI Interrupt";
129 intr_names[3] = "#BP Breakpoint Exception";
130 intr_names[4] = "#OF Overflow Exception";
131 intr_names[5] = "#BR BOUND Range Exceeded Exception";
132 intr_names[6] = "#UD Invalid Opcode Exception";
133 intr_names[7] = "#NM Device Not Available Exception";
134 intr_names[8] = "#DF Double Fault Exception";
135 intr_names[9] = "Coprocessor Segment Overrun";
136 intr_names[10] = "#TS Invalid TSS Exception";
137 intr_names[11] = "#NP Segment Not Present";
138 intr_names[12] = "#SS Stack Fault Exception";
139 intr_names[13] = "#GP General Protection Exception";
140 intr_names[14] = "#PF Page-Fault Exception";
141 intr_names[16] = "#MF x87 FPU Floating-Point Error";
142 intr_names[17] = "#AC Alignment Check Exception";
143 intr_names[18] = "#MC Machine-Check Exception";
144 intr_names[19] = "#XF SIMD Floating-Point Exception";
147 /* Registers interrupt VEC_NO to invoke HANDLER with descriptor
148 privilege level DPL. Names the interrupt NAME for debugging
149 purposes. The interrupt handler will be invoked with
150 interrupt status set to LEVEL. */
151 static void
152 register_handler (uint8_t vec_no, int dpl, enum intr_level level,
153 intr_handler_func *handler, const char *name)
155 ASSERT (intr_handlers[vec_no] == NULL);
156 if (level == INTR_ON)
157 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
158 else
159 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
160 intr_handlers[vec_no] = handler;
161 intr_names[vec_no] = name;
164 /* Registers external interrupt VEC_NO to invoke HANDLER, which
165 is named NAME for debugging purposes. The handler will
166 execute with interrupts disabled. */
167 void
168 intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
169 const char *name)
171 ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
172 register_handler (vec_no, 0, INTR_OFF, handler, name);
175 /* Registers internal interrupt VEC_NO to invoke HANDLER, which
176 is named NAME for debugging purposes. The interrupt handler
177 will be invoked with interrupt status LEVEL.
179 The handler will have descriptor privilege level DPL, meaning
180 that it can be invoked intentionally when the processor is in
181 the DPL or lower-numbered ring. In practice, DPL==3 allows
182 user mode to invoke the interrupts and DPL==0 prevents such
183 invocation. Faults and exceptions that occur in user mode
184 still cause interrupts with DPL==0 to be invoked. See
185 [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
186 "Accessing Nonconforming Code Segments" for further
187 discussion. */
188 void
189 intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
190 intr_handler_func *handler, const char *name)
192 ASSERT (vec_no < 0x20 || vec_no > 0x2f);
193 register_handler (vec_no, dpl, level, handler, name);
196 /* Returns true during processing of an external interrupt
197 and false at all other times. */
198 bool
199 intr_context (void)
201 return in_external_intr;
204 /* During processing of an external interrupt, directs the
205 interrupt handler to yield to a new process just before
206 returning from the interrupt. May not be called at any other
207 time. */
208 void
209 intr_yield_on_return (void)
211 ASSERT (intr_context ());
212 yield_on_return = true;
215 /* 8259A Programmable Interrupt Controller. */
217 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
218 chips. One is a "master" accessible at ports 0x20 and 0x21.
219 The other is a "slave" cascaded onto the master's IRQ 2 line
220 and accessible at ports 0xa0 and 0xa1. Accesses to port 0x20
221 set the A0 line to 0 and accesses to 0x21 set the A1 line to
222 1. The situation is similar for the slave PIC.
224 By default, interrupts 0...15 delivered by the PICs will go to
225 interrupt vectors 0...15. Unfortunately, those vectors are
226 also used for CPU traps and exceptions. We reprogram the PICs
227 so that interrupts 0...15 are delivered to interrupt vectors
228 32...47 (0x20...0x2f) instead. */
230 /* Initializes the PICs. Refer to [8259A] for details. */
231 static void
232 pic_init (void)
234 /* Mask all interrupts on both PICs. */
235 outb (0x21, 0xff);
236 outb (0xa1, 0xff);
238 /* Initialize master. */
239 outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
240 outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
241 outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
242 outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
244 /* Initialize slave. */
245 outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
246 outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
247 outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
248 outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
250 /* Unmask all interrupts. */
251 outb (0x21, 0x00);
252 outb (0xa1, 0x00);
255 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
256 If we don't acknowledge the IRQ, it will never be delivered to
257 us again, so this is important. */
258 static void
259 pic_end_of_interrupt (int irq)
261 ASSERT (irq >= 0x20 && irq < 0x30);
263 /* Acknowledge master PIC. */
264 outb (0x20, 0x20);
266 /* Acknowledge slave PIC if this is a slave interrupt. */
267 if (irq >= 0x28)
268 outb (0xa0, 0x20);
271 /* Creates an gate that invokes FUNCTION.
273 The gate has descriptor privilege level DPL, meaning that it
274 can be invoked intentionally when the processor is in the DPL
275 or lower-numbered ring. In practice, DPL==3 allows user mode
276 to call into the gate and DPL==0 prevents such calls. Faults
277 and exceptions that occur in user mode still cause gates with
278 DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege
279 Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
280 for further discussion.
282 TYPE must be either 14 (for an interrupt gate) or 15 (for a
283 trap gate). The difference is that entering an interrupt gate
284 disables interrupts, but entering a trap gate does not. See
285 [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
286 Interrupt-Handler Procedure" for discussion. */
287 static uint64_t
288 make_gate (void (*function) (void), int dpl, int type)
290 uint32_t e0, e1;
292 ASSERT (function != NULL);
293 ASSERT (dpl >= 0 && dpl <= 3);
294 ASSERT (type >= 0 && type <= 15);
296 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
297 | (SEL_KCSEG << 16)); /* Target code segment. */
299 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
300 | (1 << 15) /* Present. */
301 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
302 | (0 << 12) /* System. */
303 | ((uint32_t) type << 8)); /* Gate type. */
305 return e0 | ((uint64_t) e1 << 32);
308 /* Creates an interrupt gate that invokes FUNCTION with the given
309 DPL. */
310 static uint64_t
311 make_intr_gate (void (*function) (void), int dpl)
313 return make_gate (function, dpl, 14);
316 /* Creates a trap gate that invokes FUNCTION with the given
317 DPL. */
318 static uint64_t
319 make_trap_gate (void (*function) (void), int dpl)
321 return make_gate (function, dpl, 15);
324 /* Returns a descriptor that yields the given LIMIT and BASE when
325 used as an operand for the LIDT instruction. */
326 static inline uint64_t
327 make_idtr_operand (uint16_t limit, void *base)
329 return limit | ((uint64_t) (uint32_t) base << 16);
332 /* Interrupt handlers. */
334 /* Handler for all interrupts, faults, and exceptions. This
335 function is called by the assembly language interrupt stubs in
336 intr-stubs.S. FRAME describes the interrupt and the
337 interrupted thread's registers. */
338 void
339 intr_handler (struct intr_frame *frame)
341 bool external;
342 intr_handler_func *handler;
344 /* External interrupts are special.
345 We only handle one at a time (so interrupts must be off)
346 and they need to be acknowledged on the PIC (see below).
347 An external interrupt handler cannot sleep. */
348 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
349 if (external)
351 ASSERT (intr_get_level () == INTR_OFF);
352 ASSERT (!intr_context ());
354 in_external_intr = true;
355 yield_on_return = false;
358 /* Invoke the interrupt's handler. */
359 handler = intr_handlers[frame->vec_no];
360 if (handler != NULL)
361 handler (frame);
362 else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
364 /* There is no handler, but this interrupt can trigger
365 spuriously due to a hardware fault or hardware race
366 condition. Ignore it. */
368 else
370 /* No handler and not spurious. Invoke the unexpected
371 interrupt handler. */
372 intr_dump_frame (frame);
373 PANIC ("Unexpected interrupt");
376 /* Complete the processing of an external interrupt. */
377 if (external)
379 ASSERT (intr_get_level () == INTR_OFF);
380 ASSERT (intr_context ());
382 in_external_intr = false;
383 pic_end_of_interrupt (frame->vec_no);
385 if (yield_on_return)
386 thread_yield ();
390 /* Dumps interrupt frame F to the console, for debugging. */
391 void
392 intr_dump_frame (const struct intr_frame *f)
394 uint32_t cr2;
396 /* Store current value of CR2 into `cr2'.
397 CR2 is the linear address of the last page fault.
398 See [IA32-v2a] "MOV--Move to/from Control Registers" and
399 [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
400 (#PF)". */
401 asm ("movl %%cr2, %0" : "=r" (cr2));
403 printf ("Interrupt %#04x (%s) at eip=%p\n",
404 f->vec_no, intr_names[f->vec_no], f->eip);
405 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
406 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
407 f->eax, f->ebx, f->ecx, f->edx);
408 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
409 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
410 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
411 f->cs, f->ds, f->es, f->ss);
414 /* Returns the name of interrupt VEC. */
415 const char *
416 intr_name (uint8_t vec)
418 return intr_names[vec];