Clean up interrupt handling.
[pintos-anon] / src / threads / interrupt.c
1 #include "interrupt.h"
2 #include <stdint.h>
3 #include "intr-stubs.h"
4 #include "debug.h"
5 #include "io.h"
6 #include "lib.h"
7 #include "mmu.h"
8 #include "thread.h"
9 #include "timer.h"
10 \f
11 enum if_level
12 intr_get_level (void) 
13 {
14   uint32_t flags;
15   
16   asm ("pushfl; popl %0" : "=g" (flags));
17
18   return flags & (1 << 9) ? IF_ON : IF_OFF;
19 }
20
21 enum if_level
22 intr_set_level (enum if_level level) 
23 {
24   enum if_level old_level = intr_get_level ();
25   if (level == IF_ON)
26     intr_enable ();
27   else
28     intr_disable ();
29   return old_level;
30 }
31
32 enum if_level
33 intr_enable (void) 
34 {
35   enum if_level old_level = intr_get_level ();
36   asm volatile ("sti");
37   return old_level;
38 }
39
40 enum if_level
41 intr_disable (void) 
42 {
43   enum if_level old_level = intr_get_level ();
44   asm volatile ("cli");
45   return old_level;
46 }
47 \f
48 static void
49 pic_init (void)
50 {
51   /* Every PC has two 8259A Programmable Interrupt Controller
52      (PIC) chips.  One is a "master" accessible at ports 0x20 and
53      0x21.  The other is a "slave" cascaded onto the master's IRQ
54      2 line and accessible at ports 0xa0 and 0xa1.  Accesses to
55      port 0x20 set the A0 line to 0 and accesses to 0x21 set the
56      A1 line to 1.  The situation is similar for the slave PIC.
57      Refer to the 8259A datasheet for details.
58
59      By default, interrupts 0...15 delivered by the PICs will go
60      to interrupt vectors 0...15.  Unfortunately, those vectors
61      are also used for CPU traps and exceptions.  We reprogram
62      the PICs so that interrupts 0...15 are delivered to
63      interrupt vectors 32...47 instead. */
64
65   /* Mask all interrupts on both PICs. */
66   outb (0x21, 0xff);
67   outb (0xa1, 0xff);
68
69   /* Initialize master. */
70   outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
71   outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
72   outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
73   outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
74
75   /* Initialize slave. */
76   outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
77   outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
78   outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
79   outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
80
81   /* Unmask all interrupts. */
82   outb (0x21, 0x00);
83   outb (0xa1, 0x00);
84 }
85
86 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
87    If we don't acknowledge the IRQ, we'll never get it again, so
88    this is important.  */
89 static void
90 pic_eoi (void) 
91 {
92   /* FIXME?  The Linux code is much more complicated. */
93   outb (0x20, 0x20);
94 }
95 \f
96 uint64_t idt[256];
97
98 intr_handler_func *intr_handlers[256];
99
100 void intr_handler (struct intr_frame *args);
101
102 bool intr_in_progress;
103 bool yield_on_return;
104
105 void
106 intr_handler (struct intr_frame *args) 
107 {
108   bool external;
109   
110   yield_on_return = false;
111
112   external = args->vec_no >= 0x20 && args->vec_no < 0x30;
113   if (external) 
114     {
115       ASSERT (intr_get_level () == IF_OFF);
116       ASSERT (!intr_context ());
117       intr_in_progress = true;
118     }
119
120   intr_handlers[args->vec_no] (args);
121
122   if (external) 
123     {
124       ASSERT (intr_get_level () == IF_OFF);
125       ASSERT (intr_context ());
126       intr_in_progress = false;
127       pic_eoi (); 
128     }
129
130   if (yield_on_return) 
131     {
132       printk (".");
133       thread_yield (); 
134     }
135 }
136
137 bool
138 intr_context (void) 
139 {
140   return intr_in_progress;
141 }
142
143 void
144 intr_yield_on_return (void) 
145 {
146   yield_on_return = true;
147 }
148
149 /* Handles interrupts we don't know about. */
150 intr_handler_func intr_unexpected;
151
152 /* Handlers for CPU exceptions. */
153 intr_handler_func excp00_divide_error;
154 intr_handler_func excp01_debug;
155 intr_handler_func excp02_nmi;
156 intr_handler_func excp03_breakpoint;
157 intr_handler_func excp04_overflow;
158 intr_handler_func excp05_bound;
159 intr_handler_func excp06_invalid_opcode;
160 intr_handler_func excp07_device_not_available;
161 intr_handler_func excp08_double_fault;
162 intr_handler_func excp09_coprocessor_overrun;
163 intr_handler_func excp0a_invalid_tss;
164 intr_handler_func excp0b_segment_not_present;
165 intr_handler_func excp0c_stack_fault;
166 intr_handler_func excp0d_general_protection;
167 intr_handler_func excp0e_page_fault;
168 intr_handler_func excp10_fp_error;
169 intr_handler_func excp11_alignment;
170 intr_handler_func excp12_machine_check;
171 intr_handler_func excp13_simd_error;
172
173 static uint64_t
174 make_intr_gate (void (*target) (void),
175                 int dpl)
176 {
177   uint32_t offset = (uint32_t) target;
178   uint32_t e0 = ((offset & 0xffff)            /* Offset 15:0. */
179                  | (SEL_KCSEG << 16));        /* Target code segment. */
180   uint32_t e1 = ((offset & 0xffff0000)        /* Offset 31:16. */
181                  | (1 << 15)                  /* Present. */
182                  | (dpl << 13)                /* Descriptor privilege. */
183                  | (SYS_SYSTEM << 12)         /* System. */
184                  | (TYPE_INT_32 << 8));       /* 32-bit interrupt gate. */
185   return e0 | ((uint64_t) e1 << 32);
186 }
187
188 static uint64_t
189 make_trap_gate (void (*target) (void),
190                 int dpl)
191 {
192   return make_intr_gate (target, dpl) | (1 << 8);
193 }
194
195 /* We don't support nested interrupts generated by external
196    hardware, so these interrupts (vec_no 0x20...0x2f) should
197    specify IF_OFF for LEVEL.  Otherwise a timer interrupt could
198    cause a task switch during interrupt handling.  Most other
199    interrupts can and should be handled with interrupts
200    enabled. */
201 void
202 intr_register (uint8_t vec_no, int dpl, enum if_level level,
203                intr_handler_func *handler) 
204 {
205   if (level == IF_ON)
206     idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
207   else
208     idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
209   intr_handlers[vec_no] = handler;
210 }
211
212 void
213 intr_init (void)
214 {
215   uint64_t idtr_operand;
216   int i;
217
218   pic_init ();
219
220   /* Install default handlers. */
221   for (i = 0; i < 256; i++)
222     intr_register (i, 0, IF_OFF, intr_unexpected);
223
224   /* Most exceptions require ring 0.
225      Exceptions 3, 4, and 5 can be caused by ring 3 directly.
226
227      Most exceptions can be handled with interrupts turned on.
228      We need to disable interrupts for page faults because the
229      fault address is stored in CR2 and needs to be preserved.
230   */
231 #if 0
232   intr_register (0x00, 0, IF_ON, excp00_divide_error);
233   intr_register (0x01, 0, IF_ON, excp01_debug);
234   intr_register (0x02, 0, IF_ON, excp02_nmi);
235   intr_register (0x03, 3, IF_ON, excp03_breakpoint);
236   intr_register (0x04, 3, IF_ON, excp04_overflow);
237   intr_register (0x05, 3, IF_ON, excp05_bound);
238   intr_register (0x06, 0, IF_ON, excp06_invalid_opcode);
239   intr_register (0x07, 0, IF_ON, excp07_device_not_available);
240   intr_register (0x08, 0, IF_ON, excp08_double_fault);
241   intr_register (0x09, 0, IF_ON, excp09_coprocessor_overrun);
242   intr_register (0x0a, 0, IF_ON, excp0a_invalid_tss);
243   intr_register (0x0b, 0, IF_ON, excp0b_segment_not_present);
244   intr_register (0x0c, 0, IF_ON, excp0c_stack_fault);
245   intr_register (0x0d, 0, IF_ON, excp0d_general_protection);
246   intr_register (0x0e, 0, IF_OFF, excp0e_page_fault);
247   intr_register (0x10, 0, IF_ON, excp10_fp_error);
248   intr_register (0x11, 0, IF_ON, excp11_alignment);
249   intr_register (0x12, 0, IF_ON, excp12_machine_check);
250   intr_register (0x13, 0, IF_ON, excp13_simd_error);
251 #endif
252
253   idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
254   asm volatile ("lidt %0" :: "m" (idtr_operand));
255 }
256
257 void
258 intr_unexpected (struct intr_frame *regs)
259 {
260   uint32_t cr2;
261   asm ("movl %%cr2, %0" : "=r" (cr2));
262   panic ("Unexpected interrupt 0x%02x, error code %08x, cr2=%08x, eip=%p",
263          regs->vec_no, regs->error_code, cr2, (void *) regs->eip);
264 }