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: stable/11/sys/arm/arm/undefined.c 331722 2018-03-29 02:50:57Z eadler $");
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/lock.h>
62129198Scognet#include <sys/mutex.h>
63138525Scognet#include <sys/signalvar.h>
64140001Scognet#include <sys/ptrace.h>
65331017Skevans#include <sys/vmmeter.h>
66137940Scognet#ifdef KDB
67137940Scognet#include <sys/kdb.h>
68137940Scognet#endif
69129198Scognet
70129198Scognet#include <vm/vm.h>
71129198Scognet#include <vm/vm_extern.h>
72129198Scognet
73271398Sandrew#include <machine/armreg.h>
74129198Scognet#include <machine/asm.h>
75129198Scognet#include <machine/cpu.h>
76129198Scognet#include <machine/frame.h>
77129198Scognet#include <machine/undefined.h>
78129198Scognet#include <machine/trap.h>
79129198Scognet
80129198Scognet#include <machine/disassem.h>
81129198Scognet
82129198Scognet#ifdef DDB
83129198Scognet#include <ddb/db_output.h>
84129198Scognet#endif
85129198Scognet
86196484Scognet#ifdef KDB
87196484Scognet#include <machine/db_machdep.h>
88129198Scognet#endif
89129198Scognet
90282779Sandrew#define	ARM_COPROC_INSN(insn)	(((insn) & (1 << 27)) != 0)
91282779Sandrew#define	ARM_VFP_INSN(insn)	((((insn) & 0xfe000000) == 0xf2000000) || \
92282779Sandrew    (((insn) & 0xff100000) == 0xf4000000))
93282779Sandrew#define	ARM_COPROC(insn)	(((insn) >> 8) & 0xf)
94282779Sandrew
95282779Sandrew#define	THUMB_32BIT_INSN(insn)	((insn) >= 0xe800)
96282779Sandrew#define	THUMB_COPROC_INSN(insn)	(((insn) & (3 << 26)) == (3 << 26))
97282779Sandrew#define	THUMB_COPROC_UNDEFINED(insn) (((insn) & 0x3e << 20) == 0)
98282779Sandrew#define	THUMB_VFP_INSN(insn)	(((insn) & (3 << 24)) == (3 << 24))
99282779Sandrew#define	THUMB_COPROC(insn)	(((insn) >> 8) & 0xf)
100282779Sandrew
101282779Sandrew#define	COPROC_VFP	10
102282779Sandrew
103129198Scognetstatic int gdb_trapper(u_int, u_int, struct trapframe *, int);
104129198Scognet
105129198ScognetLIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS];
106129198Scognet
107129198Scognet
108129198Scognetvoid *
109129198Scognetinstall_coproc_handler(int coproc, undef_handler_t handler)
110129198Scognet{
111129198Scognet	struct undefined_handler *uh;
112129198Scognet
113129198Scognet	KASSERT(coproc >= 0 && coproc < MAX_COPROCS, ("bad coproc"));
114129198Scognet	KASSERT(handler != NULL, ("handler is NULL")); /* Used to be legal. */
115129198Scognet
116129198Scognet	/* XXX: M_TEMP??? */
117184205Sdes	uh = malloc(sizeof(*uh), M_TEMP, M_WAITOK);
118129198Scognet	uh->uh_handler = handler;
119129198Scognet	install_coproc_handler_static(coproc, uh);
120129198Scognet	return uh;
121129198Scognet}
122129198Scognet
123129198Scognetvoid
124129198Scognetinstall_coproc_handler_static(int coproc, struct undefined_handler *uh)
125129198Scognet{
126129198Scognet
127129198Scognet	LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
128129198Scognet}
129129198Scognet
130129198Scognetvoid
131129198Scognetremove_coproc_handler(void *cookie)
132129198Scognet{
133129198Scognet	struct undefined_handler *uh = cookie;
134129198Scognet
135129198Scognet	LIST_REMOVE(uh, uh_link);
136184205Sdes	free(uh, M_TEMP);
137129198Scognet}
138129198Scognet
139129198Scognet
140129198Scognetstatic int
141129198Scognetgdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code)
142129198Scognet{
143129198Scognet	struct thread *td;
144151316Sdavidxu	ksiginfo_t ksi;
145151316Sdavidxu
146129198Scognet	td = (curthread == NULL) ? &thread0 : curthread;
147129198Scognet
148129198Scognet	if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
149129198Scognet		if (code == FAULT_USER) {
150151316Sdavidxu			ksiginfo_init_trap(&ksi);
151151316Sdavidxu			ksi.ksi_signo = SIGTRAP;
152151316Sdavidxu			ksi.ksi_code = TRAP_BRKPT;
153151316Sdavidxu			ksi.ksi_addr = (u_int32_t *)addr;
154151316Sdavidxu			trapsignal(td, &ksi);
155129198Scognet			return 0;
156129198Scognet		}
157140001Scognet#if 0
158129198Scognet#ifdef KGDB
159129198Scognet		return !kgdb_trap(T_BREAKPOINT, frame);
160129198Scognet#endif
161140001Scognet#endif
162129198Scognet	}
163129198Scognet	return 1;
164129198Scognet}
165129198Scognet
166129198Scognetstatic struct undefined_handler gdb_uh;
167129198Scognet
168129198Scognetvoid
169314506Sianundefined_init(void)
170129198Scognet{
171129198Scognet	int loop;
172129198Scognet
173129198Scognet	/* Not actually necessary -- the initialiser is just NULL */
174129198Scognet	for (loop = 0; loop < MAX_COPROCS; ++loop)
175129198Scognet		LIST_INIT(&undefined_handlers[loop]);
176129198Scognet
177129198Scognet	/* Install handler for GDB breakpoints */
178129198Scognet	gdb_uh.uh_handler = gdb_trapper;
179129198Scognet	install_coproc_handler_static(0, &gdb_uh);
180129198Scognet}
181129198Scognet
182129198Scognet
183129198Scognetvoid
184257217Sianundefinedinstruction(struct trapframe *frame)
185129198Scognet{
186129198Scognet	struct thread *td;
187129198Scognet	u_int fault_pc;
188129198Scognet	int fault_instruction;
189129198Scognet	int fault_code;
190129198Scognet	int coprocessor;
191129198Scognet	struct undefined_handler *uh;
192282779Sandrew	int error;
193129198Scognet#ifdef VERBOSE_ARM32
194129198Scognet	int s;
195129198Scognet#endif
196151316Sdavidxu	ksiginfo_t ksi;
197129198Scognet
198129198Scognet	/* Enable interrupts if they were enabled before the exception. */
199271398Sandrew	if (__predict_true(frame->tf_spsr & PSR_I) == 0)
200271398Sandrew		enable_interrupts(PSR_I);
201271398Sandrew	if (__predict_true(frame->tf_spsr & PSR_F) == 0)
202271398Sandrew		enable_interrupts(PSR_F);
203129198Scognet
204170291Sattilio	PCPU_INC(cnt.v_trap);
205129198Scognet
206129198Scognet	fault_pc = frame->tf_pc;
207129198Scognet
208236991Simp	/*
209236991Simp	 * Get the current thread/proc structure or thread0/proc0 if there is
210129198Scognet	 * none.
211129198Scognet	 */
212129198Scognet	td = curthread == NULL ? &thread0 : curthread;
213129198Scognet
214282779Sandrew	coprocessor = 0;
215282779Sandrew	if ((frame->tf_spsr & PSR_T) == 0) {
216282779Sandrew		/*
217282779Sandrew		 * Make sure the program counter is correctly aligned so we
218282779Sandrew		 * don't take an alignment fault trying to read the opcode.
219282779Sandrew		 */
220282779Sandrew		if (__predict_false((fault_pc & 3) != 0)) {
221282779Sandrew			ksiginfo_init_trap(&ksi);
222282779Sandrew			ksi.ksi_signo = SIGILL;
223282779Sandrew			ksi.ksi_code = ILL_ILLADR;
224282779Sandrew			ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
225282779Sandrew			trapsignal(td, &ksi);
226282779Sandrew			userret(td, frame);
227282779Sandrew			return;
228282779Sandrew		}
229282779Sandrew
230282779Sandrew		/*
231282779Sandrew		 * Should use fuword() here .. but in the interests of
232282779Sandrew		 * squeezing every bit of speed we will just use ReadWord().
233282779Sandrew		 * We know the instruction can be read as was just executed
234282779Sandrew		 * so this will never fail unless the kernel is screwed up
235282779Sandrew		 * in which case it does not really matter does it ?
236282779Sandrew		 */
237282779Sandrew
238282779Sandrew		fault_instruction = *(u_int32_t *)fault_pc;
239282779Sandrew
240282779Sandrew		/* Check for coprocessor instruction */
241282779Sandrew
242282779Sandrew		/*
243282779Sandrew		 * According to the datasheets you only need to look at bit
244282779Sandrew		 * 27 of the instruction to tell the difference between and
245282779Sandrew		 * undefined instruction and a coprocessor instruction
246282779Sandrew		 * following an undefined instruction trap.
247282779Sandrew		 */
248282779Sandrew
249282779Sandrew		if (ARM_COPROC_INSN(fault_instruction))
250282779Sandrew			coprocessor = ARM_COPROC(fault_instruction);
251282779Sandrew		else {          /* check for special instructions */
252282779Sandrew			if (ARM_VFP_INSN(fault_instruction))
253282779Sandrew				coprocessor = COPROC_VFP; /* vfp / simd */
254282779Sandrew		}
255282779Sandrew	} else {
256282779Sandrew#if __ARM_ARCH >= 7
257282779Sandrew		fault_instruction = *(uint16_t *)fault_pc;
258282779Sandrew		if (THUMB_32BIT_INSN(fault_instruction)) {
259282779Sandrew			fault_instruction <<= 16;
260282779Sandrew			fault_instruction |= *(uint16_t *)(fault_pc + 2);
261282779Sandrew
262282779Sandrew			/*
263282779Sandrew			 * Is it a Coprocessor, Advanced SIMD, or
264282779Sandrew			 * Floating-point instruction.
265282779Sandrew			 */
266282779Sandrew			if (THUMB_COPROC_INSN(fault_instruction)) {
267282779Sandrew				if (THUMB_COPROC_UNDEFINED(fault_instruction)) {
268282779Sandrew					/* undefined insn */
269282779Sandrew				} else if (THUMB_VFP_INSN(fault_instruction))
270282779Sandrew					coprocessor = COPROC_VFP;
271282779Sandrew				else
272282779Sandrew					coprocessor =
273282779Sandrew					    THUMB_COPROC(fault_instruction);
274282779Sandrew			}
275282779Sandrew		}
276282779Sandrew#else
277282779Sandrew		/*
278282779Sandrew		 * No support for Thumb-2 on this cpu
279282779Sandrew		 */
280151316Sdavidxu		ksiginfo_init_trap(&ksi);
281151316Sdavidxu		ksi.ksi_signo = SIGILL;
282151316Sdavidxu		ksi.ksi_code = ILL_ILLADR;
283151316Sdavidxu		ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
284151316Sdavidxu		trapsignal(td, &ksi);
285156166Scognet		userret(td, frame);
286129198Scognet		return;
287129198Scognet#endif
288239268Sgonzo	}
289129198Scognet
290129198Scognet	if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
291129198Scognet		/*
292129198Scognet		 * Modify the fault_code to reflect the USR/SVC state at
293129198Scognet		 * time of fault.
294129198Scognet		 */
295129198Scognet		fault_code = FAULT_USER;
296129198Scognet		td->td_frame = frame;
297129198Scognet	} else
298129198Scognet		fault_code = 0;
299129198Scognet
300129198Scognet	/* OK this is were we do something about the instruction. */
301129198Scognet	LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link)
302129198Scognet	    if (uh->uh_handler(fault_pc, fault_instruction, frame,
303129198Scognet			       fault_code) == 0)
304129198Scognet		    break;
305129198Scognet
306282779Sandrew	if (fault_code & FAULT_USER) {
307282779Sandrew		/* TODO: No support for ptrace from Thumb-2 */
308282779Sandrew		if ((frame->tf_spsr & PSR_T) == 0 &&
309282779Sandrew		    fault_instruction == PTRACE_BREAKPOINT) {
310282779Sandrew			PROC_LOCK(td->td_proc);
311282779Sandrew			_PHOLD(td->td_proc);
312282779Sandrew			error = ptrace_clear_single_step(td);
313282779Sandrew			_PRELE(td->td_proc);
314282779Sandrew			PROC_UNLOCK(td->td_proc);
315282779Sandrew			if (error != 0) {
316282779Sandrew				ksiginfo_init_trap(&ksi);
317282779Sandrew				ksi.ksi_signo = SIGILL;
318282779Sandrew				ksi.ksi_code = ILL_ILLOPC;
319282779Sandrew				ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
320282779Sandrew				trapsignal(td, &ksi);
321282779Sandrew			}
322282779Sandrew			return;
323282779Sandrew		}
324140001Scognet	}
325140001Scognet
326137940Scognet	if (uh == NULL && (fault_code & FAULT_USER)) {
327129198Scognet		/* Fault has not been handled */
328151316Sdavidxu		ksiginfo_init_trap(&ksi);
329151316Sdavidxu		ksi.ksi_signo = SIGILL;
330151316Sdavidxu		ksi.ksi_code = ILL_ILLOPC;
331151316Sdavidxu		ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc;
332151316Sdavidxu		trapsignal(td, &ksi);
333129198Scognet	}
334129198Scognet
335137940Scognet	if ((fault_code & FAULT_USER) == 0) {
336137940Scognet		if (fault_instruction == KERNEL_BREAKPOINT) {
337137940Scognet#ifdef KDB
338155241Simp			kdb_trap(T_BREAKPOINT, 0, frame);
339137940Scognet#else
340155241Simp			printf("No debugger in kernel.\n");
341137940Scognet#endif
342155241Simp			return;
343279667Sandrew		}
344279667Sandrew		else
345137940Scognet			panic("Undefined instruction in kernel.\n");
346140001Scognet	}
347129198Scognet
348156166Scognet	userret(td, frame);
349129198Scognet}
350