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. */
20 #define IRQ_CASCADE0 2
21 #define IRQ_CASCADE1 9
23 /* Programmable Interrupt Controller (PIC) registers.
24 A PC has two PICs, called the master and slave PICs, with the
25 slave attached ("cascaded") to the master IRQ line 2. */
26 #define PIC0_CTRL 0x20 /* Master PIC control register address. */
27 #define PIC0_DATA 0x21 /* Master PIC data register address. */
28 #define PIC1_CTRL 0xa0 /* Slave PIC control register address. */
29 #define PIC1_DATA 0xa1 /* Slave PIC data register address. */
31 /* Number of x86 interrupts. */
34 /* The Interrupt Descriptor Table (IDT). The format is fixed by
35 the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor
36 Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
37 Exception- or Interrupt-Handler Procedure". */
38 static uint64_t idt[INTR_CNT];
40 /* Interrupt handler functions for each interrupt. */
41 static intr_handler_func *intr_handlers[INTR_CNT];
43 /* Names for each interrupt, for debugging purposes. */
44 static const char *intr_names[INTR_CNT];
46 /* cached values for PIC */
47 static uint8_t pic_mask[2];
49 /* External interrupts are those generated by devices outside the
50 CPU, such as the timer. External interrupts run with
51 interrupts turned off, so they never nest, nor are they ever
52 pre-empted. Handlers for external interrupts also may not
53 sleep, although they may invoke intr_yield_on_return() to
54 request that a new process be scheduled just before the
56 static bool in_external_intr; /* Are we processing an external interrupt? */
57 static bool yield_on_return; /* Should we yield on interrupt return? */
59 /* Programmable Interrupt Controller helpers. */
60 static void pic_init (void);
61 static void pic_end_of_interrupt (int irq);
63 /* Interrupt Descriptor Table helpers. */
64 static uint64_t make_intr_gate (void (*) (void), int dpl);
65 static uint64_t make_trap_gate (void (*) (void), int dpl);
66 static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
68 /* Interrupt handlers. */
69 void intr_handler (struct intr_frame *args);
71 /* Returns the current interrupt status. */
77 /* Push the flags register on the processor stack, then pop the
78 value off the stack into `flags'. See [IA32-v2b] "PUSHF"
79 and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
81 asm volatile ("pushfl; popl %0" : "=g" (flags));
83 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
86 /* Enables or disables interrupts as specified by LEVEL and
87 returns the previous interrupt status. */
89 intr_set_level (enum intr_level level)
91 return level == INTR_ON ? intr_enable () : intr_disable ();
94 /* Enables interrupts and returns the previous interrupt status. */
98 enum intr_level old_level = intr_get_level ();
99 ASSERT (!intr_context ());
101 /* Enable interrupts by setting the interrupt flag.
103 See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
104 Hardware Interrupts". */
105 asm volatile ("sti");
110 /* Disables interrupts and returns the previous interrupt status. */
114 enum intr_level old_level = intr_get_level ();
116 /* Disable interrupts by clearing the interrupt flag.
117 See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
118 Hardware Interrupts". */
119 asm volatile ("cli" : : : "memory");
124 /* Initializes the interrupt system. */
128 uint64_t idtr_operand;
131 /* Initialize interrupt controller. */
134 /* Initialize IDT. */
135 for (i = 0; i < INTR_CNT; i++)
136 idt[i] = make_intr_gate (intr_stubs[i], 0);
138 /* Load IDT register.
139 See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
140 Descriptor Table (IDT)". */
141 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
142 asm volatile ("lidt %0" : : "m" (idtr_operand));
144 /* Initialize intr_names. */
145 for (i = 0; i < INTR_CNT; i++)
146 intr_names[i] = "unknown";
147 intr_names[0] = "#DE Divide Error";
148 intr_names[1] = "#DB Debug Exception";
149 intr_names[2] = "NMI Interrupt";
150 intr_names[3] = "#BP Breakpoint Exception";
151 intr_names[4] = "#OF Overflow Exception";
152 intr_names[5] = "#BR BOUND Range Exceeded Exception";
153 intr_names[6] = "#UD Invalid Opcode Exception";
154 intr_names[7] = "#NM Device Not Available Exception";
155 intr_names[8] = "#DF Double Fault Exception";
156 intr_names[9] = "Coprocessor Segment Overrun";
157 intr_names[10] = "#TS Invalid TSS Exception";
158 intr_names[11] = "#NP Segment Not Present";
159 intr_names[12] = "#SS Stack Fault Exception";
160 intr_names[13] = "#GP General Protection Exception";
161 intr_names[14] = "#PF Page-Fault Exception";
162 intr_names[16] = "#MF x87 FPU Floating-Point Error";
163 intr_names[17] = "#AC Alignment Check Exception";
164 intr_names[18] = "#MC Machine-Check Exception";
165 intr_names[19] = "#XF SIMD Floating-Point Exception";
168 /* Registers interrupt VEC_NO to invoke HANDLER with descriptor
169 privilege level DPL. Names the interrupt NAME for debugging
170 purposes. The interrupt handler will be invoked with
171 interrupt status set to LEVEL. */
173 register_handler (uint8_t vec_no, int dpl, enum intr_level level,
174 intr_handler_func *handler, const char *name)
176 ASSERT (intr_handlers[vec_no] == NULL);
177 if (level == INTR_ON)
178 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
180 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
181 intr_handlers[vec_no] = handler;
182 intr_names[vec_no] = name;
185 /* Registers external interrupt VEC_NO to invoke HANDLER, which
186 is named NAME for debugging purposes. The handler will
187 execute with interrupts disabled. */
189 intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
192 ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
193 register_handler (vec_no, 0, INTR_OFF, handler, name);
196 /* Registers internal interrupt VEC_NO to invoke HANDLER, which
197 is named NAME for debugging purposes. The interrupt handler
198 will be invoked with interrupt status LEVEL.
200 The handler will have descriptor privilege level DPL, meaning
201 that it can be invoked intentionally when the processor is in
202 the DPL or lower-numbered ring. In practice, DPL==3 allows
203 user mode to invoke the interrupts and DPL==0 prevents such
204 invocation. Faults and exceptions that occur in user mode
205 still cause interrupts with DPL==0 to be invoked. See
206 [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
207 "Accessing Nonconforming Code Segments" for further
210 intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
211 intr_handler_func *handler, const char *name)
213 ASSERT (vec_no < 0x20 || vec_no > 0x2f);
214 register_handler (vec_no, dpl, level, handler, name);
217 /* Returns true during processing of an external interrupt
218 and false at all other times. */
222 return in_external_intr;
225 /* During processing of an external interrupt, directs the
226 interrupt handler to yield to a new process just before
227 returning from the interrupt. May not be called at any other
230 intr_yield_on_return (void)
232 ASSERT (intr_context ());
233 yield_on_return = true;
236 /* 8259A Programmable Interrupt Controller. */
238 /* Initializes the PICs. Refer to [8259A] for details.
240 By default, interrupts 0...15 delivered by the PICs will go to
241 interrupt vectors 0...15. Those vectors are also used for CPU
242 traps and exceptions, so we reprogram the PICs so that
243 interrupts 0...15 are delivered to interrupt vectors 32...47
244 (0x20...0x2f) instead. */
248 /* Mask all interrupts on both PICs. */
249 outb (PIC0_DATA, 0xff);
250 outb (PIC1_DATA, 0xff);
252 /* Initialize master. */
253 outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
254 outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
255 outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */
256 outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
258 /* Initialize slave. */
259 outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
260 outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
261 outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */
262 outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
264 /* Unmask all interrupts. */
265 outb (PIC0_DATA, 0x00);
266 outb (PIC1_DATA, 0x00);
271 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
272 If we don't acknowledge the IRQ, it will never be delivered to
273 us again, so this is important. */
275 pic_end_of_interrupt (int irq)
277 ASSERT (irq >= 0x20 && irq < 0x30);
279 /* Acknowledge master PIC. */
282 /* Acknowledge slave PIC if this is a slave interrupt. */
287 /* Creates an gate that invokes FUNCTION.
289 The gate has descriptor privilege level DPL, meaning that it
290 can be invoked intentionally when the processor is in the DPL
291 or lower-numbered ring. In practice, DPL==3 allows user mode
292 to call into the gate and DPL==0 prevents such calls. Faults
293 and exceptions that occur in user mode still cause gates with
294 DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege
295 Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
296 for further discussion.
298 TYPE must be either 14 (for an interrupt gate) or 15 (for a
299 trap gate). The difference is that entering an interrupt gate
300 disables interrupts, but entering a trap gate does not. See
301 [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
302 Interrupt-Handler Procedure" for discussion. */
304 make_gate (void (*function) (void), int dpl, int type)
308 ASSERT (function != NULL);
309 ASSERT (dpl >= 0 && dpl <= 3);
310 ASSERT (type >= 0 && type <= 15);
312 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
313 | (SEL_KCSEG << 16)); /* Target code segment. */
315 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
316 | (1 << 15) /* Present. */
317 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
318 | (0 << 12) /* System. */
319 | ((uint32_t) type << 8)); /* Gate type. */
321 return e0 | ((uint64_t) e1 << 32);
324 /* Creates an interrupt gate that invokes FUNCTION with the given
327 make_intr_gate (void (*function) (void), int dpl)
329 return make_gate (function, dpl, 14);
332 /* Creates a trap gate that invokes FUNCTION with the given
335 make_trap_gate (void (*function) (void), int dpl)
337 return make_gate (function, dpl, 15);
340 /* Returns a descriptor that yields the given LIMIT and BASE when
341 used as an operand for the LIDT instruction. */
342 static inline uint64_t
343 make_idtr_operand (uint16_t limit, void *base)
345 return limit | ((uint64_t) (uint32_t) base << 16);
348 /* Interrupt handlers. */
350 /* Handler for all interrupts, faults, and exceptions. This
351 function is called by the assembly language interrupt stubs in
352 intr-stubs.S. FRAME describes the interrupt and the
353 interrupted thread's registers. */
355 intr_handler (struct intr_frame *frame)
358 intr_handler_func *handler;
360 /* External interrupts are special.
361 We only handle one at a time (so interrupts must be off)
362 and they need to be acknowledged on the PIC (see below).
363 An external interrupt handler cannot sleep. */
364 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
367 ASSERT (intr_get_level () == INTR_OFF);
368 ASSERT (!intr_context ());
370 in_external_intr = true;
371 yield_on_return = false;
374 /* Invoke the interrupt's handler. */
375 handler = intr_handlers[frame->vec_no];
378 else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
380 /* There is no handler, but this interrupt can trigger
381 spuriously due to a hardware fault or hardware race
382 condition. Ignore it. */
386 /* No handler and not spurious. Invoke the unexpected
387 interrupt handler. */
388 intr_dump_frame (frame);
389 PANIC ("Unexpected interrupt");
392 /* Complete the processing of an external interrupt. */
395 ASSERT (intr_get_level () == INTR_OFF);
396 ASSERT (intr_context ());
398 in_external_intr = false;
399 pic_end_of_interrupt (frame->vec_no);
406 /* Dumps interrupt frame F to the console, for debugging. */
408 intr_dump_frame (const struct intr_frame *f)
412 /* Store current value of CR2 into `cr2'.
413 CR2 is the linear address of the last page fault.
414 See [IA32-v2a] "MOV--Move to/from Control Registers" and
415 [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
417 asm ("movl %%cr2, %0" : "=r" (cr2));
419 printf ("Interrupt %#04x (%s) at eip=%p\n",
420 f->vec_no, intr_names[f->vec_no], f->eip);
421 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
422 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
423 f->eax, f->ebx, f->ecx, f->edx);
424 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
425 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
426 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
427 f->cs, f->ds, f->es, f->ss);
430 /* Returns the name of interrupt VEC. */
432 intr_name (uint8_t vec)
434 return intr_names[vec];
437 /** masks a given IRQ */
438 void intr_irq_mask(int irq)
441 pic_mask[0] |= 1 << irq;
442 outb (PIC0_DATA, pic_mask[0]);
444 pic_mask[1] |= 1 << (irq - 8);
445 outb (PIC1_DATA, pic_mask[1]);
449 /** unmasks a given IRQ */
450 void intr_irq_unmask(int irq)
453 /* enable cascade if not enabled for pic2 */
454 if(pic_mask[1] & (1 << (IRQ_CASCADE1 - 8)))
455 pic_mask[1] &= ~(1 << (IRQ_CASCADE1 - 8));
457 pic_mask[1] &= ~(1 << (irq - 8));
458 outb(PIC1_DATA, pic_mask[1]);
460 /* enable cascade if not enabled for pic1 */
461 if(pic_mask[0] & (1 << IRQ_CASCADE0))
466 pic_mask[0] &= ~(1 << irq);
467 outb (PIC0_DATA, pic_mask[0]);
472 /* return whether an interrupt vector is registered */
473 bool intr_is_registered(uint8_t vec_no)
475 return (intr_handlers[vec_no] != NULL);