trap.c revision 285315
1/*-
2 * Copyright (c) 2014 Andrew Turner
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/arm64/arm64/trap.c 285315 2015-07-09 13:07:12Z andrew $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/mutex.h>
36#include <sys/pioctl.h>
37#include <sys/proc.h>
38#include <sys/ptrace.h>
39#include <sys/syscall.h>
40#include <sys/sysent.h>
41#ifdef KDB
42#include <sys/kdb.h>
43#endif
44
45#include <vm/vm.h>
46#include <vm/pmap.h>
47#include <vm/vm_kern.h>
48#include <vm/vm_map.h>
49#include <vm/vm_param.h>
50#include <vm/vm_extern.h>
51
52#include <machine/frame.h>
53#include <machine/pcb.h>
54#include <machine/pcpu.h>
55#include <machine/vmparam.h>
56
57#ifdef KDTRACE_HOOKS
58#include <sys/dtrace_bsd.h>
59#endif
60
61#ifdef VFP
62#include <machine/vfp.h>
63#endif
64
65#ifdef KDB
66#include <machine/db_machdep.h>
67#endif
68
69#ifdef DDB
70#include <ddb/db_output.h>
71#endif
72
73extern register_t fsu_intr_fault;
74
75/* Called from exception.S */
76void do_el1h_sync(struct trapframe *);
77void do_el0_sync(struct trapframe *);
78void do_el0_error(struct trapframe *);
79
80int (*dtrace_invop_jump_addr)(struct trapframe *);
81
82static __inline void
83call_trapsignal(struct thread *td, int sig, int code, void *addr)
84{
85	ksiginfo_t ksi;
86
87	ksiginfo_init_trap(&ksi);
88	ksi.ksi_signo = sig;
89	ksi.ksi_code = code;
90	ksi.ksi_addr = addr;
91	trapsignal(td, &ksi);
92}
93
94int
95cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa)
96{
97	struct proc *p;
98	register_t *ap;
99	int nap;
100
101	nap = 8;
102	p = td->td_proc;
103	ap = td->td_frame->tf_x;
104
105	sa->code = td->td_frame->tf_x[8];
106
107	if (sa->code == SYS_syscall || sa->code == SYS___syscall) {
108		sa->code = *ap++;
109		nap--;
110	}
111
112	if (p->p_sysent->sv_mask)
113		sa->code &= p->p_sysent->sv_mask;
114	if (sa->code >= p->p_sysent->sv_size)
115		sa->callp = &p->p_sysent->sv_table[0];
116	else
117		sa->callp = &p->p_sysent->sv_table[sa->code];
118
119	sa->narg = sa->callp->sy_narg;
120	memcpy(sa->args, ap, nap * sizeof(register_t));
121	if (sa->narg > nap)
122		panic("TODO: Could we have more then 8 args?");
123
124	td->td_retval[0] = 0;
125	td->td_retval[1] = 0;
126
127	return (0);
128}
129
130#include "../../kern/subr_syscall.c"
131
132static void
133svc_handler(struct trapframe *frame)
134{
135	struct syscall_args sa;
136	struct thread *td;
137	int error;
138
139	td = curthread;
140	td->td_frame = frame;
141
142	error = syscallenter(td, &sa);
143	syscallret(td, error, &sa);
144}
145
146static void
147data_abort(struct trapframe *frame, uint64_t esr, int lower)
148{
149	struct vm_map *map;
150	struct thread *td;
151	struct proc *p;
152	struct pcb *pcb;
153	vm_prot_t ftype;
154	vm_offset_t va;
155	uint64_t far;
156	int error, sig, ucode;
157
158	td = curthread;
159	pcb = td->td_pcb;
160
161	/*
162	 * Special case for fuswintr and suswintr. These can't sleep so
163	 * handle them early on in the trap handler.
164	 */
165	if (__predict_false(pcb->pcb_onfault == (vm_offset_t)&fsu_intr_fault)) {
166		frame->tf_elr = pcb->pcb_onfault;
167		return;
168	}
169
170	far = READ_SPECIALREG(far_el1);
171	p = td->td_proc;
172
173	if (lower)
174		map = &td->td_proc->p_vmspace->vm_map;
175	else {
176		/* The top bit tells us which range to use */
177		if ((far >> 63) == 1)
178			map = kernel_map;
179		else
180			map = &td->td_proc->p_vmspace->vm_map;
181	}
182
183	va = trunc_page(far);
184	ftype = ((esr >> 6) & 1) ? VM_PROT_READ | VM_PROT_WRITE : VM_PROT_READ;
185
186	if (map != kernel_map) {
187		/*
188		 * Keep swapout from messing with us during this
189		 *	critical time.
190		 */
191		PROC_LOCK(p);
192		++p->p_lock;
193		PROC_UNLOCK(p);
194
195		/* Fault in the user page: */
196		error = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
197
198		PROC_LOCK(p);
199		--p->p_lock;
200		PROC_UNLOCK(p);
201	} else {
202		/*
203		 * Don't have to worry about process locking or stacks in the
204		 * kernel.
205		 */
206		error = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
207	}
208
209	if (error != KERN_SUCCESS) {
210		if (lower) {
211			sig = SIGSEGV;
212			if (error == KERN_PROTECTION_FAILURE)
213				ucode = SEGV_ACCERR;
214			else
215				ucode = SEGV_MAPERR;
216			call_trapsignal(td, sig, ucode, (void *)far);
217		} else {
218			if (td->td_intr_nesting_level == 0 &&
219			    pcb->pcb_onfault != 0) {
220				frame->tf_x[0] = error;
221				frame->tf_elr = pcb->pcb_onfault;
222				return;
223			}
224			panic("vm_fault failed: %lx", frame->tf_elr);
225		}
226	}
227
228	if (lower)
229		userret(td, frame);
230}
231
232void
233do_el1h_sync(struct trapframe *frame)
234{
235	uint32_t exception;
236	uint64_t esr;
237
238	/* Read the esr register to get the exception details */
239	esr = READ_SPECIALREG(esr_el1);
240	exception = ESR_ELx_EXCEPTION(esr);
241
242#ifdef KDTRACE_HOOKS
243	if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, exception))
244		return;
245#endif
246
247	/*
248	 * Sanity check we are in an exception er can handle. The IL bit
249	 * is used to indicate the instruction length, except in a few
250	 * exceptions described in the ARMv8 ARM.
251	 *
252	 * It is unclear in some cases if the bit is implementation defined.
253	 * The Foundation Model and QEMU disagree on if the IL bit should
254	 * be set when we are in a data fault from the same EL and the ISV
255	 * bit (bit 24) is also set.
256	 */
257	KASSERT((esr & ESR_ELx_IL) == ESR_ELx_IL ||
258	    (exception == EXCP_DATA_ABORT && ((esr & ISS_DATA_ISV) == 0)),
259	    ("Invalid instruction length in exception"));
260
261	CTR4(KTR_TRAP,
262	    "do_el1_sync: curthread: %p, esr %lx, elr: %lx, frame: %p",
263	    curthread, esr, frame->tf_elr, frame);
264
265	switch(exception) {
266	case EXCP_FP_SIMD:
267	case EXCP_TRAP_FP:
268		panic("VFP exception in the kernel");
269	case EXCP_DATA_ABORT:
270		data_abort(frame, esr, 0);
271		break;
272	case EXCP_BRK:
273#ifdef KDTRACE_HOOKS
274		if ((esr & ESR_ELx_ISS_MASK) == 0x40d && \
275		    dtrace_invop_jump_addr != 0) {
276			dtrace_invop_jump_addr(frame);
277			break;
278		}
279#endif
280	case EXCP_WATCHPT_EL1:
281	case EXCP_SOFTSTP_EL1:
282#ifdef KDB
283		kdb_trap(exception, 0, frame);
284#else
285		panic("No debugger in kernel.\n");
286#endif
287		break;
288	default:
289		panic("Unknown kernel exception %x esr_el1 %lx\n", exception,
290		    esr);
291	}
292}
293
294void
295do_el0_sync(struct trapframe *frame)
296{
297	uint32_t exception;
298	uint64_t esr;
299
300	/* Check we have a sane environment when entering from userland */
301	KASSERT((uintptr_t)get_pcpu() >= VM_MIN_KERNEL_ADDRESS,
302	    ("Invalid pcpu address from userland: %p (tpidr %lx)",
303	     get_pcpu(), READ_SPECIALREG(tpidr_el1)));
304
305	esr = READ_SPECIALREG(esr_el1);
306	exception = ESR_ELx_EXCEPTION(esr);
307
308	CTR4(KTR_TRAP,
309	    "do_el0_sync: curthread: %p, esr %lx, elr: %lx, frame: %p",
310	    curthread, esr, frame->tf_elr, frame);
311
312	switch(exception) {
313	case EXCP_FP_SIMD:
314	case EXCP_TRAP_FP:
315#ifdef VFP
316		vfp_restore_state();
317#else
318		panic("VFP exception in userland");
319#endif
320		break;
321	case EXCP_SVC:
322		svc_handler(frame);
323		break;
324	case EXCP_INSN_ABORT_L:
325	case EXCP_DATA_ABORT_L:
326		data_abort(frame, esr, 1);
327		break;
328	default:
329		panic("Unknown userland exception %x esr_el1 %lx\n", exception,
330		    esr);
331	}
332}
333
334void
335do_el0_error(struct trapframe *frame)
336{
337
338	panic("do_el0_error");
339}
340
341