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);
43 /* Interrupt handlers. */
44 void intr_handler (struct intr_frame *args);
45 static intr_handler_func panic NO_RETURN;
46 static intr_handler_func kill NO_RETURN;
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 ();
76 /* Disables interrupts and returns the previous interrupt status. */
80 enum intr_level old_level = intr_get_level ();
85 /* Initializes the interrupt system. */
89 uint64_t idtr_operand;
94 /* Initialize intr_names. */
95 for (i = 0; i < INTR_CNT; i++)
96 intr_names[i] = "unknown";
98 /* Most exceptions require ring 0.
99 Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
100 intr_register (0, 0, INTR_ON, kill, "#DE Divide Error");
101 intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception");
102 intr_register (2, 0, INTR_ON, panic, "NMI Interrupt");
103 intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
104 intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception");
105 intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception");
106 intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
107 intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception");
108 intr_register (8, 0, INTR_ON, panic, "#DF Double Fault Exception");
109 intr_register (9, 0, INTR_ON, panic, "Coprocessor Segment Overrun");
110 intr_register (10, 0, INTR_ON, panic, "#TS Invalid TSS Exception");
111 intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present");
112 intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
113 intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception");
114 intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
115 intr_register (17, 0, INTR_ON, panic, "#AC Alignment Check Exception");
116 intr_register (18, 0, INTR_ON, panic, "#MC Machine-Check Exception");
117 intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception");
119 /* Most exceptions can be handled with interrupts turned on.
120 We need to disable interrupts for page faults because the
121 fault address is stored in CR2 and needs to be preserved. */
122 intr_register (14, 0, INTR_OFF, kill, "#PF Page-Fault Exception");
124 idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
125 asm volatile ("lidt %0" :: "m" (idtr_operand));
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 TYPE_INT_32 (for an interrupt gate) or
249 TYPE_TRAP_32 (for a trap gate). The difference is that
250 entering an interrupt gate disables interrupts, but entering a
251 trap gate does not. See [IA32-v3] section 5.12.1.2 for
254 make_gate (void (*function) (void), int dpl, enum seg_type type)
256 uint32_t offset = (uint32_t) function;
257 uint32_t e0 = ((offset & 0xffff) /* Offset 15:0. */
258 | (SEL_KCSEG << 16)); /* Target code segment. */
259 uint32_t e1 = ((offset & 0xffff0000) /* Offset 31:16. */
260 | (1 << 15) /* Present. */
261 | ((uint32_t) dpl << 13) /* Descriptor privilege. */
262 | (SYS_SYSTEM << 12) /* System. */
263 | ((uint32_t) type << 8)); /* Gate type. */
264 return e0 | ((uint64_t) e1 << 32);
267 /* Creates an interrupt gate that invokes FUNCTION with the given
270 make_intr_gate (void (*function) (void), int dpl)
272 return make_gate (function, dpl, TYPE_INT_32);
275 /* Creates a trap gate that invokes FUNCTION with the given
278 make_trap_gate (void (*function) (void), int dpl)
280 return make_gate (function, dpl, TYPE_TRAP_32);
283 /* Interrupt handlers. */
285 static void dump_intr_frame (struct intr_frame *);
287 /* Handler for all interrupts, faults, and exceptions. This
288 function is called by the assembly language interrupt stubs in
289 intr-stubs.S (see intr-stubs.pl). ARGS describes the
290 interrupt and the interrupted thread's registers. */
292 intr_handler (struct intr_frame *args)
295 intr_handler_func *handler;
297 external = args->vec_no >= 0x20 && args->vec_no < 0x30;
300 ASSERT (intr_get_level () == INTR_OFF);
301 ASSERT (!intr_context ());
303 in_external_intr = true;
304 yield_on_return = false;
307 /* Invoke the interrupt's handler.
308 If there is no handler, invoke the unexpected interrupt
310 handler = intr_handlers[args->vec_no];
315 /* Complete the processing of an external interrupt. */
318 ASSERT (intr_get_level () == INTR_OFF);
319 ASSERT (intr_context ());
321 in_external_intr = false;
322 pic_end_of_interrupt (args->vec_no);
329 /* Handler for an interrupt that should not have been invoked. */
331 panic (struct intr_frame *regs)
333 dump_intr_frame (regs);
337 /* Handler for an exception (probably) caused by a user process. */
339 kill (struct intr_frame *f)
341 /* This interrupt is one (probably) caused by a user process.
342 For example, the process might have tried to access unmapped
343 virtual memory (a page fault). For now, we simply kill the
344 user process. Later, we'll want to handle page faults in
345 the kernel. Real Unix-like operating systems pass most
346 exceptions back to the process via signals, but we don't
349 /* The interrupt frame's code segment value tells us where the
350 exception originated. */
354 /* User's code segment, so it's a user exception, as we
356 printk ("%s: dying due to interrupt %#04x (%s).\n",
357 thread_name (thread_current ()),
358 f->vec_no, intr_names[f->vec_no]);
363 /* Kernel's code segment, which indicates a kernel bug.
364 Kernel code shouldn't throw exceptions. (Page faults
365 may cause kernel exceptions--but they shouldn't arrive
367 printk ("Kernel bug - unexpected interrupt in kernel\n");
371 /* Some other code segment? Shouldn't happen. */
372 printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
373 f->vec_no, intr_names[f->vec_no], f->cs);
378 /* Dumps interrupt frame F to the console, for debugging. */
380 dump_intr_frame (struct intr_frame *f)
383 asm ("movl %%cr2, %0" : "=r" (cr2));
384 asm ("movl %%ss, %0" : "=r" (ss));
386 printk ("Interrupt %#04x (%s) at eip=%p\n",
387 f->vec_no, intr_names[f->vec_no], f->eip);
388 printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
389 printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
390 f->eax, f->ebx, f->ecx, f->edx);
391 printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
392 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
393 printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
394 f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);