xref: /freebsd/sys/riscv/riscv/trap.c (revision 780fb4a2)
1 /*-
2  * Copyright (c) 2015-2017 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * Portions of this software were developed by SRI International and the
6  * University of Cambridge Computer Laboratory under DARPA/AFRL contract
7  * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Portions of this software were developed by the University of Cambridge
10  * Computer Laboratory as part of the CTSRD Project, with support from the
11  * UK Higher Education Innovation Fund (HEIF).
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <sys/pioctl.h>
44 #include <sys/bus.h>
45 #include <sys/proc.h>
46 #include <sys/ptrace.h>
47 #include <sys/syscall.h>
48 #include <sys/sysent.h>
49 #ifdef KDB
50 #include <sys/kdb.h>
51 #endif
52 
53 #include <vm/vm.h>
54 #include <vm/pmap.h>
55 #include <vm/vm_kern.h>
56 #include <vm/vm_map.h>
57 #include <vm/vm_param.h>
58 #include <vm/vm_extern.h>
59 
60 #include <machine/frame.h>
61 #include <machine/pcb.h>
62 #include <machine/pcpu.h>
63 
64 #include <machine/resource.h>
65 #include <machine/intr.h>
66 
67 #ifdef KDTRACE_HOOKS
68 #include <sys/dtrace_bsd.h>
69 #endif
70 
71 int (*dtrace_invop_jump_addr)(struct trapframe *);
72 
73 extern register_t fsu_intr_fault;
74 
75 /* Called from exception.S */
76 void do_trap_supervisor(struct trapframe *);
77 void do_trap_user(struct trapframe *);
78 
79 static __inline void
80 call_trapsignal(struct thread *td, int sig, int code, void *addr)
81 {
82 	ksiginfo_t ksi;
83 
84 	ksiginfo_init_trap(&ksi);
85 	ksi.ksi_signo = sig;
86 	ksi.ksi_code = code;
87 	ksi.ksi_addr = addr;
88 	trapsignal(td, &ksi);
89 }
90 
91 int
92 cpu_fetch_syscall_args(struct thread *td)
93 {
94 	struct proc *p;
95 	register_t *ap;
96 	struct syscall_args *sa;
97 	int nap;
98 
99 	nap = NARGREG;
100 	p = td->td_proc;
101 	sa = &td->td_sa;
102 	ap = &td->td_frame->tf_a[0];
103 
104 	sa->code = td->td_frame->tf_t[0];
105 
106 	if (sa->code == SYS_syscall || sa->code == SYS___syscall) {
107 		sa->code = *ap++;
108 		nap--;
109 	}
110 
111 	if (p->p_sysent->sv_mask)
112 		sa->code &= p->p_sysent->sv_mask;
113 	if (sa->code >= p->p_sysent->sv_size)
114 		sa->callp = &p->p_sysent->sv_table[0];
115 	else
116 		sa->callp = &p->p_sysent->sv_table[sa->code];
117 
118 	sa->narg = sa->callp->sy_narg;
119 	memcpy(sa->args, ap, nap * sizeof(register_t));
120 	if (sa->narg > nap)
121 		panic("TODO: Could we have more then %d args?", NARGREG);
122 
123 	td->td_retval[0] = 0;
124 	td->td_retval[1] = 0;
125 
126 	return (0);
127 }
128 
129 #include "../../kern/subr_syscall.c"
130 
131 static void
132 dump_regs(struct trapframe *frame)
133 {
134 	int n;
135 	int i;
136 
137 	n = (sizeof(frame->tf_t) / sizeof(frame->tf_t[0]));
138 	for (i = 0; i < n; i++)
139 		printf("t[%d] == 0x%016lx\n", i, frame->tf_t[i]);
140 
141 	n = (sizeof(frame->tf_s) / sizeof(frame->tf_s[0]));
142 	for (i = 0; i < n; i++)
143 		printf("s[%d] == 0x%016lx\n", i, frame->tf_s[i]);
144 
145 	n = (sizeof(frame->tf_a) / sizeof(frame->tf_a[0]));
146 	for (i = 0; i < n; i++)
147 		printf("a[%d] == 0x%016lx\n", i, frame->tf_a[i]);
148 
149 	printf("sepc == 0x%016lx\n", frame->tf_sepc);
150 	printf("sstatus == 0x%016lx\n", frame->tf_sstatus);
151 }
152 
153 static void
154 svc_handler(struct trapframe *frame)
155 {
156 	struct thread *td;
157 	int error;
158 
159 	td = curthread;
160 	td->td_frame = frame;
161 
162 	error = syscallenter(td);
163 	syscallret(td, error);
164 }
165 
166 static void
167 data_abort(struct trapframe *frame, int lower)
168 {
169 	struct vm_map *map;
170 	uint64_t sbadaddr;
171 	struct thread *td;
172 	struct pcb *pcb;
173 	vm_prot_t ftype;
174 	vm_offset_t va;
175 	struct proc *p;
176 	int ucode;
177 	int error;
178 	int sig;
179 
180 #ifdef KDB
181 	if (kdb_active) {
182 		kdb_reenter();
183 		return;
184 	}
185 #endif
186 
187 	td = curthread;
188 	pcb = td->td_pcb;
189 	sbadaddr = frame->tf_sbadaddr;
190 
191 	p = td->td_proc;
192 
193 	if (lower)
194 		map = &td->td_proc->p_vmspace->vm_map;
195 	else {
196 		/* The top bit tells us which range to use */
197 		if ((sbadaddr >> 63) == 1)
198 			map = kernel_map;
199 		else
200 			map = &td->td_proc->p_vmspace->vm_map;
201 	}
202 
203 	va = trunc_page(sbadaddr);
204 
205 	if ((frame->tf_scause == EXCP_FAULT_STORE) ||
206 	    (frame->tf_scause == EXCP_STORE_PAGE_FAULT)) {
207 		ftype = (VM_PROT_READ | VM_PROT_WRITE);
208 	} else {
209 		ftype = (VM_PROT_READ);
210 	}
211 
212 	if (map != kernel_map) {
213 		/*
214 		 * Keep swapout from messing with us during this
215 		 *	critical time.
216 		 */
217 		PROC_LOCK(p);
218 		++p->p_lock;
219 		PROC_UNLOCK(p);
220 
221 		/* Fault in the user page: */
222 		error = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
223 
224 		PROC_LOCK(p);
225 		--p->p_lock;
226 		PROC_UNLOCK(p);
227 	} else {
228 		/*
229 		 * Don't have to worry about process locking or stacks in the
230 		 * kernel.
231 		 */
232 		error = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
233 	}
234 
235 	if (error != KERN_SUCCESS) {
236 		if (lower) {
237 			sig = SIGSEGV;
238 			if (error == KERN_PROTECTION_FAILURE)
239 				ucode = SEGV_ACCERR;
240 			else
241 				ucode = SEGV_MAPERR;
242 			call_trapsignal(td, sig, ucode, (void *)sbadaddr);
243 		} else {
244 			if (td->td_intr_nesting_level == 0 &&
245 			    pcb->pcb_onfault != 0) {
246 				frame->tf_a[0] = error;
247 				frame->tf_sepc = pcb->pcb_onfault;
248 				return;
249 			}
250 			dump_regs(frame);
251 			panic("vm_fault failed: %lx, va 0x%016lx",
252 				frame->tf_sepc, sbadaddr);
253 		}
254 	}
255 
256 	if (lower)
257 		userret(td, frame);
258 }
259 
260 void
261 do_trap_supervisor(struct trapframe *frame)
262 {
263 	uint64_t exception;
264 	uint64_t sstatus;
265 
266 	/* Ensure we came from supervisor mode, interrupts disabled */
267 	__asm __volatile("csrr %0, sstatus" : "=&r" (sstatus));
268 	KASSERT((sstatus & (SSTATUS_SPP | SSTATUS_SIE)) == SSTATUS_SPP,
269 			("We must came from S mode with interrupts disabled"));
270 
271 	exception = (frame->tf_scause & EXCP_MASK);
272 	if (frame->tf_scause & EXCP_INTR) {
273 		/* Interrupt */
274 		riscv_cpu_intr(frame);
275 		return;
276 	}
277 
278 #ifdef KDTRACE_HOOKS
279 	if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, exception))
280 		return;
281 #endif
282 
283 	CTR3(KTR_TRAP, "do_trap_supervisor: curthread: %p, sepc: %lx, frame: %p",
284 	    curthread, frame->tf_sepc, frame);
285 
286 	switch(exception) {
287 	case EXCP_FAULT_LOAD:
288 	case EXCP_FAULT_STORE:
289 	case EXCP_FAULT_FETCH:
290 	case EXCP_STORE_PAGE_FAULT:
291 	case EXCP_LOAD_PAGE_FAULT:
292 		data_abort(frame, 0);
293 		break;
294 	case EXCP_BREAKPOINT:
295 #ifdef KDTRACE_HOOKS
296 		if (dtrace_invop_jump_addr != 0) {
297 			dtrace_invop_jump_addr(frame);
298 			break;
299 		}
300 #endif
301 #ifdef KDB
302 		kdb_trap(exception, 0, frame);
303 #else
304 		dump_regs(frame);
305 		panic("No debugger in kernel.\n");
306 #endif
307 		break;
308 	case EXCP_ILLEGAL_INSTRUCTION:
309 		dump_regs(frame);
310 		panic("Illegal instruction at 0x%016lx\n", frame->tf_sepc);
311 		break;
312 	default:
313 		dump_regs(frame);
314 		panic("Unknown kernel exception %x badaddr %lx\n",
315 			exception, frame->tf_sbadaddr);
316 	}
317 }
318 
319 void
320 do_trap_user(struct trapframe *frame)
321 {
322 	uint64_t exception;
323 	struct thread *td;
324 	uint64_t sstatus;
325 	struct pcb *pcb;
326 
327 	td = curthread;
328 	td->td_frame = frame;
329 	pcb = td->td_pcb;
330 
331 	/* Ensure we came from usermode, interrupts disabled */
332 	__asm __volatile("csrr %0, sstatus" : "=&r" (sstatus));
333 	KASSERT((sstatus & (SSTATUS_SPP | SSTATUS_SIE)) == 0,
334 			("We must came from U mode with interrupts disabled"));
335 
336 	exception = (frame->tf_scause & EXCP_MASK);
337 	if (frame->tf_scause & EXCP_INTR) {
338 		/* Interrupt */
339 		riscv_cpu_intr(frame);
340 		return;
341 	}
342 
343 	CTR3(KTR_TRAP, "do_trap_user: curthread: %p, sepc: %lx, frame: %p",
344 	    curthread, frame->tf_sepc, frame);
345 
346 	switch(exception) {
347 	case EXCP_FAULT_LOAD:
348 	case EXCP_FAULT_STORE:
349 	case EXCP_FAULT_FETCH:
350 	case EXCP_STORE_PAGE_FAULT:
351 	case EXCP_LOAD_PAGE_FAULT:
352 	case EXCP_INST_PAGE_FAULT:
353 		data_abort(frame, 1);
354 		break;
355 	case EXCP_USER_ECALL:
356 		frame->tf_sepc += 4;	/* Next instruction */
357 		svc_handler(frame);
358 		break;
359 	case EXCP_ILLEGAL_INSTRUCTION:
360 #ifdef FPE
361 		if ((pcb->pcb_fpflags & PCB_FP_STARTED) == 0) {
362 			/*
363 			 * May be a FPE trap. Enable FPE usage
364 			 * for this thread and try again.
365 			 */
366 			frame->tf_sstatus |= SSTATUS_FS_INITIAL;
367 			pcb->pcb_fpflags |= PCB_FP_STARTED;
368 			break;
369 		}
370 #endif
371 		call_trapsignal(td, SIGILL, ILL_ILLTRP, (void *)frame->tf_sepc);
372 		userret(td, frame);
373 		break;
374 	case EXCP_BREAKPOINT:
375 		call_trapsignal(td, SIGTRAP, TRAP_BRKPT, (void *)frame->tf_sepc);
376 		userret(td, frame);
377 		break;
378 	default:
379 		dump_regs(frame);
380 		panic("Unknown userland exception %x, badaddr %lx\n",
381 			exception, frame->tf_sbadaddr);
382 	}
383 }
384