SMP Booting, APIC, and IRQs
[akaros.git] / kern / trap.c
1 #include <inc/mmu.h>
2 #include <inc/x86.h>
3 #include <inc/assert.h>
4
5 #include <kern/pmap.h>
6 #include <kern/trap.h>
7 #include <kern/console.h>
8 #include <kern/monitor.h>
9 #include <kern/env.h>
10 #include <kern/syscall.h>
11 #include <kern/apic.h>
12
13 static struct Taskstate ts;
14
15 /* Interrupt descriptor table.  (Must be built at run time because
16  * shifted function addresses can't be represented in relocation records.)
17  */
18 // should align this on an 8 byte boundary (SDM V3A 5-13)
19 struct Gatedesc idt[256] = { { 0 } };
20 struct Pseudodesc idt_pd = {
21         sizeof(idt) - 1, (uint32_t) idt
22 };
23
24
25 static const char *NTS (IN_HANDLER trapname)(int trapno)
26 {
27     // zra: excnames is NORACE because Ivy doesn't trust const
28         static const char *NT const (NORACE excnames)[] = {
29                 "Divide error",
30                 "Debug",
31                 "Non-Maskable Interrupt",
32                 "Breakpoint",
33                 "Overflow",
34                 "BOUND Range Exceeded",
35                 "Invalid Opcode",
36                 "Device Not Available",
37                 "Double Fault",
38                 "Coprocessor Segment Overrun",
39                 "Invalid TSS",
40                 "Segment Not Present",
41                 "Stack Fault",
42                 "General Protection",
43                 "Page Fault",
44                 "(unknown trap)",
45                 "x87 FPU Floating-Point Error",
46                 "Alignment Check",
47                 "Machine-Check",
48                 "SIMD Floating-Point Exception"
49         };
50
51         if (trapno < sizeof(excnames)/sizeof(excnames[0]))
52                 return excnames[trapno];
53         if (trapno == T_SYSCALL)
54                 return "System call";
55         return "(unknown trap)";
56 }
57
58
59 void
60 idt_init(void)
61 {
62         extern struct Segdesc gdt[];
63         
64         // This table is made in trapentry.S by each macro in that file.
65         // It is layed out such that the ith entry is the ith's traphandler's
66         // (uint32_t) trap addr, then (uint32_t) trap number
67         struct trapinfo { uint32_t trapaddr; uint32_t trapnumber; };
68         extern struct trapinfo (BND(__this,trap_tbl_end) trap_tbl)[];
69         extern struct trapinfo (SNT trap_tbl_end)[];
70         int i, trap_tbl_size = trap_tbl_end - trap_tbl;
71         extern void ISR_default(void);
72
73         // set all to default, to catch everything
74         for(i = 0; i < 256; i++)
75                 SETGATE(idt[i], 0, GD_KT, &ISR_default, 0);
76         
77         // set all entries that have real trap handlers
78         // we need to stop short of the last one, since the last is the default
79         // handler with a fake interrupt number (500) that is out of bounds of
80         // the idt[]
81         // if we set these to trap gates, be sure to handle the IRQs separately
82         // and we might need to break our pretty tables
83         for(i = 0; i < trap_tbl_size - 1; i++) 
84                 SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
85
86         // turn on syscall handling and other user-accessible ints
87         // DPL 3 means this can be triggered by the int instruction
88         idt[T_SYSCALL].gd_dpl = 3;
89         idt[T_BRKPT].gd_dpl = 3;
90
91         // Setup a TSS so that we get the right stack
92         // when we trap to the kernel.
93         ts.ts_esp0 = KSTACKTOP;
94         ts.ts_ss0 = GD_KD;
95
96         // Initialize the TSS field of the gdt.
97         gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
98                                         sizeof(struct Taskstate), 0);
99         gdt[GD_TSS >> 3].sd_s = 0;
100
101         // Load the TSS
102         ltr(GD_TSS);
103
104         // Load the IDT
105         asm volatile("lidt idt_pd");
106
107         // This will go away when we start using the IOAPIC properly
108         remap_pic();
109         lapic_enable();
110         // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
111         write_mmreg32(LAPIC_LVT_LINT0, 0x700); 
112         // mask it to shut it up for now
113         mask_lapic_lvt(LAPIC_LVT_LINT0);
114 }
115
116 void
117 (IN_HANDLER print_trapframe)(struct Trapframe *tf)
118 {
119         cprintf("TRAP frame at %p\n", tf);
120         print_regs(&tf->tf_regs);
121         cprintf("  es   0x----%04x\n", tf->tf_es);
122         cprintf("  ds   0x----%04x\n", tf->tf_ds);
123         cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
124         cprintf("  err  0x%08x\n", tf->tf_err);
125         cprintf("  eip  0x%08x\n", tf->tf_eip);
126         cprintf("  cs   0x----%04x\n", tf->tf_cs);
127         cprintf("  flag 0x%08x\n", tf->tf_eflags);
128         cprintf("  esp  0x%08x\n", tf->tf_esp);
129         cprintf("  ss   0x----%04x\n", tf->tf_ss);
130 }
131
132 void
133 (IN_HANDLER print_regs)(struct PushRegs *regs)
134 {
135         cprintf("  edi  0x%08x\n", regs->reg_edi);
136         cprintf("  esi  0x%08x\n", regs->reg_esi);
137         cprintf("  ebp  0x%08x\n", regs->reg_ebp);
138         cprintf("  oesp 0x%08x\n", regs->reg_oesp);
139         cprintf("  ebx  0x%08x\n", regs->reg_ebx);
140         cprintf("  edx  0x%08x\n", regs->reg_edx);
141         cprintf("  ecx  0x%08x\n", regs->reg_ecx);
142         cprintf("  eax  0x%08x\n", regs->reg_eax);
143 }
144
145 static void
146 (IN_HANDLER trap_dispatch)(struct Trapframe *tf)
147 {
148         // Handle processor exceptions.
149         
150         switch(tf->tf_trapno) {
151                 case T_BRKPT:
152                         while (1)
153                                 monitor(tf);
154                         // never get to this
155                         assert(0);
156                 case T_PGFLT:
157                         page_fault_handler(tf);
158                         break;
159                 case T_SYSCALL:
160                         // check for userspace, for now
161                         assert(tf->tf_cs != GD_KT);
162                         tf->tf_regs.reg_eax = 
163                                 syscall(tf->tf_regs.reg_eax, tf->tf_regs.reg_edx, 
164                                         tf->tf_regs.reg_ecx, tf->tf_regs.reg_ebx, 
165                                         tf->tf_regs.reg_edi, tf->tf_regs.reg_esi);
166                         env_run(curenv);
167                         break;
168                 default:
169                         // Unexpected trap: The user process or the kernel has a bug.
170                         print_trapframe(tf);
171                         if (tf->tf_cs == GD_KT)
172                                 panic("Damn Damn!  Unhandled trap in the kernel!");
173                         else {
174                                 env_destroy(curenv);
175                                 return;
176                         }
177         }
178         return;
179 }
180
181 void
182 (IN_HANDLER trap)(struct Trapframe *tf)
183 {
184         cprintf("Incoming TRAP frame at %p\n", tf);
185
186         if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT) {
187                 print_trapframe(tf);
188                 panic("Trapframe with invalid CS!");
189         }
190
191         if ((tf->tf_cs & 3) == 3) {
192                 // Trapped from user mode.
193                 // Copy trap frame (which is currently on the stack)
194                 // into 'curenv->env_tf', so that running the environment
195                 // will restart at the trap point.
196                 assert(curenv);
197                 curenv->env_tf = *tf;
198                 // The trapframe on the stack should be ignored from here on.
199                 tf = &curenv->env_tf;
200         }
201         
202         // Dispatch based on what type of trap occurred
203         trap_dispatch(tf);
204
205         // should this be if == 3?  Sort out later when we handle traps.
206         // so far we never get here
207         assert(0);
208         // Return to the current environment, which should be runnable.
209         assert(curenv && curenv->env_status == ENV_RUNNABLE);
210         env_run(curenv);
211 }
212
213 void
214 (IN_HANDLER irq_handler)(struct Trapframe *tf)
215 {
216         // send EOI.  might want to do this later, and not earlier
217         // and probably in assembly.  just need to determine what's LAPIC or not
218         // this is set up to work with an old PIC for now
219         // and this is hardcoded to have only 32 be the PIC (LINT0 ExtINT)
220         if (tf->tf_trapno == 32) {
221                 pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
222         } else // LAPIC style
223                 lapic_send_eoi();
224
225         // consider doing James-style register_interrupt_handler instead
226         //trap(tf);
227         // this is just for handling the one time use of this in smp_boot
228         cprintf("Incoming IRQ, ISR = %d\n", tf->tf_trapno);
229         if (tf->tf_trapno == 33) {
230                 cprintf("LAPIC TIMER!!!\n");
231                 extern volatile bool waiting;
232                 extern volatile uint8_t num_cpus;
233                 {HANDLER_ATOMIC 
234                         waiting = 0;
235                         cprintf("Num_Cpus: %d\n", num_cpus);
236                 }
237                 }
238 }
239
240 void
241 page_fault_handler(struct Trapframe *tf)
242 {
243         uint32_t fault_va;
244
245         // Read processor's CR2 register to find the faulting address
246         fault_va = rcr2();
247
248         // Handle kernel-mode page faults.
249         
250         // TODO - one day, we'll want to handle this.
251         if ((tf->tf_cs & 3) == 0)
252                 panic("Page Fault in the Kernel!");
253
254         // We've already handled kernel-mode exceptions, so if we get here,
255         // the page fault happened in user mode.
256
257         // Call the environment's page fault upcall, if one exists.  Set up a
258         // page fault stack frame on the user exception stack (below
259         // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
260         //
261         // The page fault upcall might cause another page fault, in which case
262         // we branch to the page fault upcall recursively, pushing another
263         // page fault stack frame on top of the user exception stack.
264         //
265         // The trap handler needs one word of scratch space at the top of the
266         // trap-time stack in order to return.  In the non-recursive case, we
267         // don't have to worry about this because the top of the regular user
268         // stack is free.  In the recursive case, this means we have to leave
269         // an extra word between the current top of the exception stack and
270         // the new stack frame because the exception stack _is_ the trap-time
271         // stack.
272         //
273         // If there's no page fault upcall, the environment didn't allocate a
274         // page for its exception stack, or the exception stack overflows,
275         // then destroy the environment that caused the fault.
276         //
277         // Hints:
278         //   user_mem_assert() and env_run() are useful here.
279         //   To change what the user environment runs, modify 'curenv->env_tf'
280         //   (the 'tf' variable points at 'curenv->env_tf').
281         
282         // LAB 4: Your code here.
283
284         // Destroy the environment that caused the fault.
285         cprintf("[%08x] user fault va %08x ip %08x\n",
286                 curenv->env_id, fault_va, tf->tf_eip);
287         print_trapframe(tf);
288         env_destroy(curenv);
289 }
290