PKUOS - Pintos
Pintos source browser for PKU Operating System course
interrupt.c
Go to the documentation of this file.
1#include "threads/interrupt.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <stdint.h>
5#include <stdio.h>
6#include "threads/flags.h"
8#include "threads/io.h"
9#include "threads/thread.h"
10#include "threads/vaddr.h"
11#include "devices/timer.h"
12
13/** Programmable Interrupt Controller (PIC) registers.
14 A PC has two PICs, called the master and slave PICs, with the
15 slave attached ("cascaded") to the master IRQ line 2. */
16#define PIC0_CTRL 0x20 /**< Master PIC control register address. */
17#define PIC0_DATA 0x21 /**< Master PIC data register address. */
18#define PIC1_CTRL 0xa0 /**< Slave PIC control register address. */
19#define PIC1_DATA 0xa1 /**< Slave PIC data register address. */
20
21/** Number of x86 interrupts. */
22#define INTR_CNT 256
23
24/** The Interrupt Descriptor Table (IDT). The format is fixed by
25 the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor
26 Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
27 Exception- or Interrupt-Handler Procedure". */
29
30/** Interrupt handler functions for each interrupt. */
32
33/** Names for each interrupt, for debugging purposes. */
34static const char *intr_names[INTR_CNT];
35
36/** Number of unexpected interrupts for each vector. An
37 unexpected interrupt is one that has no registered handler. */
38static unsigned int unexpected_cnt[INTR_CNT];
39
40/** External interrupts are those generated by devices outside the
41 CPU, such as the timer. External interrupts run with
42 interrupts turned off, so they never nest, nor are they ever
43 pre-empted. Handlers for external interrupts also may not
44 sleep, although they may invoke intr_yield_on_return() to
45 request that a new process be scheduled just before the
46 interrupt returns. */
47static bool in_external_intr; /**< Are we processing an external interrupt? */
48static bool yield_on_return; /**< Should we yield on interrupt return? */
49
50/** Programmable Interrupt Controller helpers. */
51static void pic_init (void);
52static void pic_end_of_interrupt (int irq);
53
54/** Interrupt Descriptor Table helpers. */
55static uint64_t make_intr_gate (void (*) (void), int dpl);
56static uint64_t make_trap_gate (void (*) (void), int dpl);
57static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
58
59/** Interrupt handlers. */
60void intr_handler (struct intr_frame *args);
61static void unexpected_interrupt (const struct intr_frame *);
62
63/** Returns the current interrupt status. */
64enum intr_level
66{
67 uint32_t flags;
68
69 /* Push the flags register on the processor stack, then pop the
70 value off the stack into `flags'. See [IA32-v2b] "PUSHF"
71 and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
72 Interrupts". */
73 asm volatile ("pushfl; popl %0" : "=g" (flags));
74
75 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
76}
77
78/** Enables or disables interrupts as specified by LEVEL and
79 returns the previous interrupt status. */
80enum intr_level
82{
83 return level == INTR_ON ? intr_enable () : intr_disable ();
84}
85
86/** Enables interrupts and returns the previous interrupt status. */
87enum intr_level
89{
90 enum intr_level old_level = intr_get_level ();
91 ASSERT (!intr_context ());
92
93 /* Enable interrupts by setting the interrupt flag.
94
95 See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
96 Hardware Interrupts". */
97 asm volatile ("sti");
98
99 return old_level;
100}
101
102/** Disables interrupts and returns the previous interrupt status. */
103enum intr_level
105{
106 enum intr_level old_level = intr_get_level ();
107
108 /* Disable interrupts by clearing the interrupt flag.
109 See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
110 Hardware Interrupts". */
111 asm volatile ("cli" : : : "memory");
112
113 return old_level;
114}
115
116/** Initializes the interrupt system. */
117void
119{
120 uint64_t idtr_operand;
121 int i;
122
123 /* Initialize interrupt controller. */
124 pic_init ();
125
126 /* Initialize IDT. */
127 for (i = 0; i < INTR_CNT; i++)
128 idt[i] = make_intr_gate (intr_stubs[i], 0);
129
130 /* Load IDT register.
131 See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
132 Descriptor Table (IDT)". */
133 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
134 asm volatile ("lidt %0" : : "m" (idtr_operand));
135
136 /* Initialize intr_names. */
137 for (i = 0; i < INTR_CNT; i++)
138 intr_names[i] = "unknown";
139 intr_names[0] = "#DE Divide Error";
140 intr_names[1] = "#DB Debug Exception";
141 intr_names[2] = "NMI Interrupt";
142 intr_names[3] = "#BP Breakpoint Exception";
143 intr_names[4] = "#OF Overflow Exception";
144 intr_names[5] = "#BR BOUND Range Exceeded Exception";
145 intr_names[6] = "#UD Invalid Opcode Exception";
146 intr_names[7] = "#NM Device Not Available Exception";
147 intr_names[8] = "#DF Double Fault Exception";
148 intr_names[9] = "Coprocessor Segment Overrun";
149 intr_names[10] = "#TS Invalid TSS Exception";
150 intr_names[11] = "#NP Segment Not Present";
151 intr_names[12] = "#SS Stack Fault Exception";
152 intr_names[13] = "#GP General Protection Exception";
153 intr_names[14] = "#PF Page-Fault Exception";
154 intr_names[16] = "#MF x87 FPU Floating-Point Error";
155 intr_names[17] = "#AC Alignment Check Exception";
156 intr_names[18] = "#MC Machine-Check Exception";
157 intr_names[19] = "#XF SIMD Floating-Point Exception";
158}
159
160/** Registers interrupt VEC_NO to invoke HANDLER with descriptor
161 privilege level DPL. Names the interrupt NAME for debugging
162 purposes. The interrupt handler will be invoked with
163 interrupt status set to LEVEL. */
164static void
165register_handler (uint8_t vec_no, int dpl, enum intr_level level,
166 intr_handler_func *handler, const char *name)
167{
168 ASSERT (intr_handlers[vec_no] == NULL);
169 if (level == INTR_ON)
170 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
171 else
172 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
173 intr_handlers[vec_no] = handler;
174 intr_names[vec_no] = name;
175}
176
177/** Registers external interrupt VEC_NO to invoke HANDLER, which
178 is named NAME for debugging purposes. The handler will
179 execute with interrupts disabled. */
180void
182 const char *name)
183{
184 ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
185 register_handler (vec_no, 0, INTR_OFF, handler, name);
186}
187
188/** Registers internal interrupt VEC_NO to invoke HANDLER, which
189 is named NAME for debugging purposes. The interrupt handler
190 will be invoked with interrupt status LEVEL.
191
192 The handler will have descriptor privilege level DPL, meaning
193 that it can be invoked intentionally when the processor is in
194 the DPL or lower-numbered ring. In practice, DPL==3 allows
195 user mode to invoke the interrupts and DPL==0 prevents such
196 invocation. Faults and exceptions that occur in user mode
197 still cause interrupts with DPL==0 to be invoked. See
198 [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
199 "Accessing Nonconforming Code Segments" for further
200 discussion. */
201void
202intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
203 intr_handler_func *handler, const char *name)
204{
205 ASSERT (vec_no < 0x20 || vec_no > 0x2f);
206 register_handler (vec_no, dpl, level, handler, name);
207}
208
209/** Returns true during processing of an external interrupt
210 and false at all other times. */
211bool
213{
214 return in_external_intr;
215}
216
217/** During processing of an external interrupt, directs the
218 interrupt handler to yield to a new process just before
219 returning from the interrupt. May not be called at any other
220 time. */
221void
223{
224 ASSERT (intr_context ());
225 yield_on_return = true;
226}
227
228/** 8259A Programmable Interrupt Controller. */
229
230/** Initializes the PICs. Refer to [8259A] for details.
231
232 By default, interrupts 0...15 delivered by the PICs will go to
233 interrupt vectors 0...15. Those vectors are also used for CPU
234 traps and exceptions, so we reprogram the PICs so that
235 interrupts 0...15 are delivered to interrupt vectors 32...47
236 (0x20...0x2f) instead. */
237static void
239{
240 /* Mask all interrupts on both PICs. */
241 outb (PIC0_DATA, 0xff);
242 outb (PIC1_DATA, 0xff);
243
244 /* Initialize master. */
245 outb (PIC0_CTRL, 0x11); /**< ICW1: single mode, edge triggered, expect ICW4. */
246 outb (PIC0_DATA, 0x20); /**< ICW2: line IR0...7 -> irq 0x20...0x27. */
247 outb (PIC0_DATA, 0x04); /**< ICW3: slave PIC on line IR2. */
248 outb (PIC0_DATA, 0x01); /**< ICW4: 8086 mode, normal EOI, non-buffered. */
249
250 /* Initialize slave. */
251 outb (PIC1_CTRL, 0x11); /**< ICW1: single mode, edge triggered, expect ICW4. */
252 outb (PIC1_DATA, 0x28); /**< ICW2: line IR0...7 -> irq 0x28...0x2f. */
253 outb (PIC1_DATA, 0x02); /**< ICW3: slave ID is 2. */
254 outb (PIC1_DATA, 0x01); /**< ICW4: 8086 mode, normal EOI, non-buffered. */
255
256 /* Unmask all interrupts. */
257 outb (PIC0_DATA, 0x00);
258 outb (PIC1_DATA, 0x00);
259}
260
261/** Sends an end-of-interrupt signal to the PIC for the given IRQ.
262 If we don't acknowledge the IRQ, it will never be delivered to
263 us again, so this is important. */
264static void
266{
267 ASSERT (irq >= 0x20 && irq < 0x30);
268
269 /* Acknowledge master PIC. */
270 outb (0x20, 0x20);
271
272 /* Acknowledge slave PIC if this is a slave interrupt. */
273 if (irq >= 0x28)
274 outb (0xa0, 0x20);
275}
276
277/** Creates an gate that invokes FUNCTION.
278
279 The gate has descriptor privilege level DPL, meaning that it
280 can be invoked intentionally when the processor is in the DPL
281 or lower-numbered ring. In practice, DPL==3 allows user mode
282 to call into the gate and DPL==0 prevents such calls. Faults
283 and exceptions that occur in user mode still cause gates with
284 DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege
285 Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
286 for further discussion.
287
288 TYPE must be either 14 (for an interrupt gate) or 15 (for a
289 trap gate). The difference is that entering an interrupt gate
290 disables interrupts, but entering a trap gate does not. See
291 [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
292 Interrupt-Handler Procedure" for discussion. */
293static uint64_t
294make_gate (void (*function) (void), int dpl, int type)
295{
296 uint32_t e0, e1;
297
298 ASSERT (function != NULL);
299 ASSERT (dpl >= 0 && dpl <= 3);
300 ASSERT (type >= 0 && type <= 15);
301
302 e0 = (((uint32_t) function & 0xffff) /**< Offset 15:0. */
303 | (SEL_KCSEG << 16)); /**< Target code segment. */
304
305 e1 = (((uint32_t) function & 0xffff0000) /**< Offset 31:16. */
306 | (1 << 15) /**< Present. */
307 | ((uint32_t) dpl << 13) /**< Descriptor privilege level. */
308 | (0 << 12) /**< System. */
309 | ((uint32_t) type << 8)); /**< Gate type. */
310
311 return e0 | ((uint64_t) e1 << 32);
312}
313
314/** Creates an interrupt gate that invokes FUNCTION with the given
315 DPL. */
316static uint64_t
317make_intr_gate (void (*function) (void), int dpl)
318{
319 return make_gate (function, dpl, 14);
320}
321
322/** Creates a trap gate that invokes FUNCTION with the given
323 DPL. */
324static uint64_t
325make_trap_gate (void (*function) (void), int dpl)
326{
327 return make_gate (function, dpl, 15);
328}
329
330/** Returns a descriptor that yields the given LIMIT and BASE when
331 used as an operand for the LIDT instruction. */
332static inline uint64_t
333make_idtr_operand (uint16_t limit, void *base)
334{
335 return limit | ((uint64_t) (uint32_t) base << 16);
336}
337
338/** Interrupt handlers. */
339
340/** Handler for all interrupts, faults, and exceptions. This
341 function is called by the assembly language interrupt stubs in
342 intr-stubs.S. FRAME describes the interrupt and the
343 interrupted thread's registers. */
344void
345intr_handler (struct intr_frame *frame)
346{
347 bool external;
348 intr_handler_func *handler;
349
350 /* External interrupts are special.
351 We only handle one at a time (so interrupts must be off)
352 and they need to be acknowledged on the PIC (see below).
353 An external interrupt handler cannot sleep. */
354 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
355 if (external)
356 {
358 ASSERT (!intr_context ());
359
360 in_external_intr = true;
361 yield_on_return = false;
362 }
363
364 /* Invoke the interrupt's handler. */
365 handler = intr_handlers[frame->vec_no];
366 if (handler != NULL)
367 handler (frame);
368 else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
369 {
370 /* There is no handler, but this interrupt can trigger
371 spuriously due to a hardware fault or hardware race
372 condition. Ignore it. */
373 }
374 else
375 unexpected_interrupt (frame);
376
377 /* Complete the processing of an external interrupt. */
378 if (external)
379 {
381 ASSERT (intr_context ());
382
383 in_external_intr = false;
385
386 if (yield_on_return)
387 thread_yield ();
388 }
389}
390
391/** Handles an unexpected interrupt with interrupt frame F. An
392 unexpected interrupt is one that has no registered handler. */
393static void
395{
396 /* Count the number so far. */
397 unsigned int n = ++unexpected_cnt[f->vec_no];
398
399 /* If the number is a power of 2, print a message. This rate
400 limiting means that we get information about an uncommon
401 unexpected interrupt the first time and fairly often after
402 that, but one that occurs many times will not overwhelm the
403 console. */
404 if ((n & (n - 1)) == 0)
405 printf ("Unexpected interrupt %#04x (%s)\n",
406 f->vec_no, intr_names[f->vec_no]);
407}
408
409/** Dumps interrupt frame F to the console, for debugging. */
410void
411intr_dump_frame (const struct intr_frame *f)
412{
413 uint32_t cr2;
414
415 /* Store current value of CR2 into `cr2'.
416 CR2 is the linear address of the last page fault.
417 See [IA32-v2a] "MOV--Move to/from Control Registers" and
418 [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
419 (#PF)". */
420 asm ("movl %%cr2, %0" : "=r" (cr2));
421
422 printf ("Interrupt %#04x (%s) at eip=%p\n",
423 f->vec_no, intr_names[f->vec_no], f->eip);
424 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
425 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
426 f->eax, f->ebx, f->ecx, f->edx);
427 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
428 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
429 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
430 f->cs, f->ds, f->es, f->ss);
431}
432
433/** Returns the name of interrupt VEC. */
434const char *
436{
437 return intr_names[vec];
438}
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
#define FLAG_IF
Interrupt Flag.
Definition: flags.h:6
char * name[]
Definition: insult.c:47
static uint64_t make_gate(void(*function)(void), int dpl, int type)
Creates an gate that invokes FUNCTION.
Definition: interrupt.c:294
static uint64_t idt[INTR_CNT]
The Interrupt Descriptor Table (IDT).
Definition: interrupt.c:28
static void unexpected_interrupt(const struct intr_frame *)
Handles an unexpected interrupt with interrupt frame F.
Definition: interrupt.c:394
void intr_register_int(uint8_t vec_no, int dpl, enum intr_level level, intr_handler_func *handler, const char *name)
Registers internal interrupt VEC_NO to invoke HANDLER, which is named NAME for debugging purposes.
Definition: interrupt.c:202
void intr_register_ext(uint8_t vec_no, intr_handler_func *handler, const char *name)
Registers external interrupt VEC_NO to invoke HANDLER, which is named NAME for debugging purposes.
Definition: interrupt.c:181
static unsigned int unexpected_cnt[INTR_CNT]
Number of unexpected interrupts for each vector.
Definition: interrupt.c:38
#define PIC0_CTRL
Programmable Interrupt Controller (PIC) registers.
Definition: interrupt.c:16
void intr_yield_on_return(void)
During processing of an external interrupt, directs the interrupt handler to yield to a new process j...
Definition: interrupt.c:222
enum intr_level intr_enable(void)
Enables interrupts and returns the previous interrupt status.
Definition: interrupt.c:88
#define PIC1_CTRL
Slave PIC control register address.
Definition: interrupt.c:18
#define PIC0_DATA
Master PIC data register address.
Definition: interrupt.c:17
static void pic_end_of_interrupt(int irq)
Sends an end-of-interrupt signal to the PIC for the given IRQ.
Definition: interrupt.c:265
const char * intr_name(uint8_t vec)
Returns the name of interrupt VEC.
Definition: interrupt.c:435
static void pic_init(void)
Programmable Interrupt Controller helpers.
Definition: interrupt.c:238
static uint64_t make_intr_gate(void(*)(void), int dpl)
Interrupt Descriptor Table helpers.
Definition: interrupt.c:317
static uint64_t make_trap_gate(void(*)(void), int dpl)
Creates a trap gate that invokes FUNCTION with the given DPL.
Definition: interrupt.c:325
static void register_handler(uint8_t vec_no, int dpl, enum intr_level level, intr_handler_func *handler, const char *name)
Registers interrupt VEC_NO to invoke HANDLER with descriptor privilege level DPL.
Definition: interrupt.c:165
static intr_handler_func * intr_handlers[INTR_CNT]
Interrupt handler functions for each interrupt.
Definition: interrupt.c:31
static const char * intr_names[INTR_CNT]
Names for each interrupt, for debugging purposes.
Definition: interrupt.c:34
void intr_dump_frame(const struct intr_frame *f)
Dumps interrupt frame F to the console, for debugging.
Definition: interrupt.c:411
bool intr_context(void)
Returns true during processing of an external interrupt and false at all other times.
Definition: interrupt.c:212
void intr_init(void)
Initializes the interrupt system.
Definition: interrupt.c:118
enum intr_level intr_disable(void)
Disables interrupts and returns the previous interrupt status.
Definition: interrupt.c:104
enum intr_level intr_get_level(void)
Returns the current interrupt status.
Definition: interrupt.c:65
static bool in_external_intr
External interrupts are those generated by devices outside the CPU, such as the timer.
Definition: interrupt.c:47
void intr_handler(struct intr_frame *args)
Interrupt handlers.
Definition: interrupt.c:345
enum intr_level intr_set_level(enum intr_level level)
Enables or disables interrupts as specified by LEVEL and returns the previous interrupt status.
Definition: interrupt.c:81
static bool yield_on_return
Should we yield on interrupt return?
Definition: interrupt.c:48
#define PIC1_DATA
Slave PIC data register address.
Definition: interrupt.c:19
#define INTR_CNT
Number of x86 interrupts.
Definition: interrupt.c:22
static uint64_t make_idtr_operand(uint16_t limit, void *base)
Returns a descriptor that yields the given LIMIT and BASE when used as an operand for the LIDT instru...
Definition: interrupt.c:333
void intr_handler_func(struct intr_frame *)
Definition: interrupt.h:58
intr_level
Interrupts on or off?
Definition: interrupt.h:9
@ INTR_ON
Interrupts enabled.
Definition: interrupt.h:11
@ INTR_OFF
Interrupts disabled.
Definition: interrupt.h:10
intr_stub_func * intr_stubs[256]
#define PRIx16
Definition: inttypes.h:17
#define PRIx32
Definition: inttypes.h:24
static void outb(uint16_t port, uint8_t data)
Writes byte DATA to PORT.
Definition: io.h:66
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
#define SEL_KCSEG
Kernel code selector.
Definition: loader.h:30
#define NULL
Definition: stddef.h:4
unsigned int uint32_t
Definition: stdint.h:26
unsigned char uint8_t
Definition: stdint.h:20
unsigned long long int uint64_t
Definition: stdint.h:29
unsigned short int uint16_t
Definition: stdint.h:23
Interrupt stack frame.
Definition: interrupt.h:21
uint16_t ss
Definition: interrupt.h:55
void(* eip)(void)
Next instruction to execute.
Definition: interrupt.h:51
uint16_t uint16_t uint16_t uint16_t uint32_t vec_no
Saved DS segment register.
Definition: interrupt.h:35
uint32_t error_code
Error code.
Definition: interrupt.h:43
uint32_t ebx
Saved EBX.
Definition: interrupt.h:28
uint16_t uint16_t uint16_t es
Saved FS segment register.
Definition: interrupt.h:33
uint16_t uint16_t uint16_t uint16_t ds
Saved ES segment register.
Definition: interrupt.h:34
uint32_t ecx
Saved ECX.
Definition: interrupt.h:30
uint32_t edx
Saved EDX.
Definition: interrupt.h:29
void * esp
Saved stack pointer.
Definition: interrupt.h:54
uint32_t edi
Saved EDI.
Definition: interrupt.h:24
uint32_t eax
Saved EAX.
Definition: interrupt.h:31
uint32_t ebp
Saved EBP.
Definition: interrupt.h:26
uint16_t cs
Definition: interrupt.h:52
uint32_t esi
Saved ESI.
Definition: interrupt.h:25
void thread_yield(void)
Yields the CPU.
Definition: thread.c:302