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