1 /* $NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 Ben Harris. 5 * Copyright (c) 1995 Mark Brinicombe. 6 * Copyright (c) 1995 Brini. 7 * All rights reserved. 8 * 9 * This code is derived from software written for Brini by Mark Brinicombe 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Brini. 22 * 4. The name of the company nor the name of the author may be used to 23 * endorse or promote products derived from this software without specific 24 * prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 28 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * RiscBSD kernel project 39 * 40 * undefined.c 41 * 42 * Fault handler 43 * 44 * Created : 06/01/95 45 */ 46 47 48 #include "opt_ddb.h" 49 50 #include <sys/cdefs.h> 51 __FBSDID("$FreeBSD$"); 52 53 #include <sys/param.h> 54 #include <sys/malloc.h> 55 #include <sys/queue.h> 56 #include <sys/signal.h> 57 #include <sys/systm.h> 58 #include <sys/proc.h> 59 #include <sys/syslog.h> 60 #include <sys/vmmeter.h> 61 #include <sys/lock.h> 62 #include <sys/mutex.h> 63 #include <sys/signalvar.h> 64 #include <sys/ptrace.h> 65 #ifdef KDB 66 #include <sys/kdb.h> 67 #endif 68 #ifdef FAST_FPE 69 #include <sys/acct.h> 70 #endif 71 72 #include <vm/vm.h> 73 #include <vm/vm_extern.h> 74 75 #include <machine/asm.h> 76 #include <machine/cpu.h> 77 #include <machine/frame.h> 78 #include <machine/undefined.h> 79 #include <machine/trap.h> 80 81 #include <machine/disassem.h> 82 83 #ifdef DDB 84 #include <ddb/db_output.h> 85 #include <machine/db_machdep.h> 86 #endif 87 88 #ifdef acorn26 89 #include <machine/machdep.h> 90 #endif 91 92 static int gdb_trapper(u_int, u_int, struct trapframe *, int); 93 #ifdef FAST_FPE 94 extern int want_resched; 95 #endif 96 97 LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS]; 98 99 100 void * 101 install_coproc_handler(int coproc, undef_handler_t handler) 102 { 103 struct undefined_handler *uh; 104 105 KASSERT(coproc >= 0 && coproc < MAX_COPROCS, ("bad coproc")); 106 KASSERT(handler != NULL, ("handler is NULL")); /* Used to be legal. */ 107 108 /* XXX: M_TEMP??? */ 109 MALLOC(uh, struct undefined_handler *, sizeof(*uh), M_TEMP, M_WAITOK); 110 uh->uh_handler = handler; 111 install_coproc_handler_static(coproc, uh); 112 return uh; 113 } 114 115 void 116 install_coproc_handler_static(int coproc, struct undefined_handler *uh) 117 { 118 119 LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link); 120 } 121 122 void 123 remove_coproc_handler(void *cookie) 124 { 125 struct undefined_handler *uh = cookie; 126 127 LIST_REMOVE(uh, uh_link); 128 FREE(uh, M_TEMP); 129 } 130 131 132 static int 133 gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code) 134 { 135 struct thread *td; 136 ksiginfo_t ksi; 137 138 td = (curthread == NULL) ? &thread0 : curthread; 139 140 if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) { 141 if (code == FAULT_USER) { 142 ksiginfo_init_trap(&ksi); 143 ksi.ksi_signo = SIGTRAP; 144 ksi.ksi_code = TRAP_BRKPT; 145 ksi.ksi_addr = (u_int32_t *)addr; 146 trapsignal(td, &ksi); 147 return 0; 148 } 149 #if 0 150 #ifdef KGDB 151 return !kgdb_trap(T_BREAKPOINT, frame); 152 #endif 153 #endif 154 } 155 return 1; 156 } 157 158 static struct undefined_handler gdb_uh; 159 160 void 161 undefined_init() 162 { 163 int loop; 164 165 /* Not actually necessary -- the initialiser is just NULL */ 166 for (loop = 0; loop < MAX_COPROCS; ++loop) 167 LIST_INIT(&undefined_handlers[loop]); 168 169 /* Install handler for GDB breakpoints */ 170 gdb_uh.uh_handler = gdb_trapper; 171 install_coproc_handler_static(0, &gdb_uh); 172 } 173 174 175 void 176 undefinedinstruction(trapframe_t *frame) 177 { 178 struct thread *td; 179 u_int fault_pc; 180 int fault_instruction; 181 int fault_code; 182 int coprocessor; 183 struct undefined_handler *uh; 184 #ifdef VERBOSE_ARM32 185 int s; 186 #endif 187 ksiginfo_t ksi; 188 189 /* Enable interrupts if they were enabled before the exception. */ 190 if (!(frame->tf_spsr & I32_bit)) 191 enable_interrupts(I32_bit|F32_bit); 192 193 frame->tf_pc -= INSN_SIZE; 194 PCPU_LAZY_INC(cnt.v_trap); 195 196 fault_pc = frame->tf_pc; 197 198 /* 199 * Get the current thread/proc structure or thread0/proc0 if there is 200 * none. 201 */ 202 td = curthread == NULL ? &thread0 : curthread; 203 204 /* 205 * Make sure the program counter is correctly aligned so we 206 * don't take an alignment fault trying to read the opcode. 207 */ 208 if (__predict_false((fault_pc & 3) != 0)) { 209 ksiginfo_init_trap(&ksi); 210 ksi.ksi_signo = SIGILL; 211 ksi.ksi_code = ILL_ILLADR; 212 ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc; 213 trapsignal(td, &ksi); 214 userret(td, frame); 215 return; 216 } 217 218 /* 219 * Should use fuword() here .. but in the interests of squeezing every 220 * bit of speed we will just use ReadWord(). We know the instruction 221 * can be read as was just executed so this will never fail unless the 222 * kernel is screwed up in which case it does not really matter does 223 * it ? 224 */ 225 226 fault_instruction = *(u_int32_t *)fault_pc; 227 228 /* Update vmmeter statistics */ 229 #if 0 230 uvmexp.traps++; 231 #endif 232 /* Check for coprocessor instruction */ 233 234 /* 235 * According to the datasheets you only need to look at bit 27 of the 236 * instruction to tell the difference between and undefined 237 * instruction and a coprocessor instruction following an undefined 238 * instruction trap. 239 */ 240 241 if ((fault_instruction & (1 << 27)) != 0) 242 coprocessor = (fault_instruction >> 8) & 0x0f; 243 else 244 coprocessor = 0; 245 246 if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { 247 /* 248 * Modify the fault_code to reflect the USR/SVC state at 249 * time of fault. 250 */ 251 fault_code = FAULT_USER; 252 td->td_frame = frame; 253 } else 254 fault_code = 0; 255 256 /* OK this is were we do something about the instruction. */ 257 LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link) 258 if (uh->uh_handler(fault_pc, fault_instruction, frame, 259 fault_code) == 0) 260 break; 261 262 if (fault_code & FAULT_USER && fault_instruction == PTRACE_BREAKPOINT) { 263 PROC_LOCK(td->td_proc); 264 _PHOLD(td->td_proc); 265 ptrace_clear_single_step(td); 266 _PRELE(td->td_proc); 267 PROC_UNLOCK(td->td_proc); 268 return; 269 } 270 271 if (uh == NULL && (fault_code & FAULT_USER)) { 272 /* Fault has not been handled */ 273 ksiginfo_init_trap(&ksi); 274 ksi.ksi_signo = SIGILL; 275 ksi.ksi_code = ILL_ILLOPC; 276 ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc; 277 trapsignal(td, &ksi); 278 } 279 280 if ((fault_code & FAULT_USER) == 0) { 281 if (fault_instruction == KERNEL_BREAKPOINT) { 282 #ifdef KDB 283 kdb_trap(T_BREAKPOINT, 0, frame); 284 #else 285 printf("No debugger in kernel.\n"); 286 #endif 287 return; 288 } else 289 panic("Undefined instruction in kernel.\n"); 290 } 291 292 #ifdef FAST_FPE 293 /* Optimised exit code */ 294 { 295 296 /* 297 * Check for reschedule request, at the moment there is only 298 * 1 ast so this code should always be run 299 */ 300 301 if (want_resched) { 302 /* 303 * We are being preempted. 304 */ 305 preempt(0); 306 } 307 308 /* Invoke MI userret code */ 309 mi_userret(td); 310 311 #if 0 312 l->l_priority = l->l_usrpri; 313 314 curcpu()->ci_schedstate.spc_curpriority = l->l_priority; 315 #endif 316 } 317 318 #else 319 userret(td, frame); 320 #endif 321 } 322