c4f81312914cc9c1059043df25ae88a0d6b0473e
[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
12 static struct Taskstate ts;
13
14 /* Interrupt descriptor table.  (Must be built at run time because
15  * shifted function addresses can't be represented in relocation records.)
16  */
17 struct Gatedesc idt[256] = { { 0 } };
18 struct Pseudodesc idt_pd = {
19         sizeof(idt) - 1, (uint32_t) idt
20 };
21
22
23 static const char *trapname(int trapno)
24 {
25         static const char * const excnames[] = {
26                 "Divide error",
27                 "Debug",
28                 "Non-Maskable Interrupt",
29                 "Breakpoint",
30                 "Overflow",
31                 "BOUND Range Exceeded",
32                 "Invalid Opcode",
33                 "Device Not Available",
34                 "Double Falt",
35                 "Coprocessor Segment Overrun",
36                 "Invalid TSS",
37                 "Segment Not Present",
38                 "Stack Fault",
39                 "General Protection",
40                 "Page Fault",
41                 "(unknown trap)",
42                 "x87 FPU Floating-Point Error",
43                 "Alignment Check",
44                 "Machine-Check",
45                 "SIMD Floating-Point Exception"
46         };
47
48         if (trapno < sizeof(excnames)/sizeof(excnames[0]))
49                 return excnames[trapno];
50         if (trapno == T_SYSCALL)
51                 return "System call";
52         return "(unknown trap)";
53 }
54
55
56 void
57 idt_init(void)
58 {
59         extern struct Segdesc gdt[];
60         
61         // This table is made in trapentry.S by each macro in that file.
62         // It is layed out such that the ith entry is the ith's traphandler's
63         // (uint32_t) trap addr, then (uint32_t) trap number
64         struct trapinfo { uint32_t trapaddr; uint32_t trapnumber; };
65         extern struct trapinfo trap_tbl[];
66         extern struct trapinfo trap_tbl_end[];
67         int i, trap_tbl_size = trap_tbl_end - trap_tbl;
68         extern void ISR_default(void);
69
70         // set all to default, to catch everything
71         for(i = 0; i < 256; i++)
72                 SETGATE(idt[i], 1, GD_KT, &ISR_default, 0);
73         
74         // set all entries that have real trap handlers
75         // we need to stop short of the last one, since the last is the default
76         // handler with a fake interrupt number (500) that is out of bounds of
77         // the idt[]
78         for(i = 0; i < trap_tbl_size - 1; i++)
79                 SETGATE(idt[trap_tbl[i].trapnumber], 1, GD_KT, trap_tbl[i].trapaddr, 0);
80
81         // turn on syscall handling.  
82         // DPL 3 means this can be triggered by the int instruction
83         idt[T_SYSCALL].gd_dpl = 3;
84
85         // Setup a TSS so that we get the right stack
86         // when we trap to the kernel.
87         ts.ts_esp0 = KSTACKTOP;
88         ts.ts_ss0 = GD_KD;
89
90         // Initialize the TSS field of the gdt.
91         gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
92                                         sizeof(struct Taskstate), 0);
93         gdt[GD_TSS >> 3].sd_s = 0;
94
95         // Load the TSS
96         ltr(GD_TSS);
97
98         // Load the IDT
99         asm volatile("lidt idt_pd");
100 }
101
102 void
103 print_trapframe(struct Trapframe *tf)
104 {
105         cprintf("TRAP frame at %p\n", tf);
106         print_regs(&tf->tf_regs);
107         cprintf("  es   0x----%04x\n", tf->tf_es);
108         cprintf("  ds   0x----%04x\n", tf->tf_ds);
109         cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
110         cprintf("  err  0x%08x\n", tf->tf_err);
111         cprintf("  eip  0x%08x\n", tf->tf_eip);
112         cprintf("  cs   0x----%04x\n", tf->tf_cs);
113         cprintf("  flag 0x%08x\n", tf->tf_eflags);
114         cprintf("  esp  0x%08x\n", tf->tf_esp);
115         cprintf("  ss   0x----%04x\n", tf->tf_ss);
116 }
117
118 void
119 print_regs(struct PushRegs *regs)
120 {
121         cprintf("  edi  0x%08x\n", regs->reg_edi);
122         cprintf("  esi  0x%08x\n", regs->reg_esi);
123         cprintf("  ebp  0x%08x\n", regs->reg_ebp);
124         cprintf("  oesp 0x%08x\n", regs->reg_oesp);
125         cprintf("  ebx  0x%08x\n", regs->reg_ebx);
126         cprintf("  edx  0x%08x\n", regs->reg_edx);
127         cprintf("  ecx  0x%08x\n", regs->reg_ecx);
128         cprintf("  eax  0x%08x\n", regs->reg_eax);
129 }
130
131 static void
132 trap_dispatch(struct Trapframe *tf)
133 {
134         // Handle processor exceptions.
135         // LAB 3: Your code here.
136         
137         switch(tf->tf_trapno) {
138                 case (T_PGFLT):
139                         page_fault_handler(tf);
140                         break;
141                 default:
142                         break;
143         }
144
145         // Unexpected trap: The user process or the kernel has a bug.
146         print_trapframe(tf);
147         if (tf->tf_cs == GD_KT)
148                 panic("unhandled trap in kernel");
149         else {
150                 env_destroy(curenv);
151                 return;
152         }
153 }
154
155 void
156 trap(struct Trapframe *tf)
157 {
158         cprintf("Incoming TRAP frame at %p\n", tf);
159
160         if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT)
161                 panic("Trapframe with invalid CS!");
162
163         if ((tf->tf_cs & 3) == 3) {
164                 // Trapped from user mode.
165                 // Copy trap frame (which is currently on the stack)
166                 // into 'curenv->env_tf', so that running the environment
167                 // will restart at the trap point.
168                 assert(curenv);
169                 curenv->env_tf = *tf;
170                 // The trapframe on the stack should be ignored from here on.
171                 tf = &curenv->env_tf;
172         }
173         
174         // Dispatch based on what type of trap occurred
175         trap_dispatch(tf);
176
177         // should this be if == 3?  Sort out later when we handle traps.
178         // Return to the current environment, which should be runnable.
179         assert(curenv && curenv->env_status == ENV_RUNNABLE);
180         env_run(curenv);
181 }
182
183
184 void
185 page_fault_handler(struct Trapframe *tf)
186 {
187         uint32_t fault_va;
188
189         // Read processor's CR2 register to find the faulting address
190         fault_va = rcr2();
191
192         // Handle kernel-mode page faults.
193         
194         // LAB 3: Your code here.
195
196         // We've already handled kernel-mode exceptions, so if we get here,
197         // the page fault happened in user mode.
198
199         // Call the environment's page fault upcall, if one exists.  Set up a
200         // page fault stack frame on the user exception stack (below
201         // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
202         //
203         // The page fault upcall might cause another page fault, in which case
204         // we branch to the page fault upcall recursively, pushing another
205         // page fault stack frame on top of the user exception stack.
206         //
207         // The trap handler needs one word of scratch space at the top of the
208         // trap-time stack in order to return.  In the non-recursive case, we
209         // don't have to worry about this because the top of the regular user
210         // stack is free.  In the recursive case, this means we have to leave
211         // an extra word between the current top of the exception stack and
212         // the new stack frame because the exception stack _is_ the trap-time
213         // stack.
214         //
215         // If there's no page fault upcall, the environment didn't allocate a
216         // page for its exception stack, or the exception stack overflows,
217         // then destroy the environment that caused the fault.
218         //
219         // Hints:
220         //   user_mem_assert() and env_run() are useful here.
221         //   To change what the user environment runs, modify 'curenv->env_tf'
222         //   (the 'tf' variable points at 'curenv->env_tf').
223         
224         // LAB 4: Your code here.
225
226         // Destroy the environment that caused the fault.
227         cprintf("[%08x] user fault va %08x ip %08x\n",
228                 curenv->env_id, fault_va, tf->tf_eip);
229         print_trapframe(tf);
230         env_destroy(curenv);
231 }
232