Make userspace actually work.
[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 (void) 
92 {
93   /* FIXME?  The Linux code is much more complicated. */
94   outb (0x20, 0x20);
95 }
96 \f
97 #define INTR_CNT 256
98
99 static uint64_t idt[INTR_CNT];
100 static intr_handler_func *intr_handlers[INTR_CNT];
101 static const char *intr_names[INTR_CNT];
102
103 void intr_handler (struct intr_frame *args);
104
105 bool intr_in_progress;
106 bool yield_on_return;
107
108 const char *
109 intr_name (int vec) 
110 {
111   if (vec < 0 || vec >= INTR_CNT || intr_names[vec] == NULL)
112     return "unknown";
113   else
114     return intr_names[vec];
115 }
116
117 void
118 intr_handler (struct intr_frame *args) 
119 {
120   bool external;
121   
122   yield_on_return = false;
123
124   external = args->vec_no >= 0x20 && args->vec_no < 0x30;
125   if (external) 
126     {
127       ASSERT (intr_get_level () == IF_OFF);
128       ASSERT (!intr_context ());
129       intr_in_progress = true;
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 (); 
140     }
141
142   if (yield_on_return) 
143     thread_yield (); 
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   yield_on_return = true;
156 }
157
158 intr_handler_func intr_panic NO_RETURN;
159 intr_handler_func intr_kill NO_RETURN;
160
161 static uint64_t
162 make_intr_gate (void (*target) (void),
163                 int dpl)
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                  | (dpl << 13)                /* Descriptor privilege. */
171                  | (SYS_SYSTEM << 12)         /* System. */
172                  | (TYPE_INT_32 << 8));       /* 32-bit interrupt gate. */
173   return e0 | ((uint64_t) e1 << 32);
174 }
175
176 static uint64_t
177 make_trap_gate (void (*target) (void),
178                 int dpl)
179 {
180   return make_intr_gate (target, dpl) | (1 << 8);
181 }
182
183 /* We don't support nested interrupts generated by external
184    hardware, so these interrupts (vec_no 0x20...0x2f) should
185    specify IF_OFF for LEVEL.  Otherwise a timer interrupt could
186    cause a task switch during interrupt handling.  Most other
187    interrupts can and should be handled with interrupts
188    enabled. */
189 void
190 intr_register (uint8_t vec_no, int dpl, enum if_level level,
191                intr_handler_func *handler,
192                const char *name) 
193 {
194   if (level == IF_ON)
195     idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
196   else
197     idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
198   intr_handlers[vec_no] = handler;
199   intr_names[vec_no] = name;
200 }
201
202 void
203 intr_init (void)
204 {
205   uint64_t idtr_operand;
206   int i;
207
208   pic_init ();
209
210   /* Install default handlers. */
211   for (i = 0; i < 256; i++)
212     intr_register (i, 0, IF_OFF, intr_panic, NULL);
213
214   /* Most exceptions require ring 0.
215      Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
216   intr_register (0, 0, IF_ON, intr_kill, "#DE Divide Error");
217   intr_register (1, 0, IF_ON, intr_kill, "#DB Debug Exception");
218   intr_register (2, 0, IF_ON, intr_panic, "NMI Interrupt");
219   intr_register (3, 3, IF_ON, intr_kill, "#BP Breakpoint Exception");
220   intr_register (4, 3, IF_ON, intr_kill, "#OF Overflow Exception");
221   intr_register (5, 3, IF_ON, intr_kill, "#BR BOUND Range Exceeded Exception");
222   intr_register (6, 0, IF_ON, intr_kill, "#UD Invalid Opcode Exception");
223   intr_register (7, 0, IF_ON, intr_kill, "#NM Device Not Available Exception");
224   intr_register (8, 0, IF_ON, intr_panic, "#DF Double Fault Exception");
225   intr_register (9, 0, IF_ON, intr_panic, "Coprocessor Segment Overrun");
226   intr_register (10, 0, IF_ON, intr_panic, "#TS Invalid TSS Exception");
227   intr_register (11, 0, IF_ON, intr_kill, "#NP Segment Not Present");
228   intr_register (12, 0, IF_ON, intr_kill, "#SS Stack Fault Exception");
229   intr_register (13, 0, IF_ON, intr_kill, "#GP General Protection Exception");
230   intr_register (16, 0, IF_ON, intr_kill, "#MF x87 FPU Floating-Point Error");
231   intr_register (17, 0, IF_ON, intr_panic, "#AC Alignment Check Exception");
232   intr_register (18, 0, IF_ON, intr_panic, "#MC Machine-Check Exception");
233   intr_register (19, 0, IF_ON, intr_kill, "#XF SIMD Floating-Point Exception");
234
235   /* Most exceptions can be handled with interrupts turned on.
236      We need to disable interrupts for page faults because the
237      fault address is stored in CR2 and needs to be preserved. */
238   intr_register (14, 0, IF_OFF, intr_kill, "#PF Page-Fault Exception");
239
240   idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
241   asm volatile ("lidt %0" :: "m" (idtr_operand));
242 }
243
244 static void
245 dump_intr_frame (struct intr_frame *f) 
246 {
247   uint32_t cr2, ss;
248   asm ("movl %%cr2, %0" : "=r" (cr2));
249   asm ("movl %%ss, %0" : "=r" (ss));
250
251   printk ("Interrupt %#04x (%s) at eip=%p\n",
252           f->vec_no, intr_name (f->vec_no), f->eip);
253   printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
254   printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
255           f->eax, f->ebx, f->ecx, f->edx);
256   printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
257           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
258   printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
259           f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
260 }
261
262 void
263 intr_panic (struct intr_frame *regs) 
264 {
265   dump_intr_frame (regs);
266   panic ("Panic!");
267 }
268
269 void
270 intr_kill (struct intr_frame *f) 
271 {
272   switch (f->cs)
273     {
274     case SEL_UCSEG:
275       printk ("[%p] Interrupt %#04x (%s), killing process.\n",
276               thread_current (), f->vec_no, intr_name (f->vec_no));
277       thread_exit (); 
278
279     default:
280       panic ("Interrupt %#04x (%s) in unknown segment %04x",
281              f->vec_no, intr_name (f->vec_no), f->cs);
282
283     case SEL_KCSEG:
284       printk ("intr_kill -> panic %d\n", f->vec_no);
285       intr_panic (f);
286     }
287 }
288
289