fpu.c revision 92055
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 *	from: @(#)fpu.c	8.1 (Berkeley) 6/11/93
66 *	from: NetBSD: fpu.c,v 1.11 2000/12/06 01:47:50 mrg Exp
67 *
68 * $FreeBSD: head/lib/libc/sparc64/fpu/fpu.c 92055 2002-03-11 03:18:17Z tmm $
69 */
70
71#include <sys/param.h>
72
73#include "namespace.h"
74#include <errno.h>
75#include <unistd.h>
76#include <signal.h>
77#include <stdlib.h>
78#include "un-namespace.h"
79#include "libc_private.h"
80
81#include <machine/emul.h>
82#include <machine/fp.h>
83#include <machine/frame.h>
84#include <machine/fsr.h>
85#include <machine/instr.h>
86#include <machine/pcb.h>
87#include <machine/tstate.h>
88
89#include "../sys/__sparc_utrap_private.h"
90#include "fpu_emu.h"
91#include "fpu_extern.h"
92
93/*
94 * Translate current exceptions into `first' exception.  The
95 * bits go the wrong way for ffs() (0x10 is most important, etc).
96 * There are only 5, so do it the obvious way.
97 */
98#define	X1(x) x
99#define	X2(x) x,x
100#define	X4(x) x,x,x,x
101#define	X8(x) X4(x),X4(x)
102#define	X16(x) X8(x),X8(x)
103
104static char cx_to_trapx[] = {
105	X1(FSR_NX),
106	X2(FSR_DZ),
107	X4(FSR_UF),
108	X8(FSR_OF),
109	X16(FSR_NV)
110};
111
112#ifdef FPU_DEBUG
113#ifdef FPU_DEBUG_MASK
114int __fpe_debug = FPU_DEBUG_MASK;
115#else
116int __fpe_debug = 0;
117#endif
118#endif	/* FPU_DEBUG */
119
120static int __fpu_execute(struct utrapframe *, struct fpemu *, u_int32_t, u_long);
121static void utrap_write(char *);
122static void utrap_kill_self(int);
123
124/*
125 * System call wrappers usable in an utrap environment.
126 */
127static void
128utrap_write(char *str)
129{
130	int berrno;
131
132	berrno = errno;
133	__sys_write(STDERR_FILENO, str, strlen(str));
134	errno = berrno;
135}
136
137static void
138utrap_kill_self(sig)
139{
140	int berrno;
141
142	berrno = errno;
143	__sys_kill(__sys_getpid(), sig);
144	errno = berrno;
145}
146
147void
148__fpu_panic(char *msg)
149{
150
151	utrap_write(msg);
152	utrap_write("\n");
153	utrap_kill_self(SIGKILL);
154}
155
156/*
157 * Need to use an fpstate on the stack; we could switch, so we cannot safely
158 * modify the pcb one, it might get overwritten.
159 */
160void
161__fpu_exception(struct utrapframe *uf)
162{
163	struct fpemu fe;
164	u_long fsr, tstate;
165	u_int insn;
166	int rv;
167
168	fsr = uf->uf_fsr;
169
170	switch (FSR_GET_FTT(fsr)) {
171	case FSR_FTT_NONE:
172		utrap_write("lost FPU trap type\n");
173		return;
174	case FSR_FTT_IEEE:
175		goto fatal;
176	case FSR_FTT_SEQERR:
177		utrap_write("FPU sequence error\n");
178		goto fatal;
179	case FSR_FTT_HWERR:
180		utrap_write("FPU hardware error\n");
181		goto fatal;
182	case FSR_FTT_UNFIN:
183	case FSR_FTT_UNIMP:
184		break;
185	default:
186		utrap_write("unknown FPU error\n");
187		goto fatal;
188	}
189
190	fe.fe_fsr = fsr & ~FSR_FTT_MASK;
191	insn = *(u_int32_t *)uf->uf_pc;
192	if (IF_OP(insn) != IOP_MISC || (IF_F3_OP3(insn) != INS2_FPop1 &&
193	    IF_F3_OP3(insn) != INS2_FPop2))
194		__fpu_panic("bogus FP fault");
195	tstate = uf->uf_state;
196	rv = __fpu_execute(uf, &fe, insn, tstate);
197	if (rv != 0)
198		utrap_kill_self(rv);
199	__asm __volatile("ldx %0, %%fsr" : : "m" (fe.fe_fsr));
200	return;
201fatal:
202	utrap_kill_self(SIGFPE);
203	return;
204}
205
206#ifdef FPU_DEBUG
207/*
208 * Dump a `fpn' structure.
209 */
210void
211__fpu_dumpfpn(struct fpn *fp)
212{
213	static char *class[] = {
214		"SNAN", "QNAN", "ZERO", "NUM", "INF"
215	};
216
217	printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2],
218		fp->fp_sign ? '-' : ' ',
219		fp->fp_mant[0],	fp->fp_mant[1],
220		fp->fp_mant[2], fp->fp_mant[3],
221		fp->fp_exp);
222}
223#endif
224
225static u_long
226fetch_reg(struct utrapframe *uf, int reg)
227{
228	u_long offs;
229	struct frame *frm;
230
231	if (reg == IREG_G0)
232		return (0);
233	else if (reg < IREG_O0)	/* global */
234		return (uf->uf_global[reg]);
235	else if (reg < IREG_L0)	/* out */
236		return (uf->uf_out[reg - IREG_O0]);
237	else {			/* local, in */
238		/*
239		 * The in registers are immediately after the locals in
240		 * the frame.
241		 */
242		frm = (struct frame *)(uf->uf_out[6] + SPOFF);
243		return (frm->f_local[reg - IREG_L0]);
244	}
245	__fpu_panic("fetch_reg: bogus register");
246}
247
248static void
249__fpu_mov(struct fpemu *fe, int type, int rd, int rs1, int rs2)
250{
251	int i;
252
253	i = 1 << type;
254	__fpu_setreg(rd++, rs1);
255	while (--i)
256		__fpu_setreg(rd++, __fpu_getreg(++rs2));
257}
258
259static __inline void
260__fpu_ccmov(struct fpemu *fe, int type, int rd, int rs1, int rs2,
261    u_int32_t insn, int fcc)
262{
263
264	if (IF_F4_COND(insn) == fcc)
265		__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
266}
267
268static int
269__fpu_cmpck(struct fpemu *fe)
270{
271	u_long fsr;
272	int cx;
273
274	/*
275	 * The only possible exception here is NV; catch it
276	 * early and get out, as there is no result register.
277	 */
278	cx = fe->fe_cx;
279	fsr = fe->fe_fsr | (cx << FSR_CEXC_SHIFT);
280	if (cx != 0) {
281		if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
282			fe->fe_fsr = (fsr & ~FSR_FTT_MASK) |
283			    FSR_FTT(FSR_FTT_IEEE);
284			return (SIGFPE);
285		}
286		fsr |= FSR_NV << FSR_AEXC_SHIFT;
287	}
288	fe->fe_fsr = fsr;
289	return (0);
290}
291
292static int opmask[] = {0, 0, 1, 3};
293
294/*
295 * Helper for forming the below case statements. Build only the op3 and opf
296 * field of the instruction, these are the only that need to match.
297 */
298#define	FOP(op3, opf) \
299	((op3) << IF_F3_OP3_SHIFT | (opf) << IF_F3_OPF_SHIFT)
300
301/*
302 * Execute an FPU instruction (one that runs entirely in the FPU; not
303 * FBfcc or STF, for instance).  On return, fe->fe_fs->fs_fsr will be
304 * modified to reflect the setting the hardware would have left.
305 *
306 * Note that we do not catch all illegal opcodes, so you can, for instance,
307 * multiply two integers this way.
308 */
309static int
310__fpu_execute(struct utrapframe *uf, struct fpemu *fe, u_int32_t insn, u_long tstate)
311{
312	struct fpn *fp;
313	int opf, rs1, rs2, rd, type, mask, cx, cond;
314	u_long reg, fsr;
315	u_int space[4];
316
317	/*
318	 * `Decode' and execute instruction.  Start with no exceptions.
319	 * The type of any opf opcode is in the bottom two bits, so we
320	 * squish them out here.
321	 */
322	opf = insn & (IF_MASK(IF_F3_OP3_SHIFT, IF_F3_OP3_BITS) |
323	    IF_MASK(IF_F3_OPF_SHIFT + 2, IF_F3_OPF_BITS - 2));
324	type = IF_F3_OPF(insn) & 3;
325	mask = opmask[type];
326	rs1 = IF_F3_RS1(insn) & ~mask;
327	rs2 = IF_F3_RS2(insn) & ~mask;
328	rd = IF_F3_RD(insn) & ~mask;
329	cond = 0;
330#ifdef notdef
331	if ((rs1 | rs2 | rd) & mask)
332		return (SIGILL);
333#endif
334	fsr = fe->fe_fsr;
335	fe->fe_fsr &= ~FSR_CEXC_MASK;
336	fe->fe_cx = 0;
337	switch (opf) {
338	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(0))):
339		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
340		    FSR_GET_FCC0(fsr));
341		return (0);
342	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(1))):
343		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
344		    FSR_GET_FCC1(fsr));
345		return (0);
346	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(2))):
347		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
348		    FSR_GET_FCC2(fsr));
349		return (0);
350	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(3))):
351		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
352		    FSR_GET_FCC3(fsr));
353		return (0);
354	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_ICC)):
355		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
356		    (tstate & TSTATE_ICC_MASK) >> TSTATE_ICC_SHIFT);
357		return (0);
358	case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_XCC)):
359		__fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn,
360		    (tstate & TSTATE_XCC_MASK) >> (TSTATE_XCC_SHIFT));
361		return (0);
362	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_Z)):
363		reg = fetch_reg(uf, IF_F4_RS1(insn));
364		if (reg == 0)
365			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
366		return (0);
367	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LEZ)):
368		reg = fetch_reg(uf, IF_F4_RS1(insn));
369		if (reg <= 0)
370			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
371		return (0);
372	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LZ)):
373		reg = fetch_reg(uf, IF_F4_RS1(insn));
374		if (reg < 0)
375			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
376		return (0);
377	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_NZ)):
378		reg = fetch_reg(uf, IF_F4_RS1(insn));
379		if (reg != 0)
380			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
381		return (0);
382	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GZ)):
383		reg = fetch_reg(uf, IF_F4_RS1(insn));
384		if (reg > 0)
385			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
386		return (0);
387	case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GEZ)):
388		reg = fetch_reg(uf, IF_F4_RS1(insn));
389		if (reg >= 0)
390			__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
391		return (0);
392	case FOP(INS2_FPop2, INSFP2_FCMP):
393		__fpu_explode(fe, &fe->fe_f1, type, rs1);
394		__fpu_explode(fe, &fe->fe_f2, type, rs2);
395		__fpu_compare(fe, 0, IF_F3_CC(insn));
396		return (__fpu_cmpck(fe));
397	case FOP(INS2_FPop2, INSFP2_FCMPE):
398		__fpu_explode(fe, &fe->fe_f1, type, rs1);
399		__fpu_explode(fe, &fe->fe_f2, type, rs2);
400		__fpu_compare(fe, 1, IF_F3_CC(insn));
401		return (__fpu_cmpck(fe));
402	case FOP(INS2_FPop1, INSFP1_FMOV):	/* these should all be pretty obvious */
403		__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
404		return (0);
405	case FOP(INS2_FPop1, INSFP1_FNEG):
406		__fpu_mov(fe, type, rd, __fpu_getreg(rs2) ^ (1 << 31), rs2);
407		return (0);
408	case FOP(INS2_FPop1, INSFP1_FABS):
409		__fpu_mov(fe, type, rd, __fpu_getreg(rs2) & ~(1 << 31), rs2);
410		return (0);
411	case FOP(INS2_FPop1, INSFP1_FSQRT):
412		__fpu_explode(fe, &fe->fe_f1, type, rs2);
413		fp = __fpu_sqrt(fe);
414		break;
415	case FOP(INS2_FPop1, INSFP1_FADD):
416		__fpu_explode(fe, &fe->fe_f1, type, rs1);
417		__fpu_explode(fe, &fe->fe_f2, type, rs2);
418		fp = __fpu_add(fe);
419		break;
420	case FOP(INS2_FPop1, INSFP1_FSUB):
421		__fpu_explode(fe, &fe->fe_f1, type, rs1);
422		__fpu_explode(fe, &fe->fe_f2, type, rs2);
423		fp = __fpu_sub(fe);
424		break;
425	case FOP(INS2_FPop1, INSFP1_FMUL):
426		__fpu_explode(fe, &fe->fe_f1, type, rs1);
427		__fpu_explode(fe, &fe->fe_f2, type, rs2);
428		fp = __fpu_mul(fe);
429		break;
430	case FOP(INS2_FPop1, INSFP1_FDIV):
431		__fpu_explode(fe, &fe->fe_f1, type, rs1);
432		__fpu_explode(fe, &fe->fe_f2, type, rs2);
433		fp = __fpu_div(fe);
434		break;
435	case FOP(INS2_FPop1, INSFP1_FsMULd):
436	case FOP(INS2_FPop1, INSFP1_FdMULq):
437		if (type == FTYPE_EXT)
438			return (SIGILL);
439		__fpu_explode(fe, &fe->fe_f1, type, rs1);
440		__fpu_explode(fe, &fe->fe_f2, type, rs2);
441		type++;	/* single to double, or double to quad */
442		/*
443		 * Recalculate rd (the old type applied for the source regs
444		 * only, the target one has a different size).
445		 */
446		mask = opmask[type];
447		rd = IF_F3_RD(insn) & ~mask;
448		fp = __fpu_mul(fe);
449		break;
450	case FOP(INS2_FPop1, INSFP1_FxTOs):
451	case FOP(INS2_FPop1, INSFP1_FxTOd):
452	case FOP(INS2_FPop1, INSFP1_FxTOq):
453		type = FTYPE_LNG;
454		__fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
455		/* sneaky; depends on instruction encoding */
456		type = (IF_F3_OPF(insn) >> 2) & 3;
457		mask = opmask[type];
458		rd = IF_F3_RD(insn) & ~mask;
459		break;
460	case FOP(INS2_FPop1, INSFP1_FTOx):
461		__fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
462		type = FTYPE_LNG;
463		mask = 1;	/* needs 2 registers */
464		rd = IF_F3_RD(insn) & ~mask;
465		break;
466	case FOP(INS2_FPop1, INSFP1_FTOs):
467	case FOP(INS2_FPop1, INSFP1_FTOd):
468	case FOP(INS2_FPop1, INSFP1_FTOq):
469	case FOP(INS2_FPop1, INSFP1_FTOi):
470		__fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
471		/* sneaky; depends on instruction encoding */
472		type = (IF_F3_OPF(insn) >> 2) & 3;
473		mask = opmask[type];
474		rd = IF_F3_RD(insn) & ~mask;
475		break;
476	default:
477		return (SIGILL);
478	}
479
480	/*
481	 * ALU operation is complete.  Collapse the result and then check
482	 * for exceptions.  If we got any, and they are enabled, do not
483	 * alter the destination register, just stop with an exception.
484	 * Otherwise set new current exceptions and accrue.
485	 */
486	__fpu_implode(fe, fp, type, space);
487	cx = fe->fe_cx;
488	if (cx != 0) {
489		mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
490		if (cx & mask) {
491			/* not accrued??? */
492			fsr = (fsr & ~FSR_FTT_MASK) |
493			    FSR_FTT(FSR_FTT_IEEE) |
494			    FSR_CEXC(cx_to_trapx[(cx & mask) - 1]);
495			return (SIGFPE);
496		}
497		fsr |= (cx << FSR_CEXC_SHIFT) | (cx << FSR_AEXC_SHIFT);
498	}
499	fe->fe_fsr = fsr;
500	__fpu_setreg(rd, space[0]);
501	if (type >= FTYPE_DBL || type == FTYPE_LNG) {
502		__fpu_setreg(rd + 1, space[1]);
503		if (type > FTYPE_DBL) {
504			__fpu_setreg(rd + 2, space[2]);
505			__fpu_setreg(rd + 3, space[3]);
506		}
507	}
508	return (0);	/* success */
509}
510