xref: /illumos-gate/usr/src/uts/sparc/v9/fpu/fpu.c (revision bc0e9132)
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*bc0e9132SGordon 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*bc0e9132SGordon 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*bc0e9132SGordon 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*bc0e9132SGordon 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*bc0e9132SGordon 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*bc0e9132SGordon 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