2 * arch/sparc64/math-emu/math.c
4 * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
5 * Copyright (C) 1999 David S. Miller (davem@redhat.com)
7 * Emulation routines originate from soft-fp package, which is part
8 * of glibc and has appropriate copyrights in it.
11 #include <linux/types.h>
12 #include <linux/sched.h>
13 #include <linux/errno.h>
14 #include <linux/perf_event.h>
16 #include <asm/fpumacro.h>
17 #include <asm/ptrace.h>
18 #include <asm/uaccess.h>
20 #include "sfp-util_64.h"
21 #include <math-emu/soft-fp.h>
22 #include <math-emu/single.h>
23 #include <math-emu/double.h>
24 #include <math-emu/quad.h>
44 /* SUBNORMAL - ftt == 2 */
62 #define FXTOS 0x084 /* Only Ultra-III generates this. */
63 #define FXTOD 0x088 /* Only Ultra-III generates this. */
64 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
65 #define FITOS 0x0c4 /* Only Ultra-III generates this. */
67 #define FITOD 0x0c8 /* Only Ultra-III generates this. */
84 #define FSR_TEM_SHIFT 23UL
85 #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
86 #define FSR_AEXC_SHIFT 5UL
87 #define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
88 #define FSR_CEXC_SHIFT 0UL
89 #define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
91 /* All routines returning an exception to raise should detect
92 * such exceptions _before_ rounding to be consistent with
93 * the behavior of the hardware in the implemented cases
94 * (and thus with the recommendations in the V9 architecture
97 * We return 0 if a SIGFPE should be sent, 1 otherwise.
99 static inline int record_exception(struct pt_regs
*regs
, int eflag
)
101 u64 fsr
= current_thread_info()->xfsr
[0];
104 /* Determine if this exception would have generated a trap. */
105 would_trap
= (fsr
& ((long)eflag
<< FSR_TEM_SHIFT
)) != 0UL;
107 /* If trapping, we only want to signal one bit. */
108 if(would_trap
!= 0) {
109 eflag
&= ((fsr
& FSR_TEM_MASK
) >> FSR_TEM_SHIFT
);
110 if((eflag
& (eflag
- 1)) != 0) {
111 if(eflag
& FP_EX_INVALID
)
112 eflag
= FP_EX_INVALID
;
113 else if(eflag
& FP_EX_OVERFLOW
)
114 eflag
= FP_EX_OVERFLOW
;
115 else if(eflag
& FP_EX_UNDERFLOW
)
116 eflag
= FP_EX_UNDERFLOW
;
117 else if(eflag
& FP_EX_DIVZERO
)
118 eflag
= FP_EX_DIVZERO
;
119 else if(eflag
& FP_EX_INEXACT
)
120 eflag
= FP_EX_INEXACT
;
124 /* Set CEXC, here is the rule:
126 * In general all FPU ops will set one and only one
127 * bit in the CEXC field, this is always the case
128 * when the IEEE exception trap is enabled in TEM.
130 fsr
&= ~(FSR_CEXC_MASK
);
131 fsr
|= ((long)eflag
<< FSR_CEXC_SHIFT
);
133 /* Set the AEXC field, rule is:
135 * If a trap would not be generated, the
136 * CEXC just generated is OR'd into the
137 * existing value of AEXC.
140 fsr
|= ((long)eflag
<< FSR_AEXC_SHIFT
);
142 /* If trapping, indicate fault trap type IEEE. */
146 current_thread_info()->xfsr
[0] = fsr
;
148 /* If we will not trap, advance the program counter over
149 * the instruction being handled.
151 if(would_trap
== 0) {
152 regs
->tpc
= regs
->tnpc
;
156 return (would_trap
? 0 : 1);
165 int do_mathemu(struct pt_regs
*regs
, struct fpustate
*f
)
167 unsigned long pc
= regs
->tpc
;
168 unsigned long tstate
= regs
->tstate
;
171 /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
172 whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
173 non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
174 #define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
176 static u64 zero
[2] = { 0L, 0L };
179 FP_DECL_S(SA
); FP_DECL_S(SB
); FP_DECL_S(SR
);
180 FP_DECL_D(DA
); FP_DECL_D(DB
); FP_DECL_D(DR
);
181 FP_DECL_Q(QA
); FP_DECL_Q(QB
); FP_DECL_Q(QR
);
185 if (tstate
& TSTATE_PRIV
)
186 die_if_kernel("unfinished/unimplemented FPop from kernel", regs
);
187 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS
, 1, 0, regs
, 0);
188 if (test_thread_flag(TIF_32BIT
))
190 if (get_user(insn
, (u32 __user
*) pc
) != -EFAULT
) {
191 if ((insn
& 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
192 switch ((insn
>> 5) & 0x1ff) {
193 /* QUAD - ftt == 3 */
196 case FABSQ
: TYPE(3,3,0,3,0,0,0); break;
197 case FSQRTQ
: TYPE(3,3,1,3,1,0,0); break;
201 case FDIVQ
: TYPE(3,3,1,3,1,3,1); break;
202 case FDMULQ
: TYPE(3,3,1,2,1,2,1); break;
203 case FQTOX
: TYPE(3,2,0,3,1,0,0); break;
204 case FXTOQ
: TYPE(3,3,1,2,0,0,0); break;
205 case FQTOS
: TYPE(3,1,1,3,1,0,0); break;
206 case FQTOD
: TYPE(3,2,1,3,1,0,0); break;
207 case FITOQ
: TYPE(3,3,1,1,0,0,0); break;
208 case FSTOQ
: TYPE(3,3,1,1,1,0,0); break;
209 case FDTOQ
: TYPE(3,3,1,2,1,0,0); break;
210 case FQTOI
: TYPE(3,1,0,3,1,0,0); break;
212 /* We can get either unimplemented or unfinished
213 * for these cases. Pre-Niagara systems generate
214 * unfinished fpop for SUBNORMAL cases, and Niagara
215 * always gives unimplemented fpop for fsqrt{s,d}.
218 unsigned long x
= current_thread_info()->xfsr
[0];
226 unsigned long x
= current_thread_info()->xfsr
[0];
233 /* SUBNORMAL - ftt == 2 */
237 case FDIVD
: TYPE(2,2,1,2,1,2,1); break;
241 case FDIVS
: TYPE(2,1,1,1,1,1,1); break;
242 case FSMULD
: TYPE(2,2,1,1,1,1,1); break;
243 case FSTOX
: TYPE(2,2,0,1,1,0,0); break;
244 case FDTOX
: TYPE(2,2,0,2,1,0,0); break;
245 case FDTOS
: TYPE(2,1,1,2,1,0,0); break;
246 case FSTOD
: TYPE(2,2,1,1,1,0,0); break;
247 case FSTOI
: TYPE(2,1,0,1,1,0,0); break;
248 case FDTOI
: TYPE(2,1,0,2,1,0,0); break;
250 /* Only Ultra-III generates these */
251 case FXTOS
: TYPE(2,1,1,2,0,0,0); break;
252 case FXTOD
: TYPE(2,2,1,2,0,0,0); break;
253 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
254 case FITOS
: TYPE(2,1,1,1,0,0,0); break;
256 case FITOD
: TYPE(2,2,1,1,0,0,0); break;
259 else if ((insn
& 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
261 switch ((insn
>> 5) & 0x1ff) {
262 case FCMPQ
: TYPE(3,0,0,3,1,3,1); break;
263 case FCMPEQ
: TYPE(3,0,0,3,1,3,1); break;
264 /* Now the conditional fmovq support */
269 /* fmovq %fccX, %fY, %fZ */
270 if (!((insn
>> 11) & 3))
271 XR
= current_thread_info()->xfsr
[0] >> 10;
273 XR
= current_thread_info()->xfsr
[0] >> (30 + ((insn
>> 10) & 0x6));
276 switch ((insn
>> 14) & 0x7) {
277 /* case 0: IR = 0; break; */ /* Never */
278 case 1: if (XR
) IR
= 1; break; /* Not Equal */
279 case 2: if (XR
== 1 || XR
== 2) IR
= 1; break; /* Less or Greater */
280 case 3: if (XR
& 1) IR
= 1; break; /* Unordered or Less */
281 case 4: if (XR
== 1) IR
= 1; break; /* Less */
282 case 5: if (XR
& 2) IR
= 1; break; /* Unordered or Greater */
283 case 6: if (XR
== 2) IR
= 1; break; /* Greater */
284 case 7: if (XR
== 3) IR
= 1; break; /* Unordered */
286 if ((insn
>> 14) & 8)
291 /* fmovq %[ix]cc, %fY, %fZ */
292 XR
= regs
->tstate
>> 32;
293 if ((insn
>> 5) & 0x80)
297 freg
= ((XR
>> 2) ^ XR
) & 2;
298 switch ((insn
>> 14) & 0x7) {
299 /* case 0: IR = 0; break; */ /* Never */
300 case 1: if (XR
& 4) IR
= 1; break; /* Equal */
301 case 2: if ((XR
& 4) || freg
) IR
= 1; break; /* Less or Equal */
302 case 3: if (freg
) IR
= 1; break; /* Less */
303 case 4: if (XR
& 5) IR
= 1; break; /* Less or Equal Unsigned */
304 case 5: if (XR
& 1) IR
= 1; break; /* Carry Set */
305 case 6: if (XR
& 8) IR
= 1; break; /* Negative */
306 case 7: if (XR
& 2) IR
= 1; break; /* Overflow Set */
308 if ((insn
>> 14) & 8)
317 freg
= (insn
>> 14) & 0x1f;
321 XR
= regs
->u_regs
[freg
];
322 else if (test_thread_flag(TIF_32BIT
)) {
323 struct reg_window32 __user
*win32
;
325 win32
= (struct reg_window32 __user
*)((unsigned long)((u32
)regs
->u_regs
[UREG_FP
]));
326 get_user(XR
, &win32
->locals
[freg
- 16]);
328 struct reg_window __user
*win
;
330 win
= (struct reg_window __user
*)(regs
->u_regs
[UREG_FP
] + STACK_BIAS
);
331 get_user(XR
, &win
->locals
[freg
- 16]);
334 switch ((insn
>> 10) & 3) {
335 case 1: if (!XR
) IR
= 1; break; /* Register Zero */
336 case 2: if (XR
<= 0) IR
= 1; break; /* Register Less Than or Equal to Zero */
337 case 3: if (XR
< 0) IR
= 1; break; /* Register Less Than Zero */
339 if ((insn
>> 10) & 4)
344 /* The fmov test was false. Do a nop instead */
345 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
346 regs
->tpc
= regs
->tnpc
;
349 } else if (IR
== 1) {
350 /* Change the instruction into plain fmovq */
351 insn
= (insn
& 0x3e00001f) | 0x81a00060;
357 argp rs1
= NULL
, rs2
= NULL
, rd
= NULL
;
359 freg
= (current_thread_info()->xfsr
[0] >> 14) & 0xf;
360 if (freg
!= (type
>> 9))
362 current_thread_info()->xfsr
[0] &= ~0x1c000;
363 freg
= ((insn
>> 14) & 0x1f);
364 switch (type
& 0x3) {
365 case 3: if (freg
& 2) {
366 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
369 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
370 case 1: rs1
= (argp
)&f
->regs
[freg
];
371 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
372 if (!(current_thread_info()->fpsaved
[0] & flags
))
376 switch (type
& 0x7) {
377 case 7: FP_UNPACK_QP (QA
, rs1
); break;
378 case 6: FP_UNPACK_DP (DA
, rs1
); break;
379 case 5: FP_UNPACK_SP (SA
, rs1
); break;
381 freg
= (insn
& 0x1f);
382 switch ((type
>> 3) & 0x3) {
383 case 3: if (freg
& 2) {
384 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
387 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
388 case 1: rs2
= (argp
)&f
->regs
[freg
];
389 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
390 if (!(current_thread_info()->fpsaved
[0] & flags
))
394 switch ((type
>> 3) & 0x7) {
395 case 7: FP_UNPACK_QP (QB
, rs2
); break;
396 case 6: FP_UNPACK_DP (DB
, rs2
); break;
397 case 5: FP_UNPACK_SP (SB
, rs2
); break;
399 freg
= ((insn
>> 25) & 0x1f);
400 switch ((type
>> 6) & 0x3) {
401 case 3: if (freg
& 2) {
402 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
405 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
406 case 1: rd
= (argp
)&f
->regs
[freg
];
407 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
408 if (!(current_thread_info()->fpsaved
[0] & FPRS_FEF
)) {
409 current_thread_info()->fpsaved
[0] = FPRS_FEF
;
410 current_thread_info()->gsr
[0] = 0;
412 if (!(current_thread_info()->fpsaved
[0] & flags
)) {
414 memset(f
->regs
, 0, 32*sizeof(u32
));
416 memset(f
->regs
+32, 0, 32*sizeof(u32
));
418 current_thread_info()->fpsaved
[0] |= flags
;
421 switch ((insn
>> 5) & 0x1ff) {
423 case FADDS
: FP_ADD_S (SR
, SA
, SB
); break;
424 case FADDD
: FP_ADD_D (DR
, DA
, DB
); break;
425 case FADDQ
: FP_ADD_Q (QR
, QA
, QB
); break;
427 case FSUBS
: FP_SUB_S (SR
, SA
, SB
); break;
428 case FSUBD
: FP_SUB_D (DR
, DA
, DB
); break;
429 case FSUBQ
: FP_SUB_Q (QR
, QA
, QB
); break;
431 case FMULS
: FP_MUL_S (SR
, SA
, SB
); break;
432 case FSMULD
: FP_CONV (D
, S
, 1, 1, DA
, SA
);
433 FP_CONV (D
, S
, 1, 1, DB
, SB
);
434 case FMULD
: FP_MUL_D (DR
, DA
, DB
); break;
435 case FDMULQ
: FP_CONV (Q
, D
, 2, 1, QA
, DA
);
436 FP_CONV (Q
, D
, 2, 1, QB
, DB
);
437 case FMULQ
: FP_MUL_Q (QR
, QA
, QB
); break;
439 case FDIVS
: FP_DIV_S (SR
, SA
, SB
); break;
440 case FDIVD
: FP_DIV_D (DR
, DA
, DB
); break;
441 case FDIVQ
: FP_DIV_Q (QR
, QA
, QB
); break;
443 case FSQRTS
: FP_SQRT_S (SR
, SB
); break;
444 case FSQRTD
: FP_SQRT_D (DR
, DB
); break;
445 case FSQRTQ
: FP_SQRT_Q (QR
, QB
); break;
447 case FMOVQ
: rd
->q
[0] = rs2
->q
[0]; rd
->q
[1] = rs2
->q
[1]; break;
448 case FABSQ
: rd
->q
[0] = rs2
->q
[0] & 0x7fffffffffffffffUL
; rd
->q
[1] = rs2
->q
[1]; break;
449 case FNEGQ
: rd
->q
[0] = rs2
->q
[0] ^ 0x8000000000000000UL
; rd
->q
[1] = rs2
->q
[1]; break;
451 case FSTOI
: FP_TO_INT_S (IR
, SB
, 32, 1); break;
452 case FDTOI
: FP_TO_INT_D (IR
, DB
, 32, 1); break;
453 case FQTOI
: FP_TO_INT_Q (IR
, QB
, 32, 1); break;
454 case FSTOX
: FP_TO_INT_S (XR
, SB
, 64, 1); break;
455 case FDTOX
: FP_TO_INT_D (XR
, DB
, 64, 1); break;
456 case FQTOX
: FP_TO_INT_Q (XR
, QB
, 64, 1); break;
458 case FITOQ
: IR
= rs2
->s
; FP_FROM_INT_Q (QR
, IR
, 32, int); break;
459 case FXTOQ
: XR
= rs2
->d
; FP_FROM_INT_Q (QR
, XR
, 64, long); break;
460 /* Only Ultra-III generates these */
461 case FXTOS
: XR
= rs2
->d
; FP_FROM_INT_S (SR
, XR
, 64, long); break;
462 case FXTOD
: XR
= rs2
->d
; FP_FROM_INT_D (DR
, XR
, 64, long); break;
463 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
464 case FITOS
: IR
= rs2
->s
; FP_FROM_INT_S (SR
, IR
, 32, int); break;
466 case FITOD
: IR
= rs2
->s
; FP_FROM_INT_D (DR
, IR
, 32, int); break;
468 case FSTOD
: FP_CONV (D
, S
, 1, 1, DR
, SB
); break;
469 case FSTOQ
: FP_CONV (Q
, S
, 2, 1, QR
, SB
); break;
470 case FDTOQ
: FP_CONV (Q
, D
, 2, 1, QR
, DB
); break;
471 case FDTOS
: FP_CONV (S
, D
, 1, 1, SR
, DB
); break;
472 case FQTOS
: FP_CONV (S
, Q
, 1, 2, SR
, QB
); break;
473 case FQTOD
: FP_CONV (D
, Q
, 1, 2, DR
, QB
); break;
477 FP_CMP_Q(XR
, QB
, QA
, 3);
479 (((insn
>> 5) & 0x1ff) == FCMPEQ
||
482 FP_SET_EXCEPTION (FP_EX_INVALID
);
484 if (!FP_INHIBIT_RESULTS
) {
485 switch ((type
>> 6) & 0x7) {
486 case 0: xfsr
= current_thread_info()->xfsr
[0];
487 if (XR
== -1) XR
= 2;
490 case 0: xfsr
&= ~0xc00; xfsr
|= (XR
<< 10); break;
491 case 1: xfsr
&= ~0x300000000UL
; xfsr
|= (XR
<< 32); break;
492 case 2: xfsr
&= ~0xc00000000UL
; xfsr
|= (XR
<< 34); break;
493 case 3: xfsr
&= ~0x3000000000UL
; xfsr
|= (XR
<< 36); break;
495 current_thread_info()->xfsr
[0] = xfsr
;
497 case 1: rd
->s
= IR
; break;
498 case 2: rd
->d
= XR
; break;
499 case 5: FP_PACK_SP (rd
, SR
); break;
500 case 6: FP_PACK_DP (rd
, DR
); break;
501 case 7: FP_PACK_QP (rd
, QR
); break;
506 return record_exception(regs
, _fex
);
508 /* Success and no exceptions detected. */
509 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
510 regs
->tpc
= regs
->tnpc
;