4 #include "intr-stubs.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 ();
75 /* Disables interrupts and returns the previous interrupt status. */
79 enum intr_level old_level = intr_get_level ();
84 /* Initializes the interrupt system. */
88 uint64_t idtr_operand;
91 /* Initialize interrupt controller. */
95 for (i = 0; i < INTR_CNT; i++)
96 idt[i] = make_trap_gate (intr_stubs[i], 0);
98 /* Load IDT register. */
99 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
100 asm volatile ("lidt %0" :: "m" (idtr_operand));
102 /* Initialize intr_names. */
103 for (i = 0; i < INTR_CNT; i++)
104 intr_names[i] = "unknown";
105 intr_names[0] = "#DE Divide Error";
106 intr_names[1] = "#DB Debug Exception";
107 intr_names[2] = "NMI Interrupt";
108 intr_names[3] = "#BP Breakpoint Exception";
109 intr_names[4] = "#OF Overflow Exception";
110 intr_names[5] = "#BR BOUND Range Exceeded Exception";
111 intr_names[6] = "#UD Invalid Opcode Exception";
112 intr_names[7] = "#NM Device Not Available Exception";
113 intr_names[8] = "#DF Double Fault Exception";
114 intr_names[9] = "Coprocessor Segment Overrun";
115 intr_names[10] = "#TS Invalid TSS Exception";
116 intr_names[11] = "#NP Segment Not Present";
117 intr_names[12] = "#SS Stack Fault Exception";
118 intr_names[13] = "#GP General Protection Exception";
119 intr_names[16] = "#MF x87 FPU Floating-Point Error";
120 intr_names[17] = "#AC Alignment Check Exception";
121 intr_names[18] = "#MC Machine-Check Exception";
122 intr_names[19] = "#XF SIMD Floating-Point Exception";
125 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
126 NAME for debugging purposes. The interrupt handler will be
127 invoked with interrupt status set to LEVEL.
129 The handler will have descriptor privilege level DPL, meaning
130 that it can be invoked intentionally when the processor is in
131 the DPL or lower-numbered ring. In practice, DPL==3 allows
132 user mode to invoke the interrupts and DPL==0 prevents such
133 invocation. Faults and exceptions that occur in user mode
134 still cause interrupts with DPL==0 to be invoked. See
135 [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
137 intr_register (uint8_t vec_no, int dpl, enum intr_level level,
138 intr_handler_func *handler,
141 /* Make sure this handler isn't already registered to someone
143 ASSERT (intr_handlers[vec_no] == NULL);
145 /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
146 0x2f) should specify INTR_OFF for LEVEL. Otherwise a timer
147 interrupt could cause a task switch during interrupt
148 handling. Most other interrupts can and should be handled
149 with interrupts enabled. */
150 ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
152 if (level == INTR_ON)
153 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
155 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
156 intr_handlers[vec_no] = handler;
157 intr_names[vec_no] = name;
160 /* Returns true during processing of an external interrupt
161 and false at all other times. */
165 return in_external_intr;
168 /* During processing of an external interrupt, directs the
169 interrupt handler to yield to a new process just before
170 returning from the interrupt. May not be called at any other
173 intr_yield_on_return (void)
175 ASSERT (intr_context ());
176 yield_on_return = true;
179 /* 8259A Programmable Interrupt Controller. */
181 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
182 chips. One is a "master" accessible at ports 0x20 and 0x21.
183 The other is a "slave" cascaded onto the master's IRQ 2 line
184 and accessible at ports 0xa0 and 0xa1. Accesses to port 0x20
185 set the A0 line to 0 and accesses to 0x21 set the A1 line to
186 1. The situation is similar for the slave PIC.
188 By default, interrupts 0...15 delivered by the PICs will go to
189 interrupt vectors 0...15. Unfortunately, those vectors are
190 also used for CPU traps and exceptions. We reprogram the PICs
191 so that interrupts 0...15 are delivered to interrupt vectors
192 32...47 (0x20...0x2f) instead. */
194 /* Initializes the PICs. Refer to [8259A] for details. */
198 /* Mask all interrupts on both PICs. */
202 /* Initialize master. */
203 outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
204 outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
205 outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
206 outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
208 /* Initialize slave. */
209 outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
210 outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
211 outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
212 outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
214 /* Unmask all interrupts. */
219 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
220 If we don't acknowledge the IRQ, it will never be delivered to
221 us again, so this is important. */
223 pic_end_of_interrupt (int irq)
225 ASSERT (irq >= 0x20 && irq < 0x30);
227 /* Acknowledge master PIC. */
230 /* Acknowledge slave PIC if this is a slave interrupt. */
235 /* Creates an gate that invokes FUNCTION.
237 The gate has descriptor privilege level DPL, meaning that it
238 can be invoked intentionally when the processor is in the DPL
239 or lower-numbered ring. In practice, DPL==3 allows user mode
240 to call into the gate and DPL==0 prevents such calls. Faults
241 and exceptions that occur in user mode still cause gates with
242 DPL==0 to be invoked. See [IA32-v3] sections 4.5 and 4.8.1.1
243 for further discussion.
245 TYPE must be either 14 (for an interrupt gate) or 15 (for a
246 trap gate). The difference is that entering an interrupt gate
247 disables interrupts, but entering a trap gate does not. See
248 [IA32-v3] section 5.12.1.2 for discussion. */
250 make_gate (void (*function) (void), int dpl, int type)
254 ASSERT (function != NULL);
255 ASSERT (dpl >= 0 && dpl <= 3);
256 ASSERT (type >= 0 && type <= 15);
258 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
259 | (SEL_KCSEG << 16)); /* Target code segment. */
261 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
262 | (1 << 15) /* Present. */
263 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
264 | (0 << 12) /* System. */
265 | ((uint32_t) type << 8)); /* Gate type. */
267 return e0 | ((uint64_t) e1 << 32);
270 /* Creates an interrupt gate that invokes FUNCTION with the given
273 make_intr_gate (void (*function) (void), int dpl)
275 return make_gate (function, dpl, 14);
278 /* Creates a trap gate that invokes FUNCTION with the given
281 make_trap_gate (void (*function) (void), int dpl)
283 return make_gate (function, dpl, 15);
286 /* Returns a descriptor that yields the given LIMIT and BASE when
287 used as an operand for the LIDT instruction. */
288 static inline uint64_t
289 make_idtr_operand (uint16_t limit, void *base)
291 return limit | ((uint64_t) (uint32_t) base << 16);
294 /* Interrupt handlers. */
296 /* Handler for all interrupts, faults, and exceptions. This
297 function is called by the assembly language interrupt stubs in
298 intr-stubs.S (see intr-stubs.pl). FRAME describes the
299 interrupt and the interrupted thread's registers. */
301 intr_handler (struct intr_frame *frame)
304 intr_handler_func *handler;
306 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
309 ASSERT (intr_get_level () == INTR_OFF);
310 ASSERT (!intr_context ());
312 in_external_intr = true;
313 yield_on_return = false;
316 /* Invoke the interrupt's handler.
317 If there is no handler, invoke the unexpected interrupt
319 handler = intr_handlers[frame->vec_no];
322 intr_dump_frame (frame);
323 PANIC ("Unexpected interrupt");
327 /* Complete the processing of an external interrupt. */
330 ASSERT (intr_get_level () == INTR_OFF);
331 ASSERT (intr_context ());
333 in_external_intr = false;
334 pic_end_of_interrupt (frame->vec_no);
341 /* Dumps interrupt frame F to the console, for debugging. */
343 intr_dump_frame (const struct intr_frame *f)
346 asm ("movl %%cr2, %0" : "=r" (cr2));
347 asm ("movl %%ss, %0" : "=r" (ss));
349 printk ("Interrupt %#04x (%s) at eip=%p\n",
350 f->vec_no, intr_names[f->vec_no], f->eip);
351 printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
352 printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
353 f->eax, f->ebx, f->ecx, f->edx);
354 printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
355 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
356 printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
357 f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
360 /* Returns the name of interrupt VEC. */
362 intr_name (uint8_t vec)
364 return intr_names[vec];