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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/asm_linkage.h>
29#include <sys/trap.h>
30#include <sys/machpcb.h>
31#include <sys/machtrap.h>
32#include <sys/machsig.h>
33#include <sys/machthread.h>
34
35#if !defined(lint) && !defined(__lint)
36#include "assym.h"
37#endif	/* lint */
38
39/*
40 * Floating point trap handling.
41 *
42 *	The FPU is always in a V9 current configuration.
43 *
44 *	When a user process is first started via exec,
45 *	floating point operations will be disabled by default.
46 *	Upon execution of the first floating point instruction,
47 *	a fp_disabled trap will be generated; then a word in
48 *	the uarea is written signifying use of the floating point
49 *	registers so that subsequent context switches will save
50 *	and restore the floating point them. The trapped instruction
51 *	will be restarted and processing will continue as normal.
52 *
53 *	When a operation occurs that the hardware cannot properly
54 *	handle, an unfinshed fp_op exception will be generated.
55 *	Software routines in the kernel will be	executed to
56 *	simulate proper handling of such conditions.
57 *
58 *	Exception handling will emulate all instructions
59 *	in the floating point address queue. Note that there
60 *	is no %fq in sun4u, because it has precise FP traps.
61 *
62 *	Floating point queues are now machine dependent, and std %fq
63 *	is an illegal V9 instruction. The fp_exception code has been
64 *	moved to sun4u/ml/machfloat.s.
65 *
66 *	NOTE: This code DOES NOT SUPPORT KERNEL (DEVICE DRIVER)
67 *		USE OF THE FPU
68 *
69 *	Instructions for running without the hardware fpu:
70 *	1. Setting fpu_exists to 0 now only works on a DEBUG kernel.
71 *	2. adb -w unix and set fpu_exists, use_hw_bcopy, use_hw_copyio, and
72 *		use_hw_bzero to 0 and rename libc_psr.so.1 in
73 *		/usr/platform/sun4u/lib so that it will not get used by
74 *		the libc bcopy routines. Then reboot the system and you
75 *		should see the bootup message "FPU not in use".
76 *	3. To run kaos, you must comment out the code which sets the
77 *		version number of the fsr to 7, in fldst: stfsr/stxfsr
78 *		(unless you are running against a comparison system that
79 *		has the same fsr version number).
80 *	4. The stqf{a}/ldqf{a} instructions cause kaos errors, for reasons
81 *		that appear to be a kaos bug, so don't use them!
82 */
83
84#if defined(lint) || defined(__lint)
85
86#ifdef FP_DISABLED
87int fpu_exists = 0;
88#else
89int fpu_exists = 1;
90#endif
91
92#else	/* lint */
93
94	.section ".data"
95	.align	8
96fsrholder:
97	.word	0			! dummy place to write fsr
98	.word	0
99
100	DGDEF(fpu_exists)		! always exists for V9
101#ifdef FP_DISABLED
102	.word	0
103#else
104	.word	1			! sundiag (gack) uses this variable
105#endif
106
107	DGDEF(fpu_version)
108	.word	-1
109
110#endif	/* lint */
111
112/*
113 * FPU probe - read the %fsr and get fpu_version.
114 * Called from autoconf. If a %fq is created for
115 * future cpu versions, a fq_exists variable
116 * could be created by this function.
117 */
118
119#if defined(lint) || defined(__lint)
120
121/*ARGSUSED*/
122void
123fpu_probe(void)
124{}
125
126#else	/* lint */
127
128	ENTRY_NP(fpu_probe)
129	wr	%g0, FPRS_FEF, %fprs	! enable fpu in fprs
130	rdpr	%pstate, %g2		! read pstate, save value in %g2
131	or	%g2, PSTATE_PEF, %g1	! new pstate with fpu enabled
132	wrpr	%g1, %g0, %pstate	! write pstate
133
134	sethi	%hi(fsrholder), %g2
135	stx	%fsr, [%g2 + %lo(fsrholder)]
136	ldx	[%g2 + %lo(fsrholder)], %g2	! snarf the FSR
137	set	FSR_VER, %g1
138	and	%g2, %g1, %g2			! get version
139	srl	%g2, FSR_VER_SHIFT, %g2		! and shift it down
140	sethi	%hi(fpu_version), %g3		! save the FPU version
141	st	%g2, [%g3 + %lo(fpu_version)]
142
143	ba	fp_kstat_init		! initialize the fpu_kstat
144	wr	%g0, %g0, %fprs		! disable fpu and clear fprs
145	SET_SIZE(fpu_probe)
146
147#endif	/* lint */
148
149/*
150 * fp_clearregs(fp)
151 *	struct v9_fpu *fp;
152 *
153 * Initialization for the hardware fpu.
154 * Clear the fsr and initialize registers to NaN (-1)
155 * The caller (fp_disabled) is supposed to update the fprs
156 * so when the return to userland is made, the fpu is enabled.
157 */
158
159#if defined(lint) || defined(__lint)
160
161/*ARGSUSED*/
162void
163fp_clearregs(kfpu_t *fp)
164{}
165
166#else	/* lint */
167
168	ENTRY_NP(fp_clearregs)
169	ldx	[%o0 + FPU_FSR], %fsr		! load fsr
170
171	mov	-1, %g2				! -1 is NaN
172	stx	%g2, [%o0]			! initialize %f0
173	ldd	[%o0], %d0
174	ldd	[%o0], %d2
175	ldd	[%o0], %d4
176	ldd	[%o0], %d6
177	ldd	[%o0], %d8
178	ldd	[%o0], %d10
179	ldd	[%o0], %d12
180	ldd	[%o0], %d14
181	ldd	[%o0], %d16
182	ldd	[%o0], %d18
183	ldd	[%o0], %d20
184	ldd	[%o0], %d22
185	ldd	[%o0], %d24
186	ldd	[%o0], %d26
187	ldd	[%o0], %d28
188	ldd	[%o0], %d30
189	ldd	[%o0], %d32
190	ldd	[%o0], %d34
191	ldd	[%o0], %d36
192	ldd	[%o0], %d38
193	ldd	[%o0], %d40
194	ldd	[%o0], %d42
195	ldd	[%o0], %d44
196	ldd	[%o0], %d46
197	ldd	[%o0], %d48
198	ldd	[%o0], %d50
199	ldd	[%o0], %d52
200	ldd	[%o0], %d54
201	ldd	[%o0], %d56
202	ldd	[%o0], %d58
203	ldd	[%o0], %d60
204	retl
205	ldd	[%o0], %d62
206	SET_SIZE(fp_clearregs)
207
208#endif	/* lint */
209
210/*
211 * void _fp_read_pfreg(pf, n)
212 *	uint32_t	*pf;	Old freg value.
213 *	unsigned	n;	Want to read register n
214 *
215 * {
216 *	*pf = %f[n];
217 * }
218 *
219 * void
220 * _fp_write_pfreg(pf, n)
221 *	uint32_t	*pf;	New freg value.
222 *	unsigned	n;	Want to write register n.
223 *
224 * {
225 *	%f[n] = *pf;
226 * }
227 */
228
229#if defined(lint) || defined(__lint)
230
231/*ARGSUSED*/
232void
233_fp_read_pfreg(uint32_t *pf, u_int n)
234{}
235
236/*ARGSUSED*/
237void
238_fp_write_pfreg(uint32_t *pf, u_int n)
239{}
240
241#else	/* lint */
242
243	ENTRY_NP(_fp_read_pfreg)
244	sll	%o1, 3, %o1		! Table entries are 8 bytes each.
245	set	.stable, %g1		! g1 gets base of table.
246	jmp	%g1 + %o1		! Jump into table
247	nop				! Can't follow CTI by CTI.
248
249	ENTRY_NP(_fp_write_pfreg)
250	sll	%o1, 3, %o1		! Table entries are 8 bytes each.
251	set	.ltable, %g1		! g1 gets base of table.
252	jmp	%g1 + %o1		! Jump into table
253	nop				! Can't follow CTI by CTI.
254
255#define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0]
256
257.stable:
258	STOREFP(0)
259	STOREFP(1)
260	STOREFP(2)
261	STOREFP(3)
262	STOREFP(4)
263	STOREFP(5)
264	STOREFP(6)
265	STOREFP(7)
266	STOREFP(8)
267	STOREFP(9)
268	STOREFP(10)
269	STOREFP(11)
270	STOREFP(12)
271	STOREFP(13)
272	STOREFP(14)
273	STOREFP(15)
274	STOREFP(16)
275	STOREFP(17)
276	STOREFP(18)
277	STOREFP(19)
278	STOREFP(20)
279	STOREFP(21)
280	STOREFP(22)
281	STOREFP(23)
282	STOREFP(24)
283	STOREFP(25)
284	STOREFP(26)
285	STOREFP(27)
286	STOREFP(28)
287	STOREFP(29)
288	STOREFP(30)
289	STOREFP(31)
290
291#define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n
292
293.ltable:
294	LOADFP(0)
295	LOADFP(1)
296	LOADFP(2)
297	LOADFP(3)
298	LOADFP(4)
299	LOADFP(5)
300	LOADFP(6)
301	LOADFP(7)
302	LOADFP(8)
303	LOADFP(9)
304	LOADFP(10)
305	LOADFP(11)
306	LOADFP(12)
307	LOADFP(13)
308	LOADFP(14)
309	LOADFP(15)
310	LOADFP(16)
311	LOADFP(17)
312	LOADFP(18)
313	LOADFP(19)
314	LOADFP(20)
315	LOADFP(21)
316	LOADFP(22)
317	LOADFP(23)
318	LOADFP(24)
319	LOADFP(25)
320	LOADFP(26)
321	LOADFP(27)
322	LOADFP(28)
323	LOADFP(29)
324	LOADFP(30)
325	LOADFP(31)
326	SET_SIZE(_fp_read_pfreg)
327	SET_SIZE(_fp_write_pfreg)
328
329#endif	/* lint */
330
331/*
332 * void _fp_read_pdreg(
333 *	uint64_t	*pd,	Old dreg value.
334 *	u_int	n)		Want to read register n
335 *
336 * {
337 *	*pd = %d[n];
338 * }
339 *
340 * void
341 * _fp_write_pdreg(
342 *	uint64_t	*pd,	New dreg value.
343 *	u_int	n)		Want to write register n.
344 *
345 * {
346 *	%d[n] = *pd;
347 * }
348 */
349
350#if defined(lint) || defined(__lint)
351
352/*ARGSUSED*/
353void
354_fp_read_pdreg(uint64_t *pd, u_int n)
355{}
356
357/*ARGSUSED*/
358void
359_fp_write_pdreg(uint64_t *pd, u_int n)
360{}
361
362#else	/* lint */
363
364	ENTRY_NP(_fp_read_pdreg)
365	sll	%o1, 3, %o1		! Table entries are 8 bytes each.
366	set	.dstable, %g1		! g1 gets base of table.
367	jmp	%g1 + %o1		! Jump into table
368	nop				! Can't follow CTI by CTI.
369
370	ENTRY_NP(_fp_write_pdreg)
371	sll	%o1, 3, %o1		! Table entries are 8 bytes each.
372	set	.dltable, %g1		! g1 gets base of table.
373	jmp	%g1 + %o1		! Jump into table
374	nop				! Can't follow CTI by CTI.
375
376#define STOREDP(n) jmp %o7+8 ; std %d/**/n, [%o0]
377
378.dstable:
379	STOREDP(0)
380	STOREDP(2)
381	STOREDP(4)
382	STOREDP(6)
383	STOREDP(8)
384	STOREDP(10)
385	STOREDP(12)
386	STOREDP(14)
387	STOREDP(16)
388	STOREDP(18)
389	STOREDP(20)
390	STOREDP(22)
391	STOREDP(24)
392	STOREDP(26)
393	STOREDP(28)
394	STOREDP(30)
395	STOREDP(32)
396	STOREDP(34)
397	STOREDP(36)
398	STOREDP(38)
399	STOREDP(40)
400	STOREDP(42)
401	STOREDP(44)
402	STOREDP(46)
403	STOREDP(48)
404	STOREDP(50)
405	STOREDP(52)
406	STOREDP(54)
407	STOREDP(56)
408	STOREDP(58)
409	STOREDP(60)
410	STOREDP(62)
411
412#define LOADDP(n) jmp %o7+8 ; ldd [%o0],%d/**/n
413
414.dltable:
415	LOADDP(0)
416	LOADDP(2)
417	LOADDP(4)
418	LOADDP(6)
419	LOADDP(8)
420	LOADDP(10)
421	LOADDP(12)
422	LOADDP(14)
423	LOADDP(16)
424	LOADDP(18)
425	LOADDP(20)
426	LOADDP(22)
427	LOADDP(24)
428	LOADDP(26)
429	LOADDP(28)
430	LOADDP(30)
431	LOADDP(32)
432	LOADDP(34)
433	LOADDP(36)
434	LOADDP(38)
435	LOADDP(40)
436	LOADDP(42)
437	LOADDP(44)
438	LOADDP(46)
439	LOADDP(48)
440	LOADDP(50)
441	LOADDP(52)
442	LOADDP(54)
443	LOADDP(56)
444	LOADDP(58)
445	LOADDP(60)
446	LOADDP(62)
447	SET_SIZE(_fp_read_pdreg)
448	SET_SIZE(_fp_write_pdreg)
449
450#endif	/* lint */
451
452#if defined(lint) || defined(__lint)
453
454/*ARGSUSED*/
455void
456_fp_write_pfsr(uint64_t *fsr)
457{}
458
459#else	/* lint */
460
461	ENTRY_NP(_fp_write_pfsr)
462	retl
463	ldx	[%o0], %fsr
464	SET_SIZE(_fp_write_pfsr)
465
466#endif	/* lint */
467
468#if defined(lint) || defined(__lint)
469
470/*ARGSUSED*/
471void
472_fp_read_pfsr(uint64_t *fsr)
473{}
474
475#else	/* lint */
476
477	ENTRY_NP(_fp_read_pfsr)
478	retl
479	stx	%fsr, [%o0]
480	SET_SIZE(_fp_read_pfsr)
481
482#endif	/* lint */
483
484#if defined(lint) || defined(__lint)
485
486/*ARGSUSED*/
487void
488_fp_write_fprs(u_int fprs_val)
489{}
490
491#else	/* lint */
492
493	ENTRY_NP(_fp_write_fprs)
494	retl
495	wr	%o0, %g0, %fprs			! write fprs
496	SET_SIZE(_fp_write_fprs)
497
498#endif	/* lint */
499
500#if defined(lint) || defined(__lint)
501
502unsigned
503_fp_read_fprs(void)
504{return 0;}
505
506#else	/* lint */
507
508	ENTRY_NP(_fp_read_fprs)
509	retl
510	rd	%fprs, %o0			! save fprs
511	SET_SIZE(_fp_read_fprs)
512
513#endif	/* lint */
514
515#if defined(lint) || defined(__lint)
516
517unsigned
518_fp_subcc_ccr(void)
519{return 0;}
520
521#else	/* lint */
522
523	ENTRY_NP(_fp_subcc_ccr)
524	subcc	%o0, %o1, %g0
525	retl
526	rd	%ccr, %o0			! save ccr
527	SET_SIZE(_fp_subcc_ccr)
528
529#endif	/* lint */
530
531/*
532 * Floating Point Exceptions handled according to type:
533 *	2) unfinished_fpop
534 *		re-execute the faulty instruction(s) using
535 *		software emulation (must do every instruction in FQ)
536 *	3) unimplemented_fpop
537 *		an unimplemented instruction, if it is legal,
538 *		will cause emulation of the instruction (and all
539 *		other instuctions in the FQ)
540 *	4) sequence_error
541 *		panic, this should not happen, and if it does it
542 *		it is the result of a kernel bug
543 *
544 * This code assumes the trap preamble has set up the window environment
545 * for execution of kernel code.
546 * Note: this code could be changed to be part of the cpu-specific
547 * (ie, Spitfire-specific) module code before final release.
548 */
549
550#if defined(lint)
551
552/* ARGSUSED */
553void
554_fp_exception(struct regs *rp, uint64_t fsr)
555{}
556
557#else	/* lint */
558
559	ENTRY_NP(_fp_exception)
560	mov	%o7, %l0		! saved return address
561	mov	%o0, %l1		! saved *rp
562	set     FSR_FTT, %o4		! put FSR_FTT in %o4
563	xor	%o4, 0xffffffffffffffff, %o3 ! xor FSR_FTT to get
564	and     %o1, %o3, %o2		! an fsr with a zero'd ftt
565	ldn	[THREAD_REG + T_LWP], %o3 ! get lwp
566	ldn	[%o3 + LWP_FPU], %l3	! get lwp_fpu
567	stx	%o2, [%l3 + FPU_FSR]	! save floating point status
568	and	%o1, %o4, %g2		! get the ftt trap type
569#ifdef  DEBUG
570	brnz,a,pt %g2, fttok
571	  nop
572	set	.badfpfttmsg, %o0	! panic message
573	call	panic			! %o1 has the fsr w/ftt value
574	nop
575fttok:
576#endif  /* DEBUG */
577	srl	%g2, FSR_FTT_SHIFT, %o4	! check ftt
578	cmp	%o4, FTT_SEQ		! sanity check for bogus exceptions
579	!
580	! traps are already enabled to allow other
581	! interrupts while emulating floating point instructions
582	!
583	blt,a,pt %xcc, fpeok
584	nop
585	!
586	! Sequence error or unknown ftt exception.
587	!
588seq_error:
589	set	.badfpexcpmsg, %o0	! panic if bad ftt
590	call	panic
591	sra	%o4, 0, %o1		! mov ftt to o1 for panic message
592
593fpeok:
594	call	fp_kstat_update		! fp_kstat_update(ftt)
595	mov	%o4, %o0		! ftt
596	!
597	! Get the floating point instruction, and run the floating
598	! point simulator. There is no floating point queue, so we fake one.
599	!
600	call	fp_precise		! fp_precise(&regs)
601	mov	%l1, %o0		! saved *rp
602
603fp_ret:
604	rd	%fprs, %g1		! read fprs, save value in %g1
605	st	%g1, [%l3 + FPU_FPRS]	! save fprs
606	jmp	%l0 + 8			! jump to saved return address
607	stx	%fsr, [%l3 + FPU_FSR]	! save fsr
608	SET_SIZE(_fp_exception)
609
610.badfpexcpmsg:
611	.asciz	"unexpected floating point exception %x"
612
613#ifdef	DEBUG
614.badfpfttmsg:
615	.asciz	"No floating point ftt, fsr %llx"
616#endif	/* DEBUG */
617
618#endif	/* lint */
619
620/*
621 * Floating Point Exceptions.
622 * handled according to type:
623 *	1) IEEE_exception
624 *		re-execute the faulty instruction(s) using
625 *		software emulation (must do every instruction in FQ)
626 *
627 * This code assumes the trap preamble has set up the window environment
628 * for execution of kernel code.
629 */
630
631#if defined(lint)
632
633/* ARGSUSED */
634void
635_fp_ieee_exception(struct regs *rp, uint64_t fsr)
636{}
637
638#else	/* lint */
639
640	ENTRY_NP(_fp_ieee_exception)
641	mov	%o7, %l0		! saved return address
642	mov	%o0, %l1		! saved *rp
643	mov	%o1, %l2		! saved fsr
644	set	FSR_FTT, %o4		! put FSR_FTT in %o4
645	xor	%o4, 0xffffffffffffffff, %o3 ! ! xor FSR_FTT to get
646	and	%o1, %o3, %o2		! an fsr with a zero'd ftt
647	ldn	[THREAD_REG + T_LWP], %o3 ! get lwp
648	ldn	[%o3 + LWP_FPU], %l3	! get lwp_fpu
649	stx	%o2, [%l3 + FPU_FSR]	! save floating point status
650	stub	%g0, [%l3 + FPU_QCNT]	! clear fpu_qcnt
651	and	%o1, %o4, %g2		! mask out trap type
652#ifdef  DEBUG
653	brnz,a,pt %g2, fttgd
654	  nop
655	set	.badfpfttmsg, %o0	! panic message
656	call	panic			! %o1 has the fsr w/ftt value
657	nop
658fttgd:
659#endif	/* DEBUG */
660	srl	%g2, FSR_FTT_SHIFT, %o4	! check ftt
661	cmp	%o4, FTT_SEQ		! sanity check for bogus exceptions
662	!
663	! traps are already enabled to allow other
664	! interrupts while emulating floating point instructions
665	!
666	blt,a,pt %xcc, fpegd
667	nop
668	!
669	! Sequence error or unknown ftt exception.
670	!
671seq_err:
672	set	.badfpexcpmsg, %o0	! panic if bad ftt
673	call	panic
674	sra	%o4, 0, %o1		! mov ftt to o1 for panic message
675
676fpegd:
677	call	fp_kstat_update		! fp_kstat_update(ftt)
678	mov	%o4, %o0		! ftt
679	!
680	! Call fpu_trap directly, don't bother to run the fp simulator.
681	! The *rp is already in %o0. Clear fpu_qcnt.
682	!
683	set	(T_FP_EXCEPTION_IEEE), %o2	! trap type
684
685	set	FSR_CEXC, %o3
686	and	%l2, %o3, %g2		! mask out cexc
687
688	andcc	%g2, FSR_CEXC_NX, %g0	! check for inexact
689	bnz,a,pt %xcc, fpok
690	or	%g0, FPE_FLTRES, %o3	! fp inexact code
691
692	andcc	%g2, FSR_CEXC_DZ, %g0	! check for divide-by-zero
693	bnz,a,pt %xcc, fpok
694	or	%g0, FPE_FLTDIV, %o3	! fp divide by zero code
695
696	andcc	%g2, FSR_CEXC_UF, %g0	! check for underflow
697	bnz,a,pt %xcc, fpok
698	or	%g0, FPE_FLTUND, %o3	! fp underflow code
699
700	andcc	%g2, FSR_CEXC_OF, %g0	! check for overflow
701	bnz,a,pt %xcc, fpok
702	or	%g0, FPE_FLTOVF, %o3	! fp overflow code
703
704	andcc	%g2, FSR_CEXC_NV, %g0	! check for invalid
705	bnz,a,pn %xcc, fpok
706	or	%g0, FPE_FLTINV, %o3	! fp invalid code
707
708cexec_err:
709	set	.badfpcexcmsg, %o0	! panic message
710	call	panic			! panic if no cexc bit set
711	mov	%g1, %o1
712fpok:
713	mov	%l1, %o0		! saved *rp
714	call	fpu_trap		! fpu_trap(&regs, addr, type, code)
715	ldn	[%o0 + PC_OFF], %o1 	! address of trapping instruction
716
717	rd	%fprs, %g1		! read fprs, save value in %g1
718	st	%g1, [%l3 + FPU_FPRS]	! save fprs
719	jmp	%l0 + 8			! jump to saved return address
720	stx	%fsr, [%l3 + FPU_FSR]	! save fsr
721	SET_SIZE(_fp_ieee_exception)
722
723.badfpcexcmsg:
724	.asciz	"No floating point exception, fsr %llx"
725
726#endif	/* lint */
727