trap.c revision 302408
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: stable/11/sys/arm64/arm64/trap.c 302084 2016-06-22 12:05:08Z 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
56#ifdef KDTRACE_HOOKS
57#include <sys/dtrace_bsd.h>
58#endif
59
60#ifdef VFP
61#include <machine/vfp.h>
62#endif
63
64#ifdef KDB
65#include <machine/db_machdep.h>
66#endif
67
68#ifdef DDB
69#include <ddb/db_output.h>
70#endif
71
72extern register_t fsu_intr_fault;
73
74/* Called from exception.S */
75void do_el1h_sync(struct trapframe *);
76void do_el0_sync(struct trapframe *);
77void do_el0_error(struct trapframe *);
78static void print_registers(struct trapframe *frame);
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("ARM64TODO: Could we have more than 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
141	error = syscallenter(td, &sa);
142	syscallret(td, error, &sa);
143}
144
145static void
146data_abort(struct trapframe *frame, uint64_t esr, uint64_t far, int lower)
147{
148	struct vm_map *map;
149	struct thread *td;
150	struct proc *p;
151	struct pcb *pcb;
152	vm_prot_t ftype;
153	vm_offset_t va;
154	int error, sig, ucode;
155
156	/*
157	 * According to the ARMv8-A rev. A.g, B2.10.5 "Load-Exclusive
158	 * and Store-Exclusive instruction usage restrictions", state
159	 * of the exclusive monitors after data abort exception is unknown.
160	 */
161	clrex();
162
163#ifdef KDB
164	if (kdb_active) {
165		kdb_reenter();
166		return;
167	}
168#endif
169
170	td = curthread;
171	pcb = td->td_pcb;
172
173	/*
174	 * Special case for fuswintr and suswintr. These can't sleep so
175	 * handle them early on in the trap handler.
176	 */
177	if (__predict_false(pcb->pcb_onfault == (vm_offset_t)&fsu_intr_fault)) {
178		frame->tf_elr = pcb->pcb_onfault;
179		return;
180	}
181
182	KASSERT(td->td_md.md_spinlock_count == 0,
183	    ("data abort with spinlock held"));
184	if (td->td_critnest != 0 || WITNESS_CHECK(WARN_SLEEPOK |
185	    WARN_GIANTOK, NULL, "Kernel page fault") != 0) {
186		print_registers(frame);
187		printf(" far: %16lx\n", far);
188		printf(" esr:         %.8lx\n", esr);
189		panic("data abort in critical section or under mutex");
190	}
191
192	p = td->td_proc;
193	if (lower)
194		map = &p->p_vmspace->vm_map;
195	else {
196		/* The top bit tells us which range to use */
197		if ((far >> 63) == 1)
198			map = kernel_map;
199		else
200			map = &p->p_vmspace->vm_map;
201	}
202
203	va = trunc_page(far);
204	ftype = ((esr >> 6) & 1) ? VM_PROT_READ | VM_PROT_WRITE : VM_PROT_READ;
205
206	/* Fault in the page. */
207	error = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
208	if (error != KERN_SUCCESS) {
209		if (lower) {
210			sig = SIGSEGV;
211			if (error == KERN_PROTECTION_FAILURE)
212				ucode = SEGV_ACCERR;
213			else
214				ucode = SEGV_MAPERR;
215			call_trapsignal(td, sig, ucode, (void *)far);
216		} else {
217			if (td->td_intr_nesting_level == 0 &&
218			    pcb->pcb_onfault != 0) {
219				frame->tf_x[0] = error;
220				frame->tf_elr = pcb->pcb_onfault;
221				return;
222			}
223
224			printf("Fatal data abort:\n");
225			print_registers(frame);
226			printf(" far: %16lx\n", far);
227			printf(" esr:         %.8lx\n", esr);
228
229#ifdef KDB
230			if (debugger_on_panic || kdb_active)
231				if (kdb_trap(ESR_ELx_EXCEPTION(esr), 0, frame))
232					return;
233#endif
234			panic("vm_fault failed: %lx", frame->tf_elr);
235		}
236	}
237
238	if (lower)
239		userret(td, frame);
240}
241
242static void
243print_registers(struct trapframe *frame)
244{
245	u_int reg;
246
247	for (reg = 0; reg < 31; reg++) {
248		printf(" %sx%d: %16lx\n", (reg < 10) ? " " : "", reg,
249		    frame->tf_x[reg]);
250	}
251	printf("  sp: %16lx\n", frame->tf_sp);
252	printf("  lr: %16lx\n", frame->tf_lr);
253	printf(" elr: %16lx\n", frame->tf_elr);
254	printf("spsr: %16lx\n", frame->tf_spsr);
255}
256
257void
258do_el1h_sync(struct trapframe *frame)
259{
260	uint32_t exception;
261	uint64_t esr, far;
262
263	/* Read the esr register to get the exception details */
264	esr = READ_SPECIALREG(esr_el1);
265	exception = ESR_ELx_EXCEPTION(esr);
266
267#ifdef KDTRACE_HOOKS
268	if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, exception))
269		return;
270#endif
271
272	CTR4(KTR_TRAP,
273	    "do_el1_sync: curthread: %p, esr %lx, elr: %lx, frame: %p",
274	    curthread, esr, frame->tf_elr, frame);
275
276	switch(exception) {
277	case EXCP_FP_SIMD:
278	case EXCP_TRAP_FP:
279		print_registers(frame);
280		printf(" esr:         %.8lx\n", esr);
281		panic("VFP exception in the kernel");
282	case EXCP_DATA_ABORT:
283		far = READ_SPECIALREG(far_el1);
284		intr_enable();
285		data_abort(frame, esr, far, 0);
286		break;
287	case EXCP_BRK:
288#ifdef KDTRACE_HOOKS
289		if ((esr & ESR_ELx_ISS_MASK) == 0x40d && \
290		    dtrace_invop_jump_addr != 0) {
291			dtrace_invop_jump_addr(frame);
292			break;
293		}
294#endif
295		/* FALLTHROUGH */
296	case EXCP_WATCHPT_EL1:
297	case EXCP_SOFTSTP_EL1:
298#ifdef KDB
299		kdb_trap(exception, 0, frame);
300#else
301		panic("No debugger in kernel.\n");
302#endif
303		break;
304	default:
305		print_registers(frame);
306		panic("Unknown kernel exception %x esr_el1 %lx\n", exception,
307		    esr);
308	}
309}
310
311/*
312 * The attempted execution of an instruction bit pattern that has no allocated
313 * instruction results in an exception with an unknown reason.
314 */
315static void
316el0_excp_unknown(struct trapframe *frame, uint64_t far)
317{
318	struct thread *td;
319
320	td = curthread;
321	call_trapsignal(td, SIGILL, ILL_ILLTRP, (void *)far);
322	userret(td, frame);
323}
324
325void
326do_el0_sync(struct trapframe *frame)
327{
328	struct thread *td;
329	uint32_t exception;
330	uint64_t esr, far;
331
332	/* Check we have a sane environment when entering from userland */
333	KASSERT((uintptr_t)get_pcpu() >= VM_MIN_KERNEL_ADDRESS,
334	    ("Invalid pcpu address from userland: %p (tpidr %lx)",
335	     get_pcpu(), READ_SPECIALREG(tpidr_el1)));
336
337	td = curthread;
338	td->td_frame = frame;
339
340	esr = READ_SPECIALREG(esr_el1);
341	exception = ESR_ELx_EXCEPTION(esr);
342	switch (exception) {
343	case EXCP_UNKNOWN:
344	case EXCP_INSN_ABORT_L:
345	case EXCP_DATA_ABORT_L:
346	case EXCP_DATA_ABORT:
347		far = READ_SPECIALREG(far_el1);
348	}
349	intr_enable();
350
351	CTR4(KTR_TRAP,
352	    "do_el0_sync: curthread: %p, esr %lx, elr: %lx, frame: %p",
353	    curthread, esr, frame->tf_elr, frame);
354
355	switch(exception) {
356	case EXCP_FP_SIMD:
357	case EXCP_TRAP_FP:
358#ifdef VFP
359		vfp_restore_state();
360#else
361		panic("VFP exception in userland");
362#endif
363		break;
364	case EXCP_SVC:
365		svc_handler(frame);
366		break;
367	case EXCP_INSN_ABORT_L:
368	case EXCP_DATA_ABORT_L:
369	case EXCP_DATA_ABORT:
370		data_abort(frame, esr, far, 1);
371		break;
372	case EXCP_UNKNOWN:
373		el0_excp_unknown(frame, far);
374		break;
375	case EXCP_SP_ALIGN:
376		call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_sp);
377		userret(td, frame);
378		break;
379	case EXCP_PC_ALIGN:
380		call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_elr);
381		userret(td, frame);
382		break;
383	case EXCP_BRK:
384		call_trapsignal(td, SIGTRAP, TRAP_BRKPT, (void *)frame->tf_elr);
385		userret(td, frame);
386		break;
387	case EXCP_SOFTSTP_EL0:
388		td->td_frame->tf_spsr &= ~PSR_SS;
389		td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP;
390		WRITE_SPECIALREG(MDSCR_EL1,
391		    READ_SPECIALREG(MDSCR_EL1) & ~DBG_MDSCR_SS);
392		call_trapsignal(td, SIGTRAP, TRAP_TRACE,
393		    (void *)frame->tf_elr);
394		userret(td, frame);
395		break;
396	default:
397		print_registers(frame);
398		panic("Unknown userland exception %x esr_el1 %lx\n", exception,
399		    esr);
400	}
401}
402
403void
404do_el0_error(struct trapframe *frame)
405{
406
407	panic("ARM64TODO: do_el0_error");
408}
409
410