17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate * with the License.
87c478bd9Sstevel@tonic-gate *
97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate * and limitations under the License.
137c478bd9Sstevel@tonic-gate *
147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate *
207c478bd9Sstevel@tonic-gate * CDDL HEADER END
217c478bd9Sstevel@tonic-gate */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <sys/param.h>
297c478bd9Sstevel@tonic-gate #include <sys/signal.h>
307c478bd9Sstevel@tonic-gate #include <sys/trap.h>
317c478bd9Sstevel@tonic-gate #include <sys/machtrap.h>
327c478bd9Sstevel@tonic-gate #include <sys/fault.h>
337c478bd9Sstevel@tonic-gate #include <sys/systm.h>
347c478bd9Sstevel@tonic-gate #include <sys/user.h>
357c478bd9Sstevel@tonic-gate #include <sys/file.h>
367c478bd9Sstevel@tonic-gate #include <sys/proc.h>
377c478bd9Sstevel@tonic-gate #include <sys/core.h>
387c478bd9Sstevel@tonic-gate #include <sys/pcb.h>
397c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
407c478bd9Sstevel@tonic-gate #include <sys/thread.h>
417c478bd9Sstevel@tonic-gate #include <sys/disp.h>
427c478bd9Sstevel@tonic-gate #include <sys/stack.h>
437c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
447c478bd9Sstevel@tonic-gate #include <sys/privregs.h>
457c478bd9Sstevel@tonic-gate #include <sys/debug.h>
467c478bd9Sstevel@tonic-gate
477c478bd9Sstevel@tonic-gate #include <sys/fpu/fpu_simulator.h>
487c478bd9Sstevel@tonic-gate #include <sys/fpu/globals.h>
497c478bd9Sstevel@tonic-gate #include <sys/fpu/fpusystm.h>
507c478bd9Sstevel@tonic-gate
517c478bd9Sstevel@tonic-gate int fpdispr = 0;
527c478bd9Sstevel@tonic-gate
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate * For use by procfs to save the floating point context of the thread.
557c478bd9Sstevel@tonic-gate * Note the if (ttolwp(lwp) == curthread) in prstop, which calls
567c478bd9Sstevel@tonic-gate * this function, ensures that it is safe to read the fprs here.
577c478bd9Sstevel@tonic-gate */
587c478bd9Sstevel@tonic-gate void
fp_prsave(kfpu_t * fp)597c478bd9Sstevel@tonic-gate fp_prsave(kfpu_t *fp)
607c478bd9Sstevel@tonic-gate {
617c478bd9Sstevel@tonic-gate if ((fp->fpu_en) || (fp->fpu_fprs & FPRS_FEF)) {
627c478bd9Sstevel@tonic-gate kpreempt_disable();
637c478bd9Sstevel@tonic-gate if (fpu_exists) {
647c478bd9Sstevel@tonic-gate fp->fpu_fprs = _fp_read_fprs();
657c478bd9Sstevel@tonic-gate if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
667c478bd9Sstevel@tonic-gate uint32_t fprs = (FPRS_FEF|FPRS_DU|FPRS_DL);
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate _fp_write_fprs(fprs);
697c478bd9Sstevel@tonic-gate fp->fpu_fprs = fprs;
707c478bd9Sstevel@tonic-gate #ifdef DEBUG
717c478bd9Sstevel@tonic-gate if (fpdispr)
727c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
737c478bd9Sstevel@tonic-gate "fp_prsave with fp disabled!");
747c478bd9Sstevel@tonic-gate #endif
757c478bd9Sstevel@tonic-gate }
767c478bd9Sstevel@tonic-gate fp_fksave(fp);
777c478bd9Sstevel@tonic-gate }
787c478bd9Sstevel@tonic-gate kpreempt_enable();
797c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate }
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate * Copy the floating point context of the forked thread.
847c478bd9Sstevel@tonic-gate */
857c478bd9Sstevel@tonic-gate void
fp_fork(klwp_t * lwp,klwp_t * clwp)867c478bd9Sstevel@tonic-gate fp_fork(klwp_t *lwp, klwp_t *clwp)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate kfpu_t *cfp, *pfp;
897c478bd9Sstevel@tonic-gate int i;
907c478bd9Sstevel@tonic-gate
917c478bd9Sstevel@tonic-gate cfp = lwptofpu(clwp);
927c478bd9Sstevel@tonic-gate pfp = lwptofpu(lwp);
937c478bd9Sstevel@tonic-gate
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate * copy the parents fpq
967c478bd9Sstevel@tonic-gate */
977c478bd9Sstevel@tonic-gate cfp->fpu_qcnt = pfp->fpu_qcnt;
987c478bd9Sstevel@tonic-gate for (i = 0; i < pfp->fpu_qcnt; i++)
997c478bd9Sstevel@tonic-gate cfp->fpu_q[i] = pfp->fpu_q[i];
1007c478bd9Sstevel@tonic-gate
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate * save the context of the parent into the childs fpu structure
1037c478bd9Sstevel@tonic-gate */
1047c478bd9Sstevel@tonic-gate cfp->fpu_fprs = pfp->fpu_fprs;
1057c478bd9Sstevel@tonic-gate if (ttolwp(curthread) == lwp && fpu_exists) {
1067c478bd9Sstevel@tonic-gate fp_fksave(cfp);
1077c478bd9Sstevel@tonic-gate } else {
1087c478bd9Sstevel@tonic-gate for (i = 0; i < 32; i++)
1097c478bd9Sstevel@tonic-gate cfp->fpu_fr.fpu_regs[i] = pfp->fpu_fr.fpu_regs[i];
1107c478bd9Sstevel@tonic-gate for (i = 16; i < 32; i++)
1117c478bd9Sstevel@tonic-gate cfp->fpu_fr.fpu_dregs[i] = pfp->fpu_fr.fpu_dregs[i];
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate cfp->fpu_en = 1;
1147c478bd9Sstevel@tonic-gate }
1157c478bd9Sstevel@tonic-gate
1167c478bd9Sstevel@tonic-gate /*
1177c478bd9Sstevel@tonic-gate * Free any state associated with floating point context.
1187c478bd9Sstevel@tonic-gate * Fp_free can be called in two cases:
1197c478bd9Sstevel@tonic-gate * 1) from reaper -> thread_free -> lwp_freeregs -> fp_free
1207c478bd9Sstevel@tonic-gate * fp context belongs to a thread on deathrow
1217c478bd9Sstevel@tonic-gate * nothing to do, thread will never be resumed
1227c478bd9Sstevel@tonic-gate * thread calling ctxfree is reaper
1237c478bd9Sstevel@tonic-gate *
1247c478bd9Sstevel@tonic-gate * 2) from exec -> lwp_freeregs -> fp_free
1257c478bd9Sstevel@tonic-gate * fp context belongs to the current thread
1267c478bd9Sstevel@tonic-gate * must disable fpu, thread calling ctxfree is curthread
1277c478bd9Sstevel@tonic-gate */
1287c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
1297c478bd9Sstevel@tonic-gate void
fp_free(kfpu_t * fp,int isexec)1307c478bd9Sstevel@tonic-gate fp_free(kfpu_t *fp, int isexec)
1317c478bd9Sstevel@tonic-gate {
1327c478bd9Sstevel@tonic-gate int s;
1337c478bd9Sstevel@tonic-gate uint32_t fprs = 0;
1347c478bd9Sstevel@tonic-gate
1357c478bd9Sstevel@tonic-gate if (curthread->t_lwp != NULL && lwptofpu(curthread->t_lwp) == fp) {
1367c478bd9Sstevel@tonic-gate fp->fpu_en = 0;
1377c478bd9Sstevel@tonic-gate fp->fpu_fprs = fprs;
1387c478bd9Sstevel@tonic-gate s = splhigh();
1397c478bd9Sstevel@tonic-gate _fp_write_fprs(fprs);
1407c478bd9Sstevel@tonic-gate splx(s);
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate
1447c478bd9Sstevel@tonic-gate
1457c478bd9Sstevel@tonic-gate #ifdef SF_ERRATA_30 /* call causes fp-disabled */
1467c478bd9Sstevel@tonic-gate extern int spitfire_call_bug;
1477c478bd9Sstevel@tonic-gate int ill_fpcalls;
1487c478bd9Sstevel@tonic-gate #endif
1497c478bd9Sstevel@tonic-gate
1507c478bd9Sstevel@tonic-gate void
fp_enable(void)1517c478bd9Sstevel@tonic-gate fp_enable(void)
1527c478bd9Sstevel@tonic-gate {
1537c478bd9Sstevel@tonic-gate klwp_id_t lwp;
1547c478bd9Sstevel@tonic-gate kfpu_t *fp;
1557c478bd9Sstevel@tonic-gate
1567c478bd9Sstevel@tonic-gate lwp = ttolwp(curthread);
1577c478bd9Sstevel@tonic-gate ASSERT(lwp != NULL);
1587c478bd9Sstevel@tonic-gate fp = lwptofpu(lwp);
1597c478bd9Sstevel@tonic-gate
1607c478bd9Sstevel@tonic-gate if (fpu_exists) {
1617c478bd9Sstevel@tonic-gate if (fp->fpu_en) {
1627c478bd9Sstevel@tonic-gate #ifdef DEBUG
1637c478bd9Sstevel@tonic-gate if (fpdispr)
1647c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
1657c478bd9Sstevel@tonic-gate "fpu disabled, but already enabled\n");
1667c478bd9Sstevel@tonic-gate #endif
1677c478bd9Sstevel@tonic-gate if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
1687c478bd9Sstevel@tonic-gate fp->fpu_fprs = FPRS_FEF;
1697c478bd9Sstevel@tonic-gate #ifdef DEBUG
1707c478bd9Sstevel@tonic-gate if (fpdispr)
1717c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
1727c478bd9Sstevel@tonic-gate "fpu disabled, saved fprs disabled\n");
1737c478bd9Sstevel@tonic-gate #endif
1747c478bd9Sstevel@tonic-gate }
1757c478bd9Sstevel@tonic-gate _fp_write_fprs(FPRS_FEF);
1767c478bd9Sstevel@tonic-gate fp_restore(fp);
1777c478bd9Sstevel@tonic-gate } else {
1787c478bd9Sstevel@tonic-gate fp->fpu_en = 1;
1797c478bd9Sstevel@tonic-gate fp->fpu_fsr = 0;
1807c478bd9Sstevel@tonic-gate fp->fpu_fprs = FPRS_FEF;
1817c478bd9Sstevel@tonic-gate _fp_write_fprs(FPRS_FEF);
1827c478bd9Sstevel@tonic-gate fp_clearregs(fp);
1837c478bd9Sstevel@tonic-gate }
1847c478bd9Sstevel@tonic-gate } else {
1857c478bd9Sstevel@tonic-gate int i;
1867c478bd9Sstevel@tonic-gate
1877c478bd9Sstevel@tonic-gate if (!fp->fpu_en) {
1887c478bd9Sstevel@tonic-gate fp->fpu_en = 1;
1897c478bd9Sstevel@tonic-gate fp->fpu_fsr = 0;
1907c478bd9Sstevel@tonic-gate for (i = 0; i < 32; i++)
1917c478bd9Sstevel@tonic-gate fp->fpu_fr.fpu_regs[i] = (uint_t)-1; /* NaN */
1927c478bd9Sstevel@tonic-gate for (i = 16; i < 32; i++) /* NaN */
1937c478bd9Sstevel@tonic-gate fp->fpu_fr.fpu_dregs[i] = (uint64_t)-1;
1947c478bd9Sstevel@tonic-gate }
1957c478bd9Sstevel@tonic-gate }
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate /*
1997c478bd9Sstevel@tonic-gate * fp_disabled normally occurs when the first floating point in a non-threaded
2007c478bd9Sstevel@tonic-gate * program causes an fp_disabled trap. For threaded programs, the ILP32 threads
2017c478bd9Sstevel@tonic-gate * library calls the .setpsr fasttrap, which has been modified to also set the
2027c478bd9Sstevel@tonic-gate * appropriate bits in fpu_en and fpu_fprs, as well as to enable the %fprs,
2037c478bd9Sstevel@tonic-gate * as before. The LP64 threads library will write to the %fprs directly,
2047c478bd9Sstevel@tonic-gate * so fpu_en will never get updated for LP64 threaded programs,
2057c478bd9Sstevel@tonic-gate * although fpu_fprs will, via resume.
2067c478bd9Sstevel@tonic-gate */
2077c478bd9Sstevel@tonic-gate void
fp_disabled(struct regs * rp)2087c478bd9Sstevel@tonic-gate fp_disabled(struct regs *rp)
2097c478bd9Sstevel@tonic-gate {
2107c478bd9Sstevel@tonic-gate klwp_id_t lwp;
2117c478bd9Sstevel@tonic-gate kfpu_t *fp;
2127c478bd9Sstevel@tonic-gate int ftt;
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate #ifdef SF_ERRATA_30 /* call causes fp-disabled */
2157c478bd9Sstevel@tonic-gate /*
2167c478bd9Sstevel@tonic-gate * This code is here because sometimes the call instruction
2177c478bd9Sstevel@tonic-gate * generates an fp_disabled trap when the call offset is large.
2187c478bd9Sstevel@tonic-gate */
2197c478bd9Sstevel@tonic-gate if (spitfire_call_bug) {
2207c478bd9Sstevel@tonic-gate uint_t instr = 0;
2217c478bd9Sstevel@tonic-gate extern void trap(struct regs *rp, caddr_t addr, uint32_t type,
2227c478bd9Sstevel@tonic-gate uint32_t mmu_fsr);
2237c478bd9Sstevel@tonic-gate
2247c478bd9Sstevel@tonic-gate if (USERMODE(rp->r_tstate)) {
2257c478bd9Sstevel@tonic-gate (void) fuword32((void *)rp->r_pc, &instr);
2267c478bd9Sstevel@tonic-gate } else {
2277c478bd9Sstevel@tonic-gate instr = *(uint_t *)(rp->r_pc);
2287c478bd9Sstevel@tonic-gate }
2297c478bd9Sstevel@tonic-gate if ((instr & 0xc0000000) == 0x40000000) {
2307c478bd9Sstevel@tonic-gate ill_fpcalls++;
2317c478bd9Sstevel@tonic-gate trap(rp, NULL, T_UNIMP_INSTR, 0);
2327c478bd9Sstevel@tonic-gate return;
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate #endif /* SF_ERRATA_30 - call causes fp-disabled */
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate #ifdef CHEETAH_ERRATUM_109 /* interrupts not taken during fpops */
2387c478bd9Sstevel@tonic-gate /*
2397c478bd9Sstevel@tonic-gate * UltraSPARC III will report spurious fp-disabled exceptions when
2407c478bd9Sstevel@tonic-gate * the pipe is full of fpops and an interrupt is triggered. By the
2417c478bd9Sstevel@tonic-gate * time we get here the interrupt has been taken and we just need
2427c478bd9Sstevel@tonic-gate * to return to where we came from and try again.
2437c478bd9Sstevel@tonic-gate */
2447c478bd9Sstevel@tonic-gate if (fpu_exists && _fp_read_fprs() & FPRS_FEF)
2457c478bd9Sstevel@tonic-gate return;
2467c478bd9Sstevel@tonic-gate #endif /* CHEETAH_ERRATUM_109 */
2477c478bd9Sstevel@tonic-gate
2487c478bd9Sstevel@tonic-gate lwp = ttolwp(curthread);
2497c478bd9Sstevel@tonic-gate ASSERT(lwp != NULL);
2507c478bd9Sstevel@tonic-gate fp = lwptofpu(lwp);
2517c478bd9Sstevel@tonic-gate if (fpu_exists) {
2527c478bd9Sstevel@tonic-gate kpreempt_disable();
2537c478bd9Sstevel@tonic-gate if (fp->fpu_en) {
2547c478bd9Sstevel@tonic-gate #ifdef DEBUG
2557c478bd9Sstevel@tonic-gate if (fpdispr)
2567c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
2577c478bd9Sstevel@tonic-gate "fpu disabled, but already enabled\n");
2587c478bd9Sstevel@tonic-gate #endif
2597c478bd9Sstevel@tonic-gate if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
2607c478bd9Sstevel@tonic-gate fp->fpu_fprs = FPRS_FEF;
2617c478bd9Sstevel@tonic-gate #ifdef DEBUG
2627c478bd9Sstevel@tonic-gate if (fpdispr)
2637c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
2647c478bd9Sstevel@tonic-gate "fpu disabled, saved fprs disabled\n");
2657c478bd9Sstevel@tonic-gate #endif
2667c478bd9Sstevel@tonic-gate }
2677c478bd9Sstevel@tonic-gate _fp_write_fprs(FPRS_FEF);
2687c478bd9Sstevel@tonic-gate fp_restore(fp);
2697c478bd9Sstevel@tonic-gate } else {
2707c478bd9Sstevel@tonic-gate fp->fpu_en = 1;
2717c478bd9Sstevel@tonic-gate fp->fpu_fsr = 0;
2727c478bd9Sstevel@tonic-gate fp->fpu_fprs = FPRS_FEF;
2737c478bd9Sstevel@tonic-gate _fp_write_fprs(FPRS_FEF);
2747c478bd9Sstevel@tonic-gate fp_clearregs(fp);
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate kpreempt_enable();
2777c478bd9Sstevel@tonic-gate } else {
2787c478bd9Sstevel@tonic-gate fp_simd_type fpsd;
2797c478bd9Sstevel@tonic-gate int i;
2807c478bd9Sstevel@tonic-gate
2817c478bd9Sstevel@tonic-gate (void) flush_user_windows_to_stack(NULL);
2827c478bd9Sstevel@tonic-gate if (!fp->fpu_en) {
2837c478bd9Sstevel@tonic-gate fp->fpu_en = 1;
2847c478bd9Sstevel@tonic-gate fp->fpu_fsr = 0;
2857c478bd9Sstevel@tonic-gate for (i = 0; i < 32; i++)
2867c478bd9Sstevel@tonic-gate fp->fpu_fr.fpu_regs[i] = (uint_t)-1; /* NaN */
2877c478bd9Sstevel@tonic-gate for (i = 16; i < 32; i++) /* NaN */
2887c478bd9Sstevel@tonic-gate fp->fpu_fr.fpu_dregs[i] = (uint64_t)-1;
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate if (ftt = fp_emulator(&fpsd, (fp_inst_type *)rp->r_pc,
2917c478bd9Sstevel@tonic-gate rp, (ulong_t *)rp->r_sp, fp)) {
292*c5a5e6f4SGordon Ross fp->fpu_q_entrysize = sizeof (struct _fpq);
2937c478bd9Sstevel@tonic-gate fp_traps(&fpsd, ftt, rp);
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate }
2967c478bd9Sstevel@tonic-gate }
2977c478bd9Sstevel@tonic-gate
2987c478bd9Sstevel@tonic-gate /*
2997c478bd9Sstevel@tonic-gate * Process the floating point queue in lwp->lwp_pcb.
3007c478bd9Sstevel@tonic-gate *
3017c478bd9Sstevel@tonic-gate * Each entry in the floating point queue is processed in turn.
3027c478bd9Sstevel@tonic-gate * If processing an entry results in an exception fp_traps() is called to
3037c478bd9Sstevel@tonic-gate * handle the exception - this usually results in the generation of a signal
3047c478bd9Sstevel@tonic-gate * to be delivered to the user. There are 2 possible outcomes to this (note
3057c478bd9Sstevel@tonic-gate * that hardware generated signals cannot be held!):
3067c478bd9Sstevel@tonic-gate *
3077c478bd9Sstevel@tonic-gate * 1. If the signal is being ignored we continue to process the rest
3087c478bd9Sstevel@tonic-gate * of the entries in the queue.
3097c478bd9Sstevel@tonic-gate *
3107c478bd9Sstevel@tonic-gate * 2. If arrangements have been made for return to a user signal handler,
3117c478bd9Sstevel@tonic-gate * sendsig() will have copied the floating point queue onto the user's
3127c478bd9Sstevel@tonic-gate * signal stack and zero'ed the queue count in the u_pcb. Note that
3137c478bd9Sstevel@tonic-gate * this has the side effect of terminating fp_runq's processing loop.
3147c478bd9Sstevel@tonic-gate * We will re-run the floating point queue on return from the user
3157c478bd9Sstevel@tonic-gate * signal handler if necessary as part of normal setcontext processing.
3167c478bd9Sstevel@tonic-gate */
3177c478bd9Sstevel@tonic-gate void
fp_runq(struct regs * rp)3187c478bd9Sstevel@tonic-gate fp_runq(struct regs *rp)
3197c478bd9Sstevel@tonic-gate {
3207c478bd9Sstevel@tonic-gate kfpu_t *fp = lwptofpu(curthread->t_lwp);
321*c5a5e6f4SGordon Ross struct _fq *fqp = fp->fpu_q;
3227c478bd9Sstevel@tonic-gate fp_simd_type fpsd;
3237c478bd9Sstevel@tonic-gate uint64_t gsr = get_gsr(fp);
3247c478bd9Sstevel@tonic-gate
3257c478bd9Sstevel@tonic-gate /*
3267c478bd9Sstevel@tonic-gate * don't preempt while manipulating the queue
3277c478bd9Sstevel@tonic-gate */
3287c478bd9Sstevel@tonic-gate kpreempt_disable();
3297c478bd9Sstevel@tonic-gate
3307c478bd9Sstevel@tonic-gate while (fp->fpu_qcnt) {
3317c478bd9Sstevel@tonic-gate int fptrap;
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate fptrap = fpu_simulator((fp_simd_type *)&fpsd,
3347c478bd9Sstevel@tonic-gate (fp_inst_type *)fqp->FQu.fpq.fpq_addr,
3357c478bd9Sstevel@tonic-gate (fsr_type *)&fp->fpu_fsr, gsr,
3367c478bd9Sstevel@tonic-gate fqp->FQu.fpq.fpq_instr);
3377c478bd9Sstevel@tonic-gate if (fptrap) {
3387c478bd9Sstevel@tonic-gate /*
3397c478bd9Sstevel@tonic-gate * Instruction could not be simulated so we will
3407c478bd9Sstevel@tonic-gate * attempt to deliver a signal.
3417c478bd9Sstevel@tonic-gate * We may be called again upon signal exit (setcontext)
3427c478bd9Sstevel@tonic-gate * and can continue to process the queue then.
3437c478bd9Sstevel@tonic-gate */
3447c478bd9Sstevel@tonic-gate if (fqp != fp->fpu_q) {
3457c478bd9Sstevel@tonic-gate int i;
346*c5a5e6f4SGordon Ross struct _fq *fqdp;
3477c478bd9Sstevel@tonic-gate
3487c478bd9Sstevel@tonic-gate /*
3497c478bd9Sstevel@tonic-gate * We need to normalize the floating queue so
3507c478bd9Sstevel@tonic-gate * the excepting instruction is at the head,
3517c478bd9Sstevel@tonic-gate * so that the queue may be copied onto the
3527c478bd9Sstevel@tonic-gate * user signal stack by sendsig().
3537c478bd9Sstevel@tonic-gate */
3547c478bd9Sstevel@tonic-gate fqdp = fp->fpu_q;
3557c478bd9Sstevel@tonic-gate for (i = fp->fpu_qcnt; i; i--) {
3567c478bd9Sstevel@tonic-gate *fqdp++ = *fqp++;
3577c478bd9Sstevel@tonic-gate }
3587c478bd9Sstevel@tonic-gate fqp = fp->fpu_q;
3597c478bd9Sstevel@tonic-gate }
360*c5a5e6f4SGordon Ross fp->fpu_q_entrysize = sizeof (struct _fpq);
3617c478bd9Sstevel@tonic-gate
3627c478bd9Sstevel@tonic-gate /*
3637c478bd9Sstevel@tonic-gate * fpu_simulator uses the fp registers directly but it
3647c478bd9Sstevel@tonic-gate * uses the software copy of the fsr. We need to write
3657c478bd9Sstevel@tonic-gate * that back to fpu so that fpu's state is current for
3667c478bd9Sstevel@tonic-gate * ucontext.
3677c478bd9Sstevel@tonic-gate */
3687c478bd9Sstevel@tonic-gate if (fpu_exists)
3697c478bd9Sstevel@tonic-gate _fp_write_pfsr(&fp->fpu_fsr);
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate /* post signal */
3727c478bd9Sstevel@tonic-gate fp_traps(&fpsd, fptrap, rp);
3737c478bd9Sstevel@tonic-gate
3747c478bd9Sstevel@tonic-gate /*
3757c478bd9Sstevel@tonic-gate * Break from loop to allow signal to be sent.
3767c478bd9Sstevel@tonic-gate * If there are other instructions in the fp queue
3777c478bd9Sstevel@tonic-gate * they will be processed when/if the user retuns
3787c478bd9Sstevel@tonic-gate * from the signal handler with a non-empty queue.
3797c478bd9Sstevel@tonic-gate */
3807c478bd9Sstevel@tonic-gate break;
3817c478bd9Sstevel@tonic-gate }
3827c478bd9Sstevel@tonic-gate fp->fpu_qcnt--;
3837c478bd9Sstevel@tonic-gate fqp++;
3847c478bd9Sstevel@tonic-gate }
3857c478bd9Sstevel@tonic-gate
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate * fpu_simulator uses the fp registers directly, so we have
3887c478bd9Sstevel@tonic-gate * to update the pcb copies to keep current, but it uses the
3897c478bd9Sstevel@tonic-gate * software copy of the fsr, so we write that back to fpu
3907c478bd9Sstevel@tonic-gate */
3917c478bd9Sstevel@tonic-gate if (fpu_exists) {
3927c478bd9Sstevel@tonic-gate int i;
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate for (i = 0; i < 32; i++)
3957c478bd9Sstevel@tonic-gate _fp_read_pfreg(&fp->fpu_fr.fpu_regs[i], i);
3967c478bd9Sstevel@tonic-gate for (i = 16; i < 32; i++)
3977c478bd9Sstevel@tonic-gate _fp_read_pdreg(&fp->fpu_fr.fpu_dregs[i], i);
3987c478bd9Sstevel@tonic-gate _fp_write_pfsr(&fp->fpu_fsr);
3997c478bd9Sstevel@tonic-gate }
4007c478bd9Sstevel@tonic-gate
4017c478bd9Sstevel@tonic-gate kpreempt_enable();
4027c478bd9Sstevel@tonic-gate }
4037c478bd9Sstevel@tonic-gate
4047c478bd9Sstevel@tonic-gate /*
4057c478bd9Sstevel@tonic-gate * Get the precise trapped V9 floating point instruction.
4067c478bd9Sstevel@tonic-gate * Fake up a queue to process. If getting the instruction results
4077c478bd9Sstevel@tonic-gate * in an exception fp_traps() is called to handle the exception - this
4087c478bd9Sstevel@tonic-gate * usually results in the generation of a signal to be delivered to the user.
4097c478bd9Sstevel@tonic-gate */
4107c478bd9Sstevel@tonic-gate
4117c478bd9Sstevel@tonic-gate void
fp_precise(struct regs * rp)4127c478bd9Sstevel@tonic-gate fp_precise(struct regs *rp)
4137c478bd9Sstevel@tonic-gate {
4147c478bd9Sstevel@tonic-gate fp_simd_type fpsd;
4157c478bd9Sstevel@tonic-gate int inst_ftt;
4167c478bd9Sstevel@tonic-gate
4177c478bd9Sstevel@tonic-gate union {
4187c478bd9Sstevel@tonic-gate uint_t i;
4197c478bd9Sstevel@tonic-gate fp_inst_type inst;
4207c478bd9Sstevel@tonic-gate } kluge;
4217c478bd9Sstevel@tonic-gate
4227c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread);
4237c478bd9Sstevel@tonic-gate kfpu_t *fp = lwptofpu(lwp);
4247c478bd9Sstevel@tonic-gate uint64_t gsr;
4257c478bd9Sstevel@tonic-gate int mstate;
4267c478bd9Sstevel@tonic-gate if (fpu_exists)
4277c478bd9Sstevel@tonic-gate save_gsr(fp);
4287c478bd9Sstevel@tonic-gate gsr = get_gsr(fp);
4297c478bd9Sstevel@tonic-gate
4307c478bd9Sstevel@tonic-gate /*
4317c478bd9Sstevel@tonic-gate * Get the instruction to be emulated from the pc saved by the trap.
4327c478bd9Sstevel@tonic-gate * Note that the kernel is NOT prepared to handle a kernel fp
4337c478bd9Sstevel@tonic-gate * exception if it can't pass successfully through the fp simulator.
4347c478bd9Sstevel@tonic-gate *
4357c478bd9Sstevel@tonic-gate * If the trap occurred in user mode, set lwp_state to LWP_SYS for the
4367c478bd9Sstevel@tonic-gate * purposes of clock accounting and switch to the LMS_TRAP microstate.
4377c478bd9Sstevel@tonic-gate */
4387c478bd9Sstevel@tonic-gate if (USERMODE(rp->r_tstate)) {
4397c478bd9Sstevel@tonic-gate inst_ftt = _fp_read_inst((uint32_t *)rp->r_pc, &kluge.i, &fpsd);
4407c478bd9Sstevel@tonic-gate mstate = new_mstate(curthread, LMS_TRAP);
4417c478bd9Sstevel@tonic-gate lwp->lwp_state = LWP_SYS;
4427c478bd9Sstevel@tonic-gate } else {
4437c478bd9Sstevel@tonic-gate kluge.i = *(uint_t *)rp->r_pc;
4447c478bd9Sstevel@tonic-gate inst_ftt = ftt_none;
4457c478bd9Sstevel@tonic-gate }
4467c478bd9Sstevel@tonic-gate
4477c478bd9Sstevel@tonic-gate if (inst_ftt != ftt_none) {
4487c478bd9Sstevel@tonic-gate /*
4497c478bd9Sstevel@tonic-gate * Save the bad address and post the signal.
4507c478bd9Sstevel@tonic-gate * It can only be an ftt_alignment or ftt_fault trap.
4517c478bd9Sstevel@tonic-gate * XXX - How can this work w/mainsail and do_unaligned?
4527c478bd9Sstevel@tonic-gate */
4537c478bd9Sstevel@tonic-gate fpsd.fp_trapaddr = (caddr_t)rp->r_pc;
4547c478bd9Sstevel@tonic-gate fp_traps(&fpsd, inst_ftt, rp);
4557c478bd9Sstevel@tonic-gate } else {
4567c478bd9Sstevel@tonic-gate /*
4577c478bd9Sstevel@tonic-gate * Conjure up a floating point queue and advance the pc/npc
4587c478bd9Sstevel@tonic-gate * to fake a deferred fp trap. We now run the fp simulator
4597c478bd9Sstevel@tonic-gate * in fp_precise, while allowing setfpregs to call fp_runq,
4607c478bd9Sstevel@tonic-gate * because this allows us to do the ugly machinations to
4617c478bd9Sstevel@tonic-gate * inc/dec the pc depending on the trap type, as per
4627c478bd9Sstevel@tonic-gate * bugid 1210159. fp_runq is still going to have the
4637c478bd9Sstevel@tonic-gate * generic "how do I connect the "fp queue to the pc/npc"
4647c478bd9Sstevel@tonic-gate * problem alluded to in bugid 1192883, which is only a
4657c478bd9Sstevel@tonic-gate * problem for a restorecontext of a v8 fp queue on a
4667c478bd9Sstevel@tonic-gate * v9 system, which seems like the .000000001% case (on v9)!
4677c478bd9Sstevel@tonic-gate */
468*c5a5e6f4SGordon Ross struct _fpq *pfpq = &fp->fpu_q->FQu.fpq;
4697c478bd9Sstevel@tonic-gate fp_simd_type fpsd;
4707c478bd9Sstevel@tonic-gate int fptrap;
4717c478bd9Sstevel@tonic-gate
4727c478bd9Sstevel@tonic-gate pfpq->fpq_addr = (uint_t *)rp->r_pc;
4737c478bd9Sstevel@tonic-gate pfpq->fpq_instr = kluge.i;
4747c478bd9Sstevel@tonic-gate fp->fpu_qcnt = 1;
475*c5a5e6f4SGordon Ross fp->fpu_q_entrysize = sizeof (struct _fpq);
4767c478bd9Sstevel@tonic-gate
4777c478bd9Sstevel@tonic-gate kpreempt_disable();
4787c478bd9Sstevel@tonic-gate (void) flush_user_windows_to_stack(NULL);
4797c478bd9Sstevel@tonic-gate fptrap = fpu_vis_sim((fp_simd_type *)&fpsd,
4807c478bd9Sstevel@tonic-gate (fp_inst_type *)pfpq->fpq_addr, rp,
4817c478bd9Sstevel@tonic-gate (fsr_type *)&fp->fpu_fsr, gsr, kluge.i);
4827c478bd9Sstevel@tonic-gate
4837c478bd9Sstevel@tonic-gate /* update the hardware fp fsr state for sake of ucontext */
4847c478bd9Sstevel@tonic-gate if (fpu_exists)
4857c478bd9Sstevel@tonic-gate _fp_write_pfsr(&fp->fpu_fsr);
4867c478bd9Sstevel@tonic-gate
4877c478bd9Sstevel@tonic-gate if (fptrap) {
4887c478bd9Sstevel@tonic-gate /* back up the pc if the signal needs to be precise */
4897c478bd9Sstevel@tonic-gate if (fptrap != ftt_ieee) {
4907c478bd9Sstevel@tonic-gate fp->fpu_qcnt = 0;
4917c478bd9Sstevel@tonic-gate }
4927c478bd9Sstevel@tonic-gate /* post signal */
4937c478bd9Sstevel@tonic-gate fp_traps(&fpsd, fptrap, rp);
4947c478bd9Sstevel@tonic-gate
4957c478bd9Sstevel@tonic-gate /* decrement queue count for ieee exceptions */
4967c478bd9Sstevel@tonic-gate if (fptrap == ftt_ieee) {
4977c478bd9Sstevel@tonic-gate fp->fpu_qcnt = 0;
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate } else {
5007c478bd9Sstevel@tonic-gate fp->fpu_qcnt = 0;
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate /* update the software pcb copies of hardware fp registers */
5037c478bd9Sstevel@tonic-gate if (fpu_exists) {
5047c478bd9Sstevel@tonic-gate fp_save(fp);
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate kpreempt_enable();
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate
5097c478bd9Sstevel@tonic-gate /*
5107c478bd9Sstevel@tonic-gate * Reset lwp_state to LWP_USER for the purposes of clock accounting,
5117c478bd9Sstevel@tonic-gate * and restore the previously saved microstate.
5127c478bd9Sstevel@tonic-gate */
5137c478bd9Sstevel@tonic-gate if (USERMODE(rp->r_tstate)) {
5147c478bd9Sstevel@tonic-gate (void) new_mstate(curthread, mstate);
5157c478bd9Sstevel@tonic-gate lwp->lwp_state = LWP_USER;
5167c478bd9Sstevel@tonic-gate }
5177c478bd9Sstevel@tonic-gate }
5187c478bd9Sstevel@tonic-gate
5197c478bd9Sstevel@tonic-gate /*
5207c478bd9Sstevel@tonic-gate * Handle floating point traps generated by simulation/emulation.
5217c478bd9Sstevel@tonic-gate */
5227c478bd9Sstevel@tonic-gate void
fp_traps(fp_simd_type * pfpsd,enum ftt_type ftt,struct regs * rp)5237c478bd9Sstevel@tonic-gate fp_traps(
5247c478bd9Sstevel@tonic-gate fp_simd_type *pfpsd, /* Pointer to simulator data */
5257c478bd9Sstevel@tonic-gate enum ftt_type ftt, /* trap type */
5267c478bd9Sstevel@tonic-gate struct regs *rp) /* ptr to regs fro trap */
5277c478bd9Sstevel@tonic-gate {
5287c478bd9Sstevel@tonic-gate /*
5297c478bd9Sstevel@tonic-gate * If we take a user's exception in kernel mode, we want to trap
5307c478bd9Sstevel@tonic-gate * with the user's registers.
5317c478bd9Sstevel@tonic-gate */
5327c478bd9Sstevel@tonic-gate switch (ftt) {
5337c478bd9Sstevel@tonic-gate case ftt_ieee:
5347c478bd9Sstevel@tonic-gate fpu_trap(rp, pfpsd->fp_trapaddr, T_FP_EXCEPTION_IEEE,
5357c478bd9Sstevel@tonic-gate pfpsd->fp_trapcode);
5367c478bd9Sstevel@tonic-gate break;
5377c478bd9Sstevel@tonic-gate case ftt_fault:
5387c478bd9Sstevel@tonic-gate fpu_trap(rp, pfpsd->fp_trapaddr, T_DATA_EXCEPTION, 0);
5397c478bd9Sstevel@tonic-gate break;
5407c478bd9Sstevel@tonic-gate case ftt_alignment:
5417c478bd9Sstevel@tonic-gate fpu_trap(rp, pfpsd->fp_trapaddr, T_ALIGNMENT, 0);
5427c478bd9Sstevel@tonic-gate break;
5437c478bd9Sstevel@tonic-gate case ftt_unimplemented:
5447c478bd9Sstevel@tonic-gate fpu_trap(rp, pfpsd->fp_trapaddr, T_UNIMP_INSTR, 0);
5457c478bd9Sstevel@tonic-gate break;
5467c478bd9Sstevel@tonic-gate default:
5477c478bd9Sstevel@tonic-gate /*
5487c478bd9Sstevel@tonic-gate * We don't expect any of the other types here.
5497c478bd9Sstevel@tonic-gate */
5507c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "fp_traps: bad ftt");
5517c478bd9Sstevel@tonic-gate }
5527c478bd9Sstevel@tonic-gate }
553