1 /* $NetBSD: fpu.c,v 1.25 2005/11/16 23:24:44 uwe Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This software was developed by the Computer Systems Engineering group
8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9 * contributed to Berkeley.
11 * All advertising materials mentioning features or use of this software
12 * must display the following acknowledgement:
13 * This product includes software developed by the University of
14 * California, Lawrence Berkeley Laboratory.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * @(#)fpu.c 8.1 (Berkeley) 6/11/93
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.25 2005/11/16 23:24:44 uwe Exp $");
46 #include <sys/param.h>
48 #include <sys/signal.h>
49 #include <sys/systm.h>
50 #include <sys/syslog.h>
51 #include <sys/signalvar.h>
53 #include <machine/instr.h>
54 #include <machine/reg.h>
56 #include <sparc/fpu/fpu_emu.h>
57 #include <sparc/fpu/fpu_extern.h>
63 * Dump a `fpn' structure.
66 fpu_dumpfpn(struct fpn
*fp
)
68 static const char *class[] = {
69 "SNAN", "QNAN", "ZERO", "NUM", "INF"
72 printf("%s %c.%x %x %x %xE%d", class[fp
->fp_class
+ 2],
73 fp
->fp_sign
? '-' : ' ',
74 fp
->fp_mant
[0], fp
->fp_mant
[1],
75 fp
->fp_mant
[2], fp
->fp_mant
[3],
81 * fpu_execute returns the following error numbers (0 = no error):
83 #define FPE 1 /* take a floating point exception */
84 #define NOTFPU 2 /* not an FPU instruction */
87 * Translate current exceptions into `first' exception. The
88 * bits go the wrong way for ffs() (0x10 is most important, etc).
89 * There are only 5, so do it the obvious way.
94 #define X8(x) X4(x),X4(x)
95 #define X16(x) X8(x),X8(x)
97 static char cx_to_trapx
[] = {
104 static u_char fpu_codes_native
[] = {
111 #if defined(COMPAT_SUNOS)
112 static u_char fpu_codes_sunos
[] = {
113 X1(FPE_FLTINEX_TRAP
),
117 X16(FPE_FLTOPERR_TRAP
)
119 extern struct emul emul_sunos
;
120 #endif /* SUNOS_COMPAT */
121 /* Note: SVR4(Solaris) FPE_* codes happen to be compatible with ours */
124 * The FPU gave us an exception. Clean up the mess. Note that the
125 * fp queue can only have FPops in it, never load/store FP registers
126 * nor FBfcc instructions. Experiments with `crashme' prove that
127 * unknown FPops do enter the queue, however.
135 struct fpstate64
*fs
;
138 int i
, fsr
= fs
->fs_fsr
, error
;
139 struct proc
*p
= l
->l_proc
;
147 (p
->p_emul
== &emul_sunos
) ? fpu_codes_sunos
:
151 switch ((fsr
>> FSR_FTT_SHIFT
) & FSR_FTT_MASK
) {
154 panic("fpu_cleanup: No fault"); /* ??? */
158 DPRINTF(FPE_INSN
, ("fpu_cleanup: FSR_TT_IEEE\n"));
159 /* XXX missing trap address! */
160 if ((i
= fsr
& FSR_CX
) == 0)
161 panic("fpu ieee trap, but no exception");
162 code
= fpu_codes
[i
- 1];
163 break; /* XXX should return, but queue remains */
166 DPRINTF(FPE_INSN
, ("fpu_cleanup: FSR_TT_UNFIN\n"));
168 if (fs
->fs_qsize
== 0) {
169 printf("fpu_cleanup: unfinished fpop");
170 /* The book sez reexecute or emulate. */
177 DPRINTF(FPE_INSN
, ("fpu_cleanup: FSR_TT_UNIMP\n"));
178 if (fs
->fs_qsize
== 0)
179 panic("fpu_cleanup: unimplemented fpop");
183 panic("fpu sequence error");
187 DPRINTF(FPE_INSN
, ("fpu_cleanup: FSR_TT_HWERR\n"));
188 log(LOG_ERR
, "fpu hardware error (%s[%d])\n",
189 p
->p_comm
, p
->p_pid
);
190 uprintf("%s[%d]: fpu hardware error\n", p
->p_comm
, p
->p_pid
);
195 printf("fsr=0x%x\n", fsr
);
199 /* emulate the instructions left in the queue */
201 for (i
= 0; i
< fs
->fs_qsize
; i
++) {
202 instr
.i_int
= fs
->fs_queue
[i
].fq_instr
;
203 if (instr
.i_any
.i_op
!= IOP_reg
||
204 (instr
.i_op3
.i_op3
!= IOP3_FPop1
&&
205 instr
.i_op3
.i_op3
!= IOP3_FPop2
))
206 panic("bogus fpu queue");
207 error
= fpu_execute(&fe
, instr
);
213 code
= fpu_codes
[(fs
->fs_fsr
& FSR_CX
) - 1];
219 printf("fpu_cleanup: not an FPU error -- sending SIGILL\n");
226 panic("fpu_cleanup 3");
229 /* XXX should stop here, but queue remains */
238 * If we have no FPU at all (are there any machines like this out
239 * there!?) we have to emulate each instruction, and we need a pointer
240 * to the trapframe so that we can step over them and do FBfcc's.
241 * We know the `queue' is empty, though; we just want to emulate
242 * the instruction at tf->tf_pc.
244 fpu_emulate(l
, tf
, fs
)
246 struct trapframe
*tf
;
250 struct fpstate64
*fs
;
258 struct pcb
*pcb
= lwp_getpcb(l
);
260 * We do this here, rather than earlier, to avoid
261 * losing even more badly than usual.
264 write_user_windows();
275 } else if (fpu instr
) {
276 fe
.fe_fsr
= fs
->fs_fsr
&= ~FSR_CX
;
277 error
= fpu_execute(&fe
, fs
, instr
);
283 if (want to reschedule
)
285 } while (error
== 0);
290 * Execute an FPU instruction (one that runs entirely in the FPU; not
291 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be
292 * modified to reflect the setting the hardware would have left.
294 * Note that we do not catch all illegal opcodes, so you can, for instance,
295 * multiply two integers this way.
298 fpu_execute(struct fpemu
*fe
, union instr instr
)
302 int opf
, rs1
, rs2
, rd
, type
, mask
, fsr
, cx
;
305 int opf
, rs1
, rs2
, rd
, type
, mask
, fsr
, cx
, i
, cond
;
306 struct fpstate64
*fs
;
311 * `Decode' and execute instruction. Start with no exceptions.
312 * The type of any i_opf opcode is in the bottom two bits, so we
313 * squish them out here.
315 opf
= instr
.i_opf
.i_opf
;
317 * The low two bits of the opf field for floating point insns usually
318 * correspond to the operation width:
321 * 1: Single precision float
322 * 2: Double precision float
323 * 3: Quad precision float
325 * The exceptions are the integer to float conversion instructions.
327 * For double and quad precision, the low bit if the rs or rd field
328 * is actually the high bit of the register number.
332 mask
= 0x3 >> (3 - type
);
334 rs1
= instr
.i_opf
.i_rs1
;
335 rs1
= (rs1
& ~mask
) | ((rs1
& mask
& 0x1) << 5);
336 rs2
= instr
.i_opf
.i_rs2
;
337 rs2
= (rs2
& ~mask
) | ((rs2
& mask
& 0x1) << 5);
338 rd
= instr
.i_opf
.i_rd
;
339 rd
= (rd
& ~mask
) | ((rd
& mask
& 0x1) << 5);
341 if ((rs1
| rs2
| rd
) & mask
)
342 /* This may be an FPU insn but it is illegal. */
346 fe
->fe_fsr
= fs
->fs_fsr
& ~FSR_CX
;
350 * Check to see if we're dealing with a fancy cmove and handle
353 if (instr
.i_op3
.i_op3
== IOP3_FPop2
&& (opf
&0xff0) != (FCMP
&0xff0)) {
356 DPRINTF(FPE_INSN
, ("fpu_execute: FMVFC0\n"));
357 cond
= (fs
->fs_fsr
>>FSR_FCC_SHIFT
)&FSR_FCC_MASK
;
358 if (instr
.i_fmovcc
.i_cond
!= cond
) return(0); /* success */
359 rs1
= fs
->fs_regs
[rs2
];
362 DPRINTF(FPE_INSN
, ("fpu_execute: FMVFC1\n"));
363 cond
= (fs
->fs_fsr
>>FSR_FCC1_SHIFT
)&FSR_FCC_MASK
;
364 if (instr
.i_fmovcc
.i_cond
!= cond
) return(0); /* success */
365 rs1
= fs
->fs_regs
[rs2
];
368 DPRINTF(FPE_INSN
, ("fpu_execute: FMVFC2\n"));
369 cond
= (fs
->fs_fsr
>>FSR_FCC2_SHIFT
)&FSR_FCC_MASK
;
370 if (instr
.i_fmovcc
.i_cond
!= cond
) return(0); /* success */
371 rs1
= fs
->fs_regs
[rs2
];
374 DPRINTF(FPE_INSN
, ("fpu_execute: FMVFC3\n"));
375 cond
= (fs
->fs_fsr
>>FSR_FCC3_SHIFT
)&FSR_FCC_MASK
;
376 if (instr
.i_fmovcc
.i_cond
!= cond
) return(0); /* success */
377 rs1
= fs
->fs_regs
[rs2
];
380 /* Presume we're curlwp */
381 DPRINTF(FPE_INSN
, ("fpu_execute: FMVIC\n"));
382 cond
= (curlwp
->l_md
.md_tf
->tf_tstate
>>TSTATE_CCR_SHIFT
)&PSR_ICC
;
383 if (instr
.i_fmovcc
.i_cond
!= cond
) return(0); /* success */
384 rs1
= fs
->fs_regs
[rs2
];
387 /* Presume we're curlwp */
388 DPRINTF(FPE_INSN
, ("fpu_execute: FMVXC\n"));
389 cond
= (curlwp
->l_md
.md_tf
->tf_tstate
>>(TSTATE_CCR_SHIFT
+XCC_SHIFT
))&PSR_ICC
;
390 if (instr
.i_fmovcc
.i_cond
!= cond
) return(0); /* success */
391 rs1
= fs
->fs_regs
[rs2
];
394 /* Presume we're curlwp */
395 DPRINTF(FPE_INSN
, ("fpu_execute: FMVRZ\n"));
396 rs1
= instr
.i_fmovr
.i_rs1
;
397 if (rs1
!= 0 && (int64_t)curlwp
->l_md
.md_tf
->tf_global
[rs1
] != 0)
398 return (0); /* success */
399 rs1
= fs
->fs_regs
[rs2
];
402 /* Presume we're curlwp */
403 DPRINTF(FPE_INSN
, ("fpu_execute: FMVRLEZ\n"));
404 rs1
= instr
.i_fmovr
.i_rs1
;
405 if (rs1
!= 0 && (int64_t)curlwp
->l_md
.md_tf
->tf_global
[rs1
] > 0)
406 return (0); /* success */
407 rs1
= fs
->fs_regs
[rs2
];
410 /* Presume we're curlwp */
411 DPRINTF(FPE_INSN
, ("fpu_execute: FMVRLZ\n"));
412 rs1
= instr
.i_fmovr
.i_rs1
;
413 if (rs1
== 0 || (int64_t)curlwp
->l_md
.md_tf
->tf_global
[rs1
] >= 0)
414 return (0); /* success */
415 rs1
= fs
->fs_regs
[rs2
];
418 /* Presume we're curlwp */
419 DPRINTF(FPE_INSN
, ("fpu_execute: FMVRNZ\n"));
420 rs1
= instr
.i_fmovr
.i_rs1
;
421 if (rs1
== 0 || (int64_t)curlwp
->l_md
.md_tf
->tf_global
[rs1
] == 0)
422 return (0); /* success */
423 rs1
= fs
->fs_regs
[rs2
];
426 /* Presume we're curlwp */
427 DPRINTF(FPE_INSN
, ("fpu_execute: FMVRGZ\n"));
428 rs1
= instr
.i_fmovr
.i_rs1
;
429 if (rs1
== 0 || (int64_t)curlwp
->l_md
.md_tf
->tf_global
[rs1
] <= 0)
430 return (0); /* success */
431 rs1
= fs
->fs_regs
[rs2
];
434 /* Presume we're curlwp */
435 DPRINTF(FPE_INSN
, ("fpu_execute: FMVRGEZ\n"));
436 rs1
= instr
.i_fmovr
.i_rs1
;
437 if (rs1
!= 0 && (int64_t)curlwp
->l_md
.md_tf
->tf_global
[rs1
] < 0)
438 return (0); /* success */
439 rs1
= fs
->fs_regs
[rs2
];
443 ("fpu_execute: unknown v9 FP inst %x opf %x\n",
453 ("fpu_execute: unknown basic FP inst %x opf %x\n",
457 case FMOV
>> 2: /* these should all be pretty obvious */
458 DPRINTF(FPE_INSN
, ("fpu_execute: FMOV\n"));
459 rs1
= fs
->fs_regs
[rs2
];
463 DPRINTF(FPE_INSN
, ("fpu_execute: FNEG\n"));
464 rs1
= fs
->fs_regs
[rs2
] ^ (1 << 31);
468 DPRINTF(FPE_INSN
, ("fpu_execute: FABS\n"));
469 rs1
= fs
->fs_regs
[rs2
] & ~(1 << 31);
472 fs
->fs_regs
[rd
] = rs1
;
475 fs
->fs_regs
[rd
++] = rs1
;
477 fs
->fs_regs
[rd
++] = fs
->fs_regs
[++rs2
];
479 fs
->fs_fsr
= fe
->fe_fsr
;
480 return (0); /* success */
483 DPRINTF(FPE_INSN
, ("fpu_execute: FSQRT\n"));
484 fpu_explode(fe
, &fe
->fe_f1
, type
, rs2
);
489 DPRINTF(FPE_INSN
, ("fpu_execute: FADD\n"));
490 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
491 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
496 DPRINTF(FPE_INSN
, ("fpu_execute: FSUB\n"));
497 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
498 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
503 DPRINTF(FPE_INSN
, ("fpu_execute: FMUL\n"));
504 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
505 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
510 DPRINTF(FPE_INSN
, ("fpu_execute: FDIV\n"));
511 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
512 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
517 DPRINTF(FPE_INSN
, ("fpu_execute: FCMP\n"));
518 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
519 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
524 DPRINTF(FPE_INSN
, ("fpu_execute: FCMPE\n"));
525 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
526 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
530 * The only possible exception here is NV; catch it
531 * early and get out, as there is no result register.
534 fsr
= fe
->fe_fsr
| (cx
<< FSR_CX_SHIFT
);
536 if (fsr
& (FSR_NV
<< FSR_TEM_SHIFT
)) {
537 fs
->fs_fsr
= (fsr
& ~FSR_FTT
) |
538 (FSR_TT_IEEE
<< FSR_FTT_SHIFT
);
541 fsr
|= FSR_NV
<< FSR_AX_SHIFT
;
548 DPRINTF(FPE_INSN
, ("fpu_execute: FSMULx\n"));
549 if (type
== FTYPE_EXT
)
551 fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
552 fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
553 type
++; /* single to double, or double to quad */
561 DPRINTF(FPE_INSN
, ("fpu_execute: FXTOx\n"));
563 fpu_explode(fe
, fp
= &fe
->fe_f1
, type
, rs2
);
564 type
= opf
& 3; /* sneaky; depends on instruction encoding */
568 DPRINTF(FPE_INSN
, ("fpu_execute: FTOX\n"));
569 fpu_explode(fe
, fp
= &fe
->fe_f1
, type
, rs2
);
571 /* Recalculate destination register */
572 rd
= instr
.i_opf
.i_rd
;
577 DPRINTF(FPE_INSN
, ("fpu_execute: FTOI\n"));
578 fpu_explode(fe
, fp
= &fe
->fe_f1
, type
, rs2
);
580 /* Recalculate destination register */
581 rd
= instr
.i_opf
.i_rd
;
587 DPRINTF(FPE_INSN
, ("fpu_execute: FTOx\n"));
588 fpu_explode(fe
, fp
= &fe
->fe_f1
, type
, rs2
);
589 /* Recalculate rd with correct type info. */
590 type
= opf
& 3; /* sneaky; depends on instruction encoding */
591 mask
= 0x3 >> (3 - type
);
592 rd
= instr
.i_opf
.i_rd
;
593 rd
= (rd
& ~mask
) | ((rd
& mask
& 0x1) << 5);
598 * ALU operation is complete. Collapse the result and then check
599 * for exceptions. If we got any, and they are enabled, do not
600 * alter the destination register, just stop with an exception.
601 * Otherwise set new current exceptions and accrue.
603 fpu_implode(fe
, fp
, type
, space
);
607 mask
= (fsr
>> FSR_TEM_SHIFT
) & FSR_TEM_MASK
;
610 fs
->fs_fsr
= (fsr
& ~FSR_FTT
) |
611 (FSR_TT_IEEE
<< FSR_FTT_SHIFT
) |
612 (cx_to_trapx
[(cx
& mask
) - 1] << FSR_CX_SHIFT
);
615 fsr
|= (cx
<< FSR_CX_SHIFT
) | (cx
<< FSR_AX_SHIFT
);
618 DPRINTF(FPE_REG
, ("-> %c%d\n", (type
== FTYPE_LNG
) ? 'x' :
619 ((type
== FTYPE_INT
) ? 'i' :
620 ((type
== FTYPE_SNG
) ? 's' :
621 ((type
== FTYPE_DBL
) ? 'd' :
622 ((type
== FTYPE_EXT
) ? 'q' : '?')))),
624 fs
->fs_regs
[rd
] = space
[0];
625 if (type
>= FTYPE_DBL
|| type
== FTYPE_LNG
) {
626 fs
->fs_regs
[rd
+ 1] = space
[1];
627 if (type
> FTYPE_DBL
) {
628 fs
->fs_regs
[rd
+ 2] = space
[2];
629 fs
->fs_regs
[rd
+ 3] = space
[3];
632 return (0); /* success */