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