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 asm volatile ("pushfl; popl %0" : "=g" (flags));
56 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
59 /* Enables or disables interrupts as specified by LEVEL and
60 returns the previous interrupt status. */
62 intr_set_level (enum intr_level level)
64 return level == INTR_ON ? intr_enable () : intr_disable ();
67 /* Enables interrupts and returns the previous interrupt status. */
71 enum intr_level old_level = intr_get_level ();
72 ASSERT (!intr_context ());
77 /* Disables interrupts and returns the previous interrupt status. */
81 enum intr_level old_level = intr_get_level ();
86 /* Initializes the interrupt system. */
90 uint64_t idtr_operand;
93 /* Initialize interrupt controller. */
97 for (i = 0; i < INTR_CNT; i++)
98 idt[i] = make_intr_gate (intr_stubs[i], 0);
100 /* Load IDT register. */
101 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
102 asm volatile ("lidt %0" :: "m" (idtr_operand));
104 /* Initialize intr_names. */
105 for (i = 0; i < INTR_CNT; i++)
106 intr_names[i] = "unknown";
107 intr_names[0] = "#DE Divide Error";
108 intr_names[1] = "#DB Debug Exception";
109 intr_names[2] = "NMI Interrupt";
110 intr_names[3] = "#BP Breakpoint Exception";
111 intr_names[4] = "#OF Overflow Exception";
112 intr_names[5] = "#BR BOUND Range Exceeded Exception";
113 intr_names[6] = "#UD Invalid Opcode Exception";
114 intr_names[7] = "#NM Device Not Available Exception";
115 intr_names[8] = "#DF Double Fault Exception";
116 intr_names[9] = "Coprocessor Segment Overrun";
117 intr_names[10] = "#TS Invalid TSS Exception";
118 intr_names[11] = "#NP Segment Not Present";
119 intr_names[12] = "#SS Stack Fault Exception";
120 intr_names[13] = "#GP General Protection Exception";
121 intr_names[14] = "#PF Page-Fault Exception";
122 intr_names[16] = "#MF x87 FPU Floating-Point Error";
123 intr_names[17] = "#AC Alignment Check Exception";
124 intr_names[18] = "#MC Machine-Check Exception";
125 intr_names[19] = "#XF SIMD Floating-Point Exception";
128 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
129 NAME for debugging purposes. The interrupt handler will be
130 invoked with interrupt status set to LEVEL.
132 The handler will have descriptor privilege level DPL, meaning
133 that it can be invoked intentionally when the processor is in
134 the DPL or lower-numbered ring. In practice, DPL==3 allows
135 user mode to invoke the interrupts and DPL==0 prevents such
136 invocation. Faults and exceptions that occur in user mode
137 still cause interrupts with DPL==0 to be invoked. See
138 [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
140 intr_register (uint8_t vec_no, int dpl, enum intr_level level,
141 intr_handler_func *handler,
144 /* Make sure this handler isn't already registered to someone
146 ASSERT (intr_handlers[vec_no] == NULL);
148 /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
149 0x2f) should specify INTR_OFF for LEVEL. Otherwise a timer
150 interrupt could cause a task switch during interrupt
151 handling. Most other interrupts can and should be handled
152 with interrupts enabled. */
153 ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
155 if (level == INTR_ON)
156 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
158 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
159 intr_handlers[vec_no] = handler;
160 intr_names[vec_no] = name;
163 /* Returns true during processing of an external interrupt
164 and false at all other times. */
168 return in_external_intr;
171 /* During processing of an external interrupt, directs the
172 interrupt handler to yield to a new process just before
173 returning from the interrupt. May not be called at any other
176 intr_yield_on_return (void)
178 ASSERT (intr_context ());
179 yield_on_return = true;
182 /* 8259A Programmable Interrupt Controller. */
184 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
185 chips. One is a "master" accessible at ports 0x20 and 0x21.
186 The other is a "slave" cascaded onto the master's IRQ 2 line
187 and accessible at ports 0xa0 and 0xa1. Accesses to port 0x20
188 set the A0 line to 0 and accesses to 0x21 set the A1 line to
189 1. The situation is similar for the slave PIC.
191 By default, interrupts 0...15 delivered by the PICs will go to
192 interrupt vectors 0...15. Unfortunately, those vectors are
193 also used for CPU traps and exceptions. We reprogram the PICs
194 so that interrupts 0...15 are delivered to interrupt vectors
195 32...47 (0x20...0x2f) instead. */
197 /* Initializes the PICs. Refer to [8259A] for details. */
201 /* Mask all interrupts on both PICs. */
205 /* Initialize master. */
206 outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
207 outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
208 outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
209 outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
211 /* Initialize slave. */
212 outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
213 outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
214 outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
215 outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
217 /* Unmask all interrupts. */
222 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
223 If we don't acknowledge the IRQ, it will never be delivered to
224 us again, so this is important. */
226 pic_end_of_interrupt (int irq)
228 ASSERT (irq >= 0x20 && irq < 0x30);
230 /* Acknowledge master PIC. */
233 /* Acknowledge slave PIC if this is a slave interrupt. */
238 /* Creates an gate that invokes FUNCTION.
240 The gate has descriptor privilege level DPL, meaning that it
241 can be invoked intentionally when the processor is in the DPL
242 or lower-numbered ring. In practice, DPL==3 allows user mode
243 to call into the gate and DPL==0 prevents such calls. Faults
244 and exceptions that occur in user mode still cause gates with
245 DPL==0 to be invoked. See [IA32-v3] sections 4.5 and 4.8.1.1
246 for further discussion.
248 TYPE must be either 14 (for an interrupt gate) or 15 (for a
249 trap gate). The difference is that entering an interrupt gate
250 disables interrupts, but entering a trap gate does not. See
251 [IA32-v3] section 5.12.1.2 for discussion. */
253 make_gate (void (*function) (void), int dpl, int type)
257 ASSERT (function != NULL);
258 ASSERT (dpl >= 0 && dpl <= 3);
259 ASSERT (type >= 0 && type <= 15);
261 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
262 | (SEL_KCSEG << 16)); /* Target code segment. */
264 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
265 | (1 << 15) /* Present. */
266 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
267 | (0 << 12) /* System. */
268 | ((uint32_t) type << 8)); /* Gate type. */
270 return e0 | ((uint64_t) e1 << 32);
273 /* Creates an interrupt gate that invokes FUNCTION with the given
276 make_intr_gate (void (*function) (void), int dpl)
278 return make_gate (function, dpl, 14);
281 /* Creates a trap gate that invokes FUNCTION with the given
284 make_trap_gate (void (*function) (void), int dpl)
286 return make_gate (function, dpl, 15);
289 /* Returns a descriptor that yields the given LIMIT and BASE when
290 used as an operand for the LIDT instruction. */
291 static inline uint64_t
292 make_idtr_operand (uint16_t limit, void *base)
294 return limit | ((uint64_t) (uint32_t) base << 16);
297 /* Interrupt handlers. */
299 /* Handler for all interrupts, faults, and exceptions. This
300 function is called by the assembly language interrupt stubs in
301 intr-stubs.S (see intr-stubs.pl). FRAME describes the
302 interrupt and the interrupted thread's registers. */
304 intr_handler (struct intr_frame *frame)
307 intr_handler_func *handler;
309 /* External interrupts are special.
310 We only handle one at a time (so interrupts must be off)
311 and they need to be acknowledged on the PIC (see below).
312 An external interrupt handler cannot sleep. */
313 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
316 ASSERT (intr_get_level () == INTR_OFF);
317 ASSERT (!intr_context ());
319 in_external_intr = true;
320 yield_on_return = false;
323 /* Invoke the interrupt's handler.
324 If there is no handler, invoke the unexpected interrupt
326 handler = intr_handlers[frame->vec_no];
329 intr_dump_frame (frame);
330 PANIC ("Unexpected interrupt");
334 /* Complete the processing of an external interrupt. */
337 ASSERT (intr_get_level () == INTR_OFF);
338 ASSERT (intr_context ());
340 in_external_intr = false;
341 pic_end_of_interrupt (frame->vec_no);
348 /* Dumps interrupt frame F to the console, for debugging. */
350 intr_dump_frame (const struct intr_frame *f)
353 asm ("movl %%cr2, %0" : "=r" (cr2));
354 asm ("movl %%ss, %0" : "=r" (ss));
356 printf ("Interrupt %#04x (%s) at eip=%p\n",
357 f->vec_no, intr_names[f->vec_no], f->eip);
358 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
359 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
360 f->eax, f->ebx, f->ecx, f->edx);
361 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
362 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
363 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
364 f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
367 /* Returns the name of interrupt VEC. */
369 intr_name (uint8_t vec)
371 return intr_names[vec];