1 #include "threads/interrupt.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 /* Programmable Interrupt Controller (PIC) registers.
14 A PC has two PICs, called the master and slave PICs, with the
15 slave attached ("cascaded") to the master IRQ line 2. */
16 #define PIC0_CTRL 0x20 /* Master PIC control register address. */
17 #define PIC0_DATA 0x21 /* Master PIC data register address. */
18 #define PIC1_CTRL 0xa0 /* Slave PIC control register address. */
19 #define PIC1_DATA 0xa1 /* Slave PIC data register address. */
21 /* Number of x86 interrupts. */
24 /* The Interrupt Descriptor Table (IDT). The format is fixed by
25 the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor
26 Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
27 Exception- or Interrupt-Handler Procedure". */
28 static uint64_t idt[INTR_CNT];
30 /* Interrupt handler functions for each interrupt. */
31 static intr_handler_func *intr_handlers[INTR_CNT];
33 /* Names for each interrupt, for debugging purposes. */
34 static const char *intr_names[INTR_CNT];
36 /* External interrupts are those generated by devices outside the
37 CPU, such as the timer. External interrupts run with
38 interrupts turned off, so they never nest, nor are they ever
39 pre-empted. Handlers for external interrupts also may not
40 sleep, although they may invoke intr_yield_on_return() to
41 request that a new process be scheduled just before the
43 static bool in_external_intr; /* Are we processing an external interrupt? */
44 static bool yield_on_return; /* Should we yield on interrupt return? */
46 /* Programmable Interrupt Controller helpers. */
47 static void pic_init (void);
48 static void pic_end_of_interrupt (int irq);
50 /* Interrupt Descriptor Table helpers. */
51 static uint64_t make_intr_gate (void (*) (void), int dpl);
52 static uint64_t make_trap_gate (void (*) (void), int dpl);
53 static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
55 /* Interrupt handlers. */
56 void intr_handler (struct intr_frame *args);
58 /* Returns the current interrupt status. */
64 /* Push the flags register on the processor stack, then pop the
65 value off the stack into `flags'. See [IA32-v2b] "PUSHF"
66 and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
68 asm volatile ("pushfl; popl %0" : "=g" (flags));
70 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
73 /* Enables or disables interrupts as specified by LEVEL and
74 returns the previous interrupt status. */
76 intr_set_level (enum intr_level level)
78 return level == INTR_ON ? intr_enable () : intr_disable ();
81 /* Enables interrupts and returns the previous interrupt status. */
85 enum intr_level old_level = intr_get_level ();
86 ASSERT (!intr_context ());
88 /* Enable interrupts by setting the interrupt flag.
90 See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
91 Hardware Interrupts". */
97 /* Disables interrupts and returns the previous interrupt status. */
101 enum intr_level old_level = intr_get_level ();
103 /* Disable interrupts by clearing the interrupt flag.
104 See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
105 Hardware Interrupts". */
106 asm volatile ("cli" : : : "memory");
111 /* Initializes the interrupt system. */
115 uint64_t idtr_operand;
118 /* Initialize interrupt controller. */
121 /* Initialize IDT. */
122 for (i = 0; i < INTR_CNT; i++)
123 idt[i] = make_intr_gate (intr_stubs[i], 0);
125 /* Load IDT register.
126 See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
127 Descriptor Table (IDT)". */
128 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
129 asm volatile ("lidt %0" : : "m" (idtr_operand));
131 /* Initialize intr_names. */
132 for (i = 0; i < INTR_CNT; i++)
133 intr_names[i] = "unknown";
134 intr_names[0] = "#DE Divide Error";
135 intr_names[1] = "#DB Debug Exception";
136 intr_names[2] = "NMI Interrupt";
137 intr_names[3] = "#BP Breakpoint Exception";
138 intr_names[4] = "#OF Overflow Exception";
139 intr_names[5] = "#BR BOUND Range Exceeded Exception";
140 intr_names[6] = "#UD Invalid Opcode Exception";
141 intr_names[7] = "#NM Device Not Available Exception";
142 intr_names[8] = "#DF Double Fault Exception";
143 intr_names[9] = "Coprocessor Segment Overrun";
144 intr_names[10] = "#TS Invalid TSS Exception";
145 intr_names[11] = "#NP Segment Not Present";
146 intr_names[12] = "#SS Stack Fault Exception";
147 intr_names[13] = "#GP General Protection Exception";
148 intr_names[14] = "#PF Page-Fault Exception";
149 intr_names[16] = "#MF x87 FPU Floating-Point Error";
150 intr_names[17] = "#AC Alignment Check Exception";
151 intr_names[18] = "#MC Machine-Check Exception";
152 intr_names[19] = "#XF SIMD Floating-Point Exception";
155 /* Registers interrupt VEC_NO to invoke HANDLER with descriptor
156 privilege level DPL. Names the interrupt NAME for debugging
157 purposes. The interrupt handler will be invoked with
158 interrupt status set to LEVEL. */
160 register_handler (uint8_t vec_no, int dpl, enum intr_level level,
161 intr_handler_func *handler, const char *name)
163 ASSERT (intr_handlers[vec_no] == NULL);
164 if (level == INTR_ON)
165 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
167 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
168 intr_handlers[vec_no] = handler;
169 intr_names[vec_no] = name;
172 /* Registers external interrupt VEC_NO to invoke HANDLER, which
173 is named NAME for debugging purposes. The handler will
174 execute with interrupts disabled. */
176 intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
179 ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
180 register_handler (vec_no, 0, INTR_OFF, handler, name);
183 /* Registers internal interrupt VEC_NO to invoke HANDLER, which
184 is named NAME for debugging purposes. The interrupt handler
185 will be invoked with interrupt status LEVEL.
187 The handler will have descriptor privilege level DPL, meaning
188 that it can be invoked intentionally when the processor is in
189 the DPL or lower-numbered ring. In practice, DPL==3 allows
190 user mode to invoke the interrupts and DPL==0 prevents such
191 invocation. Faults and exceptions that occur in user mode
192 still cause interrupts with DPL==0 to be invoked. See
193 [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
194 "Accessing Nonconforming Code Segments" for further
197 intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
198 intr_handler_func *handler, const char *name)
200 ASSERT (vec_no < 0x20 || vec_no > 0x2f);
201 register_handler (vec_no, dpl, level, handler, name);
204 /* Returns true during processing of an external interrupt
205 and false at all other times. */
209 return in_external_intr;
212 /* During processing of an external interrupt, directs the
213 interrupt handler to yield to a new process just before
214 returning from the interrupt. May not be called at any other
217 intr_yield_on_return (void)
219 ASSERT (intr_context ());
220 yield_on_return = true;
223 /* 8259A Programmable Interrupt Controller. */
225 /* Initializes the PICs. Refer to [8259A] for details.
227 By default, interrupts 0...15 delivered by the PICs will go to
228 interrupt vectors 0...15. Those vectors are also used for CPU
229 traps and exceptions, so we reprogram the PICs so that
230 interrupts 0...15 are delivered to interrupt vectors 32...47
231 (0x20...0x2f) instead. */
235 /* Mask all interrupts on both PICs. */
236 outb (PIC0_DATA, 0xff);
237 outb (PIC1_DATA, 0xff);
239 /* Initialize master. */
240 outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
241 outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
242 outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */
243 outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
245 /* Initialize slave. */
246 outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
247 outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
248 outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */
249 outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
251 /* Unmask all interrupts. */
252 outb (PIC0_DATA, 0x00);
253 outb (PIC1_DATA, 0x00);
256 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
257 If we don't acknowledge the IRQ, it will never be delivered to
258 us again, so this is important. */
260 pic_end_of_interrupt (int irq)
262 ASSERT (irq >= 0x20 && irq < 0x30);
264 /* Acknowledge master PIC. */
267 /* Acknowledge slave PIC if this is a slave interrupt. */
272 /* Creates an gate that invokes FUNCTION.
274 The gate has descriptor privilege level DPL, meaning that it
275 can be invoked intentionally when the processor is in the DPL
276 or lower-numbered ring. In practice, DPL==3 allows user mode
277 to call into the gate and DPL==0 prevents such calls. Faults
278 and exceptions that occur in user mode still cause gates with
279 DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege
280 Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
281 for further discussion.
283 TYPE must be either 14 (for an interrupt gate) or 15 (for a
284 trap gate). The difference is that entering an interrupt gate
285 disables interrupts, but entering a trap gate does not. See
286 [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
287 Interrupt-Handler Procedure" for discussion. */
289 make_gate (void (*function) (void), int dpl, int type)
293 ASSERT (function != NULL);
294 ASSERT (dpl >= 0 && dpl <= 3);
295 ASSERT (type >= 0 && type <= 15);
297 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
298 | (SEL_KCSEG << 16)); /* Target code segment. */
300 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
301 | (1 << 15) /* Present. */
302 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
303 | (0 << 12) /* System. */
304 | ((uint32_t) type << 8)); /* Gate type. */
306 return e0 | ((uint64_t) e1 << 32);
309 /* Creates an interrupt gate that invokes FUNCTION with the given
312 make_intr_gate (void (*function) (void), int dpl)
314 return make_gate (function, dpl, 14);
317 /* Creates a trap gate that invokes FUNCTION with the given
320 make_trap_gate (void (*function) (void), int dpl)
322 return make_gate (function, dpl, 15);
325 /* Returns a descriptor that yields the given LIMIT and BASE when
326 used as an operand for the LIDT instruction. */
327 static inline uint64_t
328 make_idtr_operand (uint16_t limit, void *base)
330 return limit | ((uint64_t) (uint32_t) base << 16);
333 /* Interrupt handlers. */
335 /* Handler for all interrupts, faults, and exceptions. This
336 function is called by the assembly language interrupt stubs in
337 intr-stubs.S. FRAME describes the interrupt and the
338 interrupted thread's registers. */
340 intr_handler (struct intr_frame *frame)
343 intr_handler_func *handler;
345 /* External interrupts are special.
346 We only handle one at a time (so interrupts must be off)
347 and they need to be acknowledged on the PIC (see below).
348 An external interrupt handler cannot sleep. */
349 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
352 ASSERT (intr_get_level () == INTR_OFF);
353 ASSERT (!intr_context ());
355 in_external_intr = true;
356 yield_on_return = false;
359 /* Invoke the interrupt's handler. */
360 handler = intr_handlers[frame->vec_no];
363 else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
365 /* There is no handler, but this interrupt can trigger
366 spuriously due to a hardware fault or hardware race
367 condition. Ignore it. */
371 /* No handler and not spurious. Invoke the unexpected
372 interrupt handler. */
373 intr_dump_frame (frame);
374 PANIC ("Unexpected interrupt");
377 /* Complete the processing of an external interrupt. */
380 ASSERT (intr_get_level () == INTR_OFF);
381 ASSERT (intr_context ());
383 in_external_intr = false;
384 pic_end_of_interrupt (frame->vec_no);
391 /* Dumps interrupt frame F to the console, for debugging. */
393 intr_dump_frame (const struct intr_frame *f)
397 /* Store current value of CR2 into `cr2'.
398 CR2 is the linear address of the last page fault.
399 See [IA32-v2a] "MOV--Move to/from Control Registers" and
400 [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
402 asm ("movl %%cr2, %0" : "=r" (cr2));
404 printf ("Interrupt %#04x (%s) at eip=%p\n",
405 f->vec_no, intr_names[f->vec_no], f->eip);
406 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
407 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
408 f->eax, f->ebx, f->ecx, f->edx);
409 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
410 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
411 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
412 f->cs, f->ds, f->es, f->ss);
415 /* Returns the name of interrupt VEC. */
417 intr_name (uint8_t vec)
419 return intr_names[vec];