1 #include "threads/interrupt.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"
13 /* Number of x86 interrupts. */
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];
20 /* Interrupt handler functions for each interrupt. */
21 static intr_handler_func *intr_handlers[INTR_CNT];
23 /* Names for each interrupt, for debugging purposes. */
24 static const char *intr_names[INTR_CNT];
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
33 static bool in_external_intr; /* Are we processing an external interrupt? */
34 static bool yield_on_return; /* Should we yield on interrupt return? */
36 /* Programmable Interrupt Controller helpers. */
37 static void pic_init (void);
38 static void pic_end_of_interrupt (int irq);
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);
45 /* Interrupt handlers. */
46 void intr_handler (struct intr_frame *args);
48 /* Returns the current interrupt status. */
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));
59 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
62 /* Enables or disables interrupts as specified by LEVEL and
63 returns the previous interrupt status. */
65 intr_set_level (enum intr_level level)
67 return level == INTR_ON ? intr_enable () : intr_disable ();
70 /* Enables interrupts and returns the previous interrupt status. */
74 enum intr_level old_level = intr_get_level ();
75 ASSERT (!intr_context ());
77 /* Enable interrupts by setting the interrupt flag.
78 See [IA32-v2b] "STI" and [IA32-v3] 5.8.1. */
84 /* Disables interrupts and returns the previous interrupt status. */
88 enum intr_level old_level = intr_get_level ();
90 /* Disable interrupts by clearing the interrupt flag.
91 See [IA32-v2b] "CLI" and [IA32-v3] 5.8.1. */
97 /* Initializes the interrupt system. */
101 uint64_t idtr_operand;
104 /* Initialize interrupt controller. */
107 /* Initialize IDT. */
108 for (i = 0; i < INTR_CNT; i++)
109 idt[i] = make_intr_gate (intr_stubs[i], 0);
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));
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";
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. */
145 register_handler (uint8_t vec_no, int dpl, enum intr_level level,
146 intr_handler_func *handler, const char *name)
148 ASSERT (intr_handlers[vec_no] == NULL);
149 if (level == INTR_ON)
150 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
152 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
153 intr_handlers[vec_no] = handler;
154 intr_names[vec_no] = name;
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. */
161 intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
164 ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
165 register_handler (vec_no, 0, INTR_OFF, handler, name);
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.
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. */
180 intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
181 intr_handler_func *handler, const char *name)
183 ASSERT (vec_no < 0x20 || vec_no > 0x2f);
184 register_handler (vec_no, dpl, level, handler, name);
187 /* Returns true during processing of an external interrupt
188 and false at all other times. */
192 return in_external_intr;
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
200 intr_yield_on_return (void)
202 ASSERT (intr_context ());
203 yield_on_return = true;
206 /* 8259A Programmable Interrupt Controller. */
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.
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. */
221 /* Initializes the PICs. Refer to [8259A] for details. */
225 /* Mask all interrupts on both PICs. */
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. */
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. */
241 /* Unmask all interrupts. */
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. */
250 pic_end_of_interrupt (int irq)
252 ASSERT (irq >= 0x20 && irq < 0x30);
254 /* Acknowledge master PIC. */
257 /* Acknowledge slave PIC if this is a slave interrupt. */
262 /* Creates an gate that invokes FUNCTION.
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.
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. */
277 make_gate (void (*function) (void), int dpl, int type)
281 ASSERT (function != NULL);
282 ASSERT (dpl >= 0 && dpl <= 3);
283 ASSERT (type >= 0 && type <= 15);
285 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
286 | (SEL_KCSEG << 16)); /* Target code segment. */
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. */
294 return e0 | ((uint64_t) e1 << 32);
297 /* Creates an interrupt gate that invokes FUNCTION with the given
300 make_intr_gate (void (*function) (void), int dpl)
302 return make_gate (function, dpl, 14);
305 /* Creates a trap gate that invokes FUNCTION with the given
308 make_trap_gate (void (*function) (void), int dpl)
310 return make_gate (function, dpl, 15);
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)
318 return limit | ((uint64_t) (uint32_t) base << 16);
321 /* Interrupt handlers. */
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. */
328 intr_handler (struct intr_frame *frame)
331 intr_handler_func *handler;
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;
340 ASSERT (intr_get_level () == INTR_OFF);
341 ASSERT (!intr_context ());
343 in_external_intr = true;
344 yield_on_return = false;
347 /* Invoke the interrupt's handler.
348 If there is no handler, invoke the unexpected interrupt
350 handler = intr_handlers[frame->vec_no];
353 intr_dump_frame (frame);
354 PANIC ("Unexpected interrupt");
358 /* Complete the processing of an external interrupt. */
361 ASSERT (intr_get_level () == INTR_OFF);
362 ASSERT (intr_context ());
364 in_external_intr = false;
365 pic_end_of_interrupt (frame->vec_no);
372 /* Dumps interrupt frame F to the console, for debugging. */
374 intr_dump_frame (const struct intr_frame *f)
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
383 asm ("movl %%cr2, %0" : "=r" (cr2));
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);
396 /* Returns the name of interrupt VEC. */
398 intr_name (uint8_t vec)
400 return intr_names[vec];