xref: /netbsd-src/sys/arch/arm/arm/undefined.c (revision 68fa58437753598de948829082f591c269b48777)
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