1/*	$NetBSD: undefined.c,v 1.42 2011/11/15 19:01:54 matt 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#define FAST_FPE
48
49#include "opt_ddb.h"
50#include "opt_kgdb.h"
51
52#include <sys/param.h>
53#ifdef KGDB
54#include <sys/kgdb.h>
55#endif
56
57__KERNEL_RCSID(0, "$NetBSD: undefined.c,v 1.42 2011/11/15 19:01:54 matt Exp $");
58
59#include <sys/malloc.h>
60#include <sys/queue.h>
61#include <sys/signal.h>
62#include <sys/systm.h>
63#include <sys/proc.h>
64#include <sys/syslog.h>
65#include <sys/vmmeter.h>
66#ifdef FAST_FPE
67#include <sys/acct.h>
68#endif
69#include <sys/userret.h>
70
71#include <uvm/uvm_extern.h>
72
73#include <machine/cpu.h>
74#include <machine/frame.h>
75#include <arm/undefined.h>
76#include <machine/trap.h>
77
78#include <arch/arm/arm/disassem.h>
79
80#ifdef DDB
81#include <ddb/db_output.h>
82#include <machine/db_machdep.h>
83#endif
84
85#ifdef acorn26
86#include <machine/machdep.h>
87#endif
88
89static int gdb_trapper(u_int, u_int, struct trapframe *, int);
90
91LIST_HEAD(, undefined_handler) undefined_handlers[NUM_UNKNOWN_HANDLERS];
92
93
94void *
95install_coproc_handler(int coproc, undef_handler_t handler)
96{
97	struct undefined_handler *uh;
98
99	KASSERT(coproc >= 0 && coproc < NUM_UNKNOWN_HANDLERS);
100	KASSERT(handler != NULL); /* Used to be legal. */
101
102	/* XXX: M_TEMP??? */
103	uh = malloc(sizeof(*uh), M_TEMP, M_WAITOK);
104	uh->uh_handler = handler;
105	install_coproc_handler_static(coproc, uh);
106	return uh;
107}
108
109void
110install_coproc_handler_static(int coproc, struct undefined_handler *uh)
111{
112
113	LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
114}
115
116void
117remove_coproc_handler(void *cookie)
118{
119	struct undefined_handler *uh = cookie;
120
121	LIST_REMOVE(uh, uh_link);
122	free(uh, M_TEMP);
123}
124
125static int
126cp15_trapper(u_int addr, u_int insn, struct trapframe *frame, int code)
127{
128	struct lwp * const l = curlwp;
129
130#ifdef THUMB_CODE
131	if (frame->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 > 12)
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 = &frame->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		frame->tf_pc += INSN_SIZE;
155		return 0;
156	}
157
158	/*
159	 * Handle {MRC,MCR} p15, 0, <Rd>, c13, c0, 2 (User read/write thread id)
160	 */
161	if ((insn & 0xffef0fff) == 0xee0d0f50) {
162		struct pcb * const pcb = lwp_getpcb(l);
163		if (insn & 0x00100000)
164			*regp = pcb->pcb_user_pid_rw;
165		else
166			pcb->pcb_user_pid_rw = *regp;
167		frame->tf_pc += INSN_SIZE;
168		return 0;
169	}
170
171	return 1;
172}
173
174static int
175gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code)
176{
177	struct lwp * const l = curlwp;
178
179#ifdef THUMB_CODE
180	if (frame->tf_spsr & PSR_T_bit) {
181		if (insn == GDB_THUMB_BREAKPOINT)
182			goto bkpt;
183	}
184	else
185#endif
186	{
187		if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
188#ifdef THUMB_CODE
189		bkpt:
190#endif
191			if (code == FAULT_USER) {
192				ksiginfo_t ksi;
193
194				KSI_INIT_TRAP(&ksi);
195				ksi.ksi_signo = SIGTRAP;
196				ksi.ksi_code = TRAP_BRKPT;
197				ksi.ksi_addr = (u_int32_t *)addr;
198				ksi.ksi_trap = 0;
199				trapsignal(l, &ksi);
200				return 0;
201			}
202#ifdef KGDB
203			return !kgdb_trap(T_BREAKPOINT, frame);
204#endif
205		}
206	}
207	return 1;
208}
209
210static struct undefined_handler cp15_uh;
211static struct undefined_handler gdb_uh;
212#ifdef THUMB_CODE
213static struct undefined_handler gdb_uh_thumb;
214#endif
215
216void
217undefined_init(void)
218{
219	int loop;
220
221	/* Not actually necessary -- the initialiser is just NULL */
222	for (loop = 0; loop < NUM_UNKNOWN_HANDLERS; ++loop)
223		LIST_INIT(&undefined_handlers[loop]);
224
225	/* Install handler for CP15 emulation */
226	cp15_uh.uh_handler = cp15_trapper;
227	install_coproc_handler_static(SYSTEM_COPROC, &cp15_uh);
228
229	/* Install handler for GDB breakpoints */
230	gdb_uh.uh_handler = gdb_trapper;
231	install_coproc_handler_static(CORE_UNKNOWN_HANDLER, &gdb_uh);
232#ifdef THUMB_CODE
233	gdb_uh_thumb.uh_handler = gdb_trapper;
234	install_coproc_handler_static(THUMB_UNKNOWN_HANDLER, &gdb_uh_thumb);
235#endif
236}
237
238void
239undefinedinstruction(trapframe_t *frame)
240{
241	struct lwp *l;
242	u_int fault_pc;
243	int fault_instruction;
244	int fault_code;
245	int coprocessor;
246	int user;
247	struct undefined_handler *uh;
248#ifdef VERBOSE_ARM32
249	int s;
250#endif
251
252	/* Enable interrupts if they were enabled before the exception. */
253#ifdef acorn26
254	if ((frame->tf_r15 & R15_IRQ_DISABLE) == 0)
255		int_on();
256#else
257	restore_interrupts(frame->tf_spsr & IF32_bits);
258#endif
259
260#ifndef acorn26
261#ifdef THUMB_CODE
262	if (frame->tf_spsr & PSR_T_bit)
263		frame->tf_pc -= THUMB_INSN_SIZE;
264	else
265#endif
266	{
267		frame->tf_pc -= INSN_SIZE;
268	}
269#endif
270
271#ifdef __PROG26
272	fault_pc = frame->tf_r15 & R15_PC;
273#else
274	fault_pc = frame->tf_pc;
275#endif
276
277	/* Get the current lwp/proc structure or lwp0/proc0 if there is none. */
278	l = curlwp;
279
280#ifdef __PROG26
281	if ((frame->tf_r15 & R15_MODE) == R15_MODE_USR) {
282#else
283	if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
284#endif
285		user = 1;
286		LWP_CACHE_CREDS(l, l->l_proc);
287	} else
288		user = 0;
289
290
291#ifdef THUMB_CODE
292	if (frame->tf_spsr & PSR_T_bit) {
293		fault_instruction = fusword((void *)(fault_pc & ~1));
294	}
295	else
296#endif
297	{
298		/*
299		 * Make sure the program counter is correctly aligned so we
300		 * don't take an alignment fault trying to read the opcode.
301		 */
302		if (__predict_false((fault_pc & 3) != 0)) {
303			ksiginfo_t ksi;
304			/* Give the user an illegal instruction signal. */
305			KSI_INIT_TRAP(&ksi);
306			ksi.ksi_signo = SIGILL;
307			ksi.ksi_code = ILL_ILLOPC;
308			ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
309			trapsignal(l, &ksi);
310			userret(l);
311			return;
312		}
313	 	/*
314		 * Should use fuword() here .. but in the interests of
315		 * squeezing every  bit of speed we will just use
316		 * ReadWord(). We know the instruction can be read
317		 * as was just executed so this will never fail unless
318		 * the kernel is screwed up in which case it does
319		 * not really matter does it ?
320		 */
321
322		fault_instruction = *(u_int32_t *)fault_pc;
323	}
324
325	/* Update vmmeter statistics */
326	curcpu()->ci_data.cpu_ntrap++;
327
328#ifdef THUMB_CODE
329	if (frame->tf_spsr & PSR_T_bit) {
330		coprocessor = THUMB_UNKNOWN_HANDLER;
331	}
332	else
333#endif
334	{
335		/* Check for coprocessor instruction */
336
337		/*
338		 * According to the datasheets you only need to look at
339		 * bit 27 of the instruction to tell the difference
340		 * between and undefined instruction and a coprocessor
341		 * instruction following an undefined instruction trap.
342		 *
343		 * ARMv5 adds undefined instructions in the NV space,
344		 * even when bit 27 is set.
345		 */
346
347		if ((fault_instruction & (1 << 27)) != 0
348		    && (fault_instruction & 0xf0000000) != 0xf0000000)
349			coprocessor = (fault_instruction >> 8) & 0x0f;
350		else
351			coprocessor = CORE_UNKNOWN_HANDLER;
352	}
353
354	if (user) {
355		struct pcb *pcb = lwp_getpcb(l);
356		/*
357		 * Modify the fault_code to reflect the USR/SVC state at
358		 * time of fault.
359		 */
360		fault_code = FAULT_USER;
361		pcb->pcb_tf = frame;
362	} else
363		fault_code = 0;
364
365	/* OK this is were we do something about the instruction. */
366	LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link)
367	    if (uh->uh_handler(fault_pc, fault_instruction, frame,
368			       fault_code) == 0)
369		    break;
370
371	if (uh == NULL) {
372		/* Fault has not been handled */
373		ksiginfo_t ksi;
374
375#ifdef VERBOSE_ARM32
376		s = spltty();
377
378		if ((fault_instruction & 0x0f000010) == 0x0e000000) {
379			printf("CDP\n");
380			disassemble(fault_pc);
381		} else if ((fault_instruction & 0x0e000000) == 0x0c000000) {
382			printf("LDC/STC\n");
383			disassemble(fault_pc);
384		} else if ((fault_instruction & 0x0f000010) == 0x0e000010) {
385			printf("MRC/MCR\n");
386			disassemble(fault_pc);
387		} else if ((fault_instruction & ~INSN_COND_MASK)
388			 != (KERNEL_BREAKPOINT & ~INSN_COND_MASK)) {
389			printf("Undefined instruction\n");
390			disassemble(fault_pc);
391		}
392
393		splx(s);
394#endif
395
396		if ((fault_code & FAULT_USER) == 0) {
397#ifdef DDB
398			db_printf("Undefined instruction in kernel\n");
399			kdb_trap(T_FAULT, frame);
400#else
401			panic("undefined instruction in kernel");
402#endif
403		}
404		KSI_INIT_TRAP(&ksi);
405		ksi.ksi_signo = SIGILL;
406		ksi.ksi_code = ILL_ILLOPC;
407		ksi.ksi_addr = (u_int32_t *)fault_pc;
408		ksi.ksi_trap = fault_instruction;
409		trapsignal(l, &ksi);
410	}
411
412	if ((fault_code & FAULT_USER) == 0)
413		return;
414
415#ifdef FAST_FPE
416	/* Optimised exit code */
417	{
418		/*
419		 * Check for reschedule request, at the moment there is only
420		 * 1 ast so this code should always be run
421		 */
422		if (curcpu()->ci_want_resched) {
423			/*
424			 * We are being preempted.
425			 */
426			preempt();
427		}
428
429		/* Invoke MI userret code */
430		mi_userret(l);
431	}
432#else
433	userret(l);
434#endif
435}
436