| #include <arch/arch.h> |
| #include <arch/console.h> |
| #include <assert.h> |
| #include <manager.h> |
| #include <mm.h> |
| #include <monitor.h> |
| #include <pmap.h> |
| #include <process.h> |
| #include <slab.h> |
| #include <smp.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <syscall.h> |
| #include <trap.h> |
| #include <umem.h> |
| |
| /* These are the stacks the kernel will load when it receives a trap from user |
| * space. The deal is that they get set right away in entry.S, and can always |
| * be used for finding the top of the stack (from which you should subtract the |
| * sizeof the trapframe. Note, we need to have a junk value in the array so |
| * that this is NOT part of the BSS. If it is in the BSS, it will get 0'd in |
| * kernel_init(), which is after these values get set. |
| * |
| * TODO: if these end up becoming contended cache lines, move this to |
| * per_cpu_info. */ |
| uintptr_t core_stacktops[MAX_NUM_CORES] = {0xcafebabe, 0}; |
| |
| void advance_pc(struct hw_trapframe *state) |
| { |
| state->epc += 4; |
| } |
| |
| /* Set stacktop for the current core to be the stack the kernel will start on |
| * when trapping/interrupting from userspace */ |
| void set_stack_top(uintptr_t stacktop) |
| { |
| core_stacktops[core_id()] = stacktop; |
| } |
| |
| /* Note the assertion assumes we are in the top page of the stack. */ |
| uintptr_t get_stack_top(void) |
| { |
| register uintptr_t sp asm("sp"); |
| uintptr_t stacktop = core_stacktops[core_id()]; |
| assert(ROUNDUP(sp, PGSIZE) == stacktop); |
| return stacktop; |
| } |
| |
| void idt_init(void) |
| { |
| } |
| |
| /* Helper. For now, this copies out the TF to pcpui, and sets cur_ctx to point |
| * to it. */ |
| static void set_current_ctx_hw(struct per_cpu_info *pcpui, |
| struct hw_trapframe *hw_tf) |
| { |
| if (irq_is_enabled()) |
| warn("Turn off IRQs until cur_ctx is set!"); |
| assert(!pcpui->cur_ctx); |
| pcpui->actual_ctx.type = ROS_HW_CTX; |
| pcpui->actual_ctx.tf.hw_tf = *hw_tf; |
| pcpui->cur_ctx = &pcpui->actual_ctx; |
| } |
| |
| static void set_current_ctx_sw(struct per_cpu_info *pcpui, |
| struct sw_trapframe *sw_tf) |
| { |
| if (irq_is_enabled()) |
| warn("Turn off IRQs until cur_ctx is set!"); |
| assert(!pcpui->cur_ctx); |
| pcpui->actual_ctx.type = ROS_SW_CTX; |
| pcpui->actual_ctx.tf.sw_tf = *sw_tf; |
| pcpui->cur_ctx = &pcpui->actual_ctx; |
| } |
| |
| static int format_trapframe(struct hw_trapframe *hw_tf, char *buf, int bufsz) |
| { |
| // slightly hackish way to read out the instruction that faulted. |
| // not guaranteed to be right 100% of the time |
| uint32_t insn; |
| if (!(current && |
| !memcpy_from_user(current, &insn, (void *)hw_tf->epc, 4))) |
| insn = -1; |
| |
| int len = snprintf(buf, bufsz, "TRAP frame at %p on core %d\n", hw_tf, |
| core_id()); |
| static const char *regnames[] = { |
| "z ", "ra", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", |
| "s9", "sA", "sB", "sp", "tp", "v0", "v1", "a0", "a1", "a2", "a3", |
| "a4", "a5", "a6", "a7", "a8", "a9", "aA", "aB", "aC", "aD"}; |
| |
| hw_tf->gpr[0] = 0; |
| |
| for (int i = 0; i < 32; i += 4) { |
| for (int j = 0; j < 4; j++) |
| len += snprintf(buf + len, bufsz - len, "%s %016lx%c", |
| regnames[i + j], hw_tf->gpr[i + j], |
| j < 3 ? ' ' : '\n'); |
| } |
| len += snprintf(buf + len, bufsz - len, |
| "sr %016lx pc %016lx va %016lx insn %08x\n", |
| hw_tf->sr, hw_tf->epc, hw_tf->badvaddr, insn); |
| |
| buf[bufsz - 1] = 0; |
| return len; |
| } |
| |
| void print_trapframe(struct hw_trapframe *hw_tf) |
| { |
| char buf[1024]; |
| int len = format_trapframe(hw_tf, buf, sizeof(buf)); |
| cputbuf(buf, len); |
| } |
| |
| void print_swtrapframe(struct sw_trapframe *sw_tf) |
| { |
| #warning "fix me" |
| } |
| |
| void print_vmtrapframe(struct vm_trapframe *vm_tf) |
| { |
| #warning "fix me" |
| } |
| |
| static void exit_halt_loop(struct hw_trapframe *hw_tf) |
| { |
| extern char after_cpu_halt; |
| if ((char *)hw_tf->epc >= (char *)&cpu_halt && |
| (char *)hw_tf->epc < &after_cpu_halt) |
| hw_tf->epc = hw_tf->gpr[GPR_RA]; |
| } |
| |
| static void handle_keypress(char c) |
| { |
| #warning "fix me" |
| /* TODO: does cons_init need to be before cons_add_char? Also, do |
| * something with CTRL-G, Q, and B. */ |
| cons_add_char(c); |
| |
| cons_init(); |
| } |
| |
| static void handle_host_interrupt(struct hw_trapframe *hw_tf) |
| { |
| uintptr_t fh = mtpcr(PCR_FROMHOST, 0); |
| switch (fh >> 56) { |
| case 0x00: |
| return; |
| case 0x01: |
| handle_keypress(fh); |
| return; |
| default: |
| assert(0); |
| } |
| } |
| |
| static void handle_timer_interrupt(struct hw_trapframe *hw_tf) |
| { |
| timer_interrupt(hw_tf, NULL); |
| } |
| |
| /* Assumes that any IPI you get is really a kernel message */ |
| static void handle_interprocessor_interrupt(struct hw_trapframe *hw_tf) |
| { |
| clear_ipi(); |
| handle_kmsg_ipi(hw_tf, 0); |
| } |
| |
| static void unhandled_trap(struct hw_trapframe *state, const char *name) |
| { |
| static spinlock_t screwup_lock = SPINLOCK_INITIALIZER; |
| spin_lock(&screwup_lock); |
| |
| if (in_kernel(state)) { |
| print_trapframe(state); |
| panic("Unhandled trap in kernel!\nTrap type: %s", name); |
| } else { |
| char tf_buf[1024]; |
| format_trapframe(state, tf_buf, sizeof(tf_buf)); |
| |
| warn("Unhandled trap in user!\nTrap type: %s\n%s", name, |
| tf_buf); |
| backtrace(); |
| spin_unlock(&screwup_lock); |
| |
| assert(current); |
| proc_destroy(current); |
| } |
| } |
| |
| static void handle_misaligned_fetch(struct hw_trapframe *state) |
| { |
| unhandled_trap(state, "Misaligned Fetch"); |
| } |
| |
| static void handle_misaligned_load(struct hw_trapframe *state) |
| { |
| unhandled_trap(state, "Misaligned Load"); |
| } |
| |
| static void handle_misaligned_store(struct hw_trapframe *state) |
| { |
| unhandled_trap(state, "Misaligned Store"); |
| } |
| |
| static void handle_fault_fetch(struct hw_trapframe *state) |
| { |
| if (in_kernel(state)) { |
| print_trapframe(state); |
| panic("Instruction Page Fault in the Kernel at %p!", |
| state->epc); |
| } |
| |
| set_current_ctx_hw(&per_cpu_info[core_id()], state); |
| |
| #warning "returns EAGAIN if you should reflect the fault" |
| if (handle_page_fault(current, state->epc, PROT_EXEC)) |
| unhandled_trap(state, "Instruction Page Fault"); |
| } |
| |
| static void handle_fault_load(struct hw_trapframe *state) |
| { |
| if (in_kernel(state)) { |
| print_trapframe(state); |
| panic("Load Page Fault in the Kernel at %p!", state->badvaddr); |
| } |
| |
| set_current_ctx_hw(&per_cpu_info[core_id()], state); |
| |
| #warning "returns EAGAIN if you should reflect the fault" |
| if (handle_page_fault(current, state->badvaddr, PROT_READ)) |
| unhandled_trap(state, "Load Page Fault"); |
| } |
| |
| static void handle_fault_store(struct hw_trapframe *state) |
| { |
| if (in_kernel(state)) { |
| print_trapframe(state); |
| panic("Store Page Fault in the Kernel at %p!", state->badvaddr); |
| } |
| |
| set_current_ctx_hw(&per_cpu_info[core_id()], state); |
| |
| if (handle_page_fault(current, state->badvaddr, PROT_WRITE)) |
| unhandled_trap(state, "Store Page Fault"); |
| } |
| |
| static void handle_illegal_instruction(struct hw_trapframe *state) |
| { |
| assert(!in_kernel(state)); |
| |
| struct per_cpu_info *pcpui = &per_cpu_info[core_id()]; |
| set_current_ctx_hw(pcpui, state); |
| if (emulate_fpu(state) == 0) { |
| advance_pc(&pcpui->cur_ctx->tf.hw_tf); |
| return; |
| } |
| |
| unhandled_trap(state, "Illegal Instruction"); |
| } |
| |
| static void handle_syscall(struct hw_trapframe *state) |
| { |
| uintptr_t a0 = state->gpr[GPR_A0]; |
| uintptr_t a1 = state->gpr[GPR_A1]; |
| |
| advance_pc(state); |
| set_current_ctx_hw(&per_cpu_info[core_id()], state); |
| enable_irq(); |
| prep_syscalls(current, (struct syscall *)a0, a1); |
| } |
| |
| static void handle_breakpoint(struct hw_trapframe *state) |
| { |
| advance_pc(state); |
| monitor(state); |
| } |
| |
| void handle_trap(struct hw_trapframe *hw_tf) |
| { |
| static void (*const trap_handlers[])(struct hw_trapframe *) = { |
| [CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch, |
| [CAUSE_FAULT_FETCH] = handle_fault_fetch, |
| [CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction, |
| [CAUSE_PRIVILEGED_INSTRUCTION] = handle_illegal_instruction, |
| [CAUSE_SYSCALL] = handle_syscall, |
| [CAUSE_BREAKPOINT] = handle_breakpoint, |
| [CAUSE_MISALIGNED_LOAD] = handle_misaligned_load, |
| [CAUSE_MISALIGNED_STORE] = handle_misaligned_store, |
| [CAUSE_FAULT_LOAD] = handle_fault_load, |
| [CAUSE_FAULT_STORE] = handle_fault_store, |
| }; |
| |
| static void (*const irq_handlers[])(struct hw_trapframe *) = { |
| [IRQ_TIMER] = handle_timer_interrupt, |
| [IRQ_HOST] = handle_host_interrupt, |
| [IRQ_IPI] = handle_interprocessor_interrupt, |
| }; |
| |
| struct per_cpu_info *pcpui = &per_cpu_info[core_id()]; |
| if (hw_tf->cause < 0) { |
| uint8_t irq = hw_tf->cause; |
| assert(irq < sizeof(irq_handlers) / sizeof(irq_handlers[0]) && |
| irq_handlers[irq]); |
| |
| if (in_kernel(hw_tf)) |
| exit_halt_loop(hw_tf); |
| else |
| set_current_ctx_hw(&per_cpu_info[core_id()], hw_tf); |
| |
| inc_irq_depth(pcpui); |
| irq_handlers[irq](hw_tf); |
| dec_irq_depth(pcpui); |
| } else { |
| assert(hw_tf->cause < |
| sizeof(trap_handlers) / sizeof(trap_handlers[0]) && |
| trap_handlers[hw_tf->cause]); |
| if (in_kernel(hw_tf)) { |
| inc_ktrap_depth(pcpui); |
| trap_handlers[hw_tf->cause](hw_tf); |
| dec_ktrap_depth(pcpui); |
| } else { |
| trap_handlers[hw_tf->cause](hw_tf); |
| } |
| #warning "if a trap wasn't handled fully, like an MCP pf, reflect it |
| reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux); |
| } |
| |
| extern void pop_hw_tf(struct hw_trapframe * tf); /* in asm */ |
| /* Return to the current process, which should be runnable. If we're |
| * the kernel, we should just return naturally. Note that current and |
| * tf need to still be okay (might not be after blocking) */ |
| if (in_kernel(hw_tf)) |
| pop_hw_tf(hw_tf); |
| else |
| proc_restartcore(); |
| } |
| |
| /* We don't have NMIs now. */ |
| void send_nmi(uint32_t os_coreid) |
| { |
| printk("%s not implemented\n", __FUNCTION); |
| } |
| |
| int register_irq(int irq, isr_t handler, void *irq_arg, uint32_t tbdf) |
| { |
| printk("%s not implemented\n", __FUNCTION); |
| return -1; |
| } |
| |
| int route_irqs(int cpu_vec, int coreid) |
| { |
| printk("%s not implemented\n", __FUNCTION); |
| return -1; |
| } |
| |
| void __arch_reflect_trap_hwtf(struct hw_trapframe *hw_tf, unsigned int trap_nr, |
| unsigned int err, unsigned long aux) |
| { |
| printk("%s not implemented\n", __FUNCTION); |
| } |