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