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