Make yield_on_return only usable in an external interrupt context: (1)
[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 static bool intr_in_progress;
106 static 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 = args->vec_no >= 0x20 && args->vec_no < 0x30;
121   if (external) 
122     {
123       ASSERT (intr_get_level () == IF_OFF);
124       ASSERT (!intr_context ());
125       intr_in_progress = true;
126       yield_on_return = false;
127     }
128
129   intr_handlers[args->vec_no] (args);
130
131   if (external) 
132     {
133       ASSERT (intr_get_level () == IF_OFF);
134       ASSERT (intr_context ());
135       intr_in_progress = false;
136       pic_eoi (); 
137
138       if (yield_on_return) 
139         thread_yield (); 
140     }
141 }
142
143 bool
144 intr_context (void) 
145 {
146   return intr_in_progress;
147 }
148
149 void
150 intr_yield_on_return (void) 
151 {
152   ASSERT (intr_context ());
153   yield_on_return = true;
154 }
155
156 intr_handler_func intr_panic NO_RETURN;
157 intr_handler_func intr_kill NO_RETURN;
158
159 static uint64_t
160 make_gate (void (*target) (void), int dpl, enum seg_type type)
161 {
162   uint32_t offset = (uint32_t) target;
163   uint32_t e0 = ((offset & 0xffff)            /* Offset 15:0. */
164                  | (SEL_KCSEG << 16));        /* Target code segment. */
165   uint32_t e1 = ((offset & 0xffff0000)        /* Offset 31:16. */
166                  | (1 << 15)                  /* Present. */
167                  | ((uint32_t) dpl << 13)     /* Descriptor privilege. */
168                  | (SYS_SYSTEM << 12)         /* System. */
169                  | ((uint32_t) type << 8));   /* Gate type. */
170   return e0 | ((uint64_t) e1 << 32);
171 }
172
173 static uint64_t
174 make_intr_gate (void (*target) (void), int dpl)
175 {
176   return make_gate (target, dpl, TYPE_INT_32);
177 }
178
179 static uint64_t
180 make_trap_gate (void (*target) (void), int dpl)
181 {
182   return make_gate (target, dpl, TYPE_TRAP_32);
183 }
184
185 void
186 intr_register (uint8_t vec_no, int dpl, enum if_level level,
187                intr_handler_func *handler,
188                const char *name) 
189 {
190   /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
191      0x2f) should specify IF_OFF for LEVEL.  Otherwise a timer
192      interrupt could cause a task switch during interrupt
193      handling.  Most other interrupts can and should be handled
194      with interrupts enabled. */
195   ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == IF_OFF);
196
197   if (level == IF_ON)
198     idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
199   else
200     idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
201   intr_handlers[vec_no] = handler;
202   intr_names[vec_no] = name;
203 }
204
205 void
206 intr_init (void)
207 {
208   uint64_t idtr_operand;
209   int i;
210
211   pic_init ();
212
213   /* Install default handlers. */
214   for (i = 0; i < 256; i++)
215     intr_register (i, 0, IF_OFF, intr_panic, NULL);
216
217   /* Most exceptions require ring 0.
218      Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
219   intr_register (0, 0, IF_ON, intr_kill, "#DE Divide Error");
220   intr_register (1, 0, IF_ON, intr_kill, "#DB Debug Exception");
221   intr_register (2, 0, IF_ON, intr_panic, "NMI Interrupt");
222   intr_register (3, 3, IF_ON, intr_kill, "#BP Breakpoint Exception");
223   intr_register (4, 3, IF_ON, intr_kill, "#OF Overflow Exception");
224   intr_register (5, 3, IF_ON, intr_kill, "#BR BOUND Range Exceeded Exception");
225   intr_register (6, 0, IF_ON, intr_kill, "#UD Invalid Opcode Exception");
226   intr_register (7, 0, IF_ON, intr_kill, "#NM Device Not Available Exception");
227   intr_register (8, 0, IF_ON, intr_panic, "#DF Double Fault Exception");
228   intr_register (9, 0, IF_ON, intr_panic, "Coprocessor Segment Overrun");
229   intr_register (10, 0, IF_ON, intr_panic, "#TS Invalid TSS Exception");
230   intr_register (11, 0, IF_ON, intr_kill, "#NP Segment Not Present");
231   intr_register (12, 0, IF_ON, intr_kill, "#SS Stack Fault Exception");
232   intr_register (13, 0, IF_ON, intr_kill, "#GP General Protection Exception");
233   intr_register (16, 0, IF_ON, intr_kill, "#MF x87 FPU Floating-Point Error");
234   intr_register (17, 0, IF_ON, intr_panic, "#AC Alignment Check Exception");
235   intr_register (18, 0, IF_ON, intr_panic, "#MC Machine-Check Exception");
236   intr_register (19, 0, IF_ON, intr_kill, "#XF SIMD Floating-Point Exception");
237
238   /* Most exceptions can be handled with interrupts turned on.
239      We need to disable interrupts for page faults because the
240      fault address is stored in CR2 and needs to be preserved. */
241   intr_register (14, 0, IF_OFF, intr_kill, "#PF Page-Fault Exception");
242
243   idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
244   asm volatile ("lidt %0" :: "m" (idtr_operand));
245 }
246
247 static void
248 dump_intr_frame (struct intr_frame *f) 
249 {
250   uint32_t cr2, ss;
251   asm ("movl %%cr2, %0" : "=r" (cr2));
252   asm ("movl %%ss, %0" : "=r" (ss));
253
254   printk ("Interrupt %#04x (%s) at eip=%p\n",
255           f->vec_no, intr_name (f->vec_no), f->eip);
256   printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
257   printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
258           f->eax, f->ebx, f->ecx, f->edx);
259   printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
260           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
261   printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
262           f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
263 }
264
265 void
266 intr_panic (struct intr_frame *regs) 
267 {
268   dump_intr_frame (regs);
269   panic ("Panic!");
270 }
271
272 void
273 intr_kill (struct intr_frame *f) 
274 {
275   switch (f->cs)
276     {
277     case SEL_UCSEG:
278       printk ("[%p] Interrupt %#04x (%s), killing process.\n",
279               thread_current (), f->vec_no, intr_name (f->vec_no));
280       thread_exit (); 
281
282     case SEL_KCSEG:
283       printk ("Kernel bug - unexpected interrupt in kernel context\n");
284       intr_panic (f);
285
286     default:
287       printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
288              f->vec_no, intr_name (f->vec_no), f->cs);
289       thread_exit ();
290     }
291 }
292
293