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 113 data_abort(int is_nested, struct proc *pr, reg_t *saved_lr, 114 struct ex_s *ep, u32_t dfar, u32_t dfsr) 115 { 116 /* Extract fault status bit [0:3, 10] from DFSR */ 117 u32_t fs = dfsr & 0x0F; 118 fs |= ((dfsr >> 6) & 0x10); 119 if (is_alignment_fault(fs)) { 120 if (is_nested) { 121 printf("KERNEL: alignment fault dfar=0x%lx\n", dfar); 122 inkernel_disaster(pr, saved_lr, ep, is_nested); 123 } 124 /* Send SIGBUS to violating process. */ 125 cause_sig(proc_nr(pr), SIGBUS); 126 return; 127 } else if (is_translation_fault(fs) || is_permission_fault(fs)) { 128 /* Ask VM to handle translation and permission faults as pagefaults */ 129 pagefault(pr, saved_lr, is_nested, dfar, dfsr); 130 return; 131 } else { 132 /* Die on unknown things... */ 133 printf("KERNEL: unhandled data abort dfar=0x%lx dfsr=0x%lx " 134 "fs=0x%lx is_nested=%d\n", dfar, dfsr, fs, is_nested); 135 panic("unhandled data abort"); 136 } 137 NOT_REACHABLE; 138 } 139 140 static void inkernel_disaster(struct proc *saved_proc, 141 reg_t *saved_lr, struct ex_s *ep, 142 int is_nested) 143 { 144 #if USE_SYSDEBUG 145 if(ep) 146 printf("\n%s\n", ep->msg); 147 148 printf("cpu %d is_nested = %d ", cpuid, is_nested); 149 150 if (saved_proc) { 151 printf("scheduled was: process %d (%s), ", saved_proc->p_endpoint, saved_proc->p_name); 152 printf("pc = 0x%x\n", (unsigned) saved_proc->p_reg.pc); 153 proc_stacktrace(saved_proc); 154 155 panic("Unhandled kernel exception"); 156 } 157 158 /* in an early stage of boot process we don't have processes yet */ 159 panic("exception in kernel while booting, no saved_proc yet"); 160 161 #endif /* USE_SYSDEBUG */ 162 } 163 164 void exception_handler(int is_nested, reg_t *saved_lr, int vector) 165 { 166 /* An exception or unexpected interrupt has occurred. */ 167 struct ex_s *ep; 168 struct proc *saved_proc; 169 170 saved_proc = get_cpulocal_var(proc_ptr); 171 172 ep = &ex_data[vector]; 173 174 assert((vir_bytes) saved_lr >= kinfo.vir_kern_start); 175 176 /* 177 * handle special cases for nested problems as they might be tricky or filter 178 * them out quickly if the traps are not nested 179 */ 180 if (is_nested) { 181 /* 182 * if a problem occurred while copying a message from userspace because 183 * of a wrong pointer supplied by userland, handle it the only way we 184 * can handle it ... 185 */ 186 if (((void*)*saved_lr >= (void*)copy_msg_to_user && 187 (void*)*saved_lr <= (void*)__copy_msg_to_user_end) || 188 ((void*)*saved_lr >= (void*)copy_msg_from_user && 189 (void*)*saved_lr <= (void*)__copy_msg_from_user_end)) { 190 switch(vector) { 191 /* these error are expected */ 192 case DATA_ABORT_VECTOR: 193 *saved_lr = (reg_t) __user_copy_msg_pointer_failure; 194 return; 195 default: 196 panic("Copy involving a user pointer failed unexpectedly!"); 197 } 198 } 199 } 200 201 if (vector == DATA_ABORT_VECTOR) { 202 data_abort(is_nested, saved_proc, saved_lr, ep, read_dfar(), read_dfsr()); 203 return; 204 } 205 206 if (!is_nested && vector == PREFETCH_ABORT_VECTOR) { 207 static int warned = FALSE; 208 reg_t ifar = read_ifar(), ifsr = read_ifsr(); 209 210 /* The saved_lr is the instruction we're going to execute after 211 * the fault is handled; IFAR is the address that pagefaulted 212 * while fetching the instruction. As far as we know the two 213 * should be the same, if not this assumption will lead to very 214 * hard to debug problems (instruction executing being off by one) 215 * and this assumption needs re-examining. 216 * 217 * UPDATE: at least qemu-linaro does in fact sometimes generate faults 218 * with LR and IFAR differing by as many as 64 bytes. While the page 219 * fault resolution code below handles this case just fine, the cause 220 * of this behavior is unknown. We have not yet seen the same on 221 * actual hardware, which is why we warn about this problem once. 222 */ 223 if (*saved_lr != ifar && !warned) { 224 printf("KERNEL: prefetch abort with differing IFAR and LR\n"); 225 printf("KERNEL: IFSR %"PRIx32" IFAR %"PRIx32" LR %"PRIx32" in " 226 "%s/%d\n", ifsr, ifar, *saved_lr, saved_proc->p_name, 227 saved_proc->p_endpoint); 228 warned = TRUE; 229 } 230 pagefault(saved_proc, saved_lr, is_nested, ifar, ifsr); 231 return; 232 } 233 234 /* If an exception occurs while running a process, the is_nested variable 235 * will be zero. Exceptions in interrupt handlers or system traps will make 236 * is_nested non-zero. 237 */ 238 if (is_nested == 0 && ! iskernelp(saved_proc)) { 239 cause_sig(proc_nr(saved_proc), ep->signum); 240 return; 241 } 242 243 /* Exception in system code. This is not supposed to happen. */ 244 inkernel_disaster(saved_proc, saved_lr, ep, is_nested); 245 246 panic("return from inkernel_disaster"); 247 } 248 249 #if USE_SYSDEBUG 250 /*===========================================================================* 251 * proc_stacktrace_execute * 252 *===========================================================================*/ 253 static void proc_stacktrace_execute(struct proc *whichproc, reg_t v_bp, reg_t pc) 254 { 255 printf("%-8.8s %6d 0x%lx \n", 256 whichproc->p_name, whichproc->p_endpoint, pc); 257 } 258 #endif 259 260 void proc_stacktrace(struct proc *whichproc) 261 { 262 #if USE_SYSDEBUG 263 proc_stacktrace_execute(whichproc, whichproc->p_reg.fp, whichproc->p_reg.pc); 264 #endif /* USE_SYSDEBUG */ 265 } 266 267 void enable_fpu_exception(void) 268 { 269 } 270 271 void disable_fpu_exception(void) 272 { 273 } 274