1/*	$OpenBSD: trap.c,v 1.19 2023/11/28 09:10:18 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2020 Shivam Waghela <shivamwaghela@gmail.com>
5 * Copyright (c) 2020 Brian Bamsch <bbamsch@google.com>
6 * Copyright (c) 2020 Mengshi Li <mengshi.li.mars@gmail.com>
7 * Copyright (c) 2015 Dale Rahn <drahn@dalerahn.com>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22#include <sys/param.h>
23#include <sys/systm.h>
24#include <sys/proc.h>
25#include <sys/user.h>
26#include <sys/signalvar.h>
27#include <sys/siginfo.h>
28
29#include <machine/riscvreg.h>
30#include <machine/syscall.h>
31#include <machine/db_machdep.h>
32
33/* Called from exception.S */
34void do_trap_supervisor(struct trapframe *);
35void do_trap_user(struct trapframe *);
36
37static void udata_abort(struct trapframe *);
38static void kdata_abort(struct trapframe *);
39
40static void
41dump_regs(struct trapframe *frame)
42{
43	int n;
44	int i;
45
46	n = (sizeof(frame->tf_t) / sizeof(frame->tf_t[0]));
47	for (i = 0; i < n; i++)
48		printf("t[%d] == 0x%016lx\n", i, frame->tf_t[i]);
49
50	n = (sizeof(frame->tf_s) / sizeof(frame->tf_s[0]));
51	for (i = 0; i < n; i++)
52		printf("s[%d] == 0x%016lx\n", i, frame->tf_s[i]);
53
54	n = (sizeof(frame->tf_a) / sizeof(frame->tf_a[0]));
55	for (i = 0; i < n; i++)
56		printf("a[%d] == 0x%016lx\n", i, frame->tf_a[i]);
57
58	printf("sepc == 0x%016lx\n", frame->tf_sepc);
59	printf("sstatus == 0x%016lx\n", frame->tf_sstatus);
60	printf("stval == 0x%016lx\n", frame->tf_stval);
61	printf("scause == 0x%016lx\n", frame->tf_scause);
62}
63
64void
65do_trap_supervisor(struct trapframe *frame)
66{
67	uint64_t exception;
68
69	/* Ensure we came from supervisor mode, interrupts disabled */
70	KASSERTMSG((csr_read(sstatus) & (SSTATUS_SPP | SSTATUS_SIE)) ==
71	    SSTATUS_SPP, "Came from S mode with interrupts enabled");
72
73	KASSERTMSG((csr_read(sstatus) & (SSTATUS_SUM)) == 0,
74	    "Came from S mode with SUM enabled");
75
76	if (frame->tf_scause & EXCP_INTR) {
77		/* Interrupt */
78		riscv_cpu_intr(frame);
79		return;
80	}
81
82	intr_enable();
83
84	exception = (frame->tf_scause & EXCP_MASK);
85	switch (exception) {
86	case EXCP_FAULT_LOAD:
87	case EXCP_FAULT_STORE:
88	case EXCP_FAULT_FETCH:
89	case EXCP_STORE_PAGE_FAULT:
90	case EXCP_LOAD_PAGE_FAULT:
91		kdata_abort(frame);
92		break;
93	case EXCP_BREAKPOINT:
94#ifdef DDB
95		db_trapper(frame->tf_sepc,0/*XXX*/, frame, exception);
96#else
97		dump_regs(frame);
98		panic("No debugger in kernel.");
99#endif
100		break;
101	case EXCP_ILLEGAL_INSTRUCTION:
102		dump_regs(frame);
103		panic("Illegal instruction at 0x%016lx", frame->tf_sepc);
104		break;
105	default:
106		dump_regs(frame);
107		panic("Unknown kernel exception %llx trap value pc 0x%lx stval %lx",
108		    exception, frame->tf_sepc, frame->tf_stval);
109	}
110}
111
112void
113do_trap_user(struct trapframe *frame)
114{
115	uint64_t exception;
116	union sigval sv;
117	struct proc *p = curcpu()->ci_curproc;
118
119	p->p_addr->u_pcb.pcb_tf = frame;
120
121	/* Ensure we came from usermode, interrupts disabled */
122	KASSERTMSG((csr_read(sstatus) & (SSTATUS_SPP | SSTATUS_SIE)) == 0,
123	    "Came from U mode with interrupts enabled");
124
125	KASSERTMSG((csr_read(sstatus) & (SSTATUS_SUM)) == 0,
126	    "Came from U mode with SUM enabled");
127
128	if (frame->tf_scause & EXCP_INTR) {
129		/* Interrupt */
130		riscv_cpu_intr(frame);
131		return;
132	}
133
134	intr_enable();
135	refreshcreds(p);
136
137	exception = (frame->tf_scause & EXCP_MASK);
138	switch (exception) {
139	case EXCP_FAULT_LOAD:
140	case EXCP_FAULT_STORE:
141	case EXCP_FAULT_FETCH:
142	case EXCP_STORE_PAGE_FAULT:
143	case EXCP_LOAD_PAGE_FAULT:
144	case EXCP_INST_PAGE_FAULT:
145		udata_abort(frame);
146		break;
147	case EXCP_USER_ECALL:
148		frame->tf_sepc += 4;	/* Next instruction */
149		svc_handler(frame);
150		break;
151	case EXCP_ILLEGAL_INSTRUCTION:
152		if ((frame->tf_sstatus & SSTATUS_FS_MASK) == SSTATUS_FS_OFF) {
153			fpu_load(p);
154			break;
155		}
156		sv.sival_ptr = (void *)frame->tf_stval;
157		trapsignal(p, SIGILL, 0, ILL_ILLTRP, sv);
158		break;
159	case EXCP_BREAKPOINT:
160		sv.sival_ptr = (void *)frame->tf_stval;
161		trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv);
162		break;
163	default:
164		dump_regs(frame);
165		panic("Unknown userland exception %llx, trap value %lx",
166		    exception, frame->tf_stval);
167	}
168
169	userret(p);
170}
171
172static inline vm_prot_t
173accesstype(struct trapframe *frame)
174{
175	vm_prot_t access_type;
176
177	if ((frame->tf_scause == EXCP_FAULT_STORE) ||
178	    (frame->tf_scause == EXCP_STORE_PAGE_FAULT)) {
179		access_type = PROT_WRITE;
180	} else if (frame->tf_scause == EXCP_INST_PAGE_FAULT) {
181		access_type = PROT_EXEC;
182	} else {
183		access_type = PROT_READ;
184	}
185	return access_type;
186}
187
188static void
189udata_abort(struct trapframe *frame)
190{
191	struct vm_map *map;
192	uint64_t stval = frame->tf_stval;
193	union sigval sv;
194	vm_prot_t access_type = accesstype(frame);
195	vaddr_t va;
196	struct proc *p;
197	int error, sig, code;
198
199	p = curcpu()->ci_curproc;
200
201	va = trunc_page(stval);
202
203	map = &p->p_vmspace->vm_map;
204
205	if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p),
206	    "[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n",
207	    uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial))
208		return;
209
210	/* Handle referenced/modified emulation */
211	if (pmap_fault_fixup(map->pmap, va, access_type))
212		return;
213
214	error = uvm_fault(map, va, 0, access_type);
215
216	if (error == 0) {
217		uvm_grow(p, va);
218		return;
219	}
220
221	if (error == ENOMEM) {
222		sig = SIGKILL;
223		code = 0;
224	} else if (error == EIO) {
225		sig = SIGBUS;
226		code = BUS_OBJERR;
227	} else if (error == EACCES) {
228		sig = SIGSEGV;
229		code = SEGV_ACCERR;
230	} else {
231		sig = SIGSEGV;
232		code = SEGV_MAPERR;
233	}
234	sv.sival_ptr = (void *)stval;
235	trapsignal(p, sig, 0, code, sv);
236}
237
238static void
239kdata_abort(struct trapframe *frame)
240{
241	struct vm_map *map;
242	uint64_t stval = frame->tf_stval;
243	struct pcb *pcb;
244	vm_prot_t access_type = accesstype(frame);
245	vaddr_t va;
246	struct proc *p;
247	int error = 0;
248
249	pcb = curcpu()->ci_curpcb;
250	p = curcpu()->ci_curproc;
251
252	va = trunc_page(stval);
253
254	if (stval >= VM_MAXUSER_ADDRESS)
255		map = kernel_map;
256	else {
257		map = &p->p_vmspace->vm_map;
258	}
259
260	/* Handle referenced/modified emulation */
261	if (!pmap_fault_fixup(map->pmap, va, access_type)) {
262		error = uvm_fault(map, va, 0, access_type);
263
264		if (error == 0 && map != kernel_map)
265			uvm_grow(p, va);
266	}
267
268	if (error != 0) {
269		if (curcpu()->ci_idepth == 0 &&
270		    pcb->pcb_onfault != 0) {
271			frame->tf_a[0] = error;
272			frame->tf_sepc = (register_t)pcb->pcb_onfault;
273			return;
274		}
275		dump_regs(frame);
276		panic("Fatal page fault at %#lx: %#08lx", frame->tf_sepc,
277		    (vaddr_t)stval);
278	}
279}
280