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