Dump interrupt frame info on user fault.
[pintos-anon] / src / threads / interrupt.c
1 #include "interrupt.h"
2 #include <inttypes.h>
3 #include <stdint.h>
4 #include "intr-stubs.h"
5 #include "debug.h"
6 #include "io.h"
7 #include "lib.h"
8 #include "mmu.h"
9 #include "thread.h"
10 #include "timer.h"
11 \f
12 enum if_level
13 intr_get_level (void) 
14 {
15   uint32_t flags;
16   
17   asm ("pushfl; popl %0" : "=g" (flags));
18
19   return flags & (1 << 9) ? IF_ON : IF_OFF;
20 }
21
22 enum if_level
23 intr_set_level (enum if_level level) 
24 {
25   enum if_level old_level = intr_get_level ();
26   if (level == IF_ON)
27     intr_enable ();
28   else
29     intr_disable ();
30   return old_level;
31 }
32
33 enum if_level
34 intr_enable (void) 
35 {
36   enum if_level old_level = intr_get_level ();
37   asm volatile ("sti");
38   return old_level;
39 }
40
41 enum if_level
42 intr_disable (void) 
43 {
44   enum if_level old_level = intr_get_level ();
45   asm volatile ("cli");
46   return old_level;
47 }
48 \f
49 static void
50 pic_init (void)
51 {
52   /* Every PC has two 8259A Programmable Interrupt Controller
53      (PIC) chips.  One is a "master" accessible at ports 0x20 and
54      0x21.  The other is a "slave" cascaded onto the master's IRQ
55      2 line and accessible at ports 0xa0 and 0xa1.  Accesses to
56      port 0x20 set the A0 line to 0 and accesses to 0x21 set the
57      A1 line to 1.  The situation is similar for the slave PIC.
58      Refer to the 8259A datasheet for details.
59
60      By default, interrupts 0...15 delivered by the PICs will go
61      to interrupt vectors 0...15.  Unfortunately, those vectors
62      are also used for CPU traps and exceptions.  We reprogram
63      the PICs so that interrupts 0...15 are delivered to
64      interrupt vectors 32...47 instead. */
65
66   /* Mask all interrupts on both PICs. */
67   outb (0x21, 0xff);
68   outb (0xa1, 0xff);
69
70   /* Initialize master. */
71   outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
72   outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
73   outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
74   outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
75
76   /* Initialize slave. */
77   outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
78   outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
79   outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
80   outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
81
82   /* Unmask all interrupts. */
83   outb (0x21, 0x00);
84   outb (0xa1, 0x00);
85 }
86
87 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
88    If we don't acknowledge the IRQ, we'll never get it again, so
89    this is important.  */
90 static void
91 pic_eoi (int irq) 
92 {
93   /* FIXME?  The Linux code is much more complicated. */
94   ASSERT (irq >= 0x20 && irq < 0x30);
95   outb (0x20, 0x20);
96   if (irq >= 0x28)
97     outb (0xa0, 0x20);
98 }
99 \f
100 #define INTR_CNT 256
101
102 static uint64_t idt[INTR_CNT];
103 static intr_handler_func *intr_handlers[INTR_CNT];
104 static const char *intr_names[INTR_CNT];
105
106 void intr_handler (struct intr_frame *args);
107
108 static bool intr_in_progress;
109 static bool yield_on_return;
110
111 const char *
112 intr_name (int vec) 
113 {
114   if (vec < 0 || vec >= INTR_CNT || intr_names[vec] == NULL)
115     return "unknown";
116   else
117     return intr_names[vec];
118 }
119
120 void
121 intr_handler (struct intr_frame *args) 
122 {
123   bool external = args->vec_no >= 0x20 && args->vec_no < 0x30;
124   if (external) 
125     {
126       ASSERT (intr_get_level () == IF_OFF);
127       ASSERT (!intr_context ());
128       intr_in_progress = true;
129       yield_on_return = false;
130     }
131
132   intr_handlers[args->vec_no] (args);
133
134   if (external) 
135     {
136       ASSERT (intr_get_level () == IF_OFF);
137       ASSERT (intr_context ());
138       intr_in_progress = false;
139       pic_eoi (args->vec_no); 
140
141       if (yield_on_return) 
142         thread_yield (); 
143     }
144 }
145
146 bool
147 intr_context (void) 
148 {
149   return intr_in_progress;
150 }
151
152 void
153 intr_yield_on_return (void) 
154 {
155   ASSERT (intr_context ());
156   yield_on_return = true;
157 }
158
159 intr_handler_func intr_panic NO_RETURN;
160 intr_handler_func intr_kill NO_RETURN;
161
162 static uint64_t
163 make_gate (void (*target) (void), int dpl, enum seg_type type)
164 {
165   uint32_t offset = (uint32_t) target;
166   uint32_t e0 = ((offset & 0xffff)            /* Offset 15:0. */
167                  | (SEL_KCSEG << 16));        /* Target code segment. */
168   uint32_t e1 = ((offset & 0xffff0000)        /* Offset 31:16. */
169                  | (1 << 15)                  /* Present. */
170                  | ((uint32_t) dpl << 13)     /* Descriptor privilege. */
171                  | (SYS_SYSTEM << 12)         /* System. */
172                  | ((uint32_t) type << 8));   /* Gate type. */
173   return e0 | ((uint64_t) e1 << 32);
174 }
175
176 static uint64_t
177 make_intr_gate (void (*target) (void), int dpl)
178 {
179   return make_gate (target, dpl, TYPE_INT_32);
180 }
181
182 static uint64_t
183 make_trap_gate (void (*target) (void), int dpl)
184 {
185   return make_gate (target, dpl, TYPE_TRAP_32);
186 }
187
188 void
189 intr_register (uint8_t vec_no, int dpl, enum if_level level,
190                intr_handler_func *handler,
191                const char *name) 
192 {
193   /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
194      0x2f) should specify IF_OFF for LEVEL.  Otherwise a timer
195      interrupt could cause a task switch during interrupt
196      handling.  Most other interrupts can and should be handled
197      with interrupts enabled. */
198   ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == IF_OFF);
199
200   if (level == IF_ON)
201     idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
202   else
203     idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
204   intr_handlers[vec_no] = handler;
205   intr_names[vec_no] = name;
206 }
207
208 void
209 intr_init (void)
210 {
211   uint64_t idtr_operand;
212   int i;
213
214   pic_init ();
215
216   /* Install default handlers. */
217   for (i = 0; i < 256; i++)
218     intr_register (i, 0, IF_OFF, intr_panic, NULL);
219
220   /* Most exceptions require ring 0.
221      Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
222   intr_register (0, 0, IF_ON, intr_kill, "#DE Divide Error");
223   intr_register (1, 0, IF_ON, intr_kill, "#DB Debug Exception");
224   intr_register (2, 0, IF_ON, intr_panic, "NMI Interrupt");
225   intr_register (3, 3, IF_ON, intr_kill, "#BP Breakpoint Exception");
226   intr_register (4, 3, IF_ON, intr_kill, "#OF Overflow Exception");
227   intr_register (5, 3, IF_ON, intr_kill, "#BR BOUND Range Exceeded Exception");
228   intr_register (6, 0, IF_ON, intr_kill, "#UD Invalid Opcode Exception");
229   intr_register (7, 0, IF_ON, intr_kill, "#NM Device Not Available Exception");
230   intr_register (8, 0, IF_ON, intr_panic, "#DF Double Fault Exception");
231   intr_register (9, 0, IF_ON, intr_panic, "Coprocessor Segment Overrun");
232   intr_register (10, 0, IF_ON, intr_panic, "#TS Invalid TSS Exception");
233   intr_register (11, 0, IF_ON, intr_kill, "#NP Segment Not Present");
234   intr_register (12, 0, IF_ON, intr_kill, "#SS Stack Fault Exception");
235   intr_register (13, 0, IF_ON, intr_kill, "#GP General Protection Exception");
236   intr_register (16, 0, IF_ON, intr_kill, "#MF x87 FPU Floating-Point Error");
237   intr_register (17, 0, IF_ON, intr_panic, "#AC Alignment Check Exception");
238   intr_register (18, 0, IF_ON, intr_panic, "#MC Machine-Check Exception");
239   intr_register (19, 0, IF_ON, intr_kill, "#XF SIMD Floating-Point Exception");
240
241   /* Most exceptions can be handled with interrupts turned on.
242      We need to disable interrupts for page faults because the
243      fault address is stored in CR2 and needs to be preserved. */
244   intr_register (14, 0, IF_OFF, intr_kill, "#PF Page-Fault Exception");
245
246   idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
247   asm volatile ("lidt %0" :: "m" (idtr_operand));
248 }
249
250 static void
251 dump_intr_frame (struct intr_frame *f) 
252 {
253   uint32_t cr2, ss;
254   asm ("movl %%cr2, %0" : "=r" (cr2));
255   asm ("movl %%ss, %0" : "=r" (ss));
256
257   printk ("Interrupt %#04x (%s) at eip=%p\n",
258           f->vec_no, intr_name (f->vec_no), f->eip);
259   printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
260   printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
261           f->eax, f->ebx, f->ecx, f->edx);
262   printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
263           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
264   printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
265           f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
266 }
267
268 void
269 intr_panic (struct intr_frame *regs) 
270 {
271   dump_intr_frame (regs);
272   PANIC ("Panic!");
273 }
274
275 void
276 intr_kill (struct intr_frame *f) 
277 {
278   switch (f->cs)
279     {
280     case SEL_UCSEG:
281       printk ("%s: dying due to interrupt %#04x (%s).\n",
282               thread_current ()->name, f->vec_no, intr_name (f->vec_no));
283       dump_intr_frame (f);
284       thread_exit (); 
285
286     case SEL_KCSEG:
287       printk ("Kernel bug - unexpected interrupt in kernel context\n");
288       intr_panic (f);
289
290     default:
291       printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
292              f->vec_no, intr_name (f->vec_no), f->cs);
293       thread_exit ();
294     }
295 }
296
297