xref: /onnv-gate/usr/src/uts/sparc/v9/fpu/fpu.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/param.h>
31*0Sstevel@tonic-gate #include <sys/signal.h>
32*0Sstevel@tonic-gate #include <sys/trap.h>
33*0Sstevel@tonic-gate #include <sys/machtrap.h>
34*0Sstevel@tonic-gate #include <sys/fault.h>
35*0Sstevel@tonic-gate #include <sys/systm.h>
36*0Sstevel@tonic-gate #include <sys/user.h>
37*0Sstevel@tonic-gate #include <sys/file.h>
38*0Sstevel@tonic-gate #include <sys/proc.h>
39*0Sstevel@tonic-gate #include <sys/core.h>
40*0Sstevel@tonic-gate #include <sys/pcb.h>
41*0Sstevel@tonic-gate #include <sys/cpuvar.h>
42*0Sstevel@tonic-gate #include <sys/thread.h>
43*0Sstevel@tonic-gate #include <sys/disp.h>
44*0Sstevel@tonic-gate #include <sys/stack.h>
45*0Sstevel@tonic-gate #include <sys/cmn_err.h>
46*0Sstevel@tonic-gate #include <sys/privregs.h>
47*0Sstevel@tonic-gate #include <sys/debug.h>
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate #include <sys/fpu/fpu_simulator.h>
50*0Sstevel@tonic-gate #include <sys/fpu/globals.h>
51*0Sstevel@tonic-gate #include <sys/fpu/fpusystm.h>
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate int fpdispr = 0;
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate /*
56*0Sstevel@tonic-gate  * For use by procfs to save the floating point context of the thread.
57*0Sstevel@tonic-gate  * Note the if (ttolwp(lwp) == curthread) in prstop, which calls
58*0Sstevel@tonic-gate  * this function, ensures that it is safe to read the fprs here.
59*0Sstevel@tonic-gate  */
60*0Sstevel@tonic-gate void
fp_prsave(kfpu_t * fp)61*0Sstevel@tonic-gate fp_prsave(kfpu_t *fp)
62*0Sstevel@tonic-gate {
63*0Sstevel@tonic-gate 	if ((fp->fpu_en) || (fp->fpu_fprs & FPRS_FEF))  {
64*0Sstevel@tonic-gate 		kpreempt_disable();
65*0Sstevel@tonic-gate 		if (fpu_exists) {
66*0Sstevel@tonic-gate 			fp->fpu_fprs = _fp_read_fprs();
67*0Sstevel@tonic-gate 			if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
68*0Sstevel@tonic-gate 				uint32_t fprs = (FPRS_FEF|FPRS_DU|FPRS_DL);
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate 				_fp_write_fprs(fprs);
71*0Sstevel@tonic-gate 				fp->fpu_fprs = fprs;
72*0Sstevel@tonic-gate #ifdef DEBUG
73*0Sstevel@tonic-gate 				if (fpdispr)
74*0Sstevel@tonic-gate 					cmn_err(CE_NOTE,
75*0Sstevel@tonic-gate 					    "fp_prsave with fp disabled!");
76*0Sstevel@tonic-gate #endif
77*0Sstevel@tonic-gate 			}
78*0Sstevel@tonic-gate 			fp_fksave(fp);
79*0Sstevel@tonic-gate 		}
80*0Sstevel@tonic-gate 		kpreempt_enable();
81*0Sstevel@tonic-gate 	}
82*0Sstevel@tonic-gate }
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate /*
85*0Sstevel@tonic-gate  * Copy the floating point context of the forked thread.
86*0Sstevel@tonic-gate  */
87*0Sstevel@tonic-gate void
fp_fork(klwp_t * lwp,klwp_t * clwp)88*0Sstevel@tonic-gate fp_fork(klwp_t *lwp, klwp_t *clwp)
89*0Sstevel@tonic-gate {
90*0Sstevel@tonic-gate 	kfpu_t *cfp, *pfp;
91*0Sstevel@tonic-gate 	int i;
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate 	cfp = lwptofpu(clwp);
94*0Sstevel@tonic-gate 	pfp = lwptofpu(lwp);
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 	/*
97*0Sstevel@tonic-gate 	 * copy the parents fpq
98*0Sstevel@tonic-gate 	 */
99*0Sstevel@tonic-gate 	cfp->fpu_qcnt = pfp->fpu_qcnt;
100*0Sstevel@tonic-gate 	for (i = 0; i < pfp->fpu_qcnt; i++)
101*0Sstevel@tonic-gate 		cfp->fpu_q[i] = pfp->fpu_q[i];
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	/*
104*0Sstevel@tonic-gate 	 * save the context of the parent into the childs fpu structure
105*0Sstevel@tonic-gate 	 */
106*0Sstevel@tonic-gate 	cfp->fpu_fprs = pfp->fpu_fprs;
107*0Sstevel@tonic-gate 	if (ttolwp(curthread) == lwp && fpu_exists) {
108*0Sstevel@tonic-gate 		fp_fksave(cfp);
109*0Sstevel@tonic-gate 	} else {
110*0Sstevel@tonic-gate 		for (i = 0; i < 32; i++)
111*0Sstevel@tonic-gate 			cfp->fpu_fr.fpu_regs[i] = pfp->fpu_fr.fpu_regs[i];
112*0Sstevel@tonic-gate 		for (i = 16; i < 32; i++)
113*0Sstevel@tonic-gate 			cfp->fpu_fr.fpu_dregs[i] = pfp->fpu_fr.fpu_dregs[i];
114*0Sstevel@tonic-gate 	}
115*0Sstevel@tonic-gate 	cfp->fpu_en = 1;
116*0Sstevel@tonic-gate }
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate /*
119*0Sstevel@tonic-gate  * Free any state associated with floating point context.
120*0Sstevel@tonic-gate  * Fp_free can be called in two cases:
121*0Sstevel@tonic-gate  * 1) from reaper -> thread_free -> lwp_freeregs -> fp_free
122*0Sstevel@tonic-gate  *	fp context belongs to a thread on deathrow
123*0Sstevel@tonic-gate  *	nothing to do,  thread will never be resumed
124*0Sstevel@tonic-gate  *	thread calling ctxfree is reaper
125*0Sstevel@tonic-gate  *
126*0Sstevel@tonic-gate  * 2) from exec -> lwp_freeregs -> fp_free
127*0Sstevel@tonic-gate  *	fp context belongs to the current thread
128*0Sstevel@tonic-gate  *	must disable fpu, thread calling ctxfree is curthread
129*0Sstevel@tonic-gate  */
130*0Sstevel@tonic-gate /*ARGSUSED1*/
131*0Sstevel@tonic-gate void
fp_free(kfpu_t * fp,int isexec)132*0Sstevel@tonic-gate fp_free(kfpu_t *fp, int isexec)
133*0Sstevel@tonic-gate {
134*0Sstevel@tonic-gate 	int s;
135*0Sstevel@tonic-gate 	uint32_t fprs = 0;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	if (curthread->t_lwp != NULL && lwptofpu(curthread->t_lwp) == fp) {
138*0Sstevel@tonic-gate 		fp->fpu_en = 0;
139*0Sstevel@tonic-gate 		fp->fpu_fprs = fprs;
140*0Sstevel@tonic-gate 		s = splhigh();
141*0Sstevel@tonic-gate 		_fp_write_fprs(fprs);
142*0Sstevel@tonic-gate 		splx(s);
143*0Sstevel@tonic-gate 	}
144*0Sstevel@tonic-gate }
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate #ifdef SF_ERRATA_30 /* call causes fp-disabled */
148*0Sstevel@tonic-gate extern int spitfire_call_bug;
149*0Sstevel@tonic-gate int ill_fpcalls;
150*0Sstevel@tonic-gate #endif
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate void
fp_enable(void)153*0Sstevel@tonic-gate fp_enable(void)
154*0Sstevel@tonic-gate {
155*0Sstevel@tonic-gate 	klwp_id_t lwp;
156*0Sstevel@tonic-gate 	kfpu_t *fp;
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	lwp = ttolwp(curthread);
159*0Sstevel@tonic-gate 	ASSERT(lwp != NULL);
160*0Sstevel@tonic-gate 	fp = lwptofpu(lwp);
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate 	if (fpu_exists) {
163*0Sstevel@tonic-gate 		if (fp->fpu_en) {
164*0Sstevel@tonic-gate #ifdef DEBUG
165*0Sstevel@tonic-gate 			if (fpdispr)
166*0Sstevel@tonic-gate 				cmn_err(CE_NOTE,
167*0Sstevel@tonic-gate 				    "fpu disabled, but already enabled\n");
168*0Sstevel@tonic-gate #endif
169*0Sstevel@tonic-gate 			if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
170*0Sstevel@tonic-gate 				fp->fpu_fprs = FPRS_FEF;
171*0Sstevel@tonic-gate #ifdef DEBUG
172*0Sstevel@tonic-gate 				if (fpdispr)
173*0Sstevel@tonic-gate 					cmn_err(CE_NOTE,
174*0Sstevel@tonic-gate 					"fpu disabled, saved fprs disabled\n");
175*0Sstevel@tonic-gate #endif
176*0Sstevel@tonic-gate 			}
177*0Sstevel@tonic-gate 			_fp_write_fprs(FPRS_FEF);
178*0Sstevel@tonic-gate 			fp_restore(fp);
179*0Sstevel@tonic-gate 		} else {
180*0Sstevel@tonic-gate 			fp->fpu_en = 1;
181*0Sstevel@tonic-gate 			fp->fpu_fsr = 0;
182*0Sstevel@tonic-gate 			fp->fpu_fprs = FPRS_FEF;
183*0Sstevel@tonic-gate 			_fp_write_fprs(FPRS_FEF);
184*0Sstevel@tonic-gate 			fp_clearregs(fp);
185*0Sstevel@tonic-gate 		}
186*0Sstevel@tonic-gate 	} else {
187*0Sstevel@tonic-gate 		int i;
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 		if (!fp->fpu_en) {
190*0Sstevel@tonic-gate 			fp->fpu_en = 1;
191*0Sstevel@tonic-gate 			fp->fpu_fsr = 0;
192*0Sstevel@tonic-gate 			for (i = 0; i < 32; i++)
193*0Sstevel@tonic-gate 				fp->fpu_fr.fpu_regs[i] = (uint_t)-1; /* NaN */
194*0Sstevel@tonic-gate 			for (i = 16; i < 32; i++)		/* NaN */
195*0Sstevel@tonic-gate 				fp->fpu_fr.fpu_dregs[i] = (uint64_t)-1;
196*0Sstevel@tonic-gate 		}
197*0Sstevel@tonic-gate 	}
198*0Sstevel@tonic-gate }
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate /*
201*0Sstevel@tonic-gate  * fp_disabled normally occurs when the first floating point in a non-threaded
202*0Sstevel@tonic-gate  * program causes an fp_disabled trap. For threaded programs, the ILP32 threads
203*0Sstevel@tonic-gate  * library calls the .setpsr fasttrap, which has been modified to also set the
204*0Sstevel@tonic-gate  * appropriate bits in fpu_en and fpu_fprs, as well as to enable the %fprs,
205*0Sstevel@tonic-gate  * as before. The LP64 threads library will write to the %fprs directly,
206*0Sstevel@tonic-gate  * so fpu_en will never get updated for LP64 threaded programs,
207*0Sstevel@tonic-gate  * although fpu_fprs will, via resume.
208*0Sstevel@tonic-gate  */
209*0Sstevel@tonic-gate void
fp_disabled(struct regs * rp)210*0Sstevel@tonic-gate fp_disabled(struct regs *rp)
211*0Sstevel@tonic-gate {
212*0Sstevel@tonic-gate 	klwp_id_t lwp;
213*0Sstevel@tonic-gate 	kfpu_t *fp;
214*0Sstevel@tonic-gate 	int ftt;
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate #ifdef SF_ERRATA_30 /* call causes fp-disabled */
217*0Sstevel@tonic-gate 	/*
218*0Sstevel@tonic-gate 	 * This code is here because sometimes the call instruction
219*0Sstevel@tonic-gate 	 * generates an fp_disabled trap when the call offset is large.
220*0Sstevel@tonic-gate 	 */
221*0Sstevel@tonic-gate 	if (spitfire_call_bug) {
222*0Sstevel@tonic-gate 		uint_t instr = 0;
223*0Sstevel@tonic-gate 		extern void trap(struct regs *rp, caddr_t addr, uint32_t type,
224*0Sstevel@tonic-gate 		    uint32_t mmu_fsr);
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 		if (USERMODE(rp->r_tstate)) {
227*0Sstevel@tonic-gate 			(void) fuword32((void *)rp->r_pc, &instr);
228*0Sstevel@tonic-gate 		} else {
229*0Sstevel@tonic-gate 			instr = *(uint_t *)(rp->r_pc);
230*0Sstevel@tonic-gate 		}
231*0Sstevel@tonic-gate 		if ((instr & 0xc0000000) == 0x40000000) {
232*0Sstevel@tonic-gate 			ill_fpcalls++;
233*0Sstevel@tonic-gate 			trap(rp, NULL, T_UNIMP_INSTR, 0);
234*0Sstevel@tonic-gate 			return;
235*0Sstevel@tonic-gate 		}
236*0Sstevel@tonic-gate 	}
237*0Sstevel@tonic-gate #endif /* SF_ERRATA_30 - call causes fp-disabled */
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate #ifdef CHEETAH_ERRATUM_109 /* interrupts not taken during fpops */
240*0Sstevel@tonic-gate 	/*
241*0Sstevel@tonic-gate 	 * UltraSPARC III will report spurious fp-disabled exceptions when
242*0Sstevel@tonic-gate 	 * the pipe is full of fpops and an interrupt is triggered.  By the
243*0Sstevel@tonic-gate 	 * time we get here the interrupt has been taken and we just need
244*0Sstevel@tonic-gate 	 * to return to where we came from and try again.
245*0Sstevel@tonic-gate 	 */
246*0Sstevel@tonic-gate 	if (fpu_exists && _fp_read_fprs() & FPRS_FEF)
247*0Sstevel@tonic-gate 		return;
248*0Sstevel@tonic-gate #endif /* CHEETAH_ERRATUM_109 */
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	lwp = ttolwp(curthread);
251*0Sstevel@tonic-gate 	ASSERT(lwp != NULL);
252*0Sstevel@tonic-gate 	fp = lwptofpu(lwp);
253*0Sstevel@tonic-gate 	if (fpu_exists) {
254*0Sstevel@tonic-gate 		kpreempt_disable();
255*0Sstevel@tonic-gate 		if (fp->fpu_en) {
256*0Sstevel@tonic-gate #ifdef DEBUG
257*0Sstevel@tonic-gate 			if (fpdispr)
258*0Sstevel@tonic-gate 				cmn_err(CE_NOTE,
259*0Sstevel@tonic-gate 				    "fpu disabled, but already enabled\n");
260*0Sstevel@tonic-gate #endif
261*0Sstevel@tonic-gate 			if ((fp->fpu_fprs & FPRS_FEF) != FPRS_FEF) {
262*0Sstevel@tonic-gate 				fp->fpu_fprs = FPRS_FEF;
263*0Sstevel@tonic-gate #ifdef DEBUG
264*0Sstevel@tonic-gate 				if (fpdispr)
265*0Sstevel@tonic-gate 					cmn_err(CE_NOTE,
266*0Sstevel@tonic-gate 					"fpu disabled, saved fprs disabled\n");
267*0Sstevel@tonic-gate #endif
268*0Sstevel@tonic-gate 			}
269*0Sstevel@tonic-gate 			_fp_write_fprs(FPRS_FEF);
270*0Sstevel@tonic-gate 			fp_restore(fp);
271*0Sstevel@tonic-gate 		} else {
272*0Sstevel@tonic-gate 			fp->fpu_en = 1;
273*0Sstevel@tonic-gate 			fp->fpu_fsr = 0;
274*0Sstevel@tonic-gate 			fp->fpu_fprs = FPRS_FEF;
275*0Sstevel@tonic-gate 			_fp_write_fprs(FPRS_FEF);
276*0Sstevel@tonic-gate 			fp_clearregs(fp);
277*0Sstevel@tonic-gate 		}
278*0Sstevel@tonic-gate 		kpreempt_enable();
279*0Sstevel@tonic-gate 	} else {
280*0Sstevel@tonic-gate 		fp_simd_type fpsd;
281*0Sstevel@tonic-gate 		int i;
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 		(void) flush_user_windows_to_stack(NULL);
284*0Sstevel@tonic-gate 		if (!fp->fpu_en) {
285*0Sstevel@tonic-gate 			fp->fpu_en = 1;
286*0Sstevel@tonic-gate 			fp->fpu_fsr = 0;
287*0Sstevel@tonic-gate 			for (i = 0; i < 32; i++)
288*0Sstevel@tonic-gate 				fp->fpu_fr.fpu_regs[i] = (uint_t)-1; /* NaN */
289*0Sstevel@tonic-gate 			for (i = 16; i < 32; i++)		/* NaN */
290*0Sstevel@tonic-gate 				fp->fpu_fr.fpu_dregs[i] = (uint64_t)-1;
291*0Sstevel@tonic-gate 		}
292*0Sstevel@tonic-gate 		if (ftt = fp_emulator(&fpsd, (fp_inst_type *)rp->r_pc,
293*0Sstevel@tonic-gate 		    rp, (ulong_t *)rp->r_sp, fp)) {
294*0Sstevel@tonic-gate 			fp->fpu_q_entrysize = sizeof (struct fpq);
295*0Sstevel@tonic-gate 			fp_traps(&fpsd, ftt, rp);
296*0Sstevel@tonic-gate 		}
297*0Sstevel@tonic-gate 	}
298*0Sstevel@tonic-gate }
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate /*
301*0Sstevel@tonic-gate  * Process the floating point queue in lwp->lwp_pcb.
302*0Sstevel@tonic-gate  *
303*0Sstevel@tonic-gate  * Each entry in the floating point queue is processed in turn.
304*0Sstevel@tonic-gate  * If processing an entry results in an exception fp_traps() is called to
305*0Sstevel@tonic-gate  * handle the exception - this usually results in the generation of a signal
306*0Sstevel@tonic-gate  * to be delivered to the user. There are 2 possible outcomes to this (note
307*0Sstevel@tonic-gate  * that hardware generated signals cannot be held!):
308*0Sstevel@tonic-gate  *
309*0Sstevel@tonic-gate  *   1. If the signal is being ignored we continue to process the rest
310*0Sstevel@tonic-gate  *	of the entries in the queue.
311*0Sstevel@tonic-gate  *
312*0Sstevel@tonic-gate  *   2. If arrangements have been made for return to a user signal handler,
313*0Sstevel@tonic-gate  *	sendsig() will have copied the floating point queue onto the user's
314*0Sstevel@tonic-gate  *	signal stack and zero'ed the queue count in the u_pcb. Note that
315*0Sstevel@tonic-gate  *	this has the side effect of terminating fp_runq's processing loop.
316*0Sstevel@tonic-gate  *	We will re-run the floating point queue on return from the user
317*0Sstevel@tonic-gate  *	signal handler if necessary as part of normal setcontext processing.
318*0Sstevel@tonic-gate  */
319*0Sstevel@tonic-gate void
fp_runq(struct regs * rp)320*0Sstevel@tonic-gate fp_runq(struct regs *rp)
321*0Sstevel@tonic-gate {
322*0Sstevel@tonic-gate 	kfpu_t *fp = lwptofpu(curthread->t_lwp);
323*0Sstevel@tonic-gate 	struct fq *fqp = fp->fpu_q;
324*0Sstevel@tonic-gate 	fp_simd_type fpsd;
325*0Sstevel@tonic-gate 	uint64_t gsr = get_gsr(fp);
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/*
328*0Sstevel@tonic-gate 	 * don't preempt while manipulating the queue
329*0Sstevel@tonic-gate 	 */
330*0Sstevel@tonic-gate 	kpreempt_disable();
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	while (fp->fpu_qcnt) {
333*0Sstevel@tonic-gate 		int fptrap;
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 		fptrap = fpu_simulator((fp_simd_type *)&fpsd,
336*0Sstevel@tonic-gate 		    (fp_inst_type *)fqp->FQu.fpq.fpq_addr,
337*0Sstevel@tonic-gate 		    (fsr_type *)&fp->fpu_fsr, gsr,
338*0Sstevel@tonic-gate 		    fqp->FQu.fpq.fpq_instr);
339*0Sstevel@tonic-gate 		if (fptrap) {
340*0Sstevel@tonic-gate 			/*
341*0Sstevel@tonic-gate 			 * Instruction could not be simulated so we will
342*0Sstevel@tonic-gate 			 * attempt to deliver a signal.
343*0Sstevel@tonic-gate 			 * We may be called again upon signal exit (setcontext)
344*0Sstevel@tonic-gate 			 * and can continue to process the queue then.
345*0Sstevel@tonic-gate 			 */
346*0Sstevel@tonic-gate 			if (fqp != fp->fpu_q) {
347*0Sstevel@tonic-gate 				int i;
348*0Sstevel@tonic-gate 				struct fq *fqdp;
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 				/*
351*0Sstevel@tonic-gate 				 * We need to normalize the floating queue so
352*0Sstevel@tonic-gate 				 * the excepting instruction is at the head,
353*0Sstevel@tonic-gate 				 * so that the queue may be copied onto the
354*0Sstevel@tonic-gate 				 * user signal stack by sendsig().
355*0Sstevel@tonic-gate 				 */
356*0Sstevel@tonic-gate 				fqdp = fp->fpu_q;
357*0Sstevel@tonic-gate 				for (i = fp->fpu_qcnt; i; i--) {
358*0Sstevel@tonic-gate 					*fqdp++ = *fqp++;
359*0Sstevel@tonic-gate 				}
360*0Sstevel@tonic-gate 				fqp = fp->fpu_q;
361*0Sstevel@tonic-gate 			}
362*0Sstevel@tonic-gate 			fp->fpu_q_entrysize = sizeof (struct fpq);
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 			/*
365*0Sstevel@tonic-gate 			 * fpu_simulator uses the fp registers directly but it
366*0Sstevel@tonic-gate 			 * uses the software copy of the fsr. We need to write
367*0Sstevel@tonic-gate 			 * that back to fpu so that fpu's state is current for
368*0Sstevel@tonic-gate 			 * ucontext.
369*0Sstevel@tonic-gate 			 */
370*0Sstevel@tonic-gate 			if (fpu_exists)
371*0Sstevel@tonic-gate 				_fp_write_pfsr(&fp->fpu_fsr);
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 			/* post signal */
374*0Sstevel@tonic-gate 			fp_traps(&fpsd, fptrap, rp);
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 			/*
377*0Sstevel@tonic-gate 			 * Break from loop to allow signal to be sent.
378*0Sstevel@tonic-gate 			 * If there are other instructions in the fp queue
379*0Sstevel@tonic-gate 			 * they will be processed when/if the user retuns
380*0Sstevel@tonic-gate 			 * from the signal handler with a non-empty queue.
381*0Sstevel@tonic-gate 			 */
382*0Sstevel@tonic-gate 			break;
383*0Sstevel@tonic-gate 		}
384*0Sstevel@tonic-gate 		fp->fpu_qcnt--;
385*0Sstevel@tonic-gate 		fqp++;
386*0Sstevel@tonic-gate 	}
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 	/*
389*0Sstevel@tonic-gate 	 * fpu_simulator uses the fp registers directly, so we have
390*0Sstevel@tonic-gate 	 * to update the pcb copies to keep current, but it uses the
391*0Sstevel@tonic-gate 	 * software copy of the fsr, so we write that back to fpu
392*0Sstevel@tonic-gate 	 */
393*0Sstevel@tonic-gate 	if (fpu_exists) {
394*0Sstevel@tonic-gate 		int i;
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 		for (i = 0; i < 32; i++)
397*0Sstevel@tonic-gate 			_fp_read_pfreg(&fp->fpu_fr.fpu_regs[i], i);
398*0Sstevel@tonic-gate 		for (i = 16; i < 32; i++)
399*0Sstevel@tonic-gate 			_fp_read_pdreg(&fp->fpu_fr.fpu_dregs[i], i);
400*0Sstevel@tonic-gate 		_fp_write_pfsr(&fp->fpu_fsr);
401*0Sstevel@tonic-gate 	}
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	kpreempt_enable();
404*0Sstevel@tonic-gate }
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate /*
407*0Sstevel@tonic-gate  * Get the precise trapped V9 floating point instruction.
408*0Sstevel@tonic-gate  * Fake up a queue to process. If getting the instruction results
409*0Sstevel@tonic-gate  * in an exception fp_traps() is called to handle the exception - this
410*0Sstevel@tonic-gate  * usually results in the generation of a signal to be delivered to the user.
411*0Sstevel@tonic-gate  */
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate void
fp_precise(struct regs * rp)414*0Sstevel@tonic-gate fp_precise(struct regs *rp)
415*0Sstevel@tonic-gate {
416*0Sstevel@tonic-gate 	fp_simd_type	fpsd;
417*0Sstevel@tonic-gate 	int		inst_ftt;
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 	union {
420*0Sstevel@tonic-gate 		uint_t		i;
421*0Sstevel@tonic-gate 		fp_inst_type	inst;
422*0Sstevel@tonic-gate 	} kluge;
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
425*0Sstevel@tonic-gate 	kfpu_t *fp = lwptofpu(lwp);
426*0Sstevel@tonic-gate 	uint64_t gsr;
427*0Sstevel@tonic-gate 	int mstate;
428*0Sstevel@tonic-gate 	if (fpu_exists)
429*0Sstevel@tonic-gate 		save_gsr(fp);
430*0Sstevel@tonic-gate 	gsr = get_gsr(fp);
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate 	/*
433*0Sstevel@tonic-gate 	 * Get the instruction to be emulated from the pc saved by the trap.
434*0Sstevel@tonic-gate 	 * Note that the kernel is NOT prepared to handle a kernel fp
435*0Sstevel@tonic-gate 	 * exception if it can't pass successfully through the fp simulator.
436*0Sstevel@tonic-gate 	 *
437*0Sstevel@tonic-gate 	 * If the trap occurred in user mode, set lwp_state to LWP_SYS for the
438*0Sstevel@tonic-gate 	 * purposes of clock accounting and switch to the LMS_TRAP microstate.
439*0Sstevel@tonic-gate 	 */
440*0Sstevel@tonic-gate 	if (USERMODE(rp->r_tstate)) {
441*0Sstevel@tonic-gate 		inst_ftt = _fp_read_inst((uint32_t *)rp->r_pc, &kluge.i, &fpsd);
442*0Sstevel@tonic-gate 		mstate = new_mstate(curthread, LMS_TRAP);
443*0Sstevel@tonic-gate 		lwp->lwp_state = LWP_SYS;
444*0Sstevel@tonic-gate 	} else {
445*0Sstevel@tonic-gate 		kluge.i = *(uint_t *)rp->r_pc;
446*0Sstevel@tonic-gate 		inst_ftt = ftt_none;
447*0Sstevel@tonic-gate 	}
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	if (inst_ftt != ftt_none) {
450*0Sstevel@tonic-gate 		/*
451*0Sstevel@tonic-gate 		 * Save the bad address and post the signal.
452*0Sstevel@tonic-gate 		 * It can only be an ftt_alignment or ftt_fault trap.
453*0Sstevel@tonic-gate 		 * XXX - How can this work w/mainsail and do_unaligned?
454*0Sstevel@tonic-gate 		 */
455*0Sstevel@tonic-gate 		fpsd.fp_trapaddr = (caddr_t)rp->r_pc;
456*0Sstevel@tonic-gate 		fp_traps(&fpsd, inst_ftt, rp);
457*0Sstevel@tonic-gate 	} else {
458*0Sstevel@tonic-gate 		/*
459*0Sstevel@tonic-gate 		 * Conjure up a floating point queue and advance the pc/npc
460*0Sstevel@tonic-gate 		 * to fake a deferred fp trap. We now run the fp simulator
461*0Sstevel@tonic-gate 		 * in fp_precise, while allowing setfpregs to call fp_runq,
462*0Sstevel@tonic-gate 		 * because this allows us to do the ugly machinations to
463*0Sstevel@tonic-gate 		 * inc/dec the pc depending on the trap type, as per
464*0Sstevel@tonic-gate 		 * bugid 1210159. fp_runq is still going to have the
465*0Sstevel@tonic-gate 		 * generic "how do I connect the "fp queue to the pc/npc"
466*0Sstevel@tonic-gate 		 * problem alluded to in bugid 1192883, which is only a
467*0Sstevel@tonic-gate 		 * problem for a restorecontext of a v8 fp queue on a
468*0Sstevel@tonic-gate 		 * v9 system, which seems like the .000000001% case (on v9)!
469*0Sstevel@tonic-gate 		 */
470*0Sstevel@tonic-gate 		struct fpq *pfpq = &fp->fpu_q->FQu.fpq;
471*0Sstevel@tonic-gate 		fp_simd_type	fpsd;
472*0Sstevel@tonic-gate 		int fptrap;
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 		pfpq->fpq_addr = (uint_t *)rp->r_pc;
475*0Sstevel@tonic-gate 		pfpq->fpq_instr = kluge.i;
476*0Sstevel@tonic-gate 		fp->fpu_qcnt = 1;
477*0Sstevel@tonic-gate 		fp->fpu_q_entrysize = sizeof (struct fpq);
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 		kpreempt_disable();
480*0Sstevel@tonic-gate 		(void) flush_user_windows_to_stack(NULL);
481*0Sstevel@tonic-gate 		fptrap = fpu_vis_sim((fp_simd_type *)&fpsd,
482*0Sstevel@tonic-gate 		    (fp_inst_type *)pfpq->fpq_addr, rp,
483*0Sstevel@tonic-gate 		    (fsr_type *)&fp->fpu_fsr, gsr, kluge.i);
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 		/* update the hardware fp fsr state for sake of ucontext */
486*0Sstevel@tonic-gate 		if (fpu_exists)
487*0Sstevel@tonic-gate 			_fp_write_pfsr(&fp->fpu_fsr);
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 		if (fptrap) {
490*0Sstevel@tonic-gate 			/* back up the pc if the signal needs to be precise */
491*0Sstevel@tonic-gate 			if (fptrap != ftt_ieee) {
492*0Sstevel@tonic-gate 				fp->fpu_qcnt = 0;
493*0Sstevel@tonic-gate 			}
494*0Sstevel@tonic-gate 			/* post signal */
495*0Sstevel@tonic-gate 			fp_traps(&fpsd, fptrap, rp);
496*0Sstevel@tonic-gate 
497*0Sstevel@tonic-gate 			/* decrement queue count for ieee exceptions */
498*0Sstevel@tonic-gate 			if (fptrap == ftt_ieee) {
499*0Sstevel@tonic-gate 				fp->fpu_qcnt = 0;
500*0Sstevel@tonic-gate 			}
501*0Sstevel@tonic-gate 		} else {
502*0Sstevel@tonic-gate 			fp->fpu_qcnt = 0;
503*0Sstevel@tonic-gate 		}
504*0Sstevel@tonic-gate 		/* update the software pcb copies of hardware fp registers */
505*0Sstevel@tonic-gate 		if (fpu_exists) {
506*0Sstevel@tonic-gate 			fp_save(fp);
507*0Sstevel@tonic-gate 		}
508*0Sstevel@tonic-gate 		kpreempt_enable();
509*0Sstevel@tonic-gate 	}
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 	/*
512*0Sstevel@tonic-gate 	 * Reset lwp_state to LWP_USER for the purposes of clock accounting,
513*0Sstevel@tonic-gate 	 * and restore the previously saved microstate.
514*0Sstevel@tonic-gate 	 */
515*0Sstevel@tonic-gate 	if (USERMODE(rp->r_tstate)) {
516*0Sstevel@tonic-gate 		(void) new_mstate(curthread, mstate);
517*0Sstevel@tonic-gate 		lwp->lwp_state = LWP_USER;
518*0Sstevel@tonic-gate 	}
519*0Sstevel@tonic-gate }
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate /*
522*0Sstevel@tonic-gate  * Handle floating point traps generated by simulation/emulation.
523*0Sstevel@tonic-gate  */
524*0Sstevel@tonic-gate void
fp_traps(fp_simd_type * pfpsd,enum ftt_type ftt,struct regs * rp)525*0Sstevel@tonic-gate fp_traps(
526*0Sstevel@tonic-gate 	fp_simd_type *pfpsd,	/* Pointer to simulator data */
527*0Sstevel@tonic-gate 	enum ftt_type ftt,	/* trap type */
528*0Sstevel@tonic-gate 	struct regs *rp)	/* ptr to regs fro trap */
529*0Sstevel@tonic-gate {
530*0Sstevel@tonic-gate 	/*
531*0Sstevel@tonic-gate 	 * If we take a user's exception in kernel mode, we want to trap
532*0Sstevel@tonic-gate 	 * with the user's registers.
533*0Sstevel@tonic-gate 	 */
534*0Sstevel@tonic-gate 	switch (ftt) {
535*0Sstevel@tonic-gate 	case ftt_ieee:
536*0Sstevel@tonic-gate 		fpu_trap(rp, pfpsd->fp_trapaddr, T_FP_EXCEPTION_IEEE,
537*0Sstevel@tonic-gate 		    pfpsd->fp_trapcode);
538*0Sstevel@tonic-gate 		break;
539*0Sstevel@tonic-gate 	case ftt_fault:
540*0Sstevel@tonic-gate 		fpu_trap(rp, pfpsd->fp_trapaddr, T_DATA_EXCEPTION, 0);
541*0Sstevel@tonic-gate 		break;
542*0Sstevel@tonic-gate 	case ftt_alignment:
543*0Sstevel@tonic-gate 		fpu_trap(rp, pfpsd->fp_trapaddr, T_ALIGNMENT, 0);
544*0Sstevel@tonic-gate 		break;
545*0Sstevel@tonic-gate 	case ftt_unimplemented:
546*0Sstevel@tonic-gate 		fpu_trap(rp, pfpsd->fp_trapaddr, T_UNIMP_INSTR, 0);
547*0Sstevel@tonic-gate 		break;
548*0Sstevel@tonic-gate 	default:
549*0Sstevel@tonic-gate 		/*
550*0Sstevel@tonic-gate 		 * We don't expect any of the other types here.
551*0Sstevel@tonic-gate 		 */
552*0Sstevel@tonic-gate 		cmn_err(CE_PANIC, "fp_traps: bad ftt");
553*0Sstevel@tonic-gate 	}
554*0Sstevel@tonic-gate }
555