* same with xv6
[mascara-docs.git] / i386 / MIT / course / src / git.lab / kern / trap.c
blob2840188abb77774e4203b369325745739bf6407d
1 #include <inc/mmu.h>
2 #include <inc/x86.h>
3 #include <inc/assert.h>
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/sched.h>
12 #include <kern/kclock.h>
13 #include <kern/picirq.h>
14 #include <kern/cpu.h>
15 #include <kern/spinlock.h>
16 #include <kern/time.h>
18 static struct Taskstate ts;
20 /* For debugging, so print_trapframe can distinguish between printing
21 * a saved trapframe and printing the current trapframe and print some
22 * additional information in the latter case.
24 static struct Trapframe *last_tf;
26 /* Interrupt descriptor table. (Must be built at run time because
27 * shifted function addresses can't be represented in relocation records.)
29 struct Gatedesc idt[256] = { { 0 } };
30 struct Pseudodesc idt_pd = {
31 sizeof(idt) - 1, (uint32_t) idt
35 static const char *trapname(int trapno)
37 static const char * const excnames[] = {
38 "Divide error",
39 "Debug",
40 "Non-Maskable Interrupt",
41 "Breakpoint",
42 "Overflow",
43 "BOUND Range Exceeded",
44 "Invalid Opcode",
45 "Device Not Available",
46 "Double Fault",
47 "Coprocessor Segment Overrun",
48 "Invalid TSS",
49 "Segment Not Present",
50 "Stack Fault",
51 "General Protection",
52 "Page Fault",
53 "(unknown trap)",
54 "x87 FPU Floating-Point Error",
55 "Alignment Check",
56 "Machine-Check",
57 "SIMD Floating-Point Exception"
60 if (trapno < sizeof(excnames)/sizeof(excnames[0]))
61 return excnames[trapno];
62 if (trapno == T_SYSCALL)
63 return "System call";
64 if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16)
65 return "Hardware Interrupt";
66 return "(unknown trap)";
70 void
71 trap_init(void)
73 extern struct Segdesc gdt[];
75 // LAB 3: Your code here.
77 // Per-CPU setup
78 trap_init_percpu();
81 // Initialize and load the per-CPU TSS and IDT
82 void
83 trap_init_percpu(void)
85 // The example code here sets up the Task State Segment (TSS) and
86 // the TSS descriptor for CPU 0. But it is incorrect if we are
87 // running on other CPUs because each CPU has its own kernel stack.
88 // Fix the code so that it works for all CPUs.
90 // Hints:
91 // - The macro "thiscpu" always refers to the current CPU's
92 // struct Cpu;
93 // - The ID of the current CPU is given by cpunum() or
94 // thiscpu->cpu_id;
95 // - Use "thiscpu->cpu_ts" as the TSS for the current CPU,
96 // rather than the global "ts" variable;
97 // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor;
98 // - You mapped the per-CPU kernel stacks in mem_init_mp()
100 // ltr sets a 'busy' flag in the TSS selector, so if you
101 // accidentally load the same TSS on more than one CPU, you'll
102 // get a triple fault. If you set up an individual CPU's TSS
103 // wrong, you may not get a fault until you try to return from
104 // user space on that CPU.
106 // LAB 4: Your code here:
108 // Setup a TSS so that we get the right stack
109 // when we trap to the kernel.
110 ts.ts_esp0 = KSTACKTOP;
111 ts.ts_ss0 = GD_KD;
113 // Initialize the TSS slot of the gdt.
114 gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
115 sizeof(struct Taskstate), 0);
116 gdt[GD_TSS0 >> 3].sd_s = 0;
118 // Load the TSS selector (like other segment selectors, the
119 // bottom three bits are special; we leave them 0)
120 ltr(GD_TSS0);
122 // Load the IDT
123 lidt(&idt_pd);
126 void
127 print_trapframe(struct Trapframe *tf)
129 cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum());
130 print_regs(&tf->tf_regs);
131 cprintf(" es 0x----%04x\n", tf->tf_es);
132 cprintf(" ds 0x----%04x\n", tf->tf_ds);
133 cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
134 // If this trap was a page fault that just happened
135 // (so %cr2 is meaningful), print the faulting linear address.
136 if (tf == last_tf && tf->tf_trapno == T_PGFLT)
137 cprintf(" cr2 0x%08x\n", rcr2());
138 cprintf(" err 0x%08x", tf->tf_err);
139 // For page faults, print decoded fault error code:
140 // U/K=fault occurred in user/kernel mode
141 // W/R=a write/read caused the fault
142 // PR=a protection violation caused the fault (NP=page not present).
143 if (tf->tf_trapno == T_PGFLT)
144 cprintf(" [%s, %s, %s]\n",
145 tf->tf_err & 4 ? "user" : "kernel",
146 tf->tf_err & 2 ? "write" : "read",
147 tf->tf_err & 1 ? "protection" : "not-present");
148 else
149 cprintf("\n");
150 cprintf(" eip 0x%08x\n", tf->tf_eip);
151 cprintf(" cs 0x----%04x\n", tf->tf_cs);
152 cprintf(" flag 0x%08x\n", tf->tf_eflags);
153 if ((tf->tf_cs & 3) != 0) {
154 cprintf(" esp 0x%08x\n", tf->tf_esp);
155 cprintf(" ss 0x----%04x\n", tf->tf_ss);
159 void
160 print_regs(struct PushRegs *regs)
162 cprintf(" edi 0x%08x\n", regs->reg_edi);
163 cprintf(" esi 0x%08x\n", regs->reg_esi);
164 cprintf(" ebp 0x%08x\n", regs->reg_ebp);
165 cprintf(" oesp 0x%08x\n", regs->reg_oesp);
166 cprintf(" ebx 0x%08x\n", regs->reg_ebx);
167 cprintf(" edx 0x%08x\n", regs->reg_edx);
168 cprintf(" ecx 0x%08x\n", regs->reg_ecx);
169 cprintf(" eax 0x%08x\n", regs->reg_eax);
172 static void
173 trap_dispatch(struct Trapframe *tf)
175 // Handle processor exceptions.
176 // LAB 3: Your code here.
178 // Handle spurious interrupts
179 // The hardware sometimes raises these because of noise on the
180 // IRQ line or other reasons. We don't care.
181 if (tf->tf_trapno == IRQ_OFFSET + IRQ_SPURIOUS) {
182 cprintf("Spurious interrupt on irq 7\n");
183 print_trapframe(tf);
184 return;
187 // Handle clock interrupts. Don't forget to acknowledge the
188 // interrupt using lapic_eoi() before calling the scheduler!
189 // LAB 4: Your code here.
191 // Add time tick increment to clock interrupts.
192 // Be careful! In multiprocessors, clock interrupts are
193 // triggered on every CPU.
194 // LAB 6: Your code here.
197 // Handle keyboard and serial interrupts.
198 // LAB 7: Your code here.
200 // Unexpected trap: The user process or the kernel has a bug.
201 print_trapframe(tf);
202 if (tf->tf_cs == GD_KT)
203 panic("unhandled trap in kernel");
204 else {
205 env_destroy(curenv);
206 return;
210 void
211 trap(struct Trapframe *tf)
213 // The environment may have set DF and some versions
214 // of GCC rely on DF being clear
215 asm volatile("cld" ::: "cc");
217 // Halt the CPU if some other CPU has called panic()
218 extern char *panicstr;
219 if (panicstr)
220 asm volatile("hlt");
222 // Check that interrupts are disabled. If this assertion
223 // fails, DO NOT be tempted to fix it by inserting a "cli" in
224 // the interrupt path.
225 assert(!(read_eflags() & FL_IF));
227 if ((tf->tf_cs & 3) == 3) {
228 // Trapped from user mode.
229 // Acquire the big kernel lock before doing any
230 // serious kernel work.
231 // LAB 4: Your code here.
232 assert(curenv);
234 // Garbage collect if current enviroment is a zombie
235 if (curenv->env_status == ENV_DYING) {
236 env_free(curenv);
237 curenv = NULL;
238 sched_yield();
241 // Copy trap frame (which is currently on the stack)
242 // into 'curenv->env_tf', so that running the environment
243 // will restart at the trap point.
244 curenv->env_tf = *tf;
245 // The trapframe on the stack should be ignored from here on.
246 tf = &curenv->env_tf;
249 // Record that tf is the last real trapframe so
250 // print_trapframe can print some additional information.
251 last_tf = tf;
253 // Dispatch based on what type of trap occurred
254 trap_dispatch(tf);
256 // If we made it to this point, then no other environment was
257 // scheduled, so we should return to the current environment
258 // if doing so makes sense.
259 if (curenv && curenv->env_status == ENV_RUNNING)
260 env_run(curenv);
261 else
262 sched_yield();
266 void
267 page_fault_handler(struct Trapframe *tf)
269 uint32_t fault_va;
271 // Read processor's CR2 register to find the faulting address
272 fault_va = rcr2();
274 // Handle kernel-mode page faults.
276 // LAB 3: Your code here.
278 // We've already handled kernel-mode exceptions, so if we get here,
279 // the page fault happened in user mode.
281 // Call the environment's page fault upcall, if one exists. Set up a
282 // page fault stack frame on the user exception stack (below
283 // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
285 // The page fault upcall might cause another page fault, in which case
286 // we branch to the page fault upcall recursively, pushing another
287 // page fault stack frame on top of the user exception stack.
289 // The trap handler needs one word of scratch space at the top of the
290 // trap-time stack in order to return. In the non-recursive case, we
291 // don't have to worry about this because the top of the regular user
292 // stack is free. In the recursive case, this means we have to leave
293 // an extra word between the current top of the exception stack and
294 // the new stack frame because the exception stack _is_ the trap-time
295 // stack.
297 // If there's no page fault upcall, the environment didn't allocate a
298 // page for its exception stack or can't write to it, or the exception
299 // stack overflows, then destroy the environment that caused the fault.
300 // Note that the grade script assumes you will first check for the page
301 // fault upcall and print the "user fault va" message below if there is
302 // none. The remaining three checks can be combined into a single test.
304 // Hints:
305 // user_mem_assert() and env_run() are useful here.
306 // To change what the user environment runs, modify 'curenv->env_tf'
307 // (the 'tf' variable points at 'curenv->env_tf').
309 // LAB 4: Your code here.
311 // Destroy the environment that caused the fault.
312 cprintf("[%08x] user fault va %08x ip %08x\n",
313 curenv->env_id, fault_va, tf->tf_eip);
314 print_trapframe(tf);
315 env_destroy(curenv);