1 /* $NetBSD: undefined.c,v 1.66 2019/10/01 18:00:07 chs 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 #define FAST_FPE 48 49 #include "opt_ddb.h" 50 #include "opt_dtrace.h" 51 #include "opt_kgdb.h" 52 53 #include <sys/param.h> 54 #ifdef KGDB 55 #include <sys/kgdb.h> 56 #endif 57 58 __KERNEL_RCSID(0, "$NetBSD: undefined.c,v 1.66 2019/10/01 18:00:07 chs Exp $"); 59 60 #include <sys/kmem.h> 61 #include <sys/queue.h> 62 #include <sys/signal.h> 63 #include <sys/systm.h> 64 #include <sys/proc.h> 65 #include <sys/syslog.h> 66 #include <sys/vmmeter.h> 67 #include <sys/cpu.h> 68 #ifdef FAST_FPE 69 #include <sys/acct.h> 70 #endif 71 #include <sys/userret.h> 72 73 #include <uvm/uvm_extern.h> 74 75 #include <arm/locore.h> 76 #include <arm/undefined.h> 77 78 #include <machine/pcb.h> 79 #include <machine/trap.h> 80 81 #include <arch/arm/arm/disassem.h> 82 83 #ifdef DDB 84 #include <ddb/db_output.h> 85 #include <machine/db_machdep.h> 86 #endif 87 88 static int gdb_trapper(u_int, u_int, struct trapframe *, int); 89 90 LIST_HEAD(, undefined_handler) undefined_handlers[NUM_UNKNOWN_HANDLERS]; 91 92 93 void * 94 install_coproc_handler(int coproc, undef_handler_t handler) 95 { 96 struct undefined_handler *uh; 97 98 KASSERT(coproc >= 0 && coproc < NUM_UNKNOWN_HANDLERS); 99 KASSERT(handler != NULL); /* Used to be legal. */ 100 101 uh = kmem_alloc(sizeof(*uh), KM_SLEEP); 102 uh->uh_handler = handler; 103 install_coproc_handler_static(coproc, uh); 104 return uh; 105 } 106 107 void 108 install_coproc_handler_static(int coproc, struct undefined_handler *uh) 109 { 110 111 LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link); 112 } 113 114 void 115 remove_coproc_handler(void *cookie) 116 { 117 struct undefined_handler *uh = cookie; 118 119 LIST_REMOVE(uh, uh_link); 120 kmem_free(uh, sizeof(*uh)); 121 } 122 123 static int 124 cp15_trapper(u_int addr, u_int insn, struct trapframe *tf, int code) 125 { 126 struct lwp * const l = curlwp; 127 128 #if defined(THUMB_CODE) && !defined(CPU_ARMV7) 129 if (tf->tf_spsr & PSR_T_bit) 130 return 1; 131 #endif 132 if (code != FAULT_USER) 133 return 1; 134 135 /* 136 * Don't overwrite sp, pc, etc. 137 */ 138 const u_int regno = (insn >> 12) & 15; 139 if (regno > 12) 140 return 1; 141 142 /* 143 * Get a pointer to the register used in the instruction to be emulated. 144 */ 145 register_t * const regp = &tf->tf_r0 + regno; 146 147 /* 148 * Handle MRC p15, 0, <Rd>, c13, c0, 3 (Read User read-only thread id) 149 */ 150 if ((insn & 0xffff0fff) == 0xee1d0f70) { 151 *regp = (uintptr_t)l->l_private; 152 tf->tf_pc += INSN_SIZE; 153 curcpu()->ci_und_cp15_ev.ev_count++; 154 return 0; 155 } 156 157 /* 158 * Handle {MRC,MCR} p15, 0, <Rd>, c13, c0, 2 (User read/write thread id) 159 */ 160 if ((insn & 0xffef0fff) == 0xee0d0f50) { 161 struct pcb * const pcb = lwp_getpcb(l); 162 if (insn & 0x00100000) 163 *regp = pcb->pcb_user_pid_rw; 164 else 165 pcb->pcb_user_pid_rw = *regp; 166 tf->tf_pc += INSN_SIZE; 167 curcpu()->ci_und_cp15_ev.ev_count++; 168 return 0; 169 } 170 171 return 1; 172 } 173 174 static int 175 gdb_trapper(u_int addr, u_int insn, struct trapframe *tf, int code) 176 { 177 struct lwp * const l = curlwp; 178 179 #ifdef THUMB_CODE 180 if (tf->tf_spsr & PSR_T_bit) { 181 if (insn == GDB_THUMB_BREAKPOINT) 182 goto bkpt; 183 } 184 else 185 #endif 186 { 187 if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) { 188 #ifdef THUMB_CODE 189 bkpt: 190 #endif 191 if (code == FAULT_USER) { 192 ksiginfo_t ksi; 193 194 KSI_INIT_TRAP(&ksi); 195 ksi.ksi_signo = SIGTRAP; 196 ksi.ksi_code = TRAP_BRKPT; 197 ksi.ksi_addr = (uint32_t *)addr; 198 ksi.ksi_trap = 0; 199 trapsignal(l, &ksi); 200 return 0; 201 } 202 #ifdef KGDB 203 return !kgdb_trap(T_BREAKPOINT, tf); 204 #endif 205 } 206 } 207 return 1; 208 } 209 210 static struct undefined_handler cp15_uh; 211 static struct undefined_handler gdb_uh; 212 #ifdef THUMB_CODE 213 static struct undefined_handler gdb_uh_thumb; 214 #endif 215 216 #ifdef KDTRACE_HOOKS 217 #include <sys/dtrace_bsd.h> 218 219 /* Not used for now, but needed for dtrace/fbt modules */ 220 dtrace_doubletrap_func_t dtrace_doubletrap_func = NULL; 221 dtrace_trap_func_t dtrace_trap_func = NULL; 222 223 int (* dtrace_invop_jump_addr)(struct trapframe *); 224 225 static int 226 dtrace_trapper(u_int addr, struct trapframe *frame) 227 { 228 u_int insn = read_insn(addr, false); 229 230 if (dtrace_invop_jump_addr == NULL) 231 return 1; 232 233 if (!DTRACE_IS_BREAKPOINT(insn)) 234 return 1; 235 236 /* cond value is encoded in the low nibble */ 237 if (!arm_cond_ok_p(__SHIFTIN(insn, INSN_COND_MASK), frame->tf_spsr)) { 238 frame->tf_pc += INSN_SIZE; 239 return 0; 240 } 241 242 dtrace_invop_jump_addr(frame); 243 return 0; 244 } 245 #endif 246 247 void 248 undefined_init(void) 249 { 250 int loop; 251 252 /* Not actually necessary -- the initialiser is just NULL */ 253 for (loop = 0; loop < NUM_UNKNOWN_HANDLERS; ++loop) 254 LIST_INIT(&undefined_handlers[loop]); 255 256 /* Install handler for CP15 emulation */ 257 cp15_uh.uh_handler = cp15_trapper; 258 install_coproc_handler_static(SYSTEM_COPROC, &cp15_uh); 259 260 /* Install handler for GDB breakpoints */ 261 gdb_uh.uh_handler = gdb_trapper; 262 install_coproc_handler_static(CORE_UNKNOWN_HANDLER, &gdb_uh); 263 #ifdef THUMB_CODE 264 gdb_uh_thumb.uh_handler = gdb_trapper; 265 install_coproc_handler_static(THUMB_UNKNOWN_HANDLER, &gdb_uh_thumb); 266 #endif 267 } 268 269 void 270 undefinedinstruction(trapframe_t *tf) 271 { 272 struct lwp *l; 273 vaddr_t fault_pc; 274 int fault_instruction; 275 int fault_code; 276 int coprocessor; 277 int user; 278 struct undefined_handler *uh; 279 #ifdef VERBOSE_ARM32 280 int s; 281 #endif 282 283 curcpu()->ci_und_ev.ev_count++; 284 285 #ifdef KDTRACE_HOOKS 286 if ((tf->tf_spsr & PSR_MODE) != PSR_USR32_MODE) { 287 tf->tf_pc -= INSN_SIZE; 288 if (dtrace_trapper(tf->tf_pc, tf) == 0) 289 return; 290 tf->tf_pc += INSN_SIZE; /* Reset for the rest code */ 291 } 292 #endif 293 294 /* Enable interrupts if they were enabled before the exception. */ 295 restore_interrupts(tf->tf_spsr & IF32_bits); 296 297 #ifdef THUMB_CODE 298 if (tf->tf_spsr & PSR_T_bit) 299 tf->tf_pc -= THUMB_INSN_SIZE; 300 else 301 #endif 302 { 303 tf->tf_pc -= INSN_SIZE; 304 } 305 306 fault_pc = tf->tf_pc; 307 308 /* Get the current lwp/proc structure or lwp0/proc0 if there is none. */ 309 l = curlwp; 310 311 if ((tf->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { 312 user = 1; 313 LWP_CACHE_CREDS(l, l->l_proc); 314 } else 315 user = 0; 316 317 318 #ifdef THUMB_CODE 319 if (tf->tf_spsr & PSR_T_bit) { 320 fault_instruction = read_thumb_insn(fault_pc, user); 321 if (fault_instruction >= 0xe000) { 322 fault_instruction = (fault_instruction << 16) 323 | read_thumb_insn(fault_pc + 2, user); 324 } 325 } 326 else 327 #endif 328 { 329 /* 330 * Make sure the program counter is correctly aligned so we 331 * don't take an alignment fault trying to read the opcode. 332 */ 333 if (__predict_false((fault_pc & 3) != 0)) { 334 ksiginfo_t ksi; 335 /* Give the user an illegal instruction signal. */ 336 KSI_INIT_TRAP(&ksi); 337 ksi.ksi_signo = SIGILL; 338 ksi.ksi_code = ILL_ILLOPC; 339 ksi.ksi_addr = (uint32_t *)(intptr_t) fault_pc; 340 trapsignal(l, &ksi); 341 userret(l); 342 return; 343 } 344 /* 345 * Should use ufetch_32() here .. but in the interests of 346 * squeezing every bit of speed we will just use 347 * read_insn(). We know the instruction can be read 348 * as was just executed so this will never fail unless 349 * the kernel is screwed up in which case it does 350 * not really matter does it? 351 */ 352 fault_instruction = read_insn(fault_pc, user); 353 } 354 355 /* Update vmmeter statistics */ 356 curcpu()->ci_data.cpu_ntrap++; 357 358 #ifdef THUMB_CODE 359 if ((tf->tf_spsr & PSR_T_bit) && !CPU_IS_ARMV7_P()) { 360 coprocessor = THUMB_UNKNOWN_HANDLER; 361 } 362 else 363 #endif 364 { 365 /* Check for coprocessor instruction */ 366 367 /* 368 * According to the datasheets you only need to look at 369 * bit 27 of the instruction to tell the difference 370 * between and undefined instruction and a coprocessor 371 * instruction following an undefined instruction trap. 372 * 373 * ARMv5 adds undefined instructions in the NV space, 374 * even when bit 27 is set. 375 */ 376 377 if ((fault_instruction & (1 << 27)) != 0 378 && (fault_instruction & 0xf0000000) != 0xf0000000) { 379 coprocessor = (fault_instruction >> 8) & 0x0f; 380 #ifdef THUMB_CODE 381 } else if ((tf->tf_spsr & PSR_T_bit) && !CPU_IS_ARMV7_P()) { 382 coprocessor = THUMB_UNKNOWN_HANDLER; 383 #endif 384 } else { 385 coprocessor = CORE_UNKNOWN_HANDLER; 386 } 387 } 388 389 if (user) { 390 /* 391 * Modify the fault_code to reflect the USR/SVC state at 392 * time of fault. 393 */ 394 fault_code = FAULT_USER; 395 KASSERTMSG(tf == lwp_trapframe(l), "tf %p vs %p", tf, 396 lwp_trapframe(l)); 397 } else 398 fault_code = 0; 399 400 /* OK this is were we do something about the instruction. */ 401 LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link) { 402 int ret = uh->uh_handler(fault_pc, fault_instruction, tf, 403 fault_code); 404 405 if (ret == 0) 406 break; 407 } 408 409 if (uh == NULL) { 410 /* Fault has not been handled */ 411 ksiginfo_t ksi; 412 413 #ifdef VERBOSE_ARM32 414 s = spltty(); 415 416 if ((fault_instruction & 0x0f000010) == 0x0e000000) { 417 printf("CDP\n"); 418 disassemble(fault_pc); 419 } else if ((fault_instruction & 0x0e000000) == 0x0c000000) { 420 printf("LDC/STC\n"); 421 disassemble(fault_pc); 422 } else if ((fault_instruction & 0x0f000010) == 0x0e000010) { 423 printf("MRC/MCR\n"); 424 disassemble(fault_pc); 425 } else if ((fault_instruction & ~INSN_COND_MASK) 426 != (KERNEL_BREAKPOINT & ~INSN_COND_MASK)) { 427 printf("Undefined instruction\n"); 428 disassemble(fault_pc); 429 } 430 431 splx(s); 432 #endif 433 434 if ((fault_code & FAULT_USER) == 0) { 435 #ifdef DDB 436 db_printf("Undefined instruction %#x in kernel at %#lx (LR %#x SP %#x)\n", 437 fault_instruction, fault_pc, tf->tf_svc_lr, tf->tf_svc_sp); 438 kdb_trap(T_FAULT, tf); 439 #else 440 panic("undefined instruction %#x in kernel at %#lx", fault_instruction, fault_pc); 441 #endif 442 } 443 KSI_INIT_TRAP(&ksi); 444 ksi.ksi_signo = SIGILL; 445 ksi.ksi_code = ILL_ILLOPC; 446 ksi.ksi_addr = (uint32_t *)fault_pc; 447 ksi.ksi_trap = fault_instruction; 448 trapsignal(l, &ksi); 449 } 450 451 if ((fault_code & FAULT_USER) == 0) 452 return; 453 454 userret(l); 455 } 456