exception.c revision 1.31
1/*	$NetBSD: exception.c,v 1.31 2006/07/22 22:43:43 uwe 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.31 2006/07/22 22:43:43 uwe Exp $");
83
84#include "opt_ddb.h"
85#include "opt_kgdb.h"
86#include "opt_ktrace.h"
87
88#include <sys/param.h>
89#include <sys/systm.h>
90#include <sys/proc.h>
91#include <sys/pool.h>
92#include <sys/user.h>
93#include <sys/kernel.h>
94#include <sys/signal.h>
95#include <sys/syscall.h>
96#include <sys/sa.h>
97#include <sys/savar.h>
98
99#ifdef KTRACE
100#include <sys/ktrace.h>
101#endif
102#ifdef DDB
103#include <sh3/db_machdep.h>
104#endif
105#ifdef KGDB
106#include <sys/kgdb.h>
107#endif
108
109#include <uvm/uvm_extern.h>
110
111#include <sh3/cpu.h>
112#include <sh3/mmu.h>
113#include <sh3/exception.h>
114#include <sh3/userret.h>
115
116const char * const exp_type[] = {
117	"--",					/* 0x000 (reset vector) */
118	"--",					/* 0x020 (reset vector) */
119	"TLB miss/invalid (load)",		/* 0x040 EXPEVT_TLB_MISS_LD */
120	"TLB miss/invalid (store)",		/* 0x060 EXPEVT_TLB_MISS_ST */
121	"initial page write",			/* 0x080 EXPEVT_TLB_MOD */
122	"TLB protection violation (load)",	/* 0x0a0 EXPEVT_TLB_PROT_LD */
123	"TLB protection violation (store)",	/* 0x0c0 EXPEVT_TLB_PROT_ST */
124	"address error (load)",			/* 0x0e0 EXPEVT_ADDR_ERR_LD */
125	"address error (store)",		/* 0x100 EXPEVT_ADDR_ERR_ST */
126	"FPU",					/* 0x120 EXPEVT_FPU */
127	"--",					/* 0x140 (reset vector) */
128	"unconditional trap (TRAPA)",		/* 0x160 EXPEVT_TRAPA */
129	"reserved instruction code exception",	/* 0x180 EXPEVT_RES_INST */
130	"illegal slot instruction exception",	/* 0x1a0 EXPEVT_SLOT_INST */
131	"--",					/* 0x1c0 (external interrupt) */
132	"user break point trap",		/* 0x1e0 EXPEVT_BREAK */
133};
134const int exp_types = sizeof exp_type / sizeof exp_type[0];
135
136void general_exception(struct lwp *, struct trapframe *, uint32_t);
137void tlb_exception(struct lwp *, struct trapframe *, uint32_t);
138void ast(struct lwp *, struct trapframe *);
139
140/*
141 * void general_exception(struct lwp *l, struct trapframe *tf):
142 *	l  ... curlwp when exception occur.
143 *	tf ... full user context.
144 *	va ... fault va for user mode EXPEVT_ADDR_ERR_{LD,ST}
145 */
146void
147general_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
148{
149	int expevt = tf->tf_expevt;
150	boolean_t usermode = !KERNELMODE(tf->tf_ssr);
151	ksiginfo_t ksi;
152
153	uvmexp.traps++;
154
155	if (l == NULL)
156 		goto do_panic;
157
158	if (usermode) {
159		KDASSERT(l->l_md.md_regs == tf); /* check exception depth */
160		expevt |= EXP_USER;
161		LWP_CACHE_CREDS(l, l->l_proc);
162	}
163
164	switch (expevt) {
165	case EXPEVT_TRAPA | EXP_USER:
166		/* Check for debugger break */
167		if (_reg_read_4(SH_(TRA)) == (_SH_TRA_BREAK << 2)) {
168			tf->tf_spc -= 2; /* back to the breakpoint address */
169			KSI_INIT_TRAP(&ksi);
170			ksi.ksi_signo = SIGTRAP;
171			ksi.ksi_code = TRAP_BRKPT;
172			ksi.ksi_addr = (void *)tf->tf_spc;
173			goto trapsignal;
174		} else {
175			/* XXX: we shouldn't treat *any* TRAPA as a syscall */
176			(*l->l_proc->p_md.md_syscall)(l, tf);
177			return;
178		}
179		break;
180
181	case EXPEVT_ADDR_ERR_LD: /* FALLTHROUGH */
182	case EXPEVT_ADDR_ERR_ST:
183		KDASSERT(l->l_md.md_pcb->pcb_onfault != NULL);
184		tf->tf_spc = (int)l->l_md.md_pcb->pcb_onfault;
185		if (tf->tf_spc == 0)
186			goto do_panic;
187		break;
188
189	case EXPEVT_ADDR_ERR_LD | EXP_USER: /* FALLTHROUGH */
190	case EXPEVT_ADDR_ERR_ST | EXP_USER:
191		KSI_INIT_TRAP(&ksi);
192		if (((int)va) < 0) {
193		    ksi.ksi_signo = SIGSEGV;
194		    ksi.ksi_code = SEGV_ACCERR;
195		} else {
196		    ksi.ksi_signo = SIGBUS;
197		    ksi.ksi_code = BUS_ADRALN;
198		}
199		ksi.ksi_addr = (void *)va;
200		goto trapsignal;
201
202	case EXPEVT_RES_INST | EXP_USER: /* FALLTHROUGH */
203	case EXPEVT_SLOT_INST | EXP_USER:
204		KSI_INIT_TRAP(&ksi);
205		ksi.ksi_signo = SIGILL;
206		ksi.ksi_code = ILL_ILLOPC; /* XXX: could be ILL_PRVOPC */
207		ksi.ksi_addr = (void *)tf->tf_spc;
208		goto trapsignal;
209
210	case EXPEVT_BREAK | EXP_USER:
211		KSI_INIT_TRAP(&ksi);
212		ksi.ksi_signo = SIGTRAP;
213		ksi.ksi_code = TRAP_TRACE;
214		ksi.ksi_addr = (void *)tf->tf_spc;
215		goto trapsignal;
216
217	default:
218		goto do_panic;
219	}
220
221	if (usermode)
222		userret(l);
223	return;
224
225 trapsignal:
226	ksi.ksi_trap = tf->tf_expevt;
227	KERNEL_PROC_LOCK(l);
228	trapsignal(l, &ksi);
229	KERNEL_PROC_UNLOCK(l);
230	userret(l);
231	return;
232
233 do_panic:
234#ifdef DDB
235	if (kdb_trap(expevt, 0, tf))
236		return;
237#endif
238#ifdef KGDB
239	if (kgdb_trap(EXPEVT_BREAK, tf))
240		return;
241#endif
242	if (expevt >> 5 < exp_types)
243		printf("fatal %s", exp_type[expevt >> 5]);
244	else
245		printf("EXPEVT 0x%03x", expevt);
246	printf(" in %s mode\n", expevt & EXP_USER ? "user" : "kernel");
247	printf(" spc %x ssr %x \n", tf->tf_spc, tf->tf_ssr);
248
249	panic("general_exception");
250
251	for (;;)
252		continue;
253	/* NOTREACHED */
254}
255
256
257/*
258 * void tlb_exception(struct lwp *l, struct trapframe *tf, uint32_t va):
259 *	l  ... curlwp when exception occur.
260 *	tf ... full user context.
261 *	va ... fault address.
262 */
263void
264tlb_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
265{
266	struct vm_map *map;
267	pmap_t pmap;
268	ksiginfo_t ksi;
269	boolean_t usermode;
270	int err, track, ftype;
271	const char *panic_msg;
272
273#define TLB_ASSERT(assert, msg)				\
274		do {					\
275			if (!(assert)) {		\
276				panic_msg =  msg;	\
277				goto tlb_panic;		\
278			}				\
279		} while(/*CONSTCOND*/0)
280
281
282	usermode = !KERNELMODE(tf->tf_ssr);
283	if (usermode) {
284		KDASSERT(l->l_md.md_regs == tf);
285		LWP_CACHE_CREDS(l, l->l_proc);
286	} else {
287		KDASSERT(l == NULL ||		/* idle */
288		    l == &lwp0 ||		/* kthread */
289		    l->l_md.md_regs != tf);	/* other */
290	}
291
292	switch (tf->tf_expevt) {
293	case EXPEVT_TLB_MISS_LD:
294		track = PVH_REFERENCED;
295		ftype = VM_PROT_READ;
296		break;
297	case EXPEVT_TLB_MISS_ST:
298		track = PVH_REFERENCED;
299		ftype = VM_PROT_WRITE;
300		break;
301	case EXPEVT_TLB_MOD:
302		track = PVH_REFERENCED | PVH_MODIFIED;
303		ftype = VM_PROT_WRITE;
304		break;
305	case EXPEVT_TLB_PROT_LD:
306		TLB_ASSERT((int)va > 0,
307		    "kernel virtual protection fault (load)");
308		if (usermode) {
309			KSI_INIT_TRAP(&ksi);
310			ksi.ksi_signo = SIGSEGV;
311			ksi.ksi_code = SEGV_ACCERR;
312			ksi.ksi_addr = (void *)va;
313			goto user_fault;
314		} else {
315			TLB_ASSERT(l && l->l_md.md_pcb->pcb_onfault != NULL,
316			    "no copyin/out fault handler (load protection)");
317			tf->tf_spc = (int)l->l_md.md_pcb->pcb_onfault;
318		}
319		return;
320
321	case EXPEVT_TLB_PROT_ST:
322		track = 0;	/* call uvm_fault first. (COW) */
323		ftype = VM_PROT_WRITE;
324		break;
325
326	default:
327		TLB_ASSERT(0, "impossible expevt");
328	}
329
330	/* Select address space */
331	if (usermode) {
332		TLB_ASSERT(l != NULL, "no curlwp");
333		map = &l->l_proc->p_vmspace->vm_map;
334		pmap = map->pmap;
335	} else {
336		if ((int)va < 0) {
337			map = kernel_map;
338			pmap = pmap_kernel();
339		} else {
340			TLB_ASSERT(l != NULL &&
341			    l->l_md.md_pcb->pcb_onfault != NULL,
342			    "invalid user-space access from kernel mode");
343			if (va == 0) {
344				tf->tf_spc = (int)l->l_md.md_pcb->pcb_onfault;
345				return;
346			}
347			map = &l->l_proc->p_vmspace->vm_map;
348			pmap = map->pmap;
349		}
350	}
351
352	/* Lookup page table. if entry found, load it. */
353	if (track && __pmap_pte_load(pmap, va, track)) {
354		if (usermode)
355			userret(l);
356		return;
357	}
358
359	/* Page not found. call fault handler */
360	if (!usermode && pmap != pmap_kernel() &&
361	    l->l_md.md_pcb->pcb_faultbail) {
362		TLB_ASSERT(l->l_md.md_pcb->pcb_onfault != NULL,
363		    "no copyin/out fault handler (interrupt context)");
364		tf->tf_spc = (int)l->l_md.md_pcb->pcb_onfault;
365		return;
366	}
367
368	if ((map != kernel_map) && (l->l_flag & L_SA)) {
369		l->l_savp->savp_faultaddr = (vaddr_t)va;
370		l->l_flag |= L_SA_PAGEFAULT;
371	}
372
373	err = uvm_fault(map, va, ftype);
374
375	/* User stack extension */
376	if (map != kernel_map &&
377	    (va >= (vaddr_t)l->l_proc->p_vmspace->vm_maxsaddr) &&
378	    (va < USRSTACK)) {
379		if (err == 0) {
380			struct vmspace *vm = l->l_proc->p_vmspace;
381			uint32_t nss;
382			nss = btoc(USRSTACK - va);
383			if (nss > vm->vm_ssize)
384				vm->vm_ssize = nss;
385		} else if (err == EACCES) {
386			err = EFAULT;
387		}
388	}
389
390	if (map != kernel_map)
391		l->l_flag &= ~L_SA_PAGEFAULT;
392	/* Page in. load PTE to TLB. */
393	if (err == 0) {
394		boolean_t loaded = __pmap_pte_load(pmap, va, track);
395		TLB_ASSERT(loaded, "page table entry not found");
396		if (usermode)
397			userret(l);
398		return;
399	}
400
401	/* Page not found. */
402	if (usermode) {
403		KSI_INIT_TRAP(&ksi);
404		if (err == ENOMEM)
405			ksi.ksi_signo = SIGKILL;
406		else {
407			ksi.ksi_signo = SIGSEGV;
408			ksi.ksi_code = SEGV_MAPERR;
409		}
410		goto user_fault;
411	} else {
412		TLB_ASSERT(l->l_md.md_pcb->pcb_onfault,
413		    "no copyin/out fault handler (page not found)");
414		tf->tf_spc = (int)l->l_md.md_pcb->pcb_onfault;
415	}
416	return;
417
418 user_fault:
419	ksi.ksi_trap = tf->tf_expevt
420	KERNEL_PROC_LOCK(l);
421	trapsignal(l, &ksi);
422	KERNEL_PROC_UNLOCK(l);
423	userret(l);
424	ast(l, tf);
425	return;
426
427 tlb_panic:
428	panic("tlb_exception: %s\n"
429	      "expevt=%x va=%08x ssr=%08x spc=%08x lwp=%p onfault=%p",
430	      panic_msg, tf->tf_expevt, va, tf->tf_ssr, tf->tf_spc,
431	      l, l ? l->l_md.md_pcb->pcb_onfault : NULL);
432#undef	TLB_ASSERT
433}
434
435
436/*
437 * void ast(struct lwp *l, struct trapframe *tf):
438 *	l  ... curlwp when exception occur.
439 *	tf ... full user context.
440 *	This is called when exception return. if return from kernel to user,
441 *	handle asynchronous software traps and context switch if needed.
442 */
443void
444ast(struct lwp *l, struct trapframe *tf)
445{
446	struct proc *p;
447
448	if (KERNELMODE(tf->tf_ssr))
449		return;
450	KDASSERT(l != NULL);
451	KDASSERT(l->l_md.md_regs == tf);
452
453	p = l->l_proc;
454
455	while (p->p_md.md_astpending) {
456		uvmexp.softs++;
457		p->p_md.md_astpending = 0;
458
459		if (p->p_flag & P_OWEUPC) {
460			p->p_flag &= ~P_OWEUPC;
461			ADDUPROF(p);
462		}
463
464		if (want_resched) {
465			/* We are being preempted. */
466			preempt(0);
467		}
468
469		userret(l);
470	}
471}
472
473/*
474 * void child_return(void *arg):
475 *
476 *	uvm_fork sets this routine to proc_trampoline's service function.
477 *	when return from here, jump to user-land.
478 */
479void
480child_return(void *arg)
481{
482	struct lwp *l = arg;
483#ifdef KTRACE
484	struct proc *p = l->l_proc;
485#endif
486	struct trapframe *tf = l->l_md.md_regs;
487
488	tf->tf_r0 = 0;
489	tf->tf_ssr |= PSL_TBIT; /* This indicates no error. */
490
491	userret(l);
492#ifdef KTRACE
493	if (KTRPOINT(p, KTR_SYSRET))
494		ktrsysret(l, SYS_fork, 0, 0);
495#endif
496}
497
498/*
499 * void startlwp(void *arg):
500 *
501 *	Start a new LWP.
502 */
503void
504startlwp(void *arg)
505{
506	ucontext_t *uc = arg;
507	struct lwp *l = curlwp;
508	int error;
509
510	error = cpu_setmcontext(l, &uc->uc_mcontext, uc->uc_flags);
511#ifdef DIAGNOSTIC
512	if (error)
513		printf("startlwp: error %d from cpu_setmcontext()", error);
514#endif
515	pool_put(&lwp_uc_pool, uc);
516
517	userret(l);
518}
519
520/*
521 * void upcallret(struct lwp *l):
522 *
523 *	Perform userret() for an LWP.
524 *	XXX This is a terrible name.
525 */
526void
527upcallret(struct lwp *l)
528{
529
530	userret(l);
531}
532