4 #include "intr-stubs.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);
44 /* Interrupt handlers. */
45 void intr_handler (struct intr_frame *args);
46 static intr_handler_func panic NO_RETURN;
47 static intr_handler_func kill NO_RETURN;
49 /* Returns the current interrupt status. */
55 asm ("pushfl; popl %0" : "=g" (flags));
57 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
60 /* Enables or disables interrupts as specified by LEVEL and
61 returns the previous interrupt status. */
63 intr_set_level (enum intr_level level)
65 return level == INTR_ON ? intr_enable () : intr_disable ();
68 /* Enables interrupts and returns the previous interrupt status. */
72 enum intr_level old_level = intr_get_level ();
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;
95 /* Initialize intr_names. */
96 for (i = 0; i < INTR_CNT; i++)
97 intr_names[i] = "unknown";
99 /* Most exceptions require ring 0.
100 Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
101 intr_register (0, 0, INTR_ON, kill, "#DE Divide Error");
102 intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception");
103 intr_register (2, 0, INTR_ON, panic, "NMI Interrupt");
104 intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
105 intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception");
106 intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception");
107 intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
108 intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception");
109 intr_register (8, 0, INTR_ON, panic, "#DF Double Fault Exception");
110 intr_register (9, 0, INTR_ON, panic, "Coprocessor Segment Overrun");
111 intr_register (10, 0, INTR_ON, panic, "#TS Invalid TSS Exception");
112 intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present");
113 intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
114 intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception");
115 intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
116 intr_register (17, 0, INTR_ON, panic, "#AC Alignment Check Exception");
117 intr_register (18, 0, INTR_ON, panic, "#MC Machine-Check Exception");
118 intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception");
120 /* Most exceptions can be handled with interrupts turned on.
121 We need to disable interrupts for page faults because the
122 fault address is stored in CR2 and needs to be preserved. */
123 intr_register (14, 0, INTR_OFF, kill, "#PF Page-Fault Exception");
125 idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
126 asm volatile ("lidt %0" :: "m" (idtr_operand));
129 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
130 NAME for debugging purposes. The interrupt handler will be
131 invoked with interrupt status set to LEVEL.
133 The handler will have descriptor privilege level DPL, meaning
134 that it can be invoked intentionally when the processor is in
135 the DPL or lower-numbered ring. In practice, DPL==3 allows
136 user mode to invoke the interrupts and DPL==0 prevents such
137 invocation. Faults and exceptions that occur in user mode
138 still cause interrupts with DPL==0 to be invoked. See
139 [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
141 intr_register (uint8_t vec_no, int dpl, enum intr_level level,
142 intr_handler_func *handler,
145 /* Make sure this handler isn't already registered to someone
147 ASSERT (intr_handlers[vec_no] == NULL);
149 /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
150 0x2f) should specify INTR_OFF for LEVEL. Otherwise a timer
151 interrupt could cause a task switch during interrupt
152 handling. Most other interrupts can and should be handled
153 with interrupts enabled. */
154 ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
156 if (level == INTR_ON)
157 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
159 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
160 intr_handlers[vec_no] = handler;
161 intr_names[vec_no] = name;
164 /* Returns true during processing of an external interrupt
165 and false at all other times. */
169 return in_external_intr;
172 /* During processing of an external interrupt, directs the
173 interrupt handler to yield to a new process just before
174 returning from the interrupt. May not be called at any other
177 intr_yield_on_return (void)
179 ASSERT (intr_context ());
180 yield_on_return = true;
183 /* 8259A Programmable Interrupt Controller. */
185 /* Every PC has two 8259A Programmable Interrupt Controller (PIC)
186 chips. One is a "master" accessible at ports 0x20 and 0x21.
187 The other is a "slave" cascaded onto the master's IRQ 2 line
188 and accessible at ports 0xa0 and 0xa1. Accesses to port 0x20
189 set the A0 line to 0 and accesses to 0x21 set the A1 line to
190 1. The situation is similar for the slave PIC.
192 By default, interrupts 0...15 delivered by the PICs will go to
193 interrupt vectors 0...15. Unfortunately, those vectors are
194 also used for CPU traps and exceptions. We reprogram the PICs
195 so that interrupts 0...15 are delivered to interrupt vectors
196 32...47 (0x20...0x2f) instead. */
198 /* Initializes the PICs. Refer to [8259A] for details. */
202 /* Mask all interrupts on both PICs. */
206 /* Initialize master. */
207 outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
208 outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
209 outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
210 outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
212 /* Initialize slave. */
213 outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
214 outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
215 outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
216 outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
218 /* Unmask all interrupts. */
223 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
224 If we don't acknowledge the IRQ, it will never be delivered to
225 us again, so this is important. */
227 pic_end_of_interrupt (int irq)
229 ASSERT (irq >= 0x20 && irq < 0x30);
231 /* Acknowledge master PIC. */
234 /* Acknowledge slave PIC if this is a slave interrupt. */
239 /* Creates an gate that invokes FUNCTION.
241 The gate has descriptor privilege level DPL, meaning that it
242 can be invoked intentionally when the processor is in the DPL
243 or lower-numbered ring. In practice, DPL==3 allows user mode
244 to call into the gate and DPL==0 prevents such calls. Faults
245 and exceptions that occur in user mode still cause gates with
246 DPL==0 to be invoked. See [IA32-v3] sections 4.5 and 4.8.1.1
247 for further discussion.
249 TYPE must be either 14 (for an interrupt gate) or 15 (for a
250 trap gate). The difference is that entering an interrupt gate
251 disables interrupts, but entering a trap gate does not. See
252 [IA32-v3] section 5.12.1.2 for discussion. */
254 make_gate (void (*function) (void), int dpl, int type)
258 ASSERT (function != NULL);
259 ASSERT (dpl >= 0 && dpl <= 3);
260 ASSERT (type >= 0 && type <= 15);
262 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
263 | (SEL_KCSEG << 16)); /* Target code segment. */
265 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
266 | (1 << 15) /* Present. */
267 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
268 | (0 << 12) /* System. */
269 | ((uint32_t) type << 8)); /* Gate type. */
271 return e0 | ((uint64_t) e1 << 32);
274 /* Creates an interrupt gate that invokes FUNCTION with the given
277 make_intr_gate (void (*function) (void), int dpl)
279 return make_gate (function, dpl, 14);
282 /* Creates a trap gate that invokes FUNCTION with the given
285 make_trap_gate (void (*function) (void), int dpl)
287 return make_gate (function, dpl, 15);
290 /* Interrupt handlers. */
292 static void dump_intr_frame (struct intr_frame *);
294 /* Handler for all interrupts, faults, and exceptions. This
295 function is called by the assembly language interrupt stubs in
296 intr-stubs.S (see intr-stubs.pl). ARGS describes the
297 interrupt and the interrupted thread's registers. */
299 intr_handler (struct intr_frame *args)
302 intr_handler_func *handler;
304 external = args->vec_no >= 0x20 && args->vec_no < 0x30;
307 ASSERT (intr_get_level () == INTR_OFF);
308 ASSERT (!intr_context ());
310 in_external_intr = true;
311 yield_on_return = false;
314 /* Invoke the interrupt's handler.
315 If there is no handler, invoke the unexpected interrupt
317 handler = intr_handlers[args->vec_no];
322 /* Complete the processing of an external interrupt. */
325 ASSERT (intr_get_level () == INTR_OFF);
326 ASSERT (intr_context ());
328 in_external_intr = false;
329 pic_end_of_interrupt (args->vec_no);
336 /* Handler for an interrupt that should not have been invoked. */
338 panic (struct intr_frame *regs)
340 dump_intr_frame (regs);
344 /* Handler for an exception (probably) caused by a user process. */
346 kill (struct intr_frame *f)
348 /* This interrupt is one (probably) caused by a user process.
349 For example, the process might have tried to access unmapped
350 virtual memory (a page fault). For now, we simply kill the
351 user process. Later, we'll want to handle page faults in
352 the kernel. Real Unix-like operating systems pass most
353 exceptions back to the process via signals, but we don't
356 /* The interrupt frame's code segment value tells us where the
357 exception originated. */
361 /* User's code segment, so it's a user exception, as we
363 printk ("%s: dying due to interrupt %#04x (%s).\n",
364 thread_name (thread_current ()),
365 f->vec_no, intr_names[f->vec_no]);
370 /* Kernel's code segment, which indicates a kernel bug.
371 Kernel code shouldn't throw exceptions. (Page faults
372 may cause kernel exceptions--but they shouldn't arrive
374 printk ("Kernel bug - unexpected interrupt in kernel\n");
378 /* Some other code segment? Shouldn't happen. */
379 printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
380 f->vec_no, intr_names[f->vec_no], f->cs);
385 /* Dumps interrupt frame F to the console, for debugging. */
387 dump_intr_frame (struct intr_frame *f)
390 asm ("movl %%cr2, %0" : "=r" (cr2));
391 asm ("movl %%ss, %0" : "=r" (ss));
393 printk ("Interrupt %#04x (%s) at eip=%p\n",
394 f->vec_no, intr_names[f->vec_no], f->eip);
395 printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
396 printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
397 f->eax, f->ebx, f->ecx, f->edx);
398 printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
399 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
400 printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
401 f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);