xref: /openbsd/sys/arch/arm/arm/undefined.c (revision b48f5b6d)
1 /*	$OpenBSD: undefined.c,v 1.2 2004/02/23 19:09:57 drahn 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 #define FAST_FPE
49 
50 #include <sys/param.h>
51 
52 #include <sys/malloc.h>
53 #include <sys/queue.h>
54 #include <sys/signal.h>
55 #include <sys/signalvar.h>
56 #include <sys/systm.h>
57 #include <sys/proc.h>
58 #include <sys/user.h>
59 #include <sys/syslog.h>
60 #include <sys/vmmeter.h>
61 #ifdef FAST_FPE
62 #include <sys/acct.h>
63 #endif
64 
65 #include <uvm/uvm_extern.h>
66 
67 #include <machine/cpu.h>
68 #include <machine/frame.h>
69 #include <arm/undefined.h>
70 #include <machine/trap.h>
71 
72 
73 #ifdef acorn26
74 #include <machine/machdep.h>
75 #endif
76 
77 static int gdb_trapper(u_int, u_int, struct trapframe *, int);
78 
79 #ifdef FAST_FPE
80 extern int want_resched;
81 #endif
82 
83 LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS];
84 
85 
86 void *
87 install_coproc_handler(int coproc, undef_handler_t handler)
88 {
89 	struct undefined_handler *uh;
90 
91 	KASSERT(coproc >= 0 && coproc < MAX_COPROCS);
92 	KASSERT(handler != NULL); /* Used to be legal. */
93 
94 	/* XXX: M_TEMP??? */
95 	MALLOC(uh, struct undefined_handler *, sizeof(*uh), M_TEMP, M_WAITOK);
96 	uh->uh_handler = handler;
97 	install_coproc_handler_static(coproc, uh);
98 	return uh;
99 }
100 
101 void
102 install_coproc_handler_static(int coproc, struct undefined_handler *uh)
103 {
104 
105 	LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
106 }
107 
108 void
109 remove_coproc_handler(void *cookie)
110 {
111 	struct undefined_handler *uh = cookie;
112 
113 	LIST_REMOVE(uh, uh_link);
114 	FREE(uh, M_TEMP);
115 }
116 
117 
118 static int
119 gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code)
120 {
121 	union sigval sv;
122 	struct proc *p;
123 	p = (curproc == NULL) ? &proc0 : curproc;
124 
125 	if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
126 		if (code == FAULT_USER) {
127 			sv.sival_int = addr;
128 			trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv);
129 			return 0;
130 		}
131 #ifdef KGDB
132 		return !kgdb_trap(T_BREAKPOINT, frame);
133 #endif
134 	}
135 	return 1;
136 }
137 
138 static struct undefined_handler gdb_uh;
139 
140 void
141 undefined_init()
142 {
143 	int loop;
144 
145 	/* Not actually necessary -- the initialiser is just NULL */
146 	for (loop = 0; loop < MAX_COPROCS; ++loop)
147 		LIST_INIT(&undefined_handlers[loop]);
148 
149 	/* Install handler for GDB breakpoints */
150 	gdb_uh.uh_handler = gdb_trapper;
151 	install_coproc_handler_static(0, &gdb_uh);
152 }
153 
154 
155 void
156 undefinedinstruction(trapframe_t *frame)
157 {
158 	struct proc *p;
159 	u_int fault_pc;
160 	int fault_instruction;
161 	int fault_code;
162 	int coprocessor;
163 	struct undefined_handler *uh;
164 #ifdef VERBOSE_ARM32
165 	int s;
166 #endif
167 	union sigval sv;
168 
169 	/* Enable interrupts if they were enabled before the exception. */
170 #ifdef acorn26
171 	if ((frame->tf_r15 & R15_IRQ_DISABLE) == 0)
172 		int_on();
173 #else
174 	if (!(frame->tf_spsr & I32_bit))
175 		enable_interrupts(I32_bit);
176 #endif
177 
178 #ifndef acorn26
179 	frame->tf_pc -= INSN_SIZE;
180 #endif
181 
182 #ifdef __PROG26
183 	fault_pc = frame->tf_r15 & R15_PC;
184 #else
185 	fault_pc = frame->tf_pc;
186 #endif
187 
188 	/* Get the current proc structure or proc0 if there is none. */
189 	p = (curproc == NULL) ? &proc0 : curproc;
190 
191 	/*
192 	 * Make sure the program counter is correctly aligned so we
193 	 * don't take an alignment fault trying to read the opcode.
194 	 */
195 	if (__predict_false((fault_pc & 3) != 0)) {
196 		/* Give the user an illegal instruction signal. */
197 		sv.sival_int = (u_int32_t) fault_pc;
198 		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
199 		userret(p, fault_pc, p->p_sticks);
200 		return;
201 	}
202 
203 	/*
204 	 * Should use fuword() here .. but in the interests of squeezing every
205 	 * bit of speed we will just use ReadWord(). We know the instruction
206 	 * can be read as was just executed so this will never fail unless the
207 	 * kernel is screwed up in which case it does not really matter does
208 	 * it ?
209 	 */
210 
211 	fault_instruction = *(u_int32_t *)fault_pc;
212 
213 	/* Update vmmeter statistics */
214 	uvmexp.traps++;
215 
216 	/* Check for coprocessor instruction */
217 
218 	/*
219 	 * According to the datasheets you only need to look at bit 27 of the
220 	 * instruction to tell the difference between and undefined
221 	 * instruction and a coprocessor instruction following an undefined
222 	 * instruction trap.
223 	 */
224 
225 	if ((fault_instruction & (1 << 27)) != 0)
226 		coprocessor = (fault_instruction >> 8) & 0x0f;
227 	else
228 		coprocessor = 0;
229 
230 	/* Get the current proc structure or proc0 if there is none. */
231 
232 	if ((p = curproc) == 0)
233 		p = &proc0;
234 
235 #ifdef __PROG26
236 	if ((frame->tf_r15 & R15_MODE) == R15_MODE_USR)
237 #else
238 	if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE)
239 #endif
240 	{
241 		/*
242 		 * Modify the fault_code to reflect the USR/SVC state at
243 		 * time of fault.
244 		 */
245 		fault_code = FAULT_USER;
246 		p->p_addr->u_pcb.pcb_tf = frame;
247 	} else
248 		fault_code = 0;
249 
250 	/* OK this is were we do something about the instruction. */
251 	LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link)
252 	    if (uh->uh_handler(fault_pc, fault_instruction, frame,
253 			       fault_code) == 0)
254 		    break;
255 
256 	if (uh == NULL) {
257 		/* Fault has not been handled */
258 
259 #ifdef VERBOSE_ARM32
260 		s = spltty();
261 
262 		if ((fault_instruction & 0x0f000010) == 0x0e000000) {
263 			printf("CDP\n");
264 			disassemble(fault_pc);
265 		} else if ((fault_instruction & 0x0e000000) == 0x0c000000) {
266 			printf("LDC/STC\n");
267 			disassemble(fault_pc);
268 		} else if ((fault_instruction & 0x0f000010) == 0x0e000010) {
269 			printf("MRC/MCR\n");
270 			disassemble(fault_pc);
271 		} else if ((fault_instruction & ~INSN_COND_MASK)
272 			 != (KERNEL_BREAKPOINT & ~INSN_COND_MASK)) {
273 			printf("Undefined instruction\n");
274 			disassemble(fault_pc);
275 		}
276 
277 		splx(s);
278 #endif
279 
280 		if ((fault_code & FAULT_USER) == 0) {
281 			printf("Undefined instruction in kernel\n");
282 #ifdef DDB
283 			Debugger();
284 #endif
285 		}
286 
287 		sv.sival_int = frame->tf_pc;
288 		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
289 	}
290 
291 	if ((fault_code & FAULT_USER) == 0)
292 		return;
293 
294 #ifdef FAST_FPE
295 	/* Optimised exit code */
296 	{
297 		int sig;
298 
299 		/* take pending signals */
300 
301 		while ((sig = (CURSIG(p))) != 0) {
302 			postsig(sig);
303 		}
304 
305 		p->p_priority = p->p_usrpri;
306 
307 		/*
308 		 * Check for reschedule request, at the moment there is only
309 		 * 1 ast so this code should always be run
310 		 */
311 
312 		if (want_resched) {
313 			/*
314 			 * We are being preempted.
315 			 */
316 			preempt(NULL);
317 			while ((sig = (CURSIG(p))) != 0) {
318 				postsig(sig);
319 			}
320 		}
321 
322 		/* XXX
323 		curcpu()->ci_schedstate.spc_curpriority = p->p_priority;
324 		*/
325 	}
326 
327 #else
328 	userret(p);
329 		userret(p, frame->tf_pc, p->p_sticks);
330 #endif
331 }
332