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