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