fpu.c revision 95587
1/*
2 * Copyright (c) 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This software was developed by the Computer Systems Engineering group
6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7 * contributed to Berkeley.
8 *
9 * All advertising materials mentioning features or use of this software
10 * must display the following acknowledgement:
11 *	This product includes software developed by the University of
12 *	California, Lawrence Berkeley Laboratory.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *	This product includes software developed by the University of
25 *	California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 *    may be used to endorse or promote products derived from this software
28 *    without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 */
42/*-
43 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
44 *
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions
47 * are met:
48 * 1. Redistributions of source code must retain the above copyright
49 *    notice, this list of conditions and the following disclaimer.
50 * 2. Redistributions in binary form must reproduce the above copyright
51 *    notice, this list of conditions and the following disclaimer in the
52 *    documentation and/or other materials provided with the distribution.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
57 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
58 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
59 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
60 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
61 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
62 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
63 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 *
65 *	@(#)fpu.c	8.1 (Berkeley) 6/11/93
66 *	$NetBSD: fpu.c,v 1.11 2000/12/06 01:47:50 mrg Exp $
67 */
68
69#include <sys/cdefs.h>
70__FBSDID("$FreeBSD: head/lib/libc/sparc64/fpu/fpu.c 95587 2002-04-27 21:56:28Z jake $");
71
72#include <sys/param.h>
73
74#include "namespace.h"
75#include <errno.h>
76#include <unistd.h>
77#include <signal.h>
78#include <stdlib.h>
79#include "un-namespace.h"
80#include "libc_private.h"
81
82#include <machine/emul.h>
83#include <machine/fp.h>
84#include <machine/frame.h>
85#include <machine/fsr.h>
86#include <machine/instr.h>
87#include <machine/pcb.h>
88#include <machine/tstate.h>
89
90#include "__sparc_utrap_private.h"
91#include "fpu_emu.h"
92#include "fpu_extern.h"
93
94/*
95 * Translate current exceptions into `first' exception.  The
96 * bits go the wrong way for ffs() (0x10 is most important, etc).
97 * There are only 5, so do it the obvious way.
98 */
99#define	X1(x) x
100#define	X2(x) x,x
101#define	X4(x) x,x,x,x
102#define	X8(x) X4(x),X4(x)
103#define	X16(x) X8(x),X8(x)
104
105static char cx_to_trapx[] = {
106	X1(FSR_NX),
107	X2(FSR_DZ),
108	X4(FSR_UF),
109	X8(FSR_OF),
110	X16(FSR_NV)
111};
112
113#ifdef FPU_DEBUG
114#ifdef FPU_DEBUG_MASK
115int __fpe_debug = FPU_DEBUG_MASK;
116#else
117int __fpe_debug = 0;
118#endif
119#endif	/* FPU_DEBUG */
120
121static int __fpu_execute(struct utrapframe *, struct fpemu *, u_int32_t, u_long);
122
123/*
124 * Need to use an fpstate on the stack; we could switch, so we cannot safely
125 * modify the pcb one, it might get overwritten.
126 */
127int
128__fpu_exception(struct utrapframe *uf)
129{
130	struct fpemu fe;
131	u_long fsr, tstate;
132	u_int insn;
133	int sig;
134
135	fsr = uf->uf_fsr;
136
137	switch (FSR_GET_FTT(fsr)) {
138	case FSR_FTT_NONE:
139		__utrap_write("lost FPU trap type\n");
140		return (0);
141	case FSR_FTT_IEEE:
142		return (SIGFPE);
143	case FSR_FTT_SEQERR:
144		__utrap_write("FPU sequence error\n");
145		return (SIGFPE);
146	case FSR_FTT_HWERR:
147		__utrap_write("FPU hardware error\n");
148		return (SIGFPE);
149	case FSR_FTT_UNFIN:
150	case FSR_FTT_UNIMP:
151		break;
152	default:
153		__utrap_write("unknown FPU error\n");
154		return (SIGFPE);
155	}
156
157	fe.fe_fsr = fsr & ~FSR_FTT_MASK;
158	insn = *(u_int32_t *)uf->uf_pc;
159	if (IF_OP(insn) != IOP_MISC || (IF_F3_OP3(insn) != INS2_FPop1 &&
160	    IF_F3_OP3(insn) != INS2_FPop2))
161		__utrap_panic("bogus FP fault");
162	tstate = uf->uf_state;
163	sig = __fpu_execute(uf, &fe, insn, tstate);
164	if (sig != 0)
165		return (sig);
166	__asm __volatile("ldx %0, %%fsr" : : "m" (fe.fe_fsr));
167	return (0);
168}
169
170#ifdef FPU_DEBUG
171/*
172 * Dump a `fpn' structure.
173 */
174void
175__fpu_dumpfpn(struct fpn *fp)
176{
177	static char *class[] = {
178		"SNAN", "QNAN", "ZERO", "NUM", "INF"
179	};
180
181	printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2],
182		fp->fp_sign ? '-' : ' ',
183		fp->fp_mant[0],	fp->fp_mant[1],
184		fp->fp_mant[2], fp->fp_mant[3],
185		fp->fp_exp);
186}
187#endif
188
189static void
190__fpu_mov(struct fpemu *fe, int type, int rd, int rs1, int rs2)
191{
192	int i;
193
194	i = 1 << type;
195	__fpu_setreg(rd++, rs1);
196	while (--i)
197		__fpu_setreg(rd++, __fpu_getreg(++rs2));
198}
199
200static __inline void
201__fpu_ccmov(struct fpemu *fe, int type, int rd, int rs1, int rs2,
202    u_int32_t insn, int fcc)
203{
204
205	if (IF_F4_COND(insn) == fcc)
206		__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
207}
208
209static int
210__fpu_cmpck(struct fpemu *fe)
211{
212	u_long fsr;
213	int cx;
214
215	/*
216	 * The only possible exception here is NV; catch it
217	 * early and get out, as there is no result register.
218	 */
219	cx = fe->fe_cx;
220	fsr = fe->fe_fsr | (cx << FSR_CEXC_SHIFT);
221	if (cx != 0) {
222		if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
223			fe->fe_fsr = (fsr & ~FSR_FTT_MASK) |
224			    FSR_FTT(FSR_FTT_IEEE);
225			return (SIGFPE);
226		}
227		fsr |= FSR_NV << FSR_AEXC_SHIFT;
228	}
229	fe->fe_fsr = fsr;
230	return (0);
231}
232
233static int opmask[] = {0, 0, 1, 3};
234
235/*
236 * Helper for forming the below case statements. Build only the op3 and opf
237 * field of the instruction, these are the only that need to match.
238 */
239#define	FOP(op3, opf) \
240	((op3) << IF_F3_OP3_SHIFT | (opf) << IF_F3_OPF_SHIFT)
241
242/*
243 * Execute an FPU instruction (one that runs entirely in the FPU; not
244 * FBfcc or STF, for instance).  On return, fe->fe_fs->fs_fsr will be
245 * modified to reflect the setting the hardware would have left.
246 *
247 * Note that we do not catch all illegal opcodes, so you can, for instance,
248 * multiply two integers this way.
249 */
250static int
251__fpu_execute(struct utrapframe *uf, struct fpemu *fe, u_int32_t insn, u_long tstate)
252{
253	struct fpn *fp;
254	int opf, rs1, rs2, rd, type, mask, cx, cond;
255	u_long reg, fsr;
256	u_int space[4];
257
258	/*
259	 * `Decode' and execute instruction.  Start with no exceptions.
260	 * The type of any opf opcode is in the bottom two bits, so we
261	 * squish them out here.
262	 */
263	opf = insn & (IF_MASK(IF_F3_OP3_SHIFT, IF_F3_OP3_BITS) |
264	    IF_MASK(IF_F3_OPF_SHIFT + 2, IF_F3_OPF_BITS - 2));
265	type = IF_F3_OPF(insn) & 3;
266	mask = opmask[type];
267	rs1 = IF_F3_RS1(insn) & ~mask;
268	rs2 = IF_F3_RS2(insn) & ~mask;
269	rd = IF_F3_RD(insn) & ~mask;
270	cond = 0;
271#ifdef notdef
272	if ((rs1 | rs2 | rd) & mask)
273		return (SIGILL);
274#endif
275	fsr = fe->fe_fsr;
276	fe->fe_fsr &= ~FSR_CEXC_MASK;
277	fe->fe_cx = 0;
278	switch (opf) {
279	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(0))):
280		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
281		    FSR_GET_FCC0(fsr));
282		return (0);
283	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(1))):
284		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
285		    FSR_GET_FCC1(fsr));
286		return (0);
287	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(2))):
288		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
289		    FSR_GET_FCC2(fsr));
290		return (0);
291	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(3))):
292		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
293		    FSR_GET_FCC3(fsr));
294		return (0);
295	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_ICC)):
296		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
297		    (tstate & TSTATE_ICC_MASK) >> TSTATE_ICC_SHIFT);
298		return (0);
299	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_XCC)):
300		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
301		    (tstate & TSTATE_XCC_MASK) >> (TSTATE_XCC_SHIFT));
302		return (0);
303	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_Z)):
304		reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
305		if (reg == 0)
306			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
307		return (0);
308	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LEZ)):
309		reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
310		if (reg <= 0)
311			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
312		return (0);
313	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LZ)):
314		reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
315		if (reg < 0)
316			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
317		return (0);
318	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_NZ)):
319		reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
320		if (reg != 0)
321			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
322		return (0);
323	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GZ)):
324		reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
325		if (reg > 0)
326			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
327		return (0);
328	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GEZ)):
329		reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
330		if (reg >= 0)
331			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
332		return (0);
333	case FOP(INS2_FPop2, INSFP2_FCMP):
334		__fpu_explode(fe, &fe->fe_f1, type, rs1);
335		__fpu_explode(fe, &fe->fe_f2, type, rs2);
336		__fpu_compare(fe, 0, IF_F3_CC(insn));
337		return (__fpu_cmpck(fe));
338	case FOP(INS2_FPop2, INSFP2_FCMPE):
339		__fpu_explode(fe, &fe->fe_f1, type, rs1);
340		__fpu_explode(fe, &fe->fe_f2, type, rs2);
341		__fpu_compare(fe, 1, IF_F3_CC(insn));
342		return (__fpu_cmpck(fe));
343	case FOP(INS2_FPop1, INSFP1_FMOV):	/* these should all be pretty obvious */
344		__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
345		return (0);
346	case FOP(INS2_FPop1, INSFP1_FNEG):
347		__fpu_mov(fe, type, rd, __fpu_getreg(rs2) ^ (1 << 31), rs2);
348		return (0);
349	case FOP(INS2_FPop1, INSFP1_FABS):
350		__fpu_mov(fe, type, rd, __fpu_getreg(rs2) & ~(1 << 31), rs2);
351		return (0);
352	case FOP(INS2_FPop1, INSFP1_FSQRT):
353		__fpu_explode(fe, &fe->fe_f1, type, rs2);
354		fp = __fpu_sqrt(fe);
355		break;
356	case FOP(INS2_FPop1, INSFP1_FADD):
357		__fpu_explode(fe, &fe->fe_f1, type, rs1);
358		__fpu_explode(fe, &fe->fe_f2, type, rs2);
359		fp = __fpu_add(fe);
360		break;
361	case FOP(INS2_FPop1, INSFP1_FSUB):
362		__fpu_explode(fe, &fe->fe_f1, type, rs1);
363		__fpu_explode(fe, &fe->fe_f2, type, rs2);
364		fp = __fpu_sub(fe);
365		break;
366	case FOP(INS2_FPop1, INSFP1_FMUL):
367		__fpu_explode(fe, &fe->fe_f1, type, rs1);
368		__fpu_explode(fe, &fe->fe_f2, type, rs2);
369		fp = __fpu_mul(fe);
370		break;
371	case FOP(INS2_FPop1, INSFP1_FDIV):
372		__fpu_explode(fe, &fe->fe_f1, type, rs1);
373		__fpu_explode(fe, &fe->fe_f2, type, rs2);
374		fp = __fpu_div(fe);
375		break;
376	case FOP(INS2_FPop1, INSFP1_FsMULd):
377	case FOP(INS2_FPop1, INSFP1_FdMULq):
378		if (type == FTYPE_EXT)
379			return (SIGILL);
380		__fpu_explode(fe, &fe->fe_f1, type, rs1);
381		__fpu_explode(fe, &fe->fe_f2, type, rs2);
382		type++;	/* single to double, or double to quad */
383		/*
384		 * Recalculate rd (the old type applied for the source regs
385		 * only, the target one has a different size).
386		 */
387		mask = opmask[type];
388		rd = IF_F3_RD(insn) & ~mask;
389		fp = __fpu_mul(fe);
390		break;
391	case FOP(INS2_FPop1, INSFP1_FxTOs):
392	case FOP(INS2_FPop1, INSFP1_FxTOd):
393	case FOP(INS2_FPop1, INSFP1_FxTOq):
394		type = FTYPE_LNG;
395		__fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
396		/* sneaky; depends on instruction encoding */
397		type = (IF_F3_OPF(insn) >> 2) & 3;
398		mask = opmask[type];
399		rd = IF_F3_RD(insn) & ~mask;
400		break;
401	case FOP(INS2_FPop1, INSFP1_FTOx):
402		__fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
403		type = FTYPE_LNG;
404		mask = 1;	/* needs 2 registers */
405		rd = IF_F3_RD(insn) & ~mask;
406		break;
407	case FOP(INS2_FPop1, INSFP1_FTOs):
408	case FOP(INS2_FPop1, INSFP1_FTOd):
409	case FOP(INS2_FPop1, INSFP1_FTOq):
410	case FOP(INS2_FPop1, INSFP1_FTOi):
411		__fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
412		/* sneaky; depends on instruction encoding */
413		type = (IF_F3_OPF(insn) >> 2) & 3;
414		mask = opmask[type];
415		rd = IF_F3_RD(insn) & ~mask;
416		break;
417	default:
418		return (SIGILL);
419	}
420
421	/*
422	 * ALU operation is complete.  Collapse the result and then check
423	 * for exceptions.  If we got any, and they are enabled, do not
424	 * alter the destination register, just stop with an exception.
425	 * Otherwise set new current exceptions and accrue.
426	 */
427	__fpu_implode(fe, fp, type, space);
428	cx = fe->fe_cx;
429	if (cx != 0) {
430		mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
431		if (cx & mask) {
432			/* not accrued??? */
433			fsr = (fsr & ~FSR_FTT_MASK) |
434			    FSR_FTT(FSR_FTT_IEEE) |
435			    FSR_CEXC(cx_to_trapx[(cx & mask) - 1]);
436			return (SIGFPE);
437		}
438		fsr |= (cx << FSR_CEXC_SHIFT) | (cx << FSR_AEXC_SHIFT);
439	}
440	fe->fe_fsr = fsr;
441	__fpu_setreg(rd, space[0]);
442	if (type >= FTYPE_DBL || type == FTYPE_LNG) {
443		__fpu_setreg(rd + 1, space[1]);
444		if (type > FTYPE_DBL) {
445			__fpu_setreg(rd + 2, space[2]);
446			__fpu_setreg(rd + 3, space[3]);
447		}
448	}
449	return (0);	/* success */
450}
451