/* * fill in the extra register state area specified with the specified lwp's * platform-dependent floating-point extra register state information. * NOTE: 'lwp' might not correspond to 'curthread' since this is * called from code in /proc to get the registers of another lwp. */ void xregs_getfpfiller(klwp_id_t lwp, caddr_t xrp) { prxregset_t *xregs = (prxregset_t *)xrp; kfpu_t *fp = lwptofpu(lwp); uint32_t fprs = (FPRS_FEF|FPRS_DU|FPRS_DL); uint64_t gsr; /* * fp_fksave() does not flush the GSR register into * the lwp area, so do it now */ kpreempt_disable(); if (ttolwp(curthread) == lwp && fpu_exists) { fp->fpu_fprs = _fp_read_fprs(); if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) { _fp_write_fprs(fprs); fp->fpu_fprs = (V9_FPU_FPRS_TYPE)fprs; } save_gsr(fp); } gsr = get_gsr(fp); kpreempt_enable(); PRXREG_GSR(xregs) = gsr; }
/* * fill in the sun4u asrs, ie, the lwp's platform-dependent * floating-point extra register state information */ void getfpasrs(klwp_t *lwp, asrset_t asr) { kfpu_t *fp = lwptofpu(lwp); uint32_t fprs = (FPRS_FEF|FPRS_DU|FPRS_DL); kpreempt_disable(); if (ttolwp(curthread) == lwp) fp->fpu_fprs = _fp_read_fprs(); if ((fp->fpu_en) || (fp->fpu_fprs & FPRS_FEF)) { if (fpu_exists && ttolwp(curthread) == lwp) { if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) { _fp_write_fprs(fprs); fp->fpu_fprs = (V9_FPU_FPRS_TYPE)fprs; } save_gsr(fp); } asr[ASR_GSR] = (int64_t)get_gsr(fp); } kpreempt_enable(); }
void fp_precise(struct regs *rp) { fp_simd_type fpsd; int inst_ftt; union { uint_t i; fp_inst_type inst; } kluge; klwp_t *lwp = ttolwp(curthread); kfpu_t *fp = lwptofpu(lwp); uint64_t gsr; int mstate; if (fpu_exists) save_gsr(fp); gsr = get_gsr(fp); /* * Get the instruction to be emulated from the pc saved by the trap. * Note that the kernel is NOT prepared to handle a kernel fp * exception if it can't pass successfully through the fp simulator. * * If the trap occurred in user mode, set lwp_state to LWP_SYS for the * purposes of clock accounting and switch to the LMS_TRAP microstate. */ if (USERMODE(rp->r_tstate)) { inst_ftt = _fp_read_inst((uint32_t *)rp->r_pc, &kluge.i, &fpsd); mstate = new_mstate(curthread, LMS_TRAP); lwp->lwp_state = LWP_SYS; } else { kluge.i = *(uint_t *)rp->r_pc; inst_ftt = ftt_none; } if (inst_ftt != ftt_none) { /* * Save the bad address and post the signal. * It can only be an ftt_alignment or ftt_fault trap. * XXX - How can this work w/mainsail and do_unaligned? */ fpsd.fp_trapaddr = (caddr_t)rp->r_pc; fp_traps(&fpsd, inst_ftt, rp); } else { /* * Conjure up a floating point queue and advance the pc/npc * to fake a deferred fp trap. We now run the fp simulator * in fp_precise, while allowing setfpregs to call fp_runq, * because this allows us to do the ugly machinations to * inc/dec the pc depending on the trap type, as per * bugid 1210159. fp_runq is still going to have the * generic "how do I connect the "fp queue to the pc/npc" * problem alluded to in bugid 1192883, which is only a * problem for a restorecontext of a v8 fp queue on a * v9 system, which seems like the .000000001% case (on v9)! */ struct _fpq *pfpq = &fp->fpu_q->FQu.fpq; fp_simd_type fpsd; int fptrap; pfpq->fpq_addr = (uint_t *)rp->r_pc; pfpq->fpq_instr = kluge.i; fp->fpu_qcnt = 1; fp->fpu_q_entrysize = sizeof (struct _fpq); kpreempt_disable(); (void) flush_user_windows_to_stack(NULL); fptrap = fpu_vis_sim((fp_simd_type *)&fpsd, (fp_inst_type *)pfpq->fpq_addr, rp, (fsr_type *)&fp->fpu_fsr, gsr, kluge.i); /* update the hardware fp fsr state for sake of ucontext */ if (fpu_exists) _fp_write_pfsr(&fp->fpu_fsr); if (fptrap) { /* back up the pc if the signal needs to be precise */ if (fptrap != ftt_ieee) { fp->fpu_qcnt = 0; } /* post signal */ fp_traps(&fpsd, fptrap, rp); /* decrement queue count for ieee exceptions */ if (fptrap == ftt_ieee) { fp->fpu_qcnt = 0; } } else { fp->fpu_qcnt = 0; } /* update the software pcb copies of hardware fp registers */ if (fpu_exists) { fp_save(fp); } kpreempt_enable(); } /* * Reset lwp_state to LWP_USER for the purposes of clock accounting, * and restore the previously saved microstate. */ if (USERMODE(rp->r_tstate)) { (void) new_mstate(curthread, mstate); lwp->lwp_state = LWP_USER; } }
/* * Copy regs from parent to child. */ void lwp_forkregs(klwp_t *lwp, klwp_t *clwp) { kthread_t *t, *pt = lwptot(lwp); struct machpcb *mpcb = lwptompcb(clwp); struct machpcb *pmpcb = lwptompcb(lwp); kfpu_t *fp, *pfp = lwptofpu(lwp); caddr_t wbuf; uint_t wstate; t = mpcb->mpcb_thread; /* * remember child's fp and wbuf since they will get erased during * the bcopy. */ fp = mpcb->mpcb_fpu; wbuf = mpcb->mpcb_wbuf; wstate = mpcb->mpcb_wstate; /* * Don't copy mpcb_frame since we hand-crafted it * in thread_load(). */ bcopy(lwp->lwp_regs, clwp->lwp_regs, sizeof (struct machpcb) - REGOFF); mpcb->mpcb_thread = t; mpcb->mpcb_fpu = fp; fp->fpu_q = mpcb->mpcb_fpu_q; /* * It is theoretically possibly for the lwp's wstate to * be different from its value assigned in lwp_stk_init, * since lwp_stk_init assumed the data model of the process. * Here, we took on the data model of the cloned lwp. */ if (mpcb->mpcb_wstate != wstate) { if (wstate == WSTATE_USER32) { kmem_cache_free(wbuf32_cache, wbuf); wbuf = kmem_cache_alloc(wbuf64_cache, KM_SLEEP); wstate = WSTATE_USER64; } else { kmem_cache_free(wbuf64_cache, wbuf); wbuf = kmem_cache_alloc(wbuf32_cache, KM_SLEEP); wstate = WSTATE_USER32; } } mpcb->mpcb_pa = va_to_pa(mpcb); mpcb->mpcb_wbuf = wbuf; mpcb->mpcb_wbuf_pa = va_to_pa(wbuf); ASSERT(mpcb->mpcb_wstate == wstate); if (mpcb->mpcb_wbcnt != 0) { bcopy(pmpcb->mpcb_wbuf, mpcb->mpcb_wbuf, mpcb->mpcb_wbcnt * ((mpcb->mpcb_wstate == WSTATE_USER32) ? sizeof (struct rwindow32) : sizeof (struct rwindow64))); } if (pt == curthread) pfp->fpu_fprs = _fp_read_fprs(); if ((pfp->fpu_en) || (pfp->fpu_fprs & FPRS_FEF)) { if (pt == curthread && fpu_exists) { save_gsr(clwp->lwp_fpu); } else { uint64_t gsr; gsr = get_gsr(lwp->lwp_fpu); set_gsr(gsr, clwp->lwp_fpu); } fp_fork(lwp, clwp); } }