Memory protection and page fault handling
[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 and other user-accessible ints
82         // DPL 3 means this can be triggered by the int instruction
83         idt[T_SYSCALL].gd_dpl = 3;
84         idt[T_BRKPT].gd_dpl = 3;
85
86         // Setup a TSS so that we get the right stack
87         // when we trap to the kernel.
88         ts.ts_esp0 = KSTACKTOP;
89         ts.ts_ss0 = GD_KD;
90
91         // Initialize the TSS field of the gdt.
92         gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
93                                         sizeof(struct Taskstate), 0);
94         gdt[GD_TSS >> 3].sd_s = 0;
95
96         // Load the TSS
97         ltr(GD_TSS);
98
99         // Load the IDT
100         asm volatile("lidt idt_pd");
101 }
102
103 void
104 print_trapframe(struct Trapframe *tf)
105 {
106         cprintf("TRAP frame at %p\n", tf);
107         print_regs(&tf->tf_regs);
108         cprintf("  es   0x----%04x\n", tf->tf_es);
109         cprintf("  ds   0x----%04x\n", tf->tf_ds);
110         cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
111         cprintf("  err  0x%08x\n", tf->tf_err);
112         cprintf("  eip  0x%08x\n", tf->tf_eip);
113         cprintf("  cs   0x----%04x\n", tf->tf_cs);
114         cprintf("  flag 0x%08x\n", tf->tf_eflags);
115         cprintf("  esp  0x%08x\n", tf->tf_esp);
116         cprintf("  ss   0x----%04x\n", tf->tf_ss);
117 }
118
119 void
120 print_regs(struct PushRegs *regs)
121 {
122         cprintf("  edi  0x%08x\n", regs->reg_edi);
123         cprintf("  esi  0x%08x\n", regs->reg_esi);
124         cprintf("  ebp  0x%08x\n", regs->reg_ebp);
125         cprintf("  oesp 0x%08x\n", regs->reg_oesp);
126         cprintf("  ebx  0x%08x\n", regs->reg_ebx);
127         cprintf("  edx  0x%08x\n", regs->reg_edx);
128         cprintf("  ecx  0x%08x\n", regs->reg_ecx);
129         cprintf("  eax  0x%08x\n", regs->reg_eax);
130 }
131
132 static void
133 trap_dispatch(struct Trapframe *tf)
134 {
135         // Handle processor exceptions.
136         
137         switch(tf->tf_trapno) {
138                 case T_BRKPT:
139                         while (1)
140                                 monitor(tf);
141                         // never get to this
142                         assert(0);
143                 case T_PGFLT:
144                         page_fault_handler(tf);
145                         break;
146                 case T_SYSCALL:
147                         // check for userspace, for now
148                         assert(tf->tf_cs != GD_KT);
149                         tf->tf_regs.reg_eax = 
150                                 syscall(tf->tf_regs.reg_eax, tf->tf_regs.reg_edx, 
151                                         tf->tf_regs.reg_ecx, tf->tf_regs.reg_ebx, 
152                                         tf->tf_regs.reg_edi, tf->tf_regs.reg_esi);
153                         env_run(curenv);
154                         break;
155                 default:
156                         // Unexpected trap: The user process or the kernel has a bug.
157                         print_trapframe(tf);
158                         if (tf->tf_cs == GD_KT)
159                                 panic("Damn Damn!  Unhandled trap in the kernel!");
160                         else {
161                                 env_destroy(curenv);
162                                 return;
163                         }
164         }
165         return;
166 }
167
168 void
169 trap(struct Trapframe *tf)
170 {
171         cprintf("Incoming TRAP frame at %p\n", tf);
172
173         if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT)
174                 panic("Trapframe with invalid CS!");
175
176         if ((tf->tf_cs & 3) == 3) {
177                 // Trapped from user mode.
178                 // Copy trap frame (which is currently on the stack)
179                 // into 'curenv->env_tf', so that running the environment
180                 // will restart at the trap point.
181                 assert(curenv);
182                 curenv->env_tf = *tf;
183                 // The trapframe on the stack should be ignored from here on.
184                 tf = &curenv->env_tf;
185         }
186         
187         // Dispatch based on what type of trap occurred
188         trap_dispatch(tf);
189
190         // should this be if == 3?  Sort out later when we handle traps.
191         // so far we never get here
192         assert(0);
193         // Return to the current environment, which should be runnable.
194         assert(curenv && curenv->env_status == ENV_RUNNABLE);
195         env_run(curenv);
196 }
197
198
199 void
200 page_fault_handler(struct Trapframe *tf)
201 {
202         uint32_t fault_va;
203
204         // Read processor's CR2 register to find the faulting address
205         fault_va = rcr2();
206
207         // Handle kernel-mode page faults.
208         
209         // TODO - one day, we'll want to handle this.
210         if ((tf->tf_cs & 3) == 0)
211                 panic("Page Fault in the Kernel!");
212
213         // We've already handled kernel-mode exceptions, so if we get here,
214         // the page fault happened in user mode.
215
216         // Call the environment's page fault upcall, if one exists.  Set up a
217         // page fault stack frame on the user exception stack (below
218         // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
219         //
220         // The page fault upcall might cause another page fault, in which case
221         // we branch to the page fault upcall recursively, pushing another
222         // page fault stack frame on top of the user exception stack.
223         //
224         // The trap handler needs one word of scratch space at the top of the
225         // trap-time stack in order to return.  In the non-recursive case, we
226         // don't have to worry about this because the top of the regular user
227         // stack is free.  In the recursive case, this means we have to leave
228         // an extra word between the current top of the exception stack and
229         // the new stack frame because the exception stack _is_ the trap-time
230         // stack.
231         //
232         // If there's no page fault upcall, the environment didn't allocate a
233         // page for its exception stack, or the exception stack overflows,
234         // then destroy the environment that caused the fault.
235         //
236         // Hints:
237         //   user_mem_assert() and env_run() are useful here.
238         //   To change what the user environment runs, modify 'curenv->env_tf'
239         //   (the 'tf' variable points at 'curenv->env_tf').
240         
241         // LAB 4: Your code here.
242
243         // Destroy the environment that caused the fault.
244         cprintf("[%08x] user fault va %08x ip %08x\n",
245                 curenv->env_id, fault_va, tf->tf_eip);
246         print_trapframe(tf);
247         env_destroy(curenv);
248 }
249