1433d6423SLionel Sambuc /* This file contains a simple exception handler. Exceptions in user 2433d6423SLionel Sambuc * processes are converted to signals. Exceptions in a kernel task cause 3433d6423SLionel Sambuc * a panic. 4433d6423SLionel Sambuc */ 5433d6423SLionel Sambuc 6433d6423SLionel Sambuc #include "kernel/kernel.h" 7433d6423SLionel Sambuc #include "arch_proto.h" 8433d6423SLionel Sambuc #include <signal.h> 9433d6423SLionel Sambuc #include <string.h> 10433d6423SLionel Sambuc #include <assert.h> 11433d6423SLionel Sambuc #include "kernel/proc.h" 12433d6423SLionel Sambuc #include "kernel/proto.h" 13433d6423SLionel Sambuc #include <machine/vm.h> 14433d6423SLionel Sambuc 15433d6423SLionel Sambuc struct ex_s { 16433d6423SLionel Sambuc char *msg; 17433d6423SLionel Sambuc int signum; 18433d6423SLionel Sambuc }; 19433d6423SLionel Sambuc 20433d6423SLionel Sambuc static struct ex_s ex_data[] = { 21433d6423SLionel Sambuc { "Reset", 0}, 22433d6423SLionel Sambuc { "Undefined instruction", SIGILL}, 23433d6423SLionel Sambuc { "Supervisor call", 0}, 24433d6423SLionel Sambuc { "Prefetch Abort", SIGILL}, 25433d6423SLionel Sambuc { "Data Abort", SIGSEGV}, 26433d6423SLionel Sambuc { "Hypervisor call", 0}, 27433d6423SLionel Sambuc { "Interrupt", 0}, 28433d6423SLionel Sambuc { "Fast Interrupt", 0}, 29433d6423SLionel Sambuc }; 30433d6423SLionel Sambuc 31433d6423SLionel Sambuc static void inkernel_disaster(struct proc *saved_proc, 32433d6423SLionel Sambuc reg_t *saved_lr, struct ex_s *ep, int is_nested); 33433d6423SLionel Sambuc 34433d6423SLionel Sambuc extern int catch_pagefaults; 35433d6423SLionel Sambuc 36433d6423SLionel Sambuc static void proc_stacktrace_execute(struct proc *whichproc, reg_t v_bp, reg_t pc); 37433d6423SLionel Sambuc 38433d6423SLionel Sambuc static void pagefault( struct proc *pr, 39433d6423SLionel Sambuc reg_t *saved_lr, 40433d6423SLionel Sambuc int is_nested, 41433d6423SLionel Sambuc u32_t pagefault_addr, 42433d6423SLionel Sambuc u32_t pagefault_status) 43433d6423SLionel Sambuc { 44433d6423SLionel Sambuc int in_physcopy = 0, in_memset = 0; 45433d6423SLionel Sambuc 46433d6423SLionel Sambuc message m_pagefault; 47433d6423SLionel Sambuc int err; 48433d6423SLionel Sambuc 49433d6423SLionel Sambuc in_physcopy = (*saved_lr > (vir_bytes) phys_copy) && 50433d6423SLionel Sambuc (*saved_lr < (vir_bytes) phys_copy_fault); 51433d6423SLionel Sambuc 52433d6423SLionel Sambuc in_memset = (*saved_lr > (vir_bytes) phys_memset) && 53433d6423SLionel Sambuc (*saved_lr < (vir_bytes) memset_fault); 54433d6423SLionel Sambuc 55433d6423SLionel Sambuc if((is_nested || iskernelp(pr)) && 56433d6423SLionel Sambuc catch_pagefaults && (in_physcopy || in_memset)) { 57433d6423SLionel Sambuc if (is_nested) { 58433d6423SLionel Sambuc if(in_physcopy) { 59433d6423SLionel Sambuc assert(!in_memset); 60433d6423SLionel Sambuc *saved_lr = (reg_t) phys_copy_fault_in_kernel; 61433d6423SLionel Sambuc } else { 62433d6423SLionel Sambuc *saved_lr = (reg_t) memset_fault_in_kernel; 63433d6423SLionel Sambuc } 64433d6423SLionel Sambuc } 65433d6423SLionel Sambuc else { 66433d6423SLionel Sambuc pr->p_reg.pc = (reg_t) phys_copy_fault; 67433d6423SLionel Sambuc pr->p_reg.retreg = pagefault_addr; 68433d6423SLionel Sambuc } 69433d6423SLionel Sambuc 70433d6423SLionel Sambuc return; 71433d6423SLionel Sambuc } 72433d6423SLionel Sambuc 73433d6423SLionel Sambuc if(is_nested) { 74433d6423SLionel Sambuc printf("pagefault in kernel at pc 0x%lx address 0x%lx\n", 75433d6423SLionel Sambuc *saved_lr, pagefault_addr); 76433d6423SLionel Sambuc inkernel_disaster(pr, saved_lr, NULL, is_nested); 77433d6423SLionel Sambuc } 78433d6423SLionel Sambuc 79433d6423SLionel Sambuc /* VM can't handle page faults. */ 80433d6423SLionel Sambuc if(pr->p_endpoint == VM_PROC_NR) { 81433d6423SLionel Sambuc /* Page fault we can't / don't want to 82433d6423SLionel Sambuc * handle. 83433d6423SLionel Sambuc */ 84433d6423SLionel Sambuc printf("pagefault for VM on CPU %d, " 85433d6423SLionel Sambuc "pc = 0x%x, addr = 0x%x, flags = 0x%x, is_nested %d\n", 86433d6423SLionel Sambuc cpuid, pr->p_reg.pc, pagefault_addr, pagefault_status, 87433d6423SLionel Sambuc is_nested); 88433d6423SLionel Sambuc proc_stacktrace(pr); 89433d6423SLionel Sambuc printf("pc of pagefault: 0x%lx\n", pr->p_reg.pc); 90433d6423SLionel Sambuc panic("pagefault in VM"); 91433d6423SLionel Sambuc 92433d6423SLionel Sambuc return; 93433d6423SLionel Sambuc } 94433d6423SLionel Sambuc 95433d6423SLionel Sambuc /* Don't schedule this process until pagefault is handled. */ 96433d6423SLionel Sambuc RTS_SET(pr, RTS_PAGEFAULT); 97433d6423SLionel Sambuc 98433d6423SLionel Sambuc /* tell Vm about the pagefault */ 99433d6423SLionel Sambuc m_pagefault.m_source = pr->p_endpoint; 100433d6423SLionel Sambuc m_pagefault.m_type = VM_PAGEFAULT; 101433d6423SLionel Sambuc m_pagefault.VPF_ADDR = pagefault_addr; 102433d6423SLionel Sambuc m_pagefault.VPF_FLAGS = pagefault_status; 103433d6423SLionel Sambuc 104433d6423SLionel Sambuc if ((err = mini_send(pr, VM_PROC_NR, 105433d6423SLionel Sambuc &m_pagefault, FROM_KERNEL))) { 106433d6423SLionel Sambuc panic("WARNING: pagefault: mini_send returned %d\n", err); 107433d6423SLionel Sambuc } 108433d6423SLionel Sambuc 109433d6423SLionel Sambuc return; 110433d6423SLionel Sambuc } 111433d6423SLionel Sambuc 112*7c3424c2SArne Welzel static void 113*7c3424c2SArne Welzel data_abort(int is_nested, struct proc *pr, reg_t *saved_lr, 114*7c3424c2SArne Welzel struct ex_s *ep, u32_t dfar, u32_t dfsr) 115*7c3424c2SArne Welzel { 116*7c3424c2SArne Welzel /* Extract fault status bit [0:3, 10] from DFSR */ 117*7c3424c2SArne Welzel u32_t fs = dfsr & 0x0F; 118*7c3424c2SArne Welzel fs |= ((dfsr >> 6) & 0x10); 119*7c3424c2SArne Welzel if (is_alignment_fault(fs)) { 120*7c3424c2SArne Welzel if (is_nested) { 121*7c3424c2SArne Welzel printf("KERNEL: alignment fault dfar=0x%lx\n", dfar); 122*7c3424c2SArne Welzel inkernel_disaster(pr, saved_lr, ep, is_nested); 123*7c3424c2SArne Welzel } 124*7c3424c2SArne Welzel /* Send SIGBUS to violating process. */ 125*7c3424c2SArne Welzel cause_sig(proc_nr(pr), SIGBUS); 126*7c3424c2SArne Welzel return; 127*7c3424c2SArne Welzel } else if (is_translation_fault(fs) || is_permission_fault(fs)) { 128*7c3424c2SArne Welzel /* Ask VM to handle translation and permission faults as pagefaults */ 129*7c3424c2SArne Welzel pagefault(pr, saved_lr, is_nested, dfar, dfsr); 130*7c3424c2SArne Welzel return; 131*7c3424c2SArne Welzel } else { 132*7c3424c2SArne Welzel /* Die on unknown things... */ 133*7c3424c2SArne Welzel printf("KERNEL: unhandled data abort dfar=0x%lx dfsr=0x%lx " 134*7c3424c2SArne Welzel "fs=0x%lx is_nested=%d\n", dfar, dfsr, fs, is_nested); 135*7c3424c2SArne Welzel panic("unhandled data abort"); 136*7c3424c2SArne Welzel } 137*7c3424c2SArne Welzel NOT_REACHABLE; 138*7c3424c2SArne Welzel } 139*7c3424c2SArne Welzel 140433d6423SLionel Sambuc static void inkernel_disaster(struct proc *saved_proc, 141433d6423SLionel Sambuc reg_t *saved_lr, struct ex_s *ep, 142433d6423SLionel Sambuc int is_nested) 143433d6423SLionel Sambuc { 144433d6423SLionel Sambuc #if USE_SYSDEBUG 145433d6423SLionel Sambuc if(ep) 146433d6423SLionel Sambuc printf("\n%s\n", ep->msg); 147433d6423SLionel Sambuc 148433d6423SLionel Sambuc printf("cpu %d is_nested = %d ", cpuid, is_nested); 149433d6423SLionel Sambuc 150433d6423SLionel Sambuc if (saved_proc) { 151433d6423SLionel Sambuc printf("scheduled was: process %d (%s), ", saved_proc->p_endpoint, saved_proc->p_name); 152433d6423SLionel Sambuc printf("pc = 0x%x\n", (unsigned) saved_proc->p_reg.pc); 153433d6423SLionel Sambuc proc_stacktrace(saved_proc); 154433d6423SLionel Sambuc 155433d6423SLionel Sambuc panic("Unhandled kernel exception"); 156433d6423SLionel Sambuc } 157433d6423SLionel Sambuc 158433d6423SLionel Sambuc /* in an early stage of boot process we don't have processes yet */ 159433d6423SLionel Sambuc panic("exception in kernel while booting, no saved_proc yet"); 160433d6423SLionel Sambuc 161433d6423SLionel Sambuc #endif /* USE_SYSDEBUG */ 162433d6423SLionel Sambuc } 163433d6423SLionel Sambuc 164433d6423SLionel Sambuc void exception_handler(int is_nested, reg_t *saved_lr, int vector) 165433d6423SLionel Sambuc { 166433d6423SLionel Sambuc /* An exception or unexpected interrupt has occurred. */ 167433d6423SLionel Sambuc struct ex_s *ep; 168433d6423SLionel Sambuc struct proc *saved_proc; 169433d6423SLionel Sambuc 170433d6423SLionel Sambuc saved_proc = get_cpulocal_var(proc_ptr); 171433d6423SLionel Sambuc 172433d6423SLionel Sambuc ep = &ex_data[vector]; 173433d6423SLionel Sambuc 174433d6423SLionel Sambuc assert((vir_bytes) saved_lr >= kinfo.vir_kern_start); 175433d6423SLionel Sambuc 176433d6423SLionel Sambuc /* 177433d6423SLionel Sambuc * handle special cases for nested problems as they might be tricky or filter 178433d6423SLionel Sambuc * them out quickly if the traps are not nested 179433d6423SLionel Sambuc */ 180433d6423SLionel Sambuc if (is_nested) { 181433d6423SLionel Sambuc /* 182433d6423SLionel Sambuc * if a problem occurred while copying a message from userspace because 183433d6423SLionel Sambuc * of a wrong pointer supplied by userland, handle it the only way we 184433d6423SLionel Sambuc * can handle it ... 185433d6423SLionel Sambuc */ 186433d6423SLionel Sambuc if (((void*)*saved_lr >= (void*)copy_msg_to_user && 187433d6423SLionel Sambuc (void*)*saved_lr <= (void*)__copy_msg_to_user_end) || 188433d6423SLionel Sambuc ((void*)*saved_lr >= (void*)copy_msg_from_user && 189433d6423SLionel Sambuc (void*)*saved_lr <= (void*)__copy_msg_from_user_end)) { 190433d6423SLionel Sambuc switch(vector) { 191433d6423SLionel Sambuc /* these error are expected */ 192433d6423SLionel Sambuc case DATA_ABORT_VECTOR: 193433d6423SLionel Sambuc *saved_lr = (reg_t) __user_copy_msg_pointer_failure; 194433d6423SLionel Sambuc return; 195433d6423SLionel Sambuc default: 196433d6423SLionel Sambuc panic("Copy involving a user pointer failed unexpectedly!"); 197433d6423SLionel Sambuc } 198433d6423SLionel Sambuc } 199433d6423SLionel Sambuc } 200433d6423SLionel Sambuc 201433d6423SLionel Sambuc if (vector == DATA_ABORT_VECTOR) { 202*7c3424c2SArne Welzel data_abort(is_nested, saved_proc, saved_lr, ep, read_dfar(), read_dfsr()); 203433d6423SLionel Sambuc return; 204433d6423SLionel Sambuc } 205433d6423SLionel Sambuc 206433d6423SLionel Sambuc if (!is_nested && vector == PREFETCH_ABORT_VECTOR) { 207a3975fbcSDavid van Moolenbroek static int warned = FALSE; 208433d6423SLionel Sambuc reg_t ifar = read_ifar(), ifsr = read_ifsr(); 209433d6423SLionel Sambuc 210433d6423SLionel Sambuc /* The saved_lr is the instruction we're going to execute after 211433d6423SLionel Sambuc * the fault is handled; IFAR is the address that pagefaulted 212433d6423SLionel Sambuc * while fetching the instruction. As far as we know the two 213433d6423SLionel Sambuc * should be the same, if not this assumption will lead to very 214433d6423SLionel Sambuc * hard to debug problems (instruction executing being off by one) 215a3975fbcSDavid van Moolenbroek * and this assumption needs re-examining. 216a3975fbcSDavid van Moolenbroek * 217a3975fbcSDavid van Moolenbroek * UPDATE: at least qemu-linaro does in fact sometimes generate faults 218a3975fbcSDavid van Moolenbroek * with LR and IFAR differing by as many as 64 bytes. While the page 219a3975fbcSDavid van Moolenbroek * fault resolution code below handles this case just fine, the cause 220a3975fbcSDavid van Moolenbroek * of this behavior is unknown. We have not yet seen the same on 221a3975fbcSDavid van Moolenbroek * actual hardware, which is why we warn about this problem once. 222433d6423SLionel Sambuc */ 223a3975fbcSDavid van Moolenbroek if (*saved_lr != ifar && !warned) { 224a3975fbcSDavid van Moolenbroek printf("KERNEL: prefetch abort with differing IFAR and LR\n"); 225a3975fbcSDavid van Moolenbroek printf("KERNEL: IFSR %"PRIx32" IFAR %"PRIx32" LR %"PRIx32" in " 226a3975fbcSDavid van Moolenbroek "%s/%d\n", ifsr, ifar, *saved_lr, saved_proc->p_name, 227a3975fbcSDavid van Moolenbroek saved_proc->p_endpoint); 228a3975fbcSDavid van Moolenbroek warned = TRUE; 229a3975fbcSDavid van Moolenbroek } 230433d6423SLionel Sambuc pagefault(saved_proc, saved_lr, is_nested, ifar, ifsr); 231433d6423SLionel Sambuc return; 232433d6423SLionel Sambuc } 233433d6423SLionel Sambuc 234433d6423SLionel Sambuc /* If an exception occurs while running a process, the is_nested variable 235433d6423SLionel Sambuc * will be zero. Exceptions in interrupt handlers or system traps will make 236433d6423SLionel Sambuc * is_nested non-zero. 237433d6423SLionel Sambuc */ 238433d6423SLionel Sambuc if (is_nested == 0 && ! iskernelp(saved_proc)) { 239433d6423SLionel Sambuc cause_sig(proc_nr(saved_proc), ep->signum); 240433d6423SLionel Sambuc return; 241433d6423SLionel Sambuc } 242433d6423SLionel Sambuc 243433d6423SLionel Sambuc /* Exception in system code. This is not supposed to happen. */ 244433d6423SLionel Sambuc inkernel_disaster(saved_proc, saved_lr, ep, is_nested); 245433d6423SLionel Sambuc 246433d6423SLionel Sambuc panic("return from inkernel_disaster"); 247433d6423SLionel Sambuc } 248433d6423SLionel Sambuc 249433d6423SLionel Sambuc #if USE_SYSDEBUG 250433d6423SLionel Sambuc /*===========================================================================* 251433d6423SLionel Sambuc * proc_stacktrace_execute * 252433d6423SLionel Sambuc *===========================================================================*/ 253433d6423SLionel Sambuc static void proc_stacktrace_execute(struct proc *whichproc, reg_t v_bp, reg_t pc) 254433d6423SLionel Sambuc { 255433d6423SLionel Sambuc printf("%-8.8s %6d 0x%lx \n", 256433d6423SLionel Sambuc whichproc->p_name, whichproc->p_endpoint, pc); 257433d6423SLionel Sambuc } 258433d6423SLionel Sambuc #endif 259433d6423SLionel Sambuc 260433d6423SLionel Sambuc void proc_stacktrace(struct proc *whichproc) 261433d6423SLionel Sambuc { 262433d6423SLionel Sambuc #if USE_SYSDEBUG 263433d6423SLionel Sambuc proc_stacktrace_execute(whichproc, whichproc->p_reg.fp, whichproc->p_reg.pc); 264433d6423SLionel Sambuc #endif /* USE_SYSDEBUG */ 265433d6423SLionel Sambuc } 266433d6423SLionel Sambuc 267433d6423SLionel Sambuc void enable_fpu_exception(void) 268433d6423SLionel Sambuc { 269433d6423SLionel Sambuc } 270433d6423SLionel Sambuc 271433d6423SLionel Sambuc void disable_fpu_exception(void) 272433d6423SLionel Sambuc { 273433d6423SLionel Sambuc } 274