Home | History | Annotate | Line # | Download | only in sh3
      1 /*	$NetBSD: exception.c,v 1.75 2023/10/05 19:41:05 ad Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2002, 2019 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.75 2023/10/05 19:41:05 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 #include <sys/intr.h>
     93 
     94 #ifdef DDB
     95 #include <sh3/db_machdep.h>
     96 #endif
     97 #ifdef KGDB
     98 #include <sys/kgdb.h>
     99 #endif
    100 
    101 #include <uvm/uvm_extern.h>
    102 
    103 #include <sh3/cpu.h>
    104 #include <sh3/mmu.h>
    105 #include <sh3/pcb.h>
    106 #include <sh3/exception.h>
    107 #include <sh3/userret.h>
    108 
    109 const char * const exp_type[] = {
    110 	"--",					/* 0x000 (reset vector) */
    111 	"--",					/* 0x020 (reset vector) */
    112 	"TLB miss/invalid (load)",		/* 0x040 EXPEVT_TLB_MISS_LD */
    113 	"TLB miss/invalid (store)",		/* 0x060 EXPEVT_TLB_MISS_ST */
    114 	"initial page write",			/* 0x080 EXPEVT_TLB_MOD */
    115 	"TLB protection violation (load)",	/* 0x0a0 EXPEVT_TLB_PROT_LD */
    116 	"TLB protection violation (store)",	/* 0x0c0 EXPEVT_TLB_PROT_ST */
    117 	"address error (load)",			/* 0x0e0 EXPEVT_ADDR_ERR_LD */
    118 	"address error (store)",		/* 0x100 EXPEVT_ADDR_ERR_ST */
    119 	"FPU",					/* 0x120 EXPEVT_FPU */
    120 	"--",					/* 0x140 (reset vector) */
    121 	"unconditional trap (TRAPA)",		/* 0x160 EXPEVT_TRAPA */
    122 	"reserved instruction code exception",	/* 0x180 EXPEVT_RES_INST */
    123 	"illegal slot instruction exception",	/* 0x1a0 EXPEVT_SLOT_INST */
    124 	"--",					/* 0x1c0 (external interrupt) */
    125 	"user break point trap",		/* 0x1e0 EXPEVT_BREAK */
    126 };
    127 const int exp_types = __arraycount(exp_type);
    128 
    129 void general_exception(struct lwp *, struct trapframe *, uint32_t);
    130 void tlb_exception(struct lwp *, struct trapframe *, uint32_t);
    131 void ast(struct lwp *, struct trapframe *);
    132 
    133 /*
    134  * void general_exception(struct lwp *l, struct trapframe *tf):
    135  *	l  ... curlwp when exception occur.
    136  *	tf ... full user context.
    137  *	va ... fault va for user mode EXPEVT_ADDR_ERR_{LD,ST}
    138  */
    139 void
    140 general_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
    141 {
    142 	int expevt = tf->tf_expevt;
    143 	bool usermode = !KERNELMODE(tf->tf_ssr);
    144 	struct pcb *pcb;
    145 	ksiginfo_t ksi;
    146 	uint32_t trapcode;
    147 #ifdef DDB
    148 	uint32_t code;
    149 #endif
    150 
    151 	curcpu()->ci_data.cpu_ntrap++;
    152 
    153 	/*
    154 	 * Read trap code from TRA before enabling interrupts,
    155 	 * otherwise it can be clobbered by a ddb breakpoint in an
    156 	 * interrupt handler.
    157 	 */
    158 	trapcode = _reg_read_4(SH_(TRA)) >> 2;
    159 
    160 	splx(tf->tf_ssr & PSL_IMASK);
    161 
    162 	if (l == NULL)
    163  		goto do_panic;
    164 
    165 	if (usermode) {
    166 		KDASSERT(l->l_md.md_regs == tf); /* check exception depth */
    167 		expevt |= EXP_USER;
    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 		if (__predict_false(pcb->pcb_onfault == NULL))
    199 			goto do_panic;
    200 		tf->tf_spc = (int)pcb->pcb_onfault;
    201 		tf->tf_r0 = EFAULT;
    202 		break;
    203 
    204 	case EXPEVT_ADDR_ERR_LD | EXP_USER: /* FALLTHROUGH */
    205 	case EXPEVT_ADDR_ERR_ST | EXP_USER:
    206 		KSI_INIT_TRAP(&ksi);
    207 		if (((int)va) < 0) {
    208 		    ksi.ksi_signo = SIGSEGV;
    209 		    ksi.ksi_code = SEGV_ACCERR;
    210 		} else {
    211 		    ksi.ksi_signo = SIGBUS;
    212 		    ksi.ksi_code = BUS_ADRALN;
    213 		}
    214 		ksi.ksi_addr = (void *)va;
    215 		goto trapsignal;
    216 
    217 	case EXPEVT_RES_INST | EXP_USER: /* FALLTHROUGH */
    218 	case EXPEVT_SLOT_INST | EXP_USER:
    219 		KSI_INIT_TRAP(&ksi);
    220 		ksi.ksi_signo = SIGILL;
    221 		ksi.ksi_code = ILL_ILLOPC; /* XXX: could be ILL_PRVOPC */
    222 		ksi.ksi_addr = (void *)tf->tf_spc;
    223 		goto trapsignal;
    224 
    225 	default:
    226 		goto do_panic;
    227 	}
    228 
    229 	if (usermode)
    230 		userret(l);
    231 	return;
    232 
    233  trapsignal:
    234 	KASSERT(usermode);
    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  */
    275 void
    276 tlb_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 	usermode = !KERNELMODE(tf->tf_ssr);
    299 	if (usermode) {
    300 		KDASSERT(l->l_md.md_regs == tf);
    301 	} else {
    302 #if 0 /* FIXME: probably wrong for yamt-idlelwp */
    303 		KDASSERT(l == NULL ||		/* idle */
    304 		    l == &lwp0 ||		/* kthread */
    305 		    l->l_md.md_regs != tf);	/* other */
    306 #endif
    307 	}
    308 
    309 	switch (tf->tf_expevt) {
    310 	case EXPEVT_TLB_MISS_LD:
    311 		track = PVH_REFERENCED;
    312 		ftype = VM_PROT_READ;
    313 		break;
    314 	case EXPEVT_TLB_MISS_ST:
    315 		track = PVH_REFERENCED;
    316 		ftype = VM_PROT_WRITE;
    317 		break;
    318 	case EXPEVT_TLB_MOD:
    319 		track = PVH_REFERENCED | PVH_MODIFIED;
    320 		ftype = VM_PROT_WRITE;
    321 		break;
    322 	case EXPEVT_TLB_PROT_LD:
    323 		TLB_ASSERT((int)va > 0,
    324 		    "kernel virtual protection fault (load)");
    325 		if (usermode) {
    326 			KSI_INIT_TRAP(&ksi);
    327 			ksi.ksi_signo = SIGSEGV;
    328 			ksi.ksi_code = SEGV_ACCERR;
    329 			ksi.ksi_addr = (void *)va;
    330 			splx(tf->tf_ssr & PSL_IMASK);
    331 			goto user_fault;
    332 		} else {
    333 			TLB_ASSERT(l && onfault != NULL,
    334 			    "no copyin/out fault handler (load protection)");
    335 			tf->tf_spc = (int)onfault;
    336 			tf->tf_r0 = EFAULT;
    337 		}
    338 		return;
    339 
    340 	case EXPEVT_TLB_PROT_ST:
    341 		track = 0;	/* call uvm_fault first. (COW) */
    342 		ftype = VM_PROT_WRITE;
    343 		break;
    344 
    345 	default:
    346 		TLB_ASSERT(0, "impossible expevt");
    347 	}
    348 
    349 	/* Select address space */
    350 	if (usermode) {
    351 		TLB_ASSERT(l != NULL, "no curlwp");
    352 		map = &l->l_proc->p_vmspace->vm_map;
    353 		pmap = map->pmap;
    354 	} else {
    355 		if ((int)va < 0) {
    356 			map = kernel_map;
    357 			pmap = pmap_kernel();
    358 		} else {
    359 			TLB_ASSERT(l != NULL && onfault != NULL,
    360 			    "invalid user-space access from kernel mode");
    361 			if (va == 0) {
    362 				tf->tf_spc = (int)onfault;
    363 				tf->tf_r0 = EFAULT;
    364 				return;
    365 			}
    366 			map = &l->l_proc->p_vmspace->vm_map;
    367 			pmap = map->pmap;
    368 		}
    369 	}
    370 
    371 	/* Lookup page table. if entry found, load it. */
    372 	if (track && __pmap_pte_load(pmap, va, track)) {
    373 		return;
    374 	}
    375 
    376 	/* Page not found. call fault handler */
    377 	splx(tf->tf_ssr & PSL_IMASK);
    378 	pcb->pcb_onfault = NULL;
    379 	err = uvm_fault(map, va, ftype);
    380 	pcb->pcb_onfault = onfault;
    381 
    382 	/* User stack extension */
    383 	if (map != kernel_map &&
    384 	    (va >= (vaddr_t)l->l_proc->p_vmspace->vm_maxsaddr) &&
    385 	    (va <  (vaddr_t)l->l_proc->p_vmspace->vm_minsaddr)) {
    386 		if (err == 0) {
    387 			struct vmspace *vm = l->l_proc->p_vmspace;
    388 			uint32_t nss;
    389 			nss = btoc((vaddr_t)vm->vm_minsaddr - va);
    390 			if (nss > vm->vm_ssize)
    391 				vm->vm_ssize = nss;
    392 		} else if (err == EACCES) {
    393 			err = EFAULT;
    394 		}
    395 	}
    396 
    397 	/* Page in. load PTE to TLB. */
    398 	if (err == 0) {
    399 		bool loaded;
    400 		if (usermode)
    401 			userret(l);
    402 		loaded = __pmap_pte_load(pmap, va, track);
    403 #if 0
    404 		/*
    405 		 * XXXAD I don't think you should do this - consider
    406 		 * a multithreaded program where another thread got
    407 		 * switched to during UVM fault and it unmapped the
    408 		 * page. I think you should just let the fault happen
    409 		 * again.
    410 		 */
    411 		TLB_ASSERT(loaded, "page table entry not found");
    412 #else
    413 		__USE(loaded);
    414 #endif
    415 		return;
    416 	}
    417 
    418 	/* Page not found. */
    419 	if (usermode) {
    420 		KSI_INIT_TRAP(&ksi);
    421 		ksi.ksi_addr = (void *)va;
    422 
    423 		switch (err) {
    424 		case ENOMEM:
    425 			ksi.ksi_signo = SIGKILL;
    426 			break;
    427 		case EINVAL:
    428 			ksi.ksi_signo = SIGBUS;
    429 			ksi.ksi_code = BUS_ADRERR;
    430 			break;
    431 		case EACCES:
    432 			ksi.ksi_signo = SIGSEGV;
    433 			ksi.ksi_code = SEGV_ACCERR;
    434 			break;
    435 		default:
    436 			ksi.ksi_signo = SIGSEGV;
    437 			ksi.ksi_code = SEGV_MAPERR;
    438 			break;
    439 		}
    440 		goto user_fault;
    441 	} else {
    442 		TLB_ASSERT(onfault,
    443 		    "no copyin/out fault handler (page not found)");
    444 		tf->tf_spc = (int)onfault;
    445 		tf->tf_r0 = err;
    446 	}
    447 	return;
    448 
    449  user_fault:
    450 	ksi.ksi_trap = tf->tf_expevt;
    451 	trapsignal(l, &ksi);
    452 	userret(l);
    453 	return;
    454 
    455  tlb_panic:
    456 	panic("tlb_exception: %s\n"
    457 	      "expevt=%x va=%08x ssr=%08x spc=%08x lwp=%p onfault=%p",
    458 	      panic_msg, tf->tf_expevt, va, tf->tf_ssr, tf->tf_spc,
    459 	      l, pcb->pcb_onfault);
    460 #undef	TLB_ASSERT
    461 }
    462 
    463 
    464 /*
    465  * void ast(struct lwp *l, struct trapframe *tf):
    466  *	l  ... curlwp when exception occur.
    467  *	tf ... full user context.
    468  *	This is called when exception return. if return from kernel to user,
    469  *	handle asynchronous software traps and context switch if needed.
    470  *	Interrupts are blocked on entry.
    471  */
    472 void
    473 ast(struct lwp *l, struct trapframe *tf)
    474 {
    475 	int s;
    476 
    477 	if (__predict_true(l->l_md.md_astpending == 0)) {
    478 		return;
    479 	}
    480 	if (__predict_false(KERNELMODE(tf->tf_ssr))) {
    481 		/* should not occur but leave it here to be safe */
    482 		return;
    483 	}
    484 
    485 	KDASSERT(l != NULL);
    486 	KDASSERT(l->l_md.md_regs == tf);
    487 
    488 	s = tf->tf_ssr & PSL_IMASK;
    489 	do {
    490 		splx(s);
    491 		/* userret() clears l_md.md_astpending */
    492 		userret(l);
    493 		s = splhigh();
    494 	} while (__predict_false(l->l_md.md_astpending));
    495 }
    496