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 ("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[16] = "#MF x87 FPU Floating-Point Error";
122 intr_names[17] = "#AC Alignment Check Exception";
123 intr_names[18] = "#MC Machine-Check Exception";
124 intr_names[19] = "#XF SIMD Floating-Point Exception";
127 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
128 NAME for debugging purposes. The interrupt handler will be
129 invoked with interrupt status set to LEVEL.
131 The handler will have descriptor privilege level DPL, meaning
132 that it can be invoked intentionally when the processor is in
133 the DPL or lower-numbered ring. In practice, DPL==3 allows
134 user mode to invoke the interrupts and DPL==0 prevents such
135 invocation. Faults and exceptions that occur in user mode
136 still cause interrupts with DPL==0 to be invoked. See
137 [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
139 intr_register (uint8_t vec_no, int dpl, enum intr_level level,
140 intr_handler_func *handler,
143 /* Make sure this handler isn't already registered to someone
145 ASSERT (intr_handlers[vec_no] == NULL);
147 /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
148 0x2f) should specify INTR_OFF for LEVEL. Otherwise a timer
149 interrupt could cause a task switch during interrupt
150 handling. Most other interrupts can and should be handled
151 with interrupts enabled. */
152 ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
154 if (level == INTR_ON)
155 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
157 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
158 intr_handlers[vec_no] = handler;
159 intr_names[vec_no] = name;
162 /* Returns true during processing of an external interrupt
163 and false at all other times. */
167 return in_external_intr;
170 /* During processing of an external interrupt, directs the
171 interrupt handler to yield to a new process just before
172 returning from the interrupt. May not be called at any other
175 intr_yield_on_return (void)
177 ASSERT (intr_context ());
178 yield_on_return = true;
181 /* 8259A Programmable Interrupt Controller. */
183 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
184 chips. One is a "master" accessible at ports 0x20 and 0x21.
185 The other is a "slave" cascaded onto the master's IRQ 2 line
186 and accessible at ports 0xa0 and 0xa1. Accesses to port 0x20
187 set the A0 line to 0 and accesses to 0x21 set the A1 line to
188 1. The situation is similar for the slave PIC.
190 By default, interrupts 0...15 delivered by the PICs will go to
191 interrupt vectors 0...15. Unfortunately, those vectors are
192 also used for CPU traps and exceptions. We reprogram the PICs
193 so that interrupts 0...15 are delivered to interrupt vectors
194 32...47 (0x20...0x2f) instead. */
196 /* Initializes the PICs. Refer to [8259A] for details. */
200 /* Mask all interrupts on both PICs. */
204 /* Initialize master. */
205 outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
206 outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
207 outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
208 outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
210 /* Initialize slave. */
211 outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
212 outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
213 outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
214 outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
216 /* Unmask all interrupts. */
221 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
222 If we don't acknowledge the IRQ, it will never be delivered to
223 us again, so this is important. */
225 pic_end_of_interrupt (int irq)
227 ASSERT (irq >= 0x20 && irq < 0x30);
229 /* Acknowledge master PIC. */
232 /* Acknowledge slave PIC if this is a slave interrupt. */
237 /* Creates an gate that invokes FUNCTION.
239 The gate has descriptor privilege level DPL, meaning that it
240 can be invoked intentionally when the processor is in the DPL
241 or lower-numbered ring. In practice, DPL==3 allows user mode
242 to call into the gate and DPL==0 prevents such calls. Faults
243 and exceptions that occur in user mode still cause gates with
244 DPL==0 to be invoked. See [IA32-v3] sections 4.5 and 4.8.1.1
245 for further discussion.
247 TYPE must be either 14 (for an interrupt gate) or 15 (for a
248 trap gate). The difference is that entering an interrupt gate
249 disables interrupts, but entering a trap gate does not. See
250 [IA32-v3] section 5.12.1.2 for discussion. */
252 make_gate (void (*function) (void), int dpl, int type)
256 ASSERT (function != NULL);
257 ASSERT (dpl >= 0 && dpl <= 3);
258 ASSERT (type >= 0 && type <= 15);
260 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
261 | (SEL_KCSEG << 16)); /* Target code segment. */
263 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
264 | (1 << 15) /* Present. */
265 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
266 | (0 << 12) /* System. */
267 | ((uint32_t) type << 8)); /* Gate type. */
269 return e0 | ((uint64_t) e1 << 32);
272 /* Creates an interrupt gate that invokes FUNCTION with the given
275 make_intr_gate (void (*function) (void), int dpl)
277 return make_gate (function, dpl, 14);
280 /* Creates a trap gate that invokes FUNCTION with the given
283 make_trap_gate (void (*function) (void), int dpl)
285 return make_gate (function, dpl, 15);
288 /* Returns a descriptor that yields the given LIMIT and BASE when
289 used as an operand for the LIDT instruction. */
290 static inline uint64_t
291 make_idtr_operand (uint16_t limit, void *base)
293 return limit | ((uint64_t) (uint32_t) base << 16);
296 /* Interrupt handlers. */
298 /* Handler for all interrupts, faults, and exceptions. This
299 function is called by the assembly language interrupt stubs in
300 intr-stubs.S (see intr-stubs.pl). FRAME describes the
301 interrupt and the interrupted thread's registers. */
303 intr_handler (struct intr_frame *frame)
306 intr_handler_func *handler;
308 /* External interrupts are special.
309 We only handle one at a time (so interrupts must be off)
310 and they need to be acknowledged on the PIC (see below).
311 An external interrupt handler cannot sleep. */
312 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
315 ASSERT (intr_get_level () == INTR_OFF);
316 ASSERT (!intr_context ());
318 in_external_intr = true;
319 yield_on_return = false;
322 /* Invoke the interrupt's handler.
323 If there is no handler, invoke the unexpected interrupt
325 handler = intr_handlers[frame->vec_no];
328 intr_dump_frame (frame);
329 PANIC ("Unexpected interrupt");
333 /* Complete the processing of an external interrupt. */
336 ASSERT (intr_get_level () == INTR_OFF);
337 ASSERT (intr_context ());
339 in_external_intr = false;
340 pic_end_of_interrupt (frame->vec_no);
347 /* Dumps interrupt frame F to the console, for debugging. */
349 intr_dump_frame (const struct intr_frame *f)
352 asm ("movl %%cr2, %0" : "=r" (cr2));
353 asm ("movl %%ss, %0" : "=r" (ss));
355 printf ("Interrupt %#04x (%s) at eip=%p\n",
356 f->vec_no, intr_names[f->vec_no], f->eip);
357 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
358 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
359 f->eax, f->ebx, f->ecx, f->edx);
360 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
361 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
362 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
363 f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
366 /* Returns the name of interrupt VEC. */
368 intr_name (uint8_t vec)
370 return intr_names[vec];