3 #include <inc/assert.h>
7 #include <kern/console.h>
8 #include <kern/monitor.h>
10 #include <kern/syscall.h>
11 #include <kern/sched.h>
12 #include <kern/kclock.h>
13 #include <kern/picirq.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
[] = {
40 "Non-Maskable Interrupt",
43 "BOUND Range Exceeded",
45 "Device Not Available",
47 "Coprocessor Segment Overrun",
49 "Segment Not Present",
54 "x87 FPU Floating-Point Error",
57 "SIMD Floating-Point Exception"
60 if (trapno
< sizeof(excnames
)/sizeof(excnames
[0]))
61 return excnames
[trapno
];
62 if (trapno
== T_SYSCALL
)
64 if (trapno
>= IRQ_OFFSET
&& trapno
< IRQ_OFFSET
+ 16)
65 return "Hardware Interrupt";
66 return "(unknown trap)";
73 extern struct Segdesc gdt
[];
75 // LAB 3: Your code here.
81 // Initialize and load the per-CPU TSS and IDT
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.
91 // - The macro "thiscpu" always refers to the current CPU's
93 // - The ID of the current CPU is given by cpunum() or
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
;
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)
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");
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
);
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
);
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");
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.
202 if (tf
->tf_cs
== GD_KT
)
203 panic("unhandled trap in kernel");
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
;
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.
234 // Garbage collect if current enviroment is a zombie
235 if (curenv
->env_status
== ENV_DYING
) {
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.
253 // Dispatch based on what type of trap occurred
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
)
267 page_fault_handler(struct Trapframe
*tf
)
271 // Read processor's CR2 register to find the faulting address
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
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.
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
);