undefined.c revision 156166
1129198Scognet/*	$NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $	*/
2129198Scognet
3139735Simp/*-
4129198Scognet * Copyright (c) 2001 Ben Harris.
5129198Scognet * Copyright (c) 1995 Mark Brinicombe.
6129198Scognet * Copyright (c) 1995 Brini.
7129198Scognet * All rights reserved.
8129198Scognet *
9129198Scognet * This code is derived from software written for Brini by Mark Brinicombe
10129198Scognet *
11129198Scognet * Redistribution and use in source and binary forms, with or without
12129198Scognet * modification, are permitted provided that the following conditions
13129198Scognet * are met:
14129198Scognet * 1. Redistributions of source code must retain the above copyright
15129198Scognet *    notice, this list of conditions and the following disclaimer.
16129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
17129198Scognet *    notice, this list of conditions and the following disclaimer in the
18129198Scognet *    documentation and/or other materials provided with the distribution.
19129198Scognet * 3. All advertising materials mentioning features or use of this software
20129198Scognet *    must display the following acknowledgement:
21129198Scognet *	This product includes software developed by Brini.
22129198Scognet * 4. The name of the company nor the name of the author may be used to
23129198Scognet *    endorse or promote products derived from this software without specific
24129198Scognet *    prior written permission.
25129198Scognet *
26129198Scognet * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
27129198Scognet * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28129198Scognet * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29129198Scognet * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30129198Scognet * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31129198Scognet * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32129198Scognet * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36129198Scognet * SUCH DAMAGE.
37129198Scognet *
38129198Scognet * RiscBSD kernel project
39129198Scognet *
40129198Scognet * undefined.c
41129198Scognet *
42129198Scognet * Fault handler
43129198Scognet *
44129198Scognet * Created      : 06/01/95
45129198Scognet */
46129198Scognet
47129198Scognet
48137975Scognet#include "opt_ddb.h"
49137975Scognet
50129198Scognet#include <sys/cdefs.h>
51129198Scognet__FBSDID("$FreeBSD: head/sys/arm/arm/undefined.c 156166 2006-03-01 18:33:45Z cognet $");
52129198Scognet
53129198Scognet#include <sys/param.h>
54129198Scognet#include <sys/malloc.h>
55129198Scognet#include <sys/queue.h>
56129198Scognet#include <sys/signal.h>
57129198Scognet#include <sys/systm.h>
58129198Scognet#include <sys/proc.h>
59129198Scognet#include <sys/syslog.h>
60129198Scognet#include <sys/vmmeter.h>
61129198Scognet#include <sys/types.h>
62129198Scognet#include <sys/lock.h>
63129198Scognet#include <sys/mutex.h>
64138525Scognet#include <sys/signalvar.h>
65140001Scognet#include <sys/ptrace.h>
66137940Scognet#ifdef KDB
67137940Scognet#include <sys/kdb.h>
68137940Scognet#endif
69129198Scognet#ifdef FAST_FPE
70129198Scognet#include <sys/acct.h>
71129198Scognet#endif
72129198Scognet
73129198Scognet#include <vm/vm.h>
74129198Scognet#include <vm/vm_extern.h>
75129198Scognet
76129198Scognet#include <machine/asm.h>
77129198Scognet#include <machine/cpu.h>
78129198Scognet#include <machine/frame.h>
79129198Scognet#include <machine/undefined.h>
80129198Scognet#include <machine/trap.h>
81129198Scognet
82129198Scognet#include <machine/disassem.h>
83129198Scognet
84129198Scognet#ifdef DDB
85129198Scognet#include <ddb/db_output.h>
86129198Scognet#include <machine/db_machdep.h>
87129198Scognet#endif
88129198Scognet
89129198Scognet#ifdef acorn26
90129198Scognet#include <machine/machdep.h>
91129198Scognet#endif
92129198Scognet
93129198Scognetstatic int gdb_trapper(u_int, u_int, struct trapframe *, int);
94129198Scognet#ifdef FAST_FPE
95129198Scognetextern int want_resched;
96129198Scognet#endif
97129198Scognet
98129198ScognetLIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS];
99129198Scognet
100129198Scognet
101129198Scognetvoid *
102129198Scognetinstall_coproc_handler(int coproc, undef_handler_t handler)
103129198Scognet{
104129198Scognet	struct undefined_handler *uh;
105129198Scognet
106129198Scognet	KASSERT(coproc >= 0 && coproc < MAX_COPROCS, ("bad coproc"));
107129198Scognet	KASSERT(handler != NULL, ("handler is NULL")); /* Used to be legal. */
108129198Scognet
109129198Scognet	/* XXX: M_TEMP??? */
110129198Scognet	MALLOC(uh, struct undefined_handler *, sizeof(*uh), M_TEMP, M_WAITOK);
111129198Scognet	uh->uh_handler = handler;
112129198Scognet	install_coproc_handler_static(coproc, uh);
113129198Scognet	return uh;
114129198Scognet}
115129198Scognet
116129198Scognetvoid
117129198Scognetinstall_coproc_handler_static(int coproc, struct undefined_handler *uh)
118129198Scognet{
119129198Scognet
120129198Scognet	LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
121129198Scognet}
122129198Scognet
123129198Scognetvoid
124129198Scognetremove_coproc_handler(void *cookie)
125129198Scognet{
126129198Scognet	struct undefined_handler *uh = cookie;
127129198Scognet
128129198Scognet	LIST_REMOVE(uh, uh_link);
129129198Scognet	FREE(uh, M_TEMP);
130129198Scognet}
131129198Scognet
132129198Scognet
133129198Scognetstatic int
134129198Scognetgdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code)
135129198Scognet{
136129198Scognet	struct thread *td;
137151316Sdavidxu	ksiginfo_t ksi;
138151316Sdavidxu
139129198Scognet	td = (curthread == NULL) ? &thread0 : curthread;
140129198Scognet
141129198Scognet	if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
142129198Scognet		if (code == FAULT_USER) {
143151316Sdavidxu			ksiginfo_init_trap(&ksi);
144151316Sdavidxu			ksi.ksi_signo = SIGTRAP;
145151316Sdavidxu			ksi.ksi_code = TRAP_BRKPT;
146151316Sdavidxu			ksi.ksi_addr = (u_int32_t *)addr;
147151316Sdavidxu			trapsignal(td, &ksi);
148129198Scognet			return 0;
149129198Scognet		}
150140001Scognet#if 0
151129198Scognet#ifdef KGDB
152129198Scognet		return !kgdb_trap(T_BREAKPOINT, frame);
153129198Scognet#endif
154140001Scognet#endif
155129198Scognet	}
156129198Scognet	return 1;
157129198Scognet}
158129198Scognet
159129198Scognetstatic struct undefined_handler gdb_uh;
160129198Scognet
161129198Scognetvoid
162129198Scognetundefined_init()
163129198Scognet{
164129198Scognet	int loop;
165129198Scognet
166129198Scognet	/* Not actually necessary -- the initialiser is just NULL */
167129198Scognet	for (loop = 0; loop < MAX_COPROCS; ++loop)
168129198Scognet		LIST_INIT(&undefined_handlers[loop]);
169129198Scognet
170129198Scognet	/* Install handler for GDB breakpoints */
171129198Scognet	gdb_uh.uh_handler = gdb_trapper;
172129198Scognet	install_coproc_handler_static(0, &gdb_uh);
173129198Scognet}
174129198Scognet
175129198Scognet
176129198Scognetvoid
177129198Scognetundefinedinstruction(trapframe_t *frame)
178129198Scognet{
179129198Scognet	struct thread *td;
180129198Scognet	u_int fault_pc;
181129198Scognet	int fault_instruction;
182129198Scognet	int fault_code;
183129198Scognet	int coprocessor;
184129198Scognet	struct undefined_handler *uh;
185129198Scognet#ifdef VERBOSE_ARM32
186129198Scognet	int s;
187129198Scognet#endif
188151316Sdavidxu	ksiginfo_t ksi;
189129198Scognet
190129198Scognet	/* Enable interrupts if they were enabled before the exception. */
191129198Scognet	if (!(frame->tf_spsr & I32_bit))
192129198Scognet		enable_interrupts(I32_bit);
193129198Scognet
194129198Scognet	frame->tf_pc -= INSN_SIZE;
195144971Sjhb	PCPU_LAZY_INC(cnt.v_trap);
196129198Scognet
197129198Scognet	fault_pc = frame->tf_pc;
198129198Scognet
199129198Scognet	/*
200129198Scognet	 * Get the current thread/proc structure or thread0/proc0 if there is
201129198Scognet	 * none.
202129198Scognet	 */
203129198Scognet	td = curthread == NULL ? &thread0 : curthread;
204129198Scognet
205129198Scognet	/*
206129198Scognet	 * Make sure the program counter is correctly aligned so we
207129198Scognet	 * don't take an alignment fault trying to read the opcode.
208129198Scognet	 */
209129198Scognet	if (__predict_false((fault_pc & 3) != 0)) {
210151316Sdavidxu		ksiginfo_init_trap(&ksi);
211151316Sdavidxu		ksi.ksi_signo = SIGILL;
212151316Sdavidxu		ksi.ksi_code = ILL_ILLADR;
213151316Sdavidxu		ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
214151316Sdavidxu		trapsignal(td, &ksi);
215156166Scognet		userret(td, frame);
216129198Scognet		return;
217129198Scognet	}
218129198Scognet
219129198Scognet	/*
220129198Scognet	 * Should use fuword() here .. but in the interests of squeezing every
221129198Scognet	 * bit of speed we will just use ReadWord(). We know the instruction
222129198Scognet	 * can be read as was just executed so this will never fail unless the
223129198Scognet	 * kernel is screwed up in which case it does not really matter does
224129198Scognet	 * it ?
225129198Scognet	 */
226129198Scognet
227129198Scognet	fault_instruction = *(u_int32_t *)fault_pc;
228129198Scognet
229129198Scognet	/* Update vmmeter statistics */
230129198Scognet#if 0
231129198Scognet	uvmexp.traps++;
232129198Scognet#endif
233129198Scognet	/* Check for coprocessor instruction */
234129198Scognet
235129198Scognet	/*
236129198Scognet	 * According to the datasheets you only need to look at bit 27 of the
237129198Scognet	 * instruction to tell the difference between and undefined
238129198Scognet	 * instruction and a coprocessor instruction following an undefined
239129198Scognet	 * instruction trap.
240129198Scognet	 */
241129198Scognet
242129198Scognet	if ((fault_instruction & (1 << 27)) != 0)
243129198Scognet		coprocessor = (fault_instruction >> 8) & 0x0f;
244129198Scognet	else
245129198Scognet		coprocessor = 0;
246129198Scognet
247129198Scognet	if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
248129198Scognet		/*
249129198Scognet		 * Modify the fault_code to reflect the USR/SVC state at
250129198Scognet		 * time of fault.
251129198Scognet		 */
252129198Scognet		fault_code = FAULT_USER;
253129198Scognet		td->td_frame = frame;
254129198Scognet	} else
255129198Scognet		fault_code = 0;
256129198Scognet
257129198Scognet	/* OK this is were we do something about the instruction. */
258129198Scognet	LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link)
259129198Scognet	    if (uh->uh_handler(fault_pc, fault_instruction, frame,
260129198Scognet			       fault_code) == 0)
261129198Scognet		    break;
262129198Scognet
263140001Scognet	if (fault_code & FAULT_USER && fault_instruction == PTRACE_BREAKPOINT) {
264155922Sjhb		PROC_LOCK(td->td_proc);
265155922Sjhb		_PHOLD(td->td_proc);
266140001Scognet		ptrace_clear_single_step(td);
267155922Sjhb		_PRELE(td->td_proc);
268155922Sjhb		PROC_UNLOCK(td->td_proc);
269140001Scognet		return;
270140001Scognet	}
271140001Scognet
272137940Scognet	if (uh == NULL && (fault_code & FAULT_USER)) {
273129198Scognet		/* Fault has not been handled */
274151316Sdavidxu		ksiginfo_init_trap(&ksi);
275151316Sdavidxu		ksi.ksi_signo = SIGILL;
276151316Sdavidxu		ksi.ksi_code = ILL_ILLOPC;
277151316Sdavidxu		ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
278151316Sdavidxu		trapsignal(td, &ksi);
279129198Scognet	}
280129198Scognet
281137940Scognet	if ((fault_code & FAULT_USER) == 0) {
282137940Scognet		if (fault_instruction == KERNEL_BREAKPOINT) {
283137940Scognet#ifdef KDB
284155241Simp			kdb_trap(T_BREAKPOINT, 0, frame);
285137940Scognet#else
286155241Simp			printf("No debugger in kernel.\n");
287137940Scognet#endif
288155241Simp			return;
289137975Scognet		} else
290137940Scognet			panic("Undefined instruction in kernel.\n");
291140001Scognet	}
292129198Scognet
293129198Scognet#ifdef FAST_FPE
294129198Scognet	/* Optimised exit code */
295129198Scognet	{
296129198Scognet
297129198Scognet		/*
298129198Scognet		 * Check for reschedule request, at the moment there is only
299129198Scognet		 * 1 ast so this code should always be run
300129198Scognet		 */
301129198Scognet
302129198Scognet		if (want_resched) {
303129198Scognet			/*
304129198Scognet			 * We are being preempted.
305129198Scognet			 */
306129198Scognet			preempt(0);
307129198Scognet		}
308129198Scognet
309129198Scognet		/* Invoke MI userret code */
310129198Scognet		mi_userret(td);
311129198Scognet
312129198Scognet#if 0
313129198Scognet		l->l_priority = l->l_usrpri;
314129198Scognet
315129198Scognet		curcpu()->ci_schedstate.spc_curpriority = l->l_priority;
316129198Scognet#endif
317129198Scognet	}
318129198Scognet
319129198Scognet#else
320156166Scognet	userret(td, frame);
321129198Scognet#endif
322129198Scognet}
323