/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 1994-1998,2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/klwp.h>
#include <sys/ucontext.h>
#include <sys/procfs.h>
#include <sys/privregs.h>
#include <sys/cpuvar.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/fpu/fpusystm.h>
/*
* Association of extra register state with a struct ucontext is
* done by placing an xrs_t within the uc_mcontext filler area.
*
* The following routines provide an interface for this association.
*/
/*
* clear the struct ucontext extra register state pointer
*/
/* ARGSUSED */
void
xregs_clrptr(klwp_id_t lwp, ucontext_t *uc)
{
uc->uc_mcontext.xrs.xrs_id = 0;
uc->uc_mcontext.xrs.xrs_ptr = NULL;
}
/*
* indicate whether or not an extra register state
* pointer is associated with a struct ucontext
*/
/* ARGSUSED */
int
xregs_hasptr(klwp_id_t lwp, ucontext_t *uc)
{
return (uc->uc_mcontext.xrs.xrs_id == XRS_ID);
}
/*
* get the struct ucontext extra register state pointer field
*/
/* ARGSUSED */
caddr_t
xregs_getptr(klwp_id_t lwp, ucontext_t *uc)
{
if (uc->uc_mcontext.xrs.xrs_id == XRS_ID)
return (uc->uc_mcontext.xrs.xrs_ptr);
return (NULL);
}
/*
* set the struct ucontext extra register state pointer field
*/
/* ARGSUSED */
void
xregs_setptr(klwp_id_t lwp, ucontext_t *uc, caddr_t xrp)
{
uc->uc_mcontext.xrs.xrs_id = XRS_ID;
uc->uc_mcontext.xrs.xrs_ptr = xrp;
}
#ifdef _SYSCALL32_IMPL
/* ARGSUSED */
void
xregs_clrptr32(klwp_id_t lwp, ucontext32_t *uc)
{
uc->uc_mcontext.xrs.xrs_id = 0;
uc->uc_mcontext.xrs.xrs_ptr = 0;
}
/* ARGSUSED */
int
xregs_hasptr32(klwp_id_t lwp, ucontext32_t *uc)
{
return (uc->uc_mcontext.xrs.xrs_id == XRS_ID);
}
/* ARGSUSED */
caddr32_t
xregs_getptr32(klwp_id_t lwp, ucontext32_t *uc)
{
if (uc->uc_mcontext.xrs.xrs_id == XRS_ID)
return (uc->uc_mcontext.xrs.xrs_ptr);
return (0);
}
/* ARGSUSED */
void
xregs_setptr32(klwp_id_t lwp, ucontext32_t *uc, caddr32_t xrp)
{
uc->uc_mcontext.xrs.xrs_id = XRS_ID;
uc->uc_mcontext.xrs.xrs_ptr = xrp;
}
#endif /* _SYSCALL32_IMPL */
/*
* Extra register state manipulation routines.
* NOTE: 'lwp' might not correspond to 'curthread' in any of the
* functions below since they are called from code in /proc to get
* or set the extra registers of another lwp.
*/
int xregs_exists = 1;
#define GET_UPPER_32(all) (uint32_t)((uint64_t)(all) >> 32)
#define SET_ALL_64(upper, lower) \
(((uint64_t)(upper) << 32) | (uint32_t)(lower))
/*
* fill in the extra register state area specified with the
* specified lwp's non-floating-point extra register state
* information
*/
void
xregs_getgregs(klwp_id_t lwp, caddr_t xrp)
{
prxregset_t *xregs = (prxregset_t *)xrp;
struct regs *rp = lwptoregs(lwp);
if (xregs == NULL)
return;
xregs->pr_type = XR_TYPE_V8P;
xregs->pr_un.pr_v8p.pr_xg[XR_G0] = 0;
xregs->pr_un.pr_v8p.pr_xg[XR_G1] = GET_UPPER_32(rp->r_g1);
xregs->pr_un.pr_v8p.pr_xg[XR_G2] = GET_UPPER_32(rp->r_g2);
xregs->pr_un.pr_v8p.pr_xg[XR_G3] = GET_UPPER_32(rp->r_g3);
xregs->pr_un.pr_v8p.pr_xg[XR_G4] = GET_UPPER_32(rp->r_g4);
xregs->pr_un.pr_v8p.pr_xg[XR_G5] = GET_UPPER_32(rp->r_g5);
xregs->pr_un.pr_v8p.pr_xg[XR_G6] = GET_UPPER_32(rp->r_g6);
xregs->pr_un.pr_v8p.pr_xg[XR_G7] = GET_UPPER_32(rp->r_g7);
xregs->pr_un.pr_v8p.pr_xo[XR_O0] = GET_UPPER_32(rp->r_o0);
xregs->pr_un.pr_v8p.pr_xo[XR_O1] = GET_UPPER_32(rp->r_o1);
xregs->pr_un.pr_v8p.pr_xo[XR_O2] = GET_UPPER_32(rp->r_o2);
xregs->pr_un.pr_v8p.pr_xo[XR_O3] = GET_UPPER_32(rp->r_o3);
xregs->pr_un.pr_v8p.pr_xo[XR_O4] = GET_UPPER_32(rp->r_o4);
xregs->pr_un.pr_v8p.pr_xo[XR_O5] = GET_UPPER_32(rp->r_o5);
xregs->pr_un.pr_v8p.pr_xo[XR_O6] = GET_UPPER_32(rp->r_o6);
xregs->pr_un.pr_v8p.pr_xo[XR_O7] = GET_UPPER_32(rp->r_o7);
xregs->pr_un.pr_v8p.pr_tstate = rp->r_tstate;
xregs_getgfiller(lwp, xrp);
}
/*
* fill in the extra register state area specified with the
* specified lwp's floating-point extra register state information
*/
void
xregs_getfpregs(klwp_id_t lwp, caddr_t xrp)
{
prxregset_t *xregs = (prxregset_t *)xrp;
kfpu_t *fp = lwptofpu(lwp);
if (xregs == NULL)
return;
kpreempt_disable();
xregs->pr_type = XR_TYPE_V8P;
if (ttolwp(curthread) == lwp)
fp->fpu_fprs = _fp_read_fprs();
if ((fp->fpu_en) || (fp->fpu_fprs & FPRS_FEF)) {
/*
* If we have an fpu and the current thread owns the fp
* context, flush fp registers into the pcb.
*/
if (fpu_exists && (ttolwp(curthread) == lwp)) {
if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
uint32_t fprs = (FPRS_FEF|FPRS_DU|FPRS_DL);
_fp_write_fprs(fprs);
fp->fpu_fprs = fprs;
#ifdef DEBUG
if (fpdispr) {
cmn_err(CE_NOTE, "xregs_getfpregs "
"with fp disabled!");
}
#endif /* DEBUG */
}
fp_v8p_fksave(fp);
}
(void) kcopy(&fp->fpu_fr.fpu_dregs[16],
&xregs->pr_un.pr_v8p.pr_xfr,
sizeof (xregs->pr_un.pr_v8p.pr_xfr));
xregs->pr_un.pr_v8p.pr_xfsr = GET_UPPER_32(fp->fpu_fsr);
xregs->pr_un.pr_v8p.pr_fprs = fp->fpu_fprs;
xregs_getfpfiller(lwp, xrp);
} else {
int i;
for (i = 0; i < 32; i++) /* Nan */
xregs->pr_un.pr_v8p.pr_xfr.pr_regs[i] = (uint32_t)-1;
}
kpreempt_enable();
}
/*
* fill in the extra register state area specified with
* the specified lwp's extra register state information
*/
void
xregs_get(klwp_id_t lwp, caddr_t xrp)
{
if (xrp != NULL) {
bzero(xrp, sizeof (prxregset_t));
xregs_getgregs(lwp, xrp);
xregs_getfpregs(lwp, xrp);
}
}
/*
* set the specified lwp's non-floating-point extra
* register state based on the specified input
*/
void
xregs_setgregs(klwp_id_t lwp, caddr_t xrp)
{
prxregset_t *xregs = (prxregset_t *)xrp;
struct regs *rp = lwptoregs(lwp);
int current = (lwp == curthread->t_lwp);
if (xregs == NULL)
return;
#ifdef DEBUG
if (xregs->pr_type != XR_TYPE_V8P) {
cmn_err(CE_WARN,
"xregs_setgregs: pr_type is %d and should be %d",
xregs->pr_type, XR_TYPE_V8P);
}
#endif /* DEBUG */
if (current) {
/*
* copy the args from the regs first
*/
(void) save_syscall_args();
}
rp->r_g1 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G1], rp->r_g1);
rp->r_g2 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G2], rp->r_g2);
rp->r_g3 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G3], rp->r_g3);
rp->r_g4 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G4], rp->r_g4);
rp->r_g5 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G5], rp->r_g5);
rp->r_g6 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G6], rp->r_g6);
rp->r_g7 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xg[XR_G7], rp->r_g7);
rp->r_o0 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O0], rp->r_o0);
rp->r_o1 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O1], rp->r_o1);
rp->r_o2 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O2], rp->r_o2);
rp->r_o3 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O3], rp->r_o3);
rp->r_o4 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O4], rp->r_o4);
rp->r_o5 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O5], rp->r_o5);
rp->r_o6 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O6], rp->r_o6);
rp->r_o7 = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xo[XR_O7], rp->r_o7);
rp->r_tstate &= ~((uint64_t)CCR_XCC << TSTATE_CCR_SHIFT);
rp->r_tstate |= xregs->pr_un.pr_v8p.pr_tstate &
((uint64_t)CCR_XCC << TSTATE_CCR_SHIFT);
rp->r_tstate &= ~((uint64_t)TSTATE_ASI_MASK << TSTATE_ASI_SHIFT);
rp->r_tstate |= xregs->pr_un.pr_v8p.pr_tstate &
((uint64_t)TSTATE_ASI_MASK << TSTATE_ASI_SHIFT);
xregs_setgfiller(lwp, xrp);
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.
*/
lwp->lwp_eosys = JUSTRETURN;
curthread->t_post_sys = 1;
}
}
/*
* set the specified lwp's floating-point extra
* register state based on the specified input
*/
void
xregs_setfpregs(klwp_id_t lwp, caddr_t xrp)
{
prxregset_t *xregs = (prxregset_t *)xrp;
kfpu_t *fp = lwptofpu(lwp);
if (xregs == NULL)
return;
#ifdef DEBUG
if (xregs->pr_type != XR_TYPE_V8P) {
cmn_err(CE_WARN,
"xregs_setfpregs: pr_type is %d and should be %d",
xregs->pr_type, XR_TYPE_V8P);
}
#endif /* DEBUG */
if ((fp->fpu_en) || (xregs->pr_un.pr_v8p.pr_fprs & FPRS_FEF)) {
kpreempt_disable();
(void) kcopy(&xregs->pr_un.pr_v8p.pr_xfr,
&fp->fpu_fr.fpu_dregs[16],
sizeof (xregs->pr_un.pr_v8p.pr_xfr));
fp->fpu_fprs = xregs->pr_un.pr_v8p.pr_fprs;
fp->fpu_fsr = SET_ALL_64(xregs->pr_un.pr_v8p.pr_xfsr,
fp->fpu_fsr);
xregs_setfpfiller(lwp, xrp);
/*
* If not the current lwp then resume() will handle it
*/
if (lwp != ttolwp(curthread)) {
/* force resume to reload fp regs */
kpreempt_enable();
return;
}
if (fpu_exists) {
fp->fpu_fprs = _fp_read_fprs();
if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
uint32_t fprs = (FPRS_FEF|FPRS_DU|FPRS_DL);
_fp_write_fprs(fprs);
fp->fpu_fprs = (V9_FPU_FPRS_TYPE)fprs;
#ifdef DEBUG
if (fpdispr) {
cmn_err(CE_NOTE, "xregs_setfpregs "
"with fp disabled!");
}
#endif /* DEBUG */
}
fp_v8p_load(fp);
}
kpreempt_enable();
}
}
/*
* set the specified lwp's extra register
* state based on the specified input
*/
void
xregs_set(klwp_id_t lwp, caddr_t xrp)
{
if (xrp != NULL) {
xregs_setgregs(lwp, xrp);
xregs_setfpregs(lwp, xrp);
}
}
/*
* return the size of the extra register state
*/
int
xregs_getsize(proc_t *p)
{
if (!xregs_exists || p->p_model == DATAMODEL_LP64)
return (0);
return (sizeof (prxregset_t));
}