497 lines
13 KiB
C
497 lines
13 KiB
C
/* $NetBSD: exception.c,v 1.64 2015/03/04 09:39:26 skrll Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2002 The NetBSD Foundation, Inc. All rights reserved.
|
|
* Copyright (c) 1990 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* the University of Utah, and William Jolitz.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)trap.c 7.4 (Berkeley) 5/13/91
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (c) 1995 Charles M. Hannum. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* the University of Utah, and William Jolitz.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)trap.c 7.4 (Berkeley) 5/13/91
|
|
*/
|
|
|
|
/*
|
|
* SH3 Trap and System call handling
|
|
*
|
|
* T.Horiuchi 1998.06.8
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: exception.c,v 1.64 2015/03/04 09:39:26 skrll Exp $");
|
|
|
|
#include "opt_ddb.h"
|
|
#include "opt_kgdb.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/signal.h>
|
|
|
|
#ifdef DDB
|
|
#include <sh3/db_machdep.h>
|
|
#endif
|
|
#ifdef KGDB
|
|
#include <sys/kgdb.h>
|
|
#endif
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#include <sh3/cpu.h>
|
|
#include <sh3/mmu.h>
|
|
#include <sh3/pcb.h>
|
|
#include <sh3/exception.h>
|
|
#include <sh3/userret.h>
|
|
|
|
const char * const exp_type[] = {
|
|
"--", /* 0x000 (reset vector) */
|
|
"--", /* 0x020 (reset vector) */
|
|
"TLB miss/invalid (load)", /* 0x040 EXPEVT_TLB_MISS_LD */
|
|
"TLB miss/invalid (store)", /* 0x060 EXPEVT_TLB_MISS_ST */
|
|
"initial page write", /* 0x080 EXPEVT_TLB_MOD */
|
|
"TLB protection violation (load)", /* 0x0a0 EXPEVT_TLB_PROT_LD */
|
|
"TLB protection violation (store)", /* 0x0c0 EXPEVT_TLB_PROT_ST */
|
|
"address error (load)", /* 0x0e0 EXPEVT_ADDR_ERR_LD */
|
|
"address error (store)", /* 0x100 EXPEVT_ADDR_ERR_ST */
|
|
"FPU", /* 0x120 EXPEVT_FPU */
|
|
"--", /* 0x140 (reset vector) */
|
|
"unconditional trap (TRAPA)", /* 0x160 EXPEVT_TRAPA */
|
|
"reserved instruction code exception", /* 0x180 EXPEVT_RES_INST */
|
|
"illegal slot instruction exception", /* 0x1a0 EXPEVT_SLOT_INST */
|
|
"--", /* 0x1c0 (external interrupt) */
|
|
"user break point trap", /* 0x1e0 EXPEVT_BREAK */
|
|
};
|
|
const int exp_types = __arraycount(exp_type);
|
|
|
|
void general_exception(struct lwp *, struct trapframe *, uint32_t);
|
|
void tlb_exception(struct lwp *, struct trapframe *, uint32_t);
|
|
void ast(struct lwp *, struct trapframe *);
|
|
|
|
/*
|
|
* void general_exception(struct lwp *l, struct trapframe *tf):
|
|
* l ... curlwp when exception occur.
|
|
* tf ... full user context.
|
|
* va ... fault va for user mode EXPEVT_ADDR_ERR_{LD,ST}
|
|
*/
|
|
void
|
|
general_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
|
|
{
|
|
int expevt = tf->tf_expevt;
|
|
bool usermode = !KERNELMODE(tf->tf_ssr);
|
|
struct pcb *pcb;
|
|
ksiginfo_t ksi;
|
|
uint32_t trapcode;
|
|
#ifdef DDB
|
|
uint32_t code;
|
|
#endif
|
|
|
|
curcpu()->ci_data.cpu_ntrap++;
|
|
|
|
/*
|
|
* Read trap code from TRA before enabling interrupts,
|
|
* otherwise it can be clobbered by a ddb breakpoint in an
|
|
* interrupt handler.
|
|
*/
|
|
trapcode = _reg_read_4(SH_(TRA)) >> 2;
|
|
|
|
splx(tf->tf_ssr & PSL_IMASK);
|
|
|
|
if (l == NULL)
|
|
goto do_panic;
|
|
|
|
if (usermode) {
|
|
KDASSERT(l->l_md.md_regs == tf); /* check exception depth */
|
|
expevt |= EXP_USER;
|
|
LWP_CACHE_CREDS(l, l->l_proc);
|
|
}
|
|
|
|
switch (expevt) {
|
|
case EXPEVT_TRAPA | EXP_USER:
|
|
/* Check for debugger break */
|
|
if (trapcode == _SH_TRA_BREAK) {
|
|
tf->tf_spc -= 2; /* back to the breakpoint address */
|
|
KSI_INIT_TRAP(&ksi);
|
|
ksi.ksi_signo = SIGTRAP;
|
|
ksi.ksi_code = TRAP_BRKPT;
|
|
ksi.ksi_addr = (void *)tf->tf_spc;
|
|
goto trapsignal;
|
|
} else {
|
|
/* XXX: we shouldn't treat *any* TRAPA as a syscall */
|
|
(*l->l_proc->p_md.md_syscall)(l, tf);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case EXPEVT_BREAK | EXP_USER:
|
|
l->l_md.md_flags &= ~MDL_SSTEP;
|
|
KSI_INIT_TRAP(&ksi);
|
|
ksi.ksi_signo = SIGTRAP;
|
|
ksi.ksi_code = TRAP_TRACE;
|
|
ksi.ksi_addr = (void *)tf->tf_spc;
|
|
goto trapsignal;
|
|
|
|
case EXPEVT_ADDR_ERR_LD: /* FALLTHROUGH */
|
|
case EXPEVT_ADDR_ERR_ST:
|
|
pcb = lwp_getpcb(l);
|
|
KDASSERT(pcb->pcb_onfault != NULL);
|
|
tf->tf_spc = (int)pcb->pcb_onfault;
|
|
tf->tf_r0 = EFAULT;
|
|
if (tf->tf_spc == 0)
|
|
goto do_panic;
|
|
break;
|
|
|
|
case EXPEVT_ADDR_ERR_LD | EXP_USER: /* FALLTHROUGH */
|
|
case EXPEVT_ADDR_ERR_ST | EXP_USER:
|
|
KSI_INIT_TRAP(&ksi);
|
|
if (((int)va) < 0) {
|
|
ksi.ksi_signo = SIGSEGV;
|
|
ksi.ksi_code = SEGV_ACCERR;
|
|
} else {
|
|
ksi.ksi_signo = SIGBUS;
|
|
ksi.ksi_code = BUS_ADRALN;
|
|
}
|
|
ksi.ksi_addr = (void *)va;
|
|
goto trapsignal;
|
|
|
|
case EXPEVT_RES_INST | EXP_USER: /* FALLTHROUGH */
|
|
case EXPEVT_SLOT_INST | EXP_USER:
|
|
KSI_INIT_TRAP(&ksi);
|
|
ksi.ksi_signo = SIGILL;
|
|
ksi.ksi_code = ILL_ILLOPC; /* XXX: could be ILL_PRVOPC */
|
|
ksi.ksi_addr = (void *)tf->tf_spc;
|
|
goto trapsignal;
|
|
|
|
default:
|
|
goto do_panic;
|
|
}
|
|
|
|
if (usermode)
|
|
userret(l);
|
|
return;
|
|
|
|
trapsignal:
|
|
ksi.ksi_trap = tf->tf_expevt;
|
|
trapsignal(l, &ksi);
|
|
userret(l);
|
|
return;
|
|
|
|
do_panic:
|
|
#ifdef DDB
|
|
switch (expevt & ~EXP_USER) {
|
|
case EXPEVT_TRAPA:
|
|
code = trapcode;
|
|
break;
|
|
default:
|
|
code = 0;
|
|
break;
|
|
}
|
|
if (kdb_trap(expevt, code, tf))
|
|
return;
|
|
#endif
|
|
#ifdef KGDB
|
|
if (kgdb_trap(EXPEVT_BREAK, tf))
|
|
return;
|
|
#endif
|
|
if (expevt >> 5 < exp_types)
|
|
printf("fatal %s", exp_type[expevt >> 5]);
|
|
else
|
|
printf("EXPEVT 0x%03x", expevt);
|
|
printf(" in %s mode\n", usermode ? "user" : "kernel");
|
|
printf(" spc %x ssr %x \n", tf->tf_spc, tf->tf_ssr);
|
|
|
|
panic("general_exception");
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
/*
|
|
* void tlb_exception(struct lwp *l, struct trapframe *tf, uint32_t va):
|
|
* l ... curlwp when exception occur.
|
|
* tf ... full user context.
|
|
* va ... fault address.
|
|
*/
|
|
void
|
|
tlb_exception(struct lwp *l, struct trapframe *tf, uint32_t va)
|
|
{
|
|
struct vm_map *map;
|
|
struct pcb *pcb;
|
|
pmap_t pmap;
|
|
void *onfault;
|
|
ksiginfo_t ksi;
|
|
bool usermode;
|
|
int err, track, ftype;
|
|
const char *panic_msg;
|
|
|
|
pcb = lwp_getpcb(l);
|
|
onfault = pcb->pcb_onfault;
|
|
|
|
#define TLB_ASSERT(assert, msg) \
|
|
do { \
|
|
if (!(assert)) { \
|
|
panic_msg = msg; \
|
|
goto tlb_panic; \
|
|
} \
|
|
} while(/*CONSTCOND*/0)
|
|
|
|
splx(tf->tf_ssr & PSL_IMASK);
|
|
|
|
usermode = !KERNELMODE(tf->tf_ssr);
|
|
if (usermode) {
|
|
KDASSERT(l->l_md.md_regs == tf);
|
|
LWP_CACHE_CREDS(l, l->l_proc);
|
|
} else {
|
|
#if 0 /* FIXME: probably wrong for yamt-idlelwp */
|
|
KDASSERT(l == NULL || /* idle */
|
|
l == &lwp0 || /* kthread */
|
|
l->l_md.md_regs != tf); /* other */
|
|
#endif
|
|
}
|
|
|
|
switch (tf->tf_expevt) {
|
|
case EXPEVT_TLB_MISS_LD:
|
|
track = PVH_REFERENCED;
|
|
ftype = VM_PROT_READ;
|
|
break;
|
|
case EXPEVT_TLB_MISS_ST:
|
|
track = PVH_REFERENCED;
|
|
ftype = VM_PROT_WRITE;
|
|
break;
|
|
case EXPEVT_TLB_MOD:
|
|
track = PVH_REFERENCED | PVH_MODIFIED;
|
|
ftype = VM_PROT_WRITE;
|
|
break;
|
|
case EXPEVT_TLB_PROT_LD:
|
|
TLB_ASSERT((int)va > 0,
|
|
"kernel virtual protection fault (load)");
|
|
if (usermode) {
|
|
KSI_INIT_TRAP(&ksi);
|
|
ksi.ksi_signo = SIGSEGV;
|
|
ksi.ksi_code = SEGV_ACCERR;
|
|
ksi.ksi_addr = (void *)va;
|
|
goto user_fault;
|
|
} else {
|
|
TLB_ASSERT(l && onfault != NULL,
|
|
"no copyin/out fault handler (load protection)");
|
|
tf->tf_spc = (int)onfault;
|
|
tf->tf_r0 = EFAULT;
|
|
}
|
|
return;
|
|
|
|
case EXPEVT_TLB_PROT_ST:
|
|
track = 0; /* call uvm_fault first. (COW) */
|
|
ftype = VM_PROT_WRITE;
|
|
break;
|
|
|
|
default:
|
|
TLB_ASSERT(0, "impossible expevt");
|
|
}
|
|
|
|
/* Select address space */
|
|
if (usermode) {
|
|
TLB_ASSERT(l != NULL, "no curlwp");
|
|
map = &l->l_proc->p_vmspace->vm_map;
|
|
pmap = map->pmap;
|
|
} else {
|
|
if ((int)va < 0) {
|
|
map = kernel_map;
|
|
pmap = pmap_kernel();
|
|
} else {
|
|
TLB_ASSERT(l != NULL && onfault != NULL,
|
|
"invalid user-space access from kernel mode");
|
|
if (va == 0) {
|
|
tf->tf_spc = (int)onfault;
|
|
tf->tf_r0 = EFAULT;
|
|
return;
|
|
}
|
|
map = &l->l_proc->p_vmspace->vm_map;
|
|
pmap = map->pmap;
|
|
}
|
|
}
|
|
|
|
/* Lookup page table. if entry found, load it. */
|
|
if (track && __pmap_pte_load(pmap, va, track)) {
|
|
if (usermode)
|
|
userret(l);
|
|
return;
|
|
}
|
|
|
|
/* Page not found. call fault handler */
|
|
if (!usermode && pmap != pmap_kernel() && pcb->pcb_faultbail) {
|
|
TLB_ASSERT(onfault != NULL,
|
|
"no copyin/out fault handler (interrupt context)");
|
|
tf->tf_spc = (int)onfault;
|
|
tf->tf_r0 = EFAULT;
|
|
return;
|
|
}
|
|
|
|
pcb->pcb_onfault = NULL;
|
|
err = uvm_fault(map, va, ftype);
|
|
pcb->pcb_onfault = onfault;
|
|
|
|
/* User stack extension */
|
|
if (map != kernel_map &&
|
|
(va >= (vaddr_t)l->l_proc->p_vmspace->vm_maxsaddr) &&
|
|
(va < USRSTACK)) {
|
|
if (err == 0) {
|
|
struct vmspace *vm = l->l_proc->p_vmspace;
|
|
uint32_t nss;
|
|
nss = btoc(USRSTACK - va);
|
|
if (nss > vm->vm_ssize)
|
|
vm->vm_ssize = nss;
|
|
} else if (err == EACCES) {
|
|
err = EFAULT;
|
|
}
|
|
}
|
|
|
|
/* Page in. load PTE to TLB. */
|
|
if (err == 0) {
|
|
bool loaded = __pmap_pte_load(pmap, va, track);
|
|
TLB_ASSERT(loaded, "page table entry not found");
|
|
if (usermode)
|
|
userret(l);
|
|
return;
|
|
}
|
|
|
|
/* Page not found. */
|
|
if (usermode) {
|
|
KSI_INIT_TRAP(&ksi);
|
|
switch (err) {
|
|
case ENOMEM:
|
|
ksi.ksi_signo = SIGKILL;
|
|
break;
|
|
case EINVAL:
|
|
ksi.ksi_signo = SIGBUS;
|
|
ksi.ksi_code = BUS_ADRERR;
|
|
break;
|
|
case EACCES:
|
|
ksi.ksi_signo = SIGSEGV;
|
|
ksi.ksi_code = SEGV_ACCERR;
|
|
break;
|
|
default:
|
|
ksi.ksi_signo = SIGSEGV;
|
|
ksi.ksi_code = SEGV_MAPERR;
|
|
break;
|
|
}
|
|
goto user_fault;
|
|
} else {
|
|
TLB_ASSERT(onfault,
|
|
"no copyin/out fault handler (page not found)");
|
|
tf->tf_spc = (int)onfault;
|
|
tf->tf_r0 = err;
|
|
}
|
|
return;
|
|
|
|
user_fault:
|
|
ksi.ksi_trap = tf->tf_expevt;
|
|
trapsignal(l, &ksi);
|
|
userret(l);
|
|
ast(l, tf);
|
|
return;
|
|
|
|
tlb_panic:
|
|
panic("tlb_exception: %s\n"
|
|
"expevt=%x va=%08x ssr=%08x spc=%08x lwp=%p onfault=%p",
|
|
panic_msg, tf->tf_expevt, va, tf->tf_ssr, tf->tf_spc,
|
|
l, pcb->pcb_onfault);
|
|
#undef TLB_ASSERT
|
|
}
|
|
|
|
|
|
/*
|
|
* void ast(struct lwp *l, struct trapframe *tf):
|
|
* l ... curlwp when exception occur.
|
|
* tf ... full user context.
|
|
* This is called when exception return. if return from kernel to user,
|
|
* handle asynchronous software traps and context switch if needed.
|
|
*/
|
|
void
|
|
ast(struct lwp *l, struct trapframe *tf)
|
|
{
|
|
|
|
if (KERNELMODE(tf->tf_ssr)) {
|
|
return;
|
|
}
|
|
|
|
KDASSERT(l != NULL);
|
|
KDASSERT(l->l_md.md_regs == tf);
|
|
|
|
while (l->l_md.md_astpending) {
|
|
//curcpu()->ci_data.cpu_nast++;
|
|
l->l_md.md_astpending = 0;
|
|
|
|
if (l->l_pflag & LP_OWEUPC) {
|
|
l->l_pflag &= ~LP_OWEUPC;
|
|
ADDUPROF(l);
|
|
}
|
|
|
|
if (l->l_cpu->ci_want_resched) {
|
|
/* We are being preempted. */
|
|
preempt();
|
|
}
|
|
|
|
userret(l);
|
|
}
|
|
}
|