trap.c revision ed7181e648ca9f07a848246d2a88324c40ba1ba7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/machtrap.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/tnf_probe.h>
#include <sys/simulate.h>
#ifdef TRAPTRACE
#include <sys/traptrace.h>
#endif
int tudebug = 0;
static int tudebugbpt = 0;
static int tudebugfpe = 0;
static int alignfaults = 0;
static int lodebug = 0;
#else
#define lodebug 0
#endif /* defined(TRAPDEBUG) || defined(lint) */
#pragma weak vis1_partial_support
void trap_async_hwerr(void);
#pragma weak trap_async_hwerr
void trap_async_berr_bto(int, struct regs *);
#pragma weak trap_async_berr_bto
void trap_rtt(void);
static int
{
#ifdef TRAPTRACE
#endif
panic("BAD TRAP: type=%x rp=%p addr=%p mmu_fsr=%x "
"occurred in module \"%s\" due to %s",
"a NULL pointer dereference" :
"an illegal access to a user address");
} else {
panic("BAD TRAP: type=%x rp=%p addr=%p mmu_fsr=%x",
}
return (0); /* avoid optimization of restore in call's delay slot */
}
int ill_calls;
#endif
/*
* are the "strong" prefetches (fcn=20-23). But we check for all flavors of
* PREFETCH, in case some future variant also causes a DATA_MMU_MISS.
*/
#define IS_FLOAT(i) (((i) & 0x1000000) != 0)
/*
* Called from the trap handler when a processor trap occurs.
*/
/*VARARGS2*/
void
{
int stepped = 0;
int mstate;
char *badaddr;
enum fault_type fault_type;
int instr;
int iskernel;
int watchcode;
int watchpage;
enum seg_rw, int);
#ifdef SF_ERRATA_23 /* call causes illegal-insn */
(type == T_UNIMP_INSTR));
#else
#endif /* SF_ERRATA_23 */
/*
* Set lwp_state before trying to acquire any
* adaptive lock
*/
/*
* Set up the current cred to use during this trap. u_cred
* no longer exists. t_cred is used instead.
* The current process credential applies to the thread for
* the entire trap. If trapping from the kernel, this
* should already be set up.
*/
/*
* DTrace accesses t_cred in probe context. t_cred
* must always be either NULL, or point to a valid,
* allocated cred structure.
*/
}
switch (type) {
case T_WIN_OVERFLOW + T_USER:
case T_WIN_UNDERFLOW + T_USER:
case T_SYS_RTT_PAGE + T_USER:
case T_DATA_MMU_MISS + T_USER:
mstate = LMS_DFAULT;
break;
case T_INSTR_MMU_MISS + T_USER:
mstate = LMS_TFAULT;
break;
default:
break;
}
/* Kernel probe */
stepped =
/* this assignment must not precede call to prundostep() */
}
"C_trap_handler_enter:type %x", type);
#ifdef F_DEFERRED
/*
* Take any pending floating point exceptions now.
* If the floating point unit has an exception to handle,
* just return to user-level to let the signal handler run.
* The instruction that got us to trap() will be reexecuted on
* return from the signal handler and we will trap to here again.
* This is necessary to disambiguate simultaneous traps which
* happen when a floating-point exception is pending and a
* machine fault is incurred.
*/
/*
* FP_TRAPPED is set only by sendsig() when it copies
* out the floating-point queue for the signal handler.
* It is set there so we can test it here and in syscall().
*/
syncfpu();
/*
* trap() has have been called recursively and may
* have stopped the process, so do single step
* support for /proc.
*/
goto out;
}
}
#endif
switch (type) {
case T_DATA_MMU_MISS:
case T_INSTR_MMU_MISS + T_USER:
case T_DATA_MMU_MISS + T_USER:
case T_DATA_PROT + T_USER:
case T_SYS_RTT_PAGE + T_USER:
case T_FLUSH_PCB + T_USER:
break;
default:
FTRACE_3("trap(): type=0x%lx, regs=0x%lx, addr=0x%lx",
break;
}
switch (type) {
default:
/*
* Check for user software trap.
*/
if (tudebug)
break;
}
}
/*NOTREACHED*/
case T_ALIGNMENT: /* supv alignment error */
goto cleanup;
if (lodebug) {
}
goto cleanup;
}
/*NOTREACHED*/
case T_INSTR_EXCEPTION: /* sys instruction access exception */
/*NOTREACHED*/
case T_INSTR_MMU_MISS: /* sys instruction mmu miss */
/*NOTREACHED*/
case T_DATA_EXCEPTION: /* system data access exception */
switch (X_FAULT_TYPE(mmu_fsr)) {
case FT_RANGE:
/*
* This happens when we attempt to dereference an
* address in the address hole. If t_ontrap is set,
* then break and fall through to T_DATA_MMU_MISS /
* T_DATA_PROT case below. If lofault is set, then
* honour it (perhaps the user gave us a bogus
* address in the hole to copyin from or copyout to?)
*/
break;
if (lodebug) {
}
goto cleanup;
}
/*NOTREACHED*/
case FT_PRIV:
/*
* This can happen if we access ASI_USER from a kernel
* thread. To support pxfs, we need to honor lofault if
*/
goto cleanup;
if (lodebug) {
}
goto cleanup;
}
/*NOTREACHED*/
default:
goto cleanup;
/*NOTREACHED*/
case FT_NFO:
break;
}
/* fall into ... */
case T_DATA_MMU_MISS: /* system data mmu miss */
case T_DATA_PROT: /* system data protection fault */
goto cleanup;
/*
* set ot_trap and return from the trap to the trampoline.
*/
"C_trap_handler_exit");
goto cleanup;
}
}
switch (type) {
case T_DATA_PROT:
fault_type = F_PROT;
break;
case T_INSTR_MMU_MISS:
break;
case T_DATA_MMU_MISS:
case T_DATA_EXCEPTION:
/*
* The hardware doesn't update the sfsr on mmu
* misses so it is not easy to find out whether
* the access was a read or a write so we need
* to decode the actual instruction.
*/
break;
default:
break;
}
/*
* We determine if access was done to kernel or user
* address space. The addr passed into trap is really the
* tag access register.
*/
res = 0;
/*
* Restore lofault. If we resolved the fault, exit.
* If we didn't and lofault wasn't set, die.
*/
if (res == 0)
goto cleanup;
if (IS_PREFETCH(instr)) {
/* skip prefetch instructions in kernel-land */
goto cleanup;
}
if (lofault == 0)
/*
* Cannot resolve fault. Return to lofault.
*/
if (lodebug) {
}
else
goto cleanup;
break;
switch (type) {
case T_INSTR_MMU_MISS + T_USER:
break;
case T_DATA_MMU_MISS + T_USER:
/*
* The hardware doesn't update the sfsr on mmu misses
* so it is not easy to find out whether the access
* was a read or a write so we need to decode the
* actual instruction. XXX BUGLY HW
*/
break;
case T_DATA_PROT + T_USER:
fault_type = F_PROT;
break;
case T_WIN_OVERFLOW + T_USER:
break;
case T_WIN_UNDERFLOW + T_USER:
case T_SYS_RTT_PAGE + T_USER:
break;
default:
break;
}
/*
* If we are single stepping do not call pagefault
*/
if (stepped) {
} else {
int ta;
watchpage = (pr_watch_active(p) &&
if (!watchpage ||
/* EMPTY */;
if (ta) {
} else {
siginfo.si_trapafter = 0;
break;
}
} else {
goto out;
}
if (pr_watch_active(p) &&
if (copy_return_window(dotwo))
goto out;
}
/*
* If pagefault succeed, ok.
* Otherwise grow the stack automatically.
*/
if (res == 0 ||
addr < p->p_usrstack &&
/*
* instr_size() is used to get the exact
* address of the fault, instead of the
* page of the fault. Unfortunately it is
* very slow, and this is an important
* code path. Don't call it unless
* correctness is needed. ie. if FLTPAGE
* is set, or we're profiling.
*/
if (ismem) {
}
goto out;
}
/*
* check for non-faulting loads, also
* fetch the instruction to check for
* flush
*/
goto out;
/* skip userland prefetch instructions */
if (IS_PREFETCH(instr)) {
goto out;
/*NOTREACHED*/
}
/*
* check if the instruction was a
* flush. ABI allows users to specify
* an illegal address on the flush
* instruction so we simply return in
* this case.
*
* NB: the hardware should set a bit
* indicating this trap was caused by
* a flush instruction. Instruction
* decoding is bugly!
*/
/* skip the flush instruction */
goto out;
/*NOTREACHED*/
}
report_stack_exec(p, addr);
}
if (tudebug)
}
/*
* In the case where both pagefault and grow fail,
* set the code to the value provided by pagefault.
*/
}
} else { /* FC_NOMAP || FC_PROT */
}
/*
* If this is the culmination of a single-step,
* reset the addr, code, signal and fault to
* indicate a hardware trace trap.
*/
if (stepped) {
fault = 0;
}
/*
* If both NORMAL_STEP and WATCH_STEP are in
* effect, give precedence to NORMAL_STEP.
* One or the other must be set at this point.
*/
(void) undo_watch_step(NULL);
} else {
}
}
break;
if (&vis1_partial_support != NULL) {
if (vis1_partial_support(rp,
goto out;
}
goto out;
/* skip the flush instruction */
goto out;
/*NOTREACHED*/
}
switch (X_FAULT_TYPE(mmu_fsr)) {
case FT_ATOMIC_NC:
/* skip the atomic */
goto out;
}
/* fall into ... */
case FT_PRIV:
break;
case FT_SPEC_LD:
case FT_ILL_ALT:
break;
default:
break;
}
break;
if (tudebug)
/*
* If the user has to do unaligned references
* the ugly stuff gets done here.
*/
alignfaults++;
if (&vis1_partial_support != NULL) {
if (vis1_partial_support(rp,
goto out;
}
goto out;
/*
* Can't do unaligned stack access
*/
break;
}
/*
* Try to fix alignment before non-faulting load test.
*/
if (p->p_fixalignment) {
goto out;
}
goto out;
} else {
goto out;
} else {
else
}
}
break;
if (tudebug)
break;
case T_UNIMP_INSTR: /* priv illegal instruction fault */
if (fpras_implemented) {
/*
* Call fpras_chktrap indicating that
* we've come from a trap handler and pass
* the regs. That function may choose to panic
* (in which case it won't return) or it may
* determine that a reboot is desired. In the
* the illegal instruction and continue at
* a controlled address.
*/
if (&fpras_chktrap) {
if (fpras_chktrap(rp))
goto cleanup;
}
}
long pc;
ill_calls++;
goto cleanup;
}
#endif /* SF_ERRATA_23 || SF_ERRATA_30 */
/*
* It's not an fpras failure and it's not SF_ERRATA_23 - die
*/
/*NOTREACHED*/
long pc;
ill_calls++;
goto out;
}
#endif /* SF_ERRATA_23 || SF_ERRATA_30 */
if (tudebug)
/*
* Try to simulate the instruction.
*/
case SIMU_RETRY:
goto out; /* regs are already set up */
/*NOTREACHED*/
case SIMU_SUCCESS:
/* skip the successfully simulated instruction */
goto out;
/*NOTREACHED*/
case SIMU_FAULT:
break;
case SIMU_DZERO:
break;
case SIMU_UNALIGN:
break;
case SIMU_ILLEGAL:
default:
(op3 == IOP_V8_STDFA)))
else
break;
}
break;
case T_UNIMP_LDD + T_USER:
case T_UNIMP_STD + T_USER:
if (tudebug)
case SIMU_SUCCESS:
/* skip the successfully simulated instruction */
goto out;
/*NOTREACHED*/
case SIMU_FAULT:
goto out;
break;
case SIMU_UNALIGN:
goto out;
break;
case SIMU_ILLEGAL:
default:
break;
}
break;
case T_UNIMP_LDD:
case T_UNIMP_STD:
/* skip the successfully simulated instruction */
goto cleanup;
/*NOTREACHED*/
}
/*
* A third party driver executed an {LDD,STD,LDDA,STDA}
* that we couldn't simulate.
*/
goto cleanup;
if (lodebug) {
}
goto cleanup;
}
/*NOTREACHED*/
if (tudebug && tudebugfpe)
break;
if (tudebug && tudebugfpe)
break;
if (tudebug && tudebugbpt)
break;
if (tudebug)
break;
/*
* This trap is entered from sys_rtt in locore.s when,
* upon return to user is is found that there are user
* windows in pcb_wbuf. This happens because they could
* not be saved on the user stack, either because it
* wasn't resident or because it was misaligned.
*/
{
int error;
/*
* Possible errors:
* error copying out
* unaligned stack pointer
* The first is given to us as the return value
* from flush_user_windows_to_stack(). The second
* results in residual windows in the pcb.
*/
if (error != 0) {
/*
* EINTR comes from a signal during copyout;
* we should not post another signal.
*/
/*
* Zap the process with a SIGSEGV - process
* may be managing its own stack growth by
* taking SIGSEGVs on a different signal stack.
*/
}
break;
} else if (mpcb->mpcb_wbcnt) {
break;
}
}
/*
* T_FLUSHW is used when handling a ta 0x3 -- the old flush
* window trap -- which is implemented by executing the
* flushw instruction. The flushw can trap if any of the
* stack pages are not writable for whatever reason. In this
* case only, we advance the pc to the next instruction so
* that the user thread doesn't needlessly execute the trap
* again. Normally this wouldn't be a problem -- we'll
* usually only end up here if this is the first touch to a
* stack page -- since the second execution won't trap, but
* if there's a watchpoint on the stack page the user thread
* would spin, continuously executing the trap instruction.
*/
}
goto out;
if (kcpc_overflow_ast()) {
/*
* Signal performance counter overflow
*/
if (tudebug)
/* for trap_cleanup(), below */
}
}
/*
* The CPC_OVERFLOW check above may already have populated
* siginfo and set fault, so the checks below must not
* touch these and the functions they call must use
* trapsig() directly.
*/
}
}
}
break;
}
out: /* We can't get here from a system trap */
trap_rtt();
/* Kernel probe */
return;
cleanup: /* system traps end up here */
}
void
int restartable)
{
extern void aio_cleanup();
if (fault) {
/*
* Remember the fault and fault address
* for real-time (SIGPROF) profiling.
*/
/*
* If a debugger has declared this fault to be an
* event of interest, stop the lwp. Otherwise just
* deliver the associated signal.
*/
}
if (lwp->lwp_oweupc)
/*
* Turn off the AST flag before checking all the conditions that
* may have caused an AST. This flag is on whenever a signal or
* unusual condition should be handled after the next trap or
* syscall.
*/
curthread->t_sig_check = 0;
/*
* The following check is legal for the following reasons:
* 1) The thread we are checking, is ourselves, so there is
* no way the proc can go away.
* 2) The only time we need to be protected by the
* lock is if the binding is changed.
*
* Note we will still take the lock and check the binding
* if the condition was true without the lock held. This
* prevents lock contention among threads owned by the
* same proc.
*/
mutex_enter(&p->p_lock);
}
mutex_exit(&p->p_lock);
}
/*
* for kaio requests that are on the per-process poll queue,
* aiop->aio_pollq, they're AIO_POLL bit is set, the kernel
* should copyout their result_t to user memory. by copying
* out the result_t, the user can poll on memory waiting
* for the kaio request to complete.
*/
if (p->p_aio)
aio_cleanup(0);
/*
* If this LWP was asked to hold, call holdlwp(), which will
* stop. holdlwps() sets this up and calls pokelwps() which
* sets the AST flag.
*
* Also check TP_EXITLWP, since this is used by fresh new LWPs
* through lwp_rtt(). That flag is set if the lwp_create(2)
* syscall failed after creating the LWP.
*/
if (ISHOLD(p))
holdlwp();
/*
* All code that sets signals and makes ISSIG evaluate true must
* set t_astflag afterwards.
*/
psig();
}
realsigprof(0, 0);
}
}
}
/*
* Called from fp_traps when a floating point trap occurs.
* Note that the T_DATA_EXCEPTION case does not use X_FAULT_TYPE(mmu_fsr),
* because mmu_fsr (now changed to code) is always 0.
* Note that the T_UNIMP_INSTR case does not call simulate_unimp(),
* because the simulator only simulates multiply and divide instructions,
* which would not cause floating point traps in the first place.
* XXX - Supervisor mode floating point traps?
*/
void
{
int mstate;
char *badaddr;
/*
* Set lwp_state before trying to acquire any
* adaptive lock
*/
/*
* Set up the current cred to use during this trap. u_cred
* no longer exists. t_cred is used instead.
* The current process credential applies to the thread for
* the entire trap. If trapping from the kernel, this
* should already be set up.
*/
/*
* DTrace accesses t_cred in probe context. t_cred
* must always be either NULL, or point to a valid,
* allocated cred structure.
*/
}
}
"C_fpu_trap_handler_enter:type %x", type);
if (tudebug && tudebugfpe)
switch (type) {
/*
* FPU arithmetic exception - fake up a fpq if we
* came here directly from _fp_ieee_exception,
* which is indicated by a zero fpu_qcnt.
*/
#ifdef SF_V9_TABLE_28
/*
* Spitfire and blackbird followed the SPARC V9 manual
* paragraph 3 of section 5.1.7.9 FSR_current_exception
* (cexc) for setting fsr.cexc bits on underflow and
* overflow traps when the fsr.tem.inexact bit is set,
* instead of following Table 28. Bugid 1263234.
*/
{
extern int spitfire_bb_fsr_bug;
if (spitfire_bb_fsr_bug &&
}
}
}
}
#endif /* SF_V9_TABLE_28 */
/*
* The user had a trap handler installed. Jump to
* the trap handler instead of signalling the process.
*/
break;
}
break;
break;
alignfaults++;
if (&vis1_partial_support != NULL) {
if (vis1_partial_support(rp,
goto out;
}
goto out;
}
break;
/*
* If the user has to do unaligned references
* the ugly stuff gets done here.
* Only handles vanilla loads and stores.
*/
alignfaults++;
if (p->p_fixalignment) {
goto out;
}
} else {
} else {
else
}
}
break;
else
break;
default:
/*NOTREACHED*/
}
/*
* We can't get here from a system trap
* Never restart any instruction which got here from an fp trap.
*/
out:
trap_rtt();
}
void
trap_rtt(void)
{
/*
* Restore register window if a debugger modified it.
* Set up to perform a single-step if a debugger requested it.
*/
xregrestore(lwp, 0);
/*
* Set state to LWP_USER here so preempt won't give us a kernel
* priority if it occurs after this point. Call CL_TRAPRET() to
* restore the user-level priority.
*
* It is important that no locks (other than spinlocks) be entered
* after this point before returning to user mode (unless lwp_state
* is set back to LWP_SYS).
*/
}
if (CPU->cpu_runrun)
preempt();
prdostep();
}
#define IS_LDASI(o) \
(o) == (uint32_t)0xC1800000)
#define IS_IMM_ASI(i) (((i) & 0x2000) == 0)
static int
{
extern int segnf_create();
else
if (instrp)
return (0);
if (IS_IMM_ASI(instr))
else
return (0);
len = 1;
}
zero = 0;
if (dbflg) { /* clever v9 reg encoding */
if (rd & 1)
rd >>= 1;
}
if (fpu_exists) {
if (!(_fp_read_fprs() & FPRS_FEF))
fp_enable();
if (dbflg)
else
} else {
fp_enable();
if (dbflg)
else
}
} else {
}
return (1);
}
/*
* The following couple of routines are for userland drivers which
* do atomics to noncached addresses. This sort of worked on previous
* platforms -- the operation really wasn't atomic, but it didn't generate
* a trap as sun4u systems do.
*/
static int
{
(void) flush_user_windows_to_stack(NULL);
return (0);
return (0);
return (0);
}
return (0);
}
return (1);
}
static int
{
(void) flush_user_windows_to_stack(NULL);
return (0);
return (0);
}
return (0);
}
return (1);
}
/*
* This function helps instr_size() determine the operand size.
*/
int
extended_asi_size(int asi)
{
switch (asi) {
case ASI_PST8_P:
case ASI_PST8_S:
case ASI_PST16_P:
case ASI_PST16_S:
case ASI_PST32_P:
case ASI_PST32_S:
case ASI_PST8_PL:
case ASI_PST8_SL:
case ASI_PST16_PL:
case ASI_PST16_SL:
case ASI_PST32_PL:
case ASI_PST32_SL:
return (8);
case ASI_FL8_P:
case ASI_FL8_S:
case ASI_FL8_PL:
case ASI_FL8_SL:
return (1);
case ASI_FL16_P:
case ASI_FL16_S:
case ASI_FL16_PL:
case ASI_FL16_SL:
return (2);
case ASI_BLK_P:
case ASI_BLK_S:
case ASI_BLK_PL:
case ASI_BLK_SL:
case ASI_BLK_COMMIT_P:
case ASI_BLK_COMMIT_S:
return (64);
}
return (0);
}
/*
* Patch non-zero to disable preemption of threads in the kernel.
*/
int IGNORE_KERNEL_PREEMPTION = 0; /* XXX - delete this someday */
struct kpreempt_cnts { /* kernel preemption statistics */
int kpc_idle; /* executing idle thread */
int kpc_intr; /* executing interrupt thread */
int kpc_clock; /* executing clock thread */
int kpc_blocked; /* thread has blocked preemption (t_preempt) */
int kpc_notonproc; /* thread is surrendering processor */
int kpc_inswtch; /* thread has ratified scheduling decision */
int kpc_prilevel; /* processor interrupt level is too high */
int kpc_apreempt; /* asynchronous preemption */
int kpc_spreempt; /* synchronous preemption */
/*
* kernel preemption: forced rescheduling
* preempt the running kernel thread.
*/
void
{
if (IGNORE_KERNEL_PREEMPTION) {
return;
}
/*
* Check that conditions are right for kernel preemption
*/
do {
/*
* either a privileged thread (idle, panic, interrupt)
* or will check when t_preempt is lowered
*/
} else
return;
}
/* this thread will be calling swtch() shortly */
/* already in swtch(), force another */
siron();
}
return;
}
getpil()) >= DISP_LEVEL) {
/*
* We can't preempt this thread if it is at
* a PIL >= DISP_LEVEL since it may be holding
* a spin lock (like sched_lock).
*/
siron(); /* check back later */
return;
}
/*
* block preemption so we don't have multiple preemptions
* pending on the interrupt stack
*/
if (asyncspl != KPREEMPT_SYNC) {
} else
preempt();
} while (CPU->cpu_kprunrun);
}
static enum seg_rw
{
else
return (S_OTHER);
return (S_WRITE);
else
return (S_READ);
}