1 #include "threads/interrupt.h"
6 #include "threads/intr-stubs.h"
7 #include "threads/io.h"
8 #include "threads/mmu.h"
9 #include "threads/thread.h"
10 #include "devices/timer.h"
12 /* Number of x86 interrupts. */
15 /* The Interrupt Descriptor Table (IDT). The format is fixed by
16 the CPU. See [IA32-v3] sections 5.10, 5.11, 5.12.1.2. */
17 static uint64_t idt[INTR_CNT];
19 /* Interrupt handler functions for each interrupt. */
20 static intr_handler_func *intr_handlers[INTR_CNT];
22 /* Names for each interrupt, for debugging purposes. */
23 static const char *intr_names[INTR_CNT];
25 /* External interrupts are those generated by devices outside the
26 CPU, such as the timer. External interrupts run with
27 interrupts turned off, so they never nest, nor are they ever
28 pre-empted. Handlers for external interrupts also may not
29 sleep, although they may invoke intr_yield_on_return() to
30 request that a new process be scheduled just before the
32 static bool in_external_intr; /* Are we processing an external interrupt? */
33 static bool yield_on_return; /* Should we yield on interrupt return? */
35 /* Programmable Interrupt Controller helpers. */
36 static void pic_init (void);
37 static void pic_end_of_interrupt (int irq);
39 /* Interrupt Descriptor Table helpers. */
40 static uint64_t make_intr_gate (void (*) (void), int dpl);
41 static uint64_t make_trap_gate (void (*) (void), int dpl);
42 static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
44 /* Interrupt handlers. */
45 void intr_handler (struct intr_frame *args);
47 /* Returns the current interrupt status. */
53 asm ("pushfl; popl %0" : "=g" (flags));
55 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
58 /* Enables or disables interrupts as specified by LEVEL and
59 returns the previous interrupt status. */
61 intr_set_level (enum intr_level level)
63 return level == INTR_ON ? intr_enable () : intr_disable ();
66 /* Enables interrupts and returns the previous interrupt status. */
70 enum intr_level old_level = intr_get_level ();
71 ASSERT (!intr_context ());
76 /* Disables interrupts and returns the previous interrupt status. */
80 enum intr_level old_level = intr_get_level ();
85 /* Initializes the interrupt system. */
89 uint64_t idtr_operand;
92 /* Initialize interrupt controller. */
96 for (i = 0; i < INTR_CNT; i++)
97 idt[i] = make_intr_gate (intr_stubs[i], 0);
99 /* Load IDT register. */
100 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
101 asm volatile ("lidt %0" :: "m" (idtr_operand));
103 /* Initialize intr_names. */
104 for (i = 0; i < INTR_CNT; i++)
105 intr_names[i] = "unknown";
106 intr_names[0] = "#DE Divide Error";
107 intr_names[1] = "#DB Debug Exception";
108 intr_names[2] = "NMI Interrupt";
109 intr_names[3] = "#BP Breakpoint Exception";
110 intr_names[4] = "#OF Overflow Exception";
111 intr_names[5] = "#BR BOUND Range Exceeded Exception";
112 intr_names[6] = "#UD Invalid Opcode Exception";
113 intr_names[7] = "#NM Device Not Available Exception";
114 intr_names[8] = "#DF Double Fault Exception";
115 intr_names[9] = "Coprocessor Segment Overrun";
116 intr_names[10] = "#TS Invalid TSS Exception";
117 intr_names[11] = "#NP Segment Not Present";
118 intr_names[12] = "#SS Stack Fault Exception";
119 intr_names[13] = "#GP General Protection Exception";
120 intr_names[16] = "#MF x87 FPU Floating-Point Error";
121 intr_names[17] = "#AC Alignment Check Exception";
122 intr_names[18] = "#MC Machine-Check Exception";
123 intr_names[19] = "#XF SIMD Floating-Point Exception";
126 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
127 NAME for debugging purposes. The interrupt handler will be
128 invoked with interrupt status set to LEVEL.
130 The handler will have descriptor privilege level DPL, meaning
131 that it can be invoked intentionally when the processor is in
132 the DPL or lower-numbered ring. In practice, DPL==3 allows
133 user mode to invoke the interrupts and DPL==0 prevents such
134 invocation. Faults and exceptions that occur in user mode
135 still cause interrupts with DPL==0 to be invoked. See
136 [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
138 intr_register (uint8_t vec_no, int dpl, enum intr_level level,
139 intr_handler_func *handler,
142 /* Make sure this handler isn't already registered to someone
144 ASSERT (intr_handlers[vec_no] == NULL);
146 /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
147 0x2f) should specify INTR_OFF for LEVEL. Otherwise a timer
148 interrupt could cause a task switch during interrupt
149 handling. Most other interrupts can and should be handled
150 with interrupts enabled. */
151 ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
153 if (level == INTR_ON)
154 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
156 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
157 intr_handlers[vec_no] = handler;
158 intr_names[vec_no] = name;
161 /* Returns true during processing of an external interrupt
162 and false at all other times. */
166 return in_external_intr;
169 /* During processing of an external interrupt, directs the
170 interrupt handler to yield to a new process just before
171 returning from the interrupt. May not be called at any other
174 intr_yield_on_return (void)
176 ASSERT (intr_context ());
177 yield_on_return = true;
180 /* 8259A Programmable Interrupt Controller. */
182 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
183 chips. One is a "master" accessible at ports 0x20 and 0x21.
184 The other is a "slave" cascaded onto the master's IRQ 2 line
185 and accessible at ports 0xa0 and 0xa1. Accesses to port 0x20
186 set the A0 line to 0 and accesses to 0x21 set the A1 line to
187 1. The situation is similar for the slave PIC.
189 By default, interrupts 0...15 delivered by the PICs will go to
190 interrupt vectors 0...15. Unfortunately, those vectors are
191 also used for CPU traps and exceptions. We reprogram the PICs
192 so that interrupts 0...15 are delivered to interrupt vectors
193 32...47 (0x20...0x2f) instead. */
195 /* Initializes the PICs. Refer to [8259A] for details. */
199 /* Mask all interrupts on both PICs. */
203 /* Initialize master. */
204 outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
205 outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
206 outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
207 outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
209 /* Initialize slave. */
210 outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
211 outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
212 outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
213 outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
215 /* Unmask all interrupts. */
220 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
221 If we don't acknowledge the IRQ, it will never be delivered to
222 us again, so this is important. */
224 pic_end_of_interrupt (int irq)
226 ASSERT (irq >= 0x20 && irq < 0x30);
228 /* Acknowledge master PIC. */
231 /* Acknowledge slave PIC if this is a slave interrupt. */
236 /* Creates an gate that invokes FUNCTION.
238 The gate has descriptor privilege level DPL, meaning that it
239 can be invoked intentionally when the processor is in the DPL
240 or lower-numbered ring. In practice, DPL==3 allows user mode
241 to call into the gate and DPL==0 prevents such calls. Faults
242 and exceptions that occur in user mode still cause gates with
243 DPL==0 to be invoked. See [IA32-v3] sections 4.5 and 4.8.1.1
244 for further discussion.
246 TYPE must be either 14 (for an interrupt gate) or 15 (for a
247 trap gate). The difference is that entering an interrupt gate
248 disables interrupts, but entering a trap gate does not. See
249 [IA32-v3] section 5.12.1.2 for discussion. */
251 make_gate (void (*function) (void), int dpl, int type)
255 ASSERT (function != NULL);
256 ASSERT (dpl >= 0 && dpl <= 3);
257 ASSERT (type >= 0 && type <= 15);
259 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
260 | (SEL_KCSEG << 16)); /* Target code segment. */
262 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
263 | (1 << 15) /* Present. */
264 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
265 | (0 << 12) /* System. */
266 | ((uint32_t) type << 8)); /* Gate type. */
268 return e0 | ((uint64_t) e1 << 32);
271 /* Creates an interrupt gate that invokes FUNCTION with the given
274 make_intr_gate (void (*function) (void), int dpl)
276 return make_gate (function, dpl, 14);
279 /* Creates a trap gate that invokes FUNCTION with the given
282 make_trap_gate (void (*function) (void), int dpl)
284 return make_gate (function, dpl, 15);
287 /* Returns a descriptor that yields the given LIMIT and BASE when
288 used as an operand for the LIDT instruction. */
289 static inline uint64_t
290 make_idtr_operand (uint16_t limit, void *base)
292 return limit | ((uint64_t) (uint32_t) base << 16);
295 /* Interrupt handlers. */
297 /* Handler for all interrupts, faults, and exceptions. This
298 function is called by the assembly language interrupt stubs in
299 intr-stubs.S (see intr-stubs.pl). FRAME describes the
300 interrupt and the interrupted thread's registers. */
302 intr_handler (struct intr_frame *frame)
305 intr_handler_func *handler;
307 /* External interrupts are special.
308 We only handle one at a time (so interrupts must be off)
309 and they need to be acknowledged on the PIC (see below).
310 An external interrupt handler cannot sleep. */
311 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
314 ASSERT (intr_get_level () == INTR_OFF);
315 ASSERT (!intr_context ());
317 in_external_intr = true;
318 yield_on_return = false;
321 /* Invoke the interrupt's handler.
322 If there is no handler, invoke the unexpected interrupt
324 handler = intr_handlers[frame->vec_no];
327 intr_dump_frame (frame);
328 PANIC ("Unexpected interrupt");
332 /* Complete the processing of an external interrupt. */
335 ASSERT (intr_get_level () == INTR_OFF);
336 ASSERT (intr_context ());
338 in_external_intr = false;
339 pic_end_of_interrupt (frame->vec_no);
346 /* Dumps interrupt frame F to the console, for debugging. */
348 intr_dump_frame (const struct intr_frame *f)
351 asm ("movl %%cr2, %0" : "=r" (cr2));
352 asm ("movl %%ss, %0" : "=r" (ss));
354 printf ("Interrupt %#04x (%s) at eip=%p\n",
355 f->vec_no, intr_names[f->vec_no], f->eip);
356 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
357 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
358 f->eax, f->ebx, f->ecx, f->edx);
359 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
360 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
361 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
362 f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
365 /* Returns the name of interrupt VEC. */
367 intr_name (uint8_t vec)
369 return intr_names[vec];