/* * 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 #include #include #include #include #include #include #include #include #include #include #include /* * 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)); }