v9dep.c revision a3c558251a68374a017f360afdc6c4f377a667ea
/*
* 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ucontext.h>
#include <sys/asm_linkage.h>
#include <sys/bootconf.h>
#include <sys/archsystm.h>
#include <sys/privregs.h>
#include <sys/psr_compat.h>
/*
* modify the lower 32bits of a uint64_t
*/
#ifdef _SYSCALL32_IMPL
#endif /* _SYSCALL32_IMPL */
/*
* Set floating-point registers.
* NOTE: 'lwp' might not correspond to 'curthread' since this is
* called from code in /proc to set the registers of another lwp.
*/
void
{
/*
* This is always true for both "real" fp programs and memcpy fp
* programs, because we force fpu_en to MEMCPY_FPU_EN in getfpregs,
* for the memcpy and threads cases where (fpu_en == 0) &&
* (fpu_fprs & FPRS_FEF), if setfpregs is called after getfpregs.
*/
fpu_exists) {
/*
* He's not currently using the FPU but wants to in his
* new context - arrange for this on return to userland.
*/
}
/*
* Get setfpregs to restore fpu_en to zero
* (pfp->fp_fprs & FPRS_FEF) == FPRS_FEF).
*/
/*
* Load up a user's floating point context.
*/
/*
* For v9 kernel, copy all of the fp regs.
* For v8 kernel, copy v8 fp regs (lower half of v9 fp regs).
* Restore entire fsr for v9, only lower half for v8.
*/
if (model == DATAMODEL_LP64)
else
/* FSR ignores these bits on load, so they can not be set */
/*
* If not the current process then resume() will handle it.
*/
/* force resume to reload fp regs */
return;
}
/*
* Load up FPU with new floating point context.
*/
if (fpu_exists) {
#ifdef DEBUG
if (fpdispr)
"setfpregs with fp disabled!\n");
#endif
}
/*
* Load all fp regs for v9 user programs, but only
* load the lower half for v8[plus] programs.
*/
if (model == DATAMODEL_LP64)
else
}
} else {
/*
* Currently the lwp has floating point enabled.
* Turn off FPRS_FEF in user's fprs, saved and
* real copies thereof.
*/
if (fpu_exists) {
fprs = 0;
}
}
}
}
#ifdef _SYSCALL32_IMPL
void
{
}
#endif /* _SYSCALL32_IMPL */
/*
* NOTE: 'lwp' might not correspond to 'curthread' since this is
* called from code in /proc to set the registers of another lwp.
*/
void
{
/*
* If the context being loaded up includes a floating queue,
* we need to simulate those instructions (since we can't reload
* the fpu) and pass the process any appropriate signals
*/
if (fpu_exists) {
}
}
}
/*
* Get floating-point registers.
* NOTE: 'lwp' might not correspond to 'curthread' since this is
* called from code in /proc to set the registers of another lwp.
*/
void
{
/*
* First check the fpu_en case, for normal fp programs.
*/
/*
* Force setfpregs to restore the fp context in
* setfpregs for the memcpy and threads cases (where
* pfp->fpu_en == 0 && (pfp->fp_fprs & FPRS_FEF) == FPRS_FEF).
*/
/*
* If we have an fpu and the current thread owns the fp
* context, flush fp * registers into the pcb. Save all
* the fp regs for v9, xregs_getfpregs saves the upper half
* for v8plus. Save entire fsr for v9, only lower half for v8.
*/
#ifdef DEBUG
if (fpdispr)
"getfpregs with fp disabled!\n");
#endif
}
if (model == DATAMODEL_LP64)
else
}
if (model == DATAMODEL_LP64)
else
} else {
int i;
for (i = 0; i < 32; i++) /* NaN */
if (model == DATAMODEL_LP64) {
for (i = 16; i < 32; i++) /* NaN */
(uint64_t)-1;
}
}
}
#ifdef _SYSCALL32_IMPL
void
{
}
#endif /* _SYSCALL32_IMPL */
/*
* Set general registers.
* NOTE: 'lwp' might not correspond to 'curthread' since this is
* called from code in /proc to set the registers of another lwp.
*/
/* 64-bit gregset_t */
void
{
if (current)
(void) save_syscall_args(); /* copy the args first */
/*
* pc and npc must be 4-byte aligned on sparc.
* We silently make it so to avoid a watchdog reset.
*/
if (current) {
/*
* This was called from a system call, but we
* do not want to return via the shared window;
* restoring the CPU context changes everything.
*/
}
}
/*
* Return the general registers.
* NOTE: 'lwp' might not correspond to 'curthread' since this is
* called from code in /proc to get the registers of another lwp.
*/
void
{
fprs = _fp_read_fprs();
} else {
}
}
void
{
fprs = _fp_read_fprs();
} else {
}
}
/*
* Return the user-level PC.
* If in a system call, return the address of the syscall trap.
*/
{
}
/*
* Set register windows.
*/
void
{
int i;
int wbuf_rwindow_size;
int is64;
is64 = 0;
} else {
is64 = 1;
}
mpcb->mpcb_wbcnt = 0;
for (i = 0; i < wbcnt; i++) {
else
mpcb->mpcb_wbcnt++;
}
}
void
{
int i;
int wbuf_rwindow_size;
int is64;
is64 = 0;
} else {
is64 = 1;
}
mpcb->mpcb_wbcnt = 0;
for (i = 0; i < wbcnt; i++) {
else
mpcb->mpcb_wbcnt++;
}
}
/*
* Get register windows.
* NOTE: 'lwp' might not correspond to 'curthread' since this is
* called from code in /proc to set the registers of another lwp.
*/
void
{
int i;
int wbuf_rwindow_size;
int is64;
is64 = 0;
} else {
is64 = 1;
}
for (i = 0; i < wbcnt; i++) {
else
}
}
void
{
int i;
int wbuf_rwindow_size;
int is64;
is64 = 0;
} else {
is64 = 1;
}
for (i = 0; i < wbcnt; i++) {
else
}
}
/*
* For things that depend on register state being on the stack,
* copy any register windows that get saved into the window buffer
* (in the pcb) onto the stack. This normally gets fixed up
* before returning to a user program. Callers of this routine
* require this to happen immediately because a later kernel
* operation depends on window state (like instruction simulation).
*/
int
{
int j, k;
int err;
int error = 0;
int wbuf_rwindow_size;
int rwindow_size;
int stack_align;
int watched;
else
j = mpcb->mpcb_wbcnt;
while (j > 0) {
IS_V9STACK(sp)) {
} else {
/*
* Reduce sp to a 32 bit value. This was originally
* done by casting down to uint32_t and back up to
* caddr_t, but one compiler didn't like that, so the
* uintptr_t casts were added. The temporary 32 bit
* variable was introduced to avoid depending on all
* compilers to generate the desired assembly code for a
* quadruple cast in a single expression.
*/
}
continue;
if (err != 0) {
/*
* Determine the offending address.
* It may not be the stack pointer itself.
*/
(j * wbuf_rwindow_size));
for (k = 0;
k < rwindow_size / sizeof (int);
break;
}
/* can't happen? */
if (k == rwindow_size / sizeof (int))
}
} else {
/*
* stack was aligned and copyout succeeded;
* move other windows down.
*/
mpcb->mpcb_wbcnt--;
for (k = j; k < mpcb->mpcb_wbcnt; k++) {
((k+1) * wbuf_rwindow_size),
(k * wbuf_rwindow_size),
}
}
if (watched)
} /* while there are windows in the wbuf */
return (error);
}
static int
{
(void) flush_user_windows_to_stack(NULL);
/*
* Reduce r_sp to a 32 bit value before storing it in sp1. This
* was originally done by casting down to uint32_t and back up
* to caddr_t, but that generated complaints under one compiler.
* The uintptr_t cast was added to address that, and the
* temporary 32 bit variable was introduced to avoid depending
* on all compilers to generate the desired assembly code for a
* triple cast in a single expression.
*/
sizeof (struct rwindow32))) == 0)
}
sizeof (struct rwindow32)) == 0))
}
}
int
copy_return_window(int dotwo)
{
if (p->p_model == DATAMODEL_ILP32)
return (copy_return_window32(dotwo));
(void) flush_user_windows_to_stack(NULL);
sizeof (struct rwindow)) == 0))
}
sp2 += STACK_BIAS;
sizeof (struct rwindow)) == 0))
}
}
/*
* Clear registers on exec(2).
*/
void
{
/*
* Initialize user registers.
*/
(void) save_syscall_args(); /* copy args from registers first */
if (p->p_model == DATAMODEL_ILP32)
else
if (!fpu_exists)
/*
* Clear the fixalignment flag
*/
p->p_fixalignment = 0;
/*
* Throw out old user windows, init window buf.
*/
if (p->p_model == DATAMODEL_LP64 &&
} else if (p->p_model == DATAMODEL_ILP32 &&
}
/*
* Here we initialize minimal fpu state.
* The rest is done at the first floating
* point instruction that a process executes
* or by the lib_psr memcpy routines.
*/
if (fpu_exists) {
extern void _fp_write_fprs(unsigned);
_fp_write_fprs(0);
}
}
void
{
}
/*
* Construct the execution environment for the user's signal
* handler and arrange for control to be given to it on return
* to userland. The library code now calls setcontext() to
* clean up after the signal handler, so sigret() is no longer
* needed.
*/
int
{
/*
* 'volatile' is needed to ensure that values are
* correct on the error return from on_fault().
*/
volatile int minstacksz; /* min stack required to catch signal */
int newstack = 0; /* if true, switching to altstack */
int fpq_size = 0;
struct sigframe {
};
volatile size_t xregs_size = 0;
volatile int gwin_size = 0;
volatile int watched = 0;
volatile int watched2 = 0;
/*
* Make sure the current last user window has been flushed to
* the stack save area before we change the sp.
* Restore register window if a debugger modified it.
*/
(void) flush_user_windows_to_stack(NULL);
xregrestore(lwp, 0);
/*
* Clear the watchpoint return stack pointers.
*/
minstacksz = sizeof (struct sigframe);
/*
* We know that sizeof (siginfo_t) is stack-aligned:
* 128 bytes for ILP32, 256 bytes for LP64.
*/
minstacksz += sizeof (siginfo_t);
/*
* These two fields are pointed to by ABI structures and may
* be of arbitrary length. Size them now so we know how big
* the signal frame has to be.
*/
}
if (mpcb->mpcb_wbcnt != 0) {
(SPARC_MAXREGWINDOW * sizeof (caddr_t)) + sizeof (long);
}
/*
* Extra registers, if support by this platform, may be of arbitrary
* length. Size them now so we know how big the signal frame has to be.
* For sparcv9 _LP64 user programs, use asrs instead of the xregs.
*/
/*
* Figure out whether we will be handling this signal on
* an alternate stack specified by the user. Then allocate
* and validate the stack requirements for the signal handler
* context. on_fault will catch any faults.
*/
/*
* Force proper stack pointer alignment, even in the face of a
* misaligned stack pointer from user-level before the signal.
* Don't use the SA() macro because that rounds up, not down.
*/
if (newstack != 0) {
SA(minstacksz));
} else {
/*
* If we were unable to flush all register windows to
* the stack and we are not now on an alternate stack,
* just dump core with a SIGSEGV back in psig().
*/
mpcb->mpcb_wbcnt != 0 &&
return (0);
/*
* Could call grow here, but stack growth now handled below
* in code protected by on_fault().
*/
}
/*
* Make sure process hasn't trashed its stack.
*/
#ifdef DEBUG
printf("sendsig: bad signal stack cmd=%s, pid=%d, sig=%d\n",
printf("sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n",
printf("fp above USRSTACK\n");
#endif
return (0);
}
goto badstack;
/*
* save extra register state if it exists
*/
if (xregs_size != 0) {
}
if (SI_FROMUSER(sip) &&
} else {
}
/*
* We stand on our head to deal with
* the real time profiling signal.
* Fill in the stuff that doesn't fit
* in a normal k_siginfo structure.
*/
int i = sip->si_nsysarg;
while (--i >= 0) {
}
}
} else {
}
/*
* When flush_user_windows_to_stack() can't save all the
* windows to the stack, it puts them in the lwp's pcb.
*/
if (gwin_size != 0) {
} else
if (fpq_size != 0) {
/*
* forget the fp queue so that the signal handler can run
* without being harrassed--it will do a setcontext that will
* re-establish the queue if there still is one
*
* NOTE: fp_runq() relies on the qcnt field being zeroed here
* to terminate its processing of the queue after signal
* delivery.
*/
/* Also, syscall needs to know about this */
} else {
}
/*
* Since we flushed the user's windows and we are changing his
* stack pointer, the window that the user will return to will
* be restored from the save area in the frame we are setting up.
* We copy in save area for old stack pointer so that debuggers
* can do a proper stack backtrace from the signal handler.
*/
if (mpcb->mpcb_wbcnt == 0) {
S_READ);
}
if (newstack != 0) {
if (lwp->lwp_ustack) {
}
}
no_fault();
if (watched2)
if (watched)
/*
* Set up user registers for execution of signal handler.
*/
/* make sure %asi is ASI_PNF */
/*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
*/
return (1);
no_fault();
if (watched2)
if (watched)
if (tuc)
if (xregs)
if (gwp)
#ifdef DEBUG
printf("sendsig: bad signal stack cmd=%s, pid=%d, sig=%d\n",
printf("on fault, sigsp = %p, action = %p, upc = 0x%lx\n",
#endif
return (0);
}
#ifdef _SYSCALL32_IMPL
/*
* Construct the execution environment for the user's signal
* handler and arrange for control to be given to it on return
* to userland. The library code now calls setcontext() to
* clean up after the signal handler, so sigret() is no longer
* needed.
*/
int
{
/*
* 'volatile' is needed to ensure that values are
* correct on the error return from on_fault().
*/
volatile int minstacksz; /* min stack required to catch signal */
int newstack = 0; /* if true, switching to altstack */
struct sigframe32 {
};
struct sigframe32 *volatile fp;
volatile int xregs_size = 0;
volatile int watched = 0;
volatile int watched2 = 0;
/*
* Make sure the current last user window has been flushed to
* the stack save area before we change the sp.
* Restore register window if a debugger modified it.
*/
(void) flush_user_windows_to_stack(NULL);
xregrestore(lwp, 0);
/*
* Clear the watchpoint return stack pointers.
*/
minstacksz = sizeof (struct sigframe32);
minstacksz += sizeof (siginfo32_t);
/*
* These two fields are pointed to by ABI structures and may
* be of arbitrary length. Size them now so we know how big
* the signal frame has to be.
*/
minstacksz += fpq_size;
}
if (mpcb->mpcb_wbcnt != 0) {
(SPARC_MAXREGWINDOW * sizeof (caddr32_t)) +
sizeof (int32_t);
minstacksz += gwin_size;
}
/*
* Extra registers, if supported by this platform, may be of arbitrary
* length. Size them now so we know how big the signal frame has to be.
*/
xregs_size = xregs_getsize(p);
/*
* Figure out whether we will be handling this signal on
* an alternate stack specified by the user. Then allocate
* and validate the stack requirements for the signal handler
* context. on_fault will catch any faults.
*/
/*
* Force proper stack pointer alignment, even in the face of a
* misaligned stack pointer from user-level before the signal.
* Don't use the SA32() macro because that rounds up, not down.
*/
if (newstack != 0) {
fp = (struct sigframe32 *)
SA32(minstacksz));
} else {
/*
* If we were unable to flush all register windows to
* the stack and we are not now on an alternate stack,
* just dump core with a SIGSEGV back in psig().
*/
mpcb->mpcb_wbcnt != 0 &&
return (0);
/*
* Could call grow here, but stack growth now handled below
* in code protected by on_fault().
*/
}
/*
* Make sure process hasn't trashed its stack.
*/
#ifdef DEBUG
printf("sendsig32: bad signal stack cmd=%s, pid=%d, sig=%d\n",
printf("sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n",
printf("fp above USRSTACK32\n");
#endif
return (0);
}
goto badstack;
/*
* save extra register state if it exists
*/
if (xregs_size != 0) {
}
if (SI_FROMUSER(sip) &&
}
sp += sizeof (siginfo32_t);
/*
* We stand on our head to deal with
* the real time profiling signal.
* Fill in the stuff that doesn't fit
* in a normal k_siginfo structure.
*/
int i = sip->si_nsysarg;
while (--i >= 0) {
}
}
} else {
}
/*
* When flush_user_windows_to_stack() can't save all the
* windows to the stack, it puts them in the lwp's pcb.
*/
if (gwin_size != 0) {
} else {
}
if (fpq_size != 0) {
/*
* Update the (already copied out) fpu32.fpu_q pointer
* from NULL to the 32-bit address on the user's stack
* where we then copyout the fq32 to.
*/
/*
* forget the fp queue so that the signal handler can run
* without being harrassed--it will do a setcontext that will
* re-establish the queue if there still is one
*
* NOTE: fp_runq() relies on the qcnt field being zeroed here
* to terminate its processing of the queue after signal
* delivery.
*/
/* Also, syscall needs to know about this */
} else {
}
/*
* Since we flushed the user's windows and we are changing his
* stack pointer, the window that the user will return to will
* be restored from the save area in the frame we are setting up.
* We copy in save area for old stack pointer so that debuggers
* can do a proper stack backtrace from the signal handler.
*/
if (mpcb->mpcb_wbcnt == 0) {
S_READ);
}
if (newstack != 0) {
if (lwp->lwp_ustack) {
sizeof (stack32_t));
}
}
no_fault();
if (watched2)
if (watched)
/*
* Set up user registers for execution of signal handler.
*/
/* make sure %asi is ASI_PNF */
/*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
*/
return (1);
no_fault();
if (watched2)
if (watched)
if (tuc)
if (xregs)
if (gwp)
#ifdef DEBUG
printf("sendsig32: bad signal stack cmd=%s, pid=%d, sig=%d\n",
printf("on fault, sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n",
#endif
return (0);
}
#endif /* _SYSCALL32_IMPL */
/*
* load user registers into lwp.
* thrptr ignored for sparc.
*/
/* ARGSUSED2 */
void
{
else
if (!fpu_exists)
}
/*
* set syscall()'s return values for a lwp.
*/
void
{
}
/*
* set stack pointer for a lwp
*/
void
{
}
/*
* Take any PCB specific actions that are required or flagged in the PCB.
*/
extern void trap_async_hwerr(void);
#pragma weak trap_async_hwerr
void
lwp_pcb_exit(void)
{
}
}
/*
* Invalidate the saved user register windows in the pcb struct
* for the current thread. They will no longer be preserved.
*/
void
lwp_clear_uwin(void)
{
/*
* This has the effect of invalidating all (any) of the
* user level windows that are currently sitting in the
* kernel buffer.
*/
m->mpcb_wbcnt = 0;
}
static uint_t
{
if (tstate & TSTATE_PRIV)
psr |= V9_PSR_IMPLVER;
return (psr);
}
void
{
va += 8;
}
}
#ifdef _SYSCALL32_IMPL
/*
* Copy the floating point queue if and only if there is a queue and a place
* to copy it to. Let xregs take care of the other fp regs, for v8plus.
* The issue is that while we are handling the fq32 in sendsig, we
* still need a 64-bit pointer to it, and the caddr32_t in fpregset32_t
* will not suffice, so we have the third parameter to this function.
*/
void
{
int i;
for (i = 0; i < 32; i++)
}
}
}
/*
* Copy the floating point queue if and only if there is a queue and a place
* to copy it to. Let xregs take care of the other fp regs, for v8plus.
* The *dfq is required to escape the bzero in both this function and in
* ucontext_32ton. The *sfq is required because once the fq32 is copied
* into the kernel, in setcontext, then we need a 64-bit pointer to it.
*/
static void
{
int i;
for (i = 0; i < 32; i++)
else
dest->fpu_q_entrysize = 0;
}
}
}
void
{
int i;
for (i = 0; i < 4; i++) {
}
/* REG_CCR is 0, skip over it and handle it after this loop */
for (i = 1; i < _NGREG32; i++)
/*
* A valid fpregs is only copied in if (uc.uc_flags & UC_FPU),
* otherwise there is no guarantee that anything in fpregs is valid.
*/
} else {
}
}
}
void
{
int i;
for (i = 0; i < 16; i++)
*d++ = (greg32_t)*s++;
}
void
{
int i;
for (i = 0; i < 16; i++)
*d++ = (uint32_t)*s++;
}
#endif /* _SYSCALL32_IMPL */
/*
* The panic code invokes panic_saveregs() to record the contents of a
* regs structure into the specified panic_data structure for debuggers.
*/
void
{
}