1 /* This file contains a simple exception handler. Exceptions in user 2 * processes are converted to signals. Exceptions in a kernel task cause 3 * a panic. 4 */ 5 6 #include "kernel/kernel.h" 7 #include "arch_proto.h" 8 #include <signal.h> 9 #include <string.h> 10 #include <assert.h> 11 #include "kernel/proc.h" 12 #include "kernel/proto.h" 13 #include <machine/vm.h> 14 15 struct ex_s { 16 char *msg; 17 int signum; 18 }; 19 20 static struct ex_s ex_data[] = { 21 { "Reset", 0}, 22 { "Undefined instruction", SIGILL}, 23 { "Supervisor call", 0}, 24 { "Prefetch Abort", SIGILL}, 25 { "Data Abort", SIGSEGV}, 26 { "Hypervisor call", 0}, 27 { "Interrupt", 0}, 28 { "Fast Interrupt", 0}, 29 }; 30 31 static void inkernel_disaster(struct proc *saved_proc, 32 reg_t *saved_lr, struct ex_s *ep, int is_nested); 33 34 extern int catch_pagefaults; 35 36 static void proc_stacktrace_execute(struct proc *whichproc, reg_t v_bp, reg_t pc); 37 38 static void pagefault( struct proc *pr, 39 reg_t *saved_lr, 40 int is_nested, 41 u32_t pagefault_addr, 42 u32_t pagefault_status) 43 { 44 int in_physcopy = 0, in_memset = 0; 45 46 message m_pagefault; 47 int err; 48 49 in_physcopy = (*saved_lr > (vir_bytes) phys_copy) && 50 (*saved_lr < (vir_bytes) phys_copy_fault); 51 52 in_memset = (*saved_lr > (vir_bytes) phys_memset) && 53 (*saved_lr < (vir_bytes) memset_fault); 54 55 if((is_nested || iskernelp(pr)) && 56 catch_pagefaults && (in_physcopy || in_memset)) { 57 if (is_nested) { 58 if(in_physcopy) { 59 assert(!in_memset); 60 *saved_lr = (reg_t) phys_copy_fault_in_kernel; 61 } else { 62 *saved_lr = (reg_t) memset_fault_in_kernel; 63 } 64 } 65 else { 66 pr->p_reg.pc = (reg_t) phys_copy_fault; 67 pr->p_reg.retreg = pagefault_addr; 68 } 69 70 return; 71 } 72 73 if(is_nested) { 74 printf("pagefault in kernel at pc 0x%lx address 0x%lx\n", 75 *saved_lr, pagefault_addr); 76 inkernel_disaster(pr, saved_lr, NULL, is_nested); 77 } 78 79 /* VM can't handle page faults. */ 80 if(pr->p_endpoint == VM_PROC_NR) { 81 /* Page fault we can't / don't want to 82 * handle. 83 */ 84 printf("pagefault for VM on CPU %d, " 85 "pc = 0x%x, addr = 0x%x, flags = 0x%x, is_nested %d\n", 86 cpuid, pr->p_reg.pc, pagefault_addr, pagefault_status, 87 is_nested); 88 proc_stacktrace(pr); 89 printf("pc of pagefault: 0x%lx\n", pr->p_reg.pc); 90 panic("pagefault in VM"); 91 92 return; 93 } 94 95 /* Don't schedule this process until pagefault is handled. */ 96 RTS_SET(pr, RTS_PAGEFAULT); 97 98 /* tell Vm about the pagefault */ 99 m_pagefault.m_source = pr->p_endpoint; 100 m_pagefault.m_type = VM_PAGEFAULT; 101 m_pagefault.VPF_ADDR = pagefault_addr; 102 m_pagefault.VPF_FLAGS = pagefault_status; 103 104 if ((err = mini_send(pr, VM_PROC_NR, 105 &m_pagefault, FROM_KERNEL))) { 106 panic("WARNING: pagefault: mini_send returned %d\n", err); 107 } 108 109 return; 110 } 111 112 static void inkernel_disaster(struct proc *saved_proc, 113 reg_t *saved_lr, struct ex_s *ep, 114 int is_nested) 115 { 116 #if USE_SYSDEBUG 117 if(ep) 118 printf("\n%s\n", ep->msg); 119 120 printf("cpu %d is_nested = %d ", cpuid, is_nested); 121 122 if (saved_proc) { 123 printf("scheduled was: process %d (%s), ", saved_proc->p_endpoint, saved_proc->p_name); 124 printf("pc = 0x%x\n", (unsigned) saved_proc->p_reg.pc); 125 proc_stacktrace(saved_proc); 126 127 panic("Unhandled kernel exception"); 128 } 129 130 /* in an early stage of boot process we don't have processes yet */ 131 panic("exception in kernel while booting, no saved_proc yet"); 132 133 #endif /* USE_SYSDEBUG */ 134 } 135 136 void exception_handler(int is_nested, reg_t *saved_lr, int vector) 137 { 138 /* An exception or unexpected interrupt has occurred. */ 139 struct ex_s *ep; 140 struct proc *saved_proc; 141 142 saved_proc = get_cpulocal_var(proc_ptr); 143 144 ep = &ex_data[vector]; 145 146 assert((vir_bytes) saved_lr >= kinfo.vir_kern_start); 147 148 /* 149 * handle special cases for nested problems as they might be tricky or filter 150 * them out quickly if the traps are not nested 151 */ 152 if (is_nested) { 153 /* 154 * if a problem occurred while copying a message from userspace because 155 * of a wrong pointer supplied by userland, handle it the only way we 156 * can handle it ... 157 */ 158 if (((void*)*saved_lr >= (void*)copy_msg_to_user && 159 (void*)*saved_lr <= (void*)__copy_msg_to_user_end) || 160 ((void*)*saved_lr >= (void*)copy_msg_from_user && 161 (void*)*saved_lr <= (void*)__copy_msg_from_user_end)) { 162 switch(vector) { 163 /* these error are expected */ 164 case DATA_ABORT_VECTOR: 165 *saved_lr = (reg_t) __user_copy_msg_pointer_failure; 166 return; 167 default: 168 panic("Copy involving a user pointer failed unexpectedly!"); 169 } 170 } 171 } 172 173 if (vector == DATA_ABORT_VECTOR) { 174 pagefault(saved_proc, saved_lr, is_nested, read_dfar(), read_dfsr()); 175 return; 176 } 177 178 if (!is_nested && vector == PREFETCH_ABORT_VECTOR) { 179 static int warned = FALSE; 180 reg_t ifar = read_ifar(), ifsr = read_ifsr(); 181 182 /* The saved_lr is the instruction we're going to execute after 183 * the fault is handled; IFAR is the address that pagefaulted 184 * while fetching the instruction. As far as we know the two 185 * should be the same, if not this assumption will lead to very 186 * hard to debug problems (instruction executing being off by one) 187 * and this assumption needs re-examining. 188 * 189 * UPDATE: at least qemu-linaro does in fact sometimes generate faults 190 * with LR and IFAR differing by as many as 64 bytes. While the page 191 * fault resolution code below handles this case just fine, the cause 192 * of this behavior is unknown. We have not yet seen the same on 193 * actual hardware, which is why we warn about this problem once. 194 */ 195 if (*saved_lr != ifar && !warned) { 196 printf("KERNEL: prefetch abort with differing IFAR and LR\n"); 197 printf("KERNEL: IFSR %"PRIx32" IFAR %"PRIx32" LR %"PRIx32" in " 198 "%s/%d\n", ifsr, ifar, *saved_lr, saved_proc->p_name, 199 saved_proc->p_endpoint); 200 warned = TRUE; 201 } 202 pagefault(saved_proc, saved_lr, is_nested, ifar, ifsr); 203 return; 204 } 205 206 /* If an exception occurs while running a process, the is_nested variable 207 * will be zero. Exceptions in interrupt handlers or system traps will make 208 * is_nested non-zero. 209 */ 210 if (is_nested == 0 && ! iskernelp(saved_proc)) { 211 cause_sig(proc_nr(saved_proc), ep->signum); 212 return; 213 } 214 215 /* Exception in system code. This is not supposed to happen. */ 216 inkernel_disaster(saved_proc, saved_lr, ep, is_nested); 217 218 panic("return from inkernel_disaster"); 219 } 220 221 #if USE_SYSDEBUG 222 /*===========================================================================* 223 * proc_stacktrace_execute * 224 *===========================================================================*/ 225 static void proc_stacktrace_execute(struct proc *whichproc, reg_t v_bp, reg_t pc) 226 { 227 printf("%-8.8s %6d 0x%lx \n", 228 whichproc->p_name, whichproc->p_endpoint, pc); 229 } 230 #endif 231 232 void proc_stacktrace(struct proc *whichproc) 233 { 234 #if USE_SYSDEBUG 235 proc_stacktrace_execute(whichproc, whichproc->p_reg.fp, whichproc->p_reg.pc); 236 #endif /* USE_SYSDEBUG */ 237 } 238 239 void enable_fpu_exception(void) 240 { 241 } 242 243 void disable_fpu_exception(void) 244 { 245 } 246