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