1 /* $OpenBSD: undefined.c,v 1.13 2019/03/13 09:28:21 patrick Exp $ */ 2 /* $NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $ */ 3 4 /* 5 * Copyright (c) 2001 Ben Harris. 6 * Copyright (c) 1995 Mark Brinicombe. 7 * Copyright (c) 1995 Brini. 8 * All rights reserved. 9 * 10 * This code is derived from software written for Brini by Mark Brinicombe 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by Brini. 23 * 4. The name of the company nor the name of the author may be used to 24 * endorse or promote products derived from this software without specific 25 * prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 29 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * RiscBSD kernel project 40 * 41 * undefined.c 42 * 43 * Fault handler 44 * 45 * Created : 06/01/95 46 */ 47 48 #include <sys/param.h> 49 50 #include <sys/malloc.h> 51 #include <sys/queue.h> 52 #include <sys/signal.h> 53 #include <sys/signalvar.h> 54 #include <sys/systm.h> 55 #include <sys/proc.h> 56 #include <sys/user.h> 57 #include <sys/syslog.h> 58 #include <sys/vmmeter.h> 59 60 #include <uvm/uvm_extern.h> 61 62 #include <machine/cpu.h> 63 #include <machine/frame.h> 64 #include <arm/undefined.h> 65 #include <arm/vfp.h> 66 #include <machine/trap.h> 67 68 69 static int gdb_trapper(u_int, u_int, struct trapframe *, int, uint32_t); 70 71 LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS]; 72 73 74 void * 75 install_coproc_handler(int coproc, undef_handler_t handler) 76 { 77 struct undefined_handler *uh; 78 79 KASSERT(coproc >= 0 && coproc < MAX_COPROCS); 80 KASSERT(handler != NULL); /* Used to be legal. */ 81 82 /* XXX: M_TEMP??? */ 83 uh = (struct undefined_handler *)malloc(sizeof(*uh), M_TEMP, M_WAITOK); 84 uh->uh_handler = handler; 85 install_coproc_handler_static(coproc, uh); 86 return uh; 87 } 88 89 void 90 install_coproc_handler_static(int coproc, struct undefined_handler *uh) 91 { 92 93 LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link); 94 } 95 96 void 97 remove_coproc_handler(void *cookie) 98 { 99 struct undefined_handler *uh = cookie; 100 101 LIST_REMOVE(uh, uh_link); 102 free(uh, M_TEMP, 0); 103 } 104 105 106 static int 107 gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code, uint32_t fpexc) 108 { 109 union sigval sv; 110 struct proc *p; 111 p = (curproc == NULL) ? &proc0 : curproc; 112 113 if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) { 114 if (code == FAULT_USER) { 115 sv.sival_int = addr; 116 KERNEL_LOCK(); 117 trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv); 118 KERNEL_UNLOCK(); 119 return 0; 120 } 121 } 122 return 1; 123 } 124 125 static struct undefined_handler gdb_uh; 126 127 void 128 undefined_init() 129 { 130 int loop; 131 132 /* Not actually necessary -- the initialiser is just NULL */ 133 for (loop = 0; loop < MAX_COPROCS; ++loop) 134 LIST_INIT(&undefined_handlers[loop]); 135 136 /* Install handler for GDB breakpoints */ 137 gdb_uh.uh_handler = gdb_trapper; 138 install_coproc_handler_static(0, &gdb_uh); 139 } 140 141 142 void 143 undefinedinstruction(trapframe_t *frame) 144 { 145 struct proc *p; 146 u_int fault_pc; 147 int fault_instruction; 148 int fault_code; 149 int coprocessor; 150 struct undefined_handler *uh; 151 uint32_t fpexc; 152 #ifdef VERBOSE_ARM32 153 int s; 154 #endif 155 union sigval sv; 156 157 /* Before enabling interrupts, save FPU state */ 158 fpexc = vfp_save(); 159 160 /* Enable interrupts if they were enabled before the exception. */ 161 if (!(frame->tf_spsr & PSR_I)) 162 enable_interrupts(PSR_I); 163 164 frame->tf_pc -= INSN_SIZE; 165 fault_pc = frame->tf_pc; 166 167 /* Get the current proc structure or proc0 if there is none. */ 168 p = (curproc == NULL) ? &proc0 : curproc; 169 170 /* 171 * Make sure the program counter is correctly aligned so we 172 * don't take an alignment fault trying to read the opcode. 173 */ 174 if (__predict_false((fault_pc & 3) != 0)) { 175 /* Give the user an illegal instruction signal. */ 176 sv.sival_int = (u_int32_t) fault_pc; 177 KERNEL_LOCK(); 178 trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv); 179 KERNEL_UNLOCK(); 180 userret(p); 181 return; 182 } 183 184 /* 185 * Should use copyin() here .. but in the interests of squeezing every 186 * bit of speed we will just read it directly. We know the instruction 187 * can be read as was just executed so this will never fail unless the 188 * kernel is screwed up in which case it does not really matter does 189 * it? 190 */ 191 192 fault_instruction = *(u_int32_t *)fault_pc; 193 194 /* Update vmmeter statistics */ 195 uvmexp.traps++; 196 197 /* Check for coprocessor instruction */ 198 199 /* 200 * According to the datasheets you only need to look at bit 27 of the 201 * instruction to tell the difference between an undefined 202 * instruction and a coprocessor instruction following an undefined 203 * instruction trap. 204 */ 205 206 coprocessor = 0; 207 if ((fault_instruction & (1 << 27)) != 0) 208 coprocessor = (fault_instruction >> 8) & 0x0f; 209 else { /* check for special instructions */ 210 if (((fault_instruction & 0xfe000000) == 0xf2000000) || 211 ((fault_instruction & 0xff100000) == 0xf4000000)) 212 coprocessor = 10; /* vfp / simd */ 213 } 214 215 if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { 216 /* 217 * Modify the fault_code to reflect the USR/SVC state at 218 * time of fault. 219 */ 220 fault_code = FAULT_USER; 221 p->p_addr->u_pcb.pcb_tf = frame; 222 } else 223 fault_code = 0; 224 225 /* OK this is were we do something about the instruction. */ 226 LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link) 227 if (uh->uh_handler(fault_pc, fault_instruction, frame, 228 fault_code, fpexc) == 0) 229 break; 230 231 if (uh == NULL) { 232 /* Fault has not been handled */ 233 234 #ifdef VERBOSE_ARM32 235 s = spltty(); 236 237 if ((fault_instruction & 0x0f000010) == 0x0e000000) { 238 printf("CDP\n"); 239 disassemble(fault_pc); 240 } else if ((fault_instruction & 0x0e000000) == 0x0c000000) { 241 printf("LDC/STC\n"); 242 disassemble(fault_pc); 243 } else if ((fault_instruction & 0x0f000010) == 0x0e000010) { 244 printf("MRC/MCR\n"); 245 disassemble(fault_pc); 246 } else if ((fault_instruction & ~INSN_COND_MASK) 247 != (KERNEL_BREAKPOINT & ~INSN_COND_MASK)) { 248 printf("Undefined instruction\n"); 249 disassemble(fault_pc); 250 } 251 252 splx(s); 253 #endif 254 255 if ((fault_code & FAULT_USER) == 0) { 256 printf("Undefined instruction in kernel\n"); 257 #ifdef DDB 258 db_enter(); 259 #endif 260 } 261 262 sv.sival_int = frame->tf_pc; 263 KERNEL_LOCK(); 264 trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv); 265 KERNEL_UNLOCK(); 266 } 267 268 if ((fault_code & FAULT_USER) == 0) 269 return; 270 271 userret(p); 272 } 273