exception.c revision 1.69
1/*	$NetBSD: exception.c,v 1.69 2019/11/29 18:27:33 ad Exp $	*/
2
3/*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc. All rights reserved.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the University of Utah, and William Jolitz.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *	@(#)trap.c	7.4 (Berkeley) 5/13/91
36 */
37
38/*-
39 * Copyright (c) 1995 Charles M. Hannum.  All rights reserved.
40 *
41 * This code is derived from software contributed to Berkeley by
42 * the University of Utah, and William Jolitz.
43 *
44 * Redistribution and use in source and binary forms, with or without
45 * modification, are permitted provided that the following conditions
46 * are met:
47 * 1. Redistributions of source code must retain the above copyright
48 *    notice, this list of conditions and the following disclaimer.
49 * 2. Redistributions in binary form must reproduce the above copyright
50 *    notice, this list of conditions and the following disclaimer in the
51 *    documentation and/or other materials provided with the distribution.
52 * 3. All advertising materials mentioning features or use of this software
53 *    must display the following acknowledgement:
54 *	This product includes software developed by the University of
55 *	California, Berkeley and its contributors.
56 * 4. Neither the name of the University nor the names of its contributors
57 *    may be used to endorse or promote products derived from this software
58 *    without specific prior written permission.
59 *
60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
70 * SUCH DAMAGE.
71 *
72 *	@(#)trap.c	7.4 (Berkeley) 5/13/91
73 */
74
75/*
76 * SH3 Trap and System call handling
77 *
78 * T.Horiuchi 1998.06.8
79 */
80
81#include <sys/cdefs.h>
82__KERNEL_RCSID(0, "$NetBSD: exception.c,v 1.69 2019/11/29 18:27:33 ad Exp $");
83
84#include "opt_ddb.h"
85#include "opt_kgdb.h"
86
87#include <sys/param.h>
88#include <sys/systm.h>
89#include <sys/kernel.h>
90#include <sys/proc.h>
91#include <sys/signal.h>
92
93#ifdef DDB
94#include <sh3/db_machdep.h>
95#endif
96#ifdef KGDB
97#include <sys/kgdb.h>
98#endif
99
100#include <uvm/uvm_extern.h>
101
102#include <sh3/cpu.h>
103#include <sh3/mmu.h>
104#include <sh3/pcb.h>
105#include <sh3/exception.h>
106#include <sh3/userret.h>
107
108const char * const exp_type[] = {
109	"--",					/* 0x000 (reset vector) */
110	"--",					/* 0x020 (reset vector) */
111	"TLB miss/invalid (load)",		/* 0x040 EXPEVT_TLB_MISS_LD */
112	"TLB miss/invalid (store)",		/* 0x060 EXPEVT_TLB_MISS_ST */
113	"initial page write",			/* 0x080 EXPEVT_TLB_MOD */
114	"TLB protection violation (load)",	/* 0x0a0 EXPEVT_TLB_PROT_LD */
115	"TLB protection violation (store)",	/* 0x0c0 EXPEVT_TLB_PROT_ST */
116	"address error (load)",			/* 0x0e0 EXPEVT_ADDR_ERR_LD */
117	"address error (store)",		/* 0x100 EXPEVT_ADDR_ERR_ST */
118	"FPU",					/* 0x120 EXPEVT_FPU */
119	"--",					/* 0x140 (reset vector) */
120	"unconditional trap (TRAPA)",		/* 0x160 EXPEVT_TRAPA */
121	"reserved instruction code exception",	/* 0x180 EXPEVT_RES_INST */
122	"illegal slot instruction exception",	/* 0x1a0 EXPEVT_SLOT_INST */
123	"--",					/* 0x1c0 (external interrupt) */
124	"user break point trap",		/* 0x1e0 EXPEVT_BREAK */
125};
126const int exp_types = __arraycount(exp_type);
127
128void general_exception(struct lwp *, struct trapframe *, uint32_t);
129void tlb_exception(struct lwp *, struct trapframe *, uint32_t);
130void ast(struct lwp *, struct trapframe *);
131
132/*
133 * void general_exception(struct lwp *l, struct trapframe *tf):
134 *	l  ... curlwp when exception occur.
135 *	tf ... full user context.
136 *	va ... fault va for user mode EXPEVT_ADDR_ERR_{LD,ST}
137 */
138void
139general_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
140{
141	int expevt = tf->tf_expevt;
142	bool usermode = !KERNELMODE(tf->tf_ssr);
143	struct pcb *pcb;
144	ksiginfo_t ksi;
145	uint32_t trapcode;
146#ifdef DDB
147	uint32_t code;
148#endif
149
150	curcpu()->ci_data.cpu_ntrap++;
151
152	/*
153	 * Read trap code from TRA before enabling interrupts,
154	 * otherwise it can be clobbered by a ddb breakpoint in an
155	 * interrupt handler.
156	 */
157	trapcode = _reg_read_4(SH_(TRA)) >> 2;
158
159	splx(tf->tf_ssr & PSL_IMASK);
160
161	if (l == NULL)
162 		goto do_panic;
163
164	if (usermode) {
165		KDASSERT(l->l_md.md_regs == tf); /* check exception depth */
166		expevt |= EXP_USER;
167		LWP_CACHE_CREDS(l, l->l_proc);
168	}
169
170	switch (expevt) {
171	case EXPEVT_TRAPA | EXP_USER:
172		/* Check for debugger break */
173		if (trapcode == _SH_TRA_BREAK) {
174			tf->tf_spc -= 2; /* back to the breakpoint address */
175			KSI_INIT_TRAP(&ksi);
176			ksi.ksi_signo = SIGTRAP;
177			ksi.ksi_code = TRAP_BRKPT;
178			ksi.ksi_addr = (void *)tf->tf_spc;
179			goto trapsignal;
180		} else {
181			/* XXX: we shouldn't treat *any* TRAPA as a syscall */
182			(*l->l_proc->p_md.md_syscall)(l, tf);
183			return;
184		}
185		break;
186
187	case EXPEVT_BREAK | EXP_USER:
188		l->l_md.md_flags &= ~MDL_SSTEP;
189		KSI_INIT_TRAP(&ksi);
190		ksi.ksi_signo = SIGTRAP;
191		ksi.ksi_code = TRAP_TRACE;
192		ksi.ksi_addr = (void *)tf->tf_spc;
193		goto trapsignal;
194
195	case EXPEVT_ADDR_ERR_LD: /* FALLTHROUGH */
196	case EXPEVT_ADDR_ERR_ST:
197		pcb = lwp_getpcb(l);
198		KDASSERT(pcb->pcb_onfault != NULL);
199		tf->tf_spc = (int)pcb->pcb_onfault;
200		tf->tf_r0 = EFAULT;
201		if (tf->tf_spc == 0)
202			goto do_panic;
203		break;
204
205	case EXPEVT_ADDR_ERR_LD | EXP_USER: /* FALLTHROUGH */
206	case EXPEVT_ADDR_ERR_ST | EXP_USER:
207		KSI_INIT_TRAP(&ksi);
208		if (((int)va) < 0) {
209		    ksi.ksi_signo = SIGSEGV;
210		    ksi.ksi_code = SEGV_ACCERR;
211		} else {
212		    ksi.ksi_signo = SIGBUS;
213		    ksi.ksi_code = BUS_ADRALN;
214		}
215		ksi.ksi_addr = (void *)va;
216		goto trapsignal;
217
218	case EXPEVT_RES_INST | EXP_USER: /* FALLTHROUGH */
219	case EXPEVT_SLOT_INST | EXP_USER:
220		KSI_INIT_TRAP(&ksi);
221		ksi.ksi_signo = SIGILL;
222		ksi.ksi_code = ILL_ILLOPC; /* XXX: could be ILL_PRVOPC */
223		ksi.ksi_addr = (void *)tf->tf_spc;
224		goto trapsignal;
225
226	default:
227		goto do_panic;
228	}
229
230	if (usermode)
231		userret(l);
232	return;
233
234 trapsignal:
235	ksi.ksi_trap = tf->tf_expevt;
236	trapsignal(l, &ksi);
237	userret(l);
238	return;
239
240 do_panic:
241#ifdef DDB
242	switch (expevt & ~EXP_USER) {
243	case EXPEVT_TRAPA:
244		code = trapcode;
245		break;
246	default:
247		code = 0;
248		break;
249	}
250	if (kdb_trap(expevt, code, tf))
251		return;
252#endif
253#ifdef KGDB
254	if (kgdb_trap(EXPEVT_BREAK, tf))
255		return;
256#endif
257	if (expevt >> 5 < exp_types)
258		printf("fatal %s", exp_type[expevt >> 5]);
259	else
260		printf("EXPEVT 0x%03x", expevt);
261	printf(" in %s mode\n", usermode ? "user" : "kernel");
262	printf(" spc %x ssr %x \n", tf->tf_spc, tf->tf_ssr);
263
264	panic("general_exception");
265	/* NOTREACHED */
266}
267
268
269/*
270 * void tlb_exception(struct lwp *l, struct trapframe *tf, uint32_t va):
271 *	l  ... curlwp when exception occur.
272 *	tf ... full user context.
273 *	va ... fault address.
274 */
275void
276tlb_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
277{
278	struct vm_map *map;
279	struct pcb *pcb;
280	pmap_t pmap;
281	void *onfault;
282	ksiginfo_t ksi;
283	bool usermode;
284	int err, track, ftype;
285	const char *panic_msg;
286
287	pcb = lwp_getpcb(l);
288	onfault = pcb->pcb_onfault;
289
290#define TLB_ASSERT(assert, msg)				\
291		do {					\
292			if (!(assert)) {		\
293				panic_msg =  msg;	\
294				goto tlb_panic;		\
295			}				\
296		} while(/*CONSTCOND*/0)
297
298	splx(tf->tf_ssr & PSL_IMASK);
299
300	usermode = !KERNELMODE(tf->tf_ssr);
301	if (usermode) {
302		KDASSERT(l->l_md.md_regs == tf);
303		LWP_CACHE_CREDS(l, l->l_proc);
304	} else {
305#if 0 /* FIXME: probably wrong for yamt-idlelwp */
306		KDASSERT(l == NULL ||		/* idle */
307		    l == &lwp0 ||		/* kthread */
308		    l->l_md.md_regs != tf);	/* other */
309#endif
310	}
311
312	switch (tf->tf_expevt) {
313	case EXPEVT_TLB_MISS_LD:
314		track = PVH_REFERENCED;
315		ftype = VM_PROT_READ;
316		break;
317	case EXPEVT_TLB_MISS_ST:
318		track = PVH_REFERENCED;
319		ftype = VM_PROT_WRITE;
320		break;
321	case EXPEVT_TLB_MOD:
322		track = PVH_REFERENCED | PVH_MODIFIED;
323		ftype = VM_PROT_WRITE;
324		break;
325	case EXPEVT_TLB_PROT_LD:
326		TLB_ASSERT((int)va > 0,
327		    "kernel virtual protection fault (load)");
328		if (usermode) {
329			KSI_INIT_TRAP(&ksi);
330			ksi.ksi_signo = SIGSEGV;
331			ksi.ksi_code = SEGV_ACCERR;
332			ksi.ksi_addr = (void *)va;
333			goto user_fault;
334		} else {
335			TLB_ASSERT(l && onfault != NULL,
336			    "no copyin/out fault handler (load protection)");
337			tf->tf_spc = (int)onfault;
338			tf->tf_r0 = EFAULT;
339		}
340		return;
341
342	case EXPEVT_TLB_PROT_ST:
343		track = 0;	/* call uvm_fault first. (COW) */
344		ftype = VM_PROT_WRITE;
345		break;
346
347	default:
348		TLB_ASSERT(0, "impossible expevt");
349	}
350
351	/* Select address space */
352	if (usermode) {
353		TLB_ASSERT(l != NULL, "no curlwp");
354		map = &l->l_proc->p_vmspace->vm_map;
355		pmap = map->pmap;
356	} else {
357		if ((int)va < 0) {
358			map = kernel_map;
359			pmap = pmap_kernel();
360		} else {
361			TLB_ASSERT(l != NULL && onfault != NULL,
362			    "invalid user-space access from kernel mode");
363			if (va == 0) {
364				tf->tf_spc = (int)onfault;
365				tf->tf_r0 = EFAULT;
366				return;
367			}
368			map = &l->l_proc->p_vmspace->vm_map;
369			pmap = map->pmap;
370		}
371	}
372
373	/* Lookup page table. if entry found, load it. */
374	if (track && __pmap_pte_load(pmap, va, track)) {
375		if (usermode)
376			userret(l);
377		return;
378	}
379
380	/* Page not found. call fault handler */
381	pcb->pcb_onfault = NULL;
382	err = uvm_fault(map, va, ftype);
383	pcb->pcb_onfault = onfault;
384
385	/* User stack extension */
386	if (map != kernel_map &&
387	    (va >= (vaddr_t)l->l_proc->p_vmspace->vm_maxsaddr) &&
388	    (va <  (vaddr_t)l->l_proc->p_vmspace->vm_minsaddr)) {
389		if (err == 0) {
390			struct vmspace *vm = l->l_proc->p_vmspace;
391			uint32_t nss;
392			nss = btoc((vaddr_t)vm->vm_minsaddr - va);
393			if (nss > vm->vm_ssize)
394				vm->vm_ssize = nss;
395		} else if (err == EACCES) {
396			err = EFAULT;
397		}
398	}
399
400	/* Page in. load PTE to TLB. */
401	if (err == 0) {
402		bool loaded = __pmap_pte_load(pmap, va, track);
403		TLB_ASSERT(loaded, "page table entry not found");
404		if (usermode)
405			userret(l);
406		return;
407	}
408
409	/* Page not found. */
410	if (usermode) {
411		KSI_INIT_TRAP(&ksi);
412		ksi.ksi_addr = (void *)va;
413
414		switch (err) {
415		case ENOMEM:
416			ksi.ksi_signo = SIGKILL;
417			break;
418		case EINVAL:
419			ksi.ksi_signo = SIGBUS;
420			ksi.ksi_code = BUS_ADRERR;
421			break;
422		case EACCES:
423			ksi.ksi_signo = SIGSEGV;
424			ksi.ksi_code = SEGV_ACCERR;
425			break;
426		default:
427			ksi.ksi_signo = SIGSEGV;
428			ksi.ksi_code = SEGV_MAPERR;
429			break;
430		}
431		goto user_fault;
432	} else {
433		TLB_ASSERT(onfault,
434		    "no copyin/out fault handler (page not found)");
435		tf->tf_spc = (int)onfault;
436		tf->tf_r0 = err;
437	}
438	return;
439
440 user_fault:
441	ksi.ksi_trap = tf->tf_expevt;
442	trapsignal(l, &ksi);
443	userret(l);
444	ast(l, tf);
445	return;
446
447 tlb_panic:
448	panic("tlb_exception: %s\n"
449	      "expevt=%x va=%08x ssr=%08x spc=%08x lwp=%p onfault=%p",
450	      panic_msg, tf->tf_expevt, va, tf->tf_ssr, tf->tf_spc,
451	      l, pcb->pcb_onfault);
452#undef	TLB_ASSERT
453}
454
455
456/*
457 * void ast(struct lwp *l, struct trapframe *tf):
458 *	l  ... curlwp when exception occur.
459 *	tf ... full user context.
460 *	This is called when exception return. if return from kernel to user,
461 *	handle asynchronous software traps and context switch if needed.
462 */
463void
464ast(struct lwp *l, struct trapframe *tf)
465{
466
467	if (KERNELMODE(tf->tf_ssr)) {
468		return;
469	}
470
471	KDASSERT(l != NULL);
472	KDASSERT(l->l_md.md_regs == tf);
473
474	userret(l);
475}
476