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 <linux/uaccess.h>
19 #include <asm/cacheflush.h>
21 #include "sfp-util_64.h"
22 #include <math-emu/soft-fp.h>
23 #include <math-emu/single.h>
24 #include <math-emu/double.h>
25 #include <math-emu/quad.h>
45 /* SUBNORMAL - ftt == 2 */
63 #define FXTOS 0x084 /* Only Ultra-III generates this. */
64 #define FXTOD 0x088 /* Only Ultra-III generates this. */
65 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
66 #define FITOS 0x0c4 /* Only Ultra-III generates this. */
68 #define FITOD 0x0c8 /* Only Ultra-III generates this. */
85 #define FSR_TEM_SHIFT 23UL
86 #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
87 #define FSR_AEXC_SHIFT 5UL
88 #define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
89 #define FSR_CEXC_SHIFT 0UL
90 #define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
92 /* All routines returning an exception to raise should detect
93 * such exceptions _before_ rounding to be consistent with
94 * the behavior of the hardware in the implemented cases
95 * (and thus with the recommendations in the V9 architecture
98 * We return 0 if a SIGFPE should be sent, 1 otherwise.
100 static inline int record_exception(struct pt_regs
*regs
, int eflag
)
102 u64 fsr
= current_thread_info()->xfsr
[0];
105 /* Determine if this exception would have generated a trap. */
106 would_trap
= (fsr
& ((long)eflag
<< FSR_TEM_SHIFT
)) != 0UL;
108 /* If trapping, we only want to signal one bit. */
109 if(would_trap
!= 0) {
110 eflag
&= ((fsr
& FSR_TEM_MASK
) >> FSR_TEM_SHIFT
);
111 if((eflag
& (eflag
- 1)) != 0) {
112 if(eflag
& FP_EX_INVALID
)
113 eflag
= FP_EX_INVALID
;
114 else if(eflag
& FP_EX_OVERFLOW
)
115 eflag
= FP_EX_OVERFLOW
;
116 else if(eflag
& FP_EX_UNDERFLOW
)
117 eflag
= FP_EX_UNDERFLOW
;
118 else if(eflag
& FP_EX_DIVZERO
)
119 eflag
= FP_EX_DIVZERO
;
120 else if(eflag
& FP_EX_INEXACT
)
121 eflag
= FP_EX_INEXACT
;
125 /* Set CEXC, here is the rule:
127 * In general all FPU ops will set one and only one
128 * bit in the CEXC field, this is always the case
129 * when the IEEE exception trap is enabled in TEM.
131 fsr
&= ~(FSR_CEXC_MASK
);
132 fsr
|= ((long)eflag
<< FSR_CEXC_SHIFT
);
134 /* Set the AEXC field, rule is:
136 * If a trap would not be generated, the
137 * CEXC just generated is OR'd into the
138 * existing value of AEXC.
141 fsr
|= ((long)eflag
<< FSR_AEXC_SHIFT
);
143 /* If trapping, indicate fault trap type IEEE. */
147 current_thread_info()->xfsr
[0] = fsr
;
149 /* If we will not trap, advance the program counter over
150 * the instruction being handled.
152 if(would_trap
== 0) {
153 regs
->tpc
= regs
->tnpc
;
157 return (would_trap
? 0 : 1);
166 int do_mathemu(struct pt_regs
*regs
, struct fpustate
*f
, bool illegal_insn_trap
)
168 unsigned long pc
= regs
->tpc
;
169 unsigned long tstate
= regs
->tstate
;
172 /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
173 whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
174 non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
175 #define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
177 static u64 zero
[2] = { 0L, 0L };
180 FP_DECL_S(SA
); FP_DECL_S(SB
); FP_DECL_S(SR
);
181 FP_DECL_D(DA
); FP_DECL_D(DB
); FP_DECL_D(DR
);
182 FP_DECL_Q(QA
); FP_DECL_Q(QB
); FP_DECL_Q(QR
);
186 if (tstate
& TSTATE_PRIV
)
187 die_if_kernel("unfinished/unimplemented FPop from kernel", regs
);
188 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS
, 1, regs
, 0);
189 if (test_thread_flag(TIF_32BIT
))
191 if (get_user(insn
, (u32 __user
*) pc
) != -EFAULT
) {
192 if ((insn
& 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
193 switch ((insn
>> 5) & 0x1ff) {
194 /* QUAD - ftt == 3 */
197 case FABSQ
: TYPE(3,3,0,3,0,0,0); break;
198 case FSQRTQ
: TYPE(3,3,1,3,1,0,0); break;
202 case FDIVQ
: TYPE(3,3,1,3,1,3,1); break;
203 case FDMULQ
: TYPE(3,3,1,2,1,2,1); break;
204 case FQTOX
: TYPE(3,2,0,3,1,0,0); break;
205 case FXTOQ
: TYPE(3,3,1,2,0,0,0); break;
206 case FQTOS
: TYPE(3,1,1,3,1,0,0); break;
207 case FQTOD
: TYPE(3,2,1,3,1,0,0); break;
208 case FITOQ
: TYPE(3,3,1,1,0,0,0); break;
209 case FSTOQ
: TYPE(3,3,1,1,1,0,0); break;
210 case FDTOQ
: TYPE(3,3,1,2,1,0,0); break;
211 case FQTOI
: TYPE(3,1,0,3,1,0,0); break;
213 /* We can get either unimplemented or unfinished
214 * for these cases. Pre-Niagara systems generate
215 * unfinished fpop for SUBNORMAL cases, and Niagara
216 * always gives unimplemented fpop for fsqrt{s,d}.
219 unsigned long x
= current_thread_info()->xfsr
[0];
227 unsigned long x
= current_thread_info()->xfsr
[0];
234 /* SUBNORMAL - ftt == 2 */
238 case FDIVD
: TYPE(2,2,1,2,1,2,1); break;
242 case FDIVS
: TYPE(2,1,1,1,1,1,1); break;
243 case FSMULD
: TYPE(2,2,1,1,1,1,1); break;
244 case FSTOX
: TYPE(2,2,0,1,1,0,0); break;
245 case FDTOX
: TYPE(2,2,0,2,1,0,0); break;
246 case FDTOS
: TYPE(2,1,1,2,1,0,0); break;
247 case FSTOD
: TYPE(2,2,1,1,1,0,0); break;
248 case FSTOI
: TYPE(2,1,0,1,1,0,0); break;
249 case FDTOI
: TYPE(2,1,0,2,1,0,0); break;
251 /* Only Ultra-III generates these */
252 case FXTOS
: TYPE(2,1,1,2,0,0,0); break;
253 case FXTOD
: TYPE(2,2,1,2,0,0,0); break;
254 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
255 case FITOS
: TYPE(2,1,1,1,0,0,0); break;
257 case FITOD
: TYPE(2,2,1,1,0,0,0); break;
260 else if ((insn
& 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
262 switch ((insn
>> 5) & 0x1ff) {
263 case FCMPQ
: TYPE(3,0,0,3,1,3,1); break;
264 case FCMPEQ
: TYPE(3,0,0,3,1,3,1); break;
265 /* Now the conditional fmovq support */
270 /* fmovq %fccX, %fY, %fZ */
271 if (!((insn
>> 11) & 3))
272 XR
= current_thread_info()->xfsr
[0] >> 10;
274 XR
= current_thread_info()->xfsr
[0] >> (30 + ((insn
>> 10) & 0x6));
277 switch ((insn
>> 14) & 0x7) {
278 /* case 0: IR = 0; break; */ /* Never */
279 case 1: if (XR
) IR
= 1; break; /* Not Equal */
280 case 2: if (XR
== 1 || XR
== 2) IR
= 1; break; /* Less or Greater */
281 case 3: if (XR
& 1) IR
= 1; break; /* Unordered or Less */
282 case 4: if (XR
== 1) IR
= 1; break; /* Less */
283 case 5: if (XR
& 2) IR
= 1; break; /* Unordered or Greater */
284 case 6: if (XR
== 2) IR
= 1; break; /* Greater */
285 case 7: if (XR
== 3) IR
= 1; break; /* Unordered */
287 if ((insn
>> 14) & 8)
292 /* fmovq %[ix]cc, %fY, %fZ */
293 XR
= regs
->tstate
>> 32;
294 if ((insn
>> 5) & 0x80)
298 freg
= ((XR
>> 2) ^ XR
) & 2;
299 switch ((insn
>> 14) & 0x7) {
300 /* case 0: IR = 0; break; */ /* Never */
301 case 1: if (XR
& 4) IR
= 1; break; /* Equal */
302 case 2: if ((XR
& 4) || freg
) IR
= 1; break; /* Less or Equal */
303 case 3: if (freg
) IR
= 1; break; /* Less */
304 case 4: if (XR
& 5) IR
= 1; break; /* Less or Equal Unsigned */
305 case 5: if (XR
& 1) IR
= 1; break; /* Carry Set */
306 case 6: if (XR
& 8) IR
= 1; break; /* Negative */
307 case 7: if (XR
& 2) IR
= 1; break; /* Overflow Set */
309 if ((insn
>> 14) & 8)
318 freg
= (insn
>> 14) & 0x1f;
322 XR
= regs
->u_regs
[freg
];
323 else if (!test_thread_64bit_stack(regs
->u_regs
[UREG_FP
])) {
324 struct reg_window32 __user
*win32
;
326 win32
= (struct reg_window32 __user
*)((unsigned long)((u32
)regs
->u_regs
[UREG_FP
]));
327 get_user(XR
, &win32
->locals
[freg
- 16]);
329 struct reg_window __user
*win
;
331 win
= (struct reg_window __user
*)(regs
->u_regs
[UREG_FP
] + STACK_BIAS
);
332 get_user(XR
, &win
->locals
[freg
- 16]);
335 switch ((insn
>> 10) & 3) {
336 case 1: if (!XR
) IR
= 1; break; /* Register Zero */
337 case 2: if (XR
<= 0) IR
= 1; break; /* Register Less Than or Equal to Zero */
338 case 3: if (XR
< 0) IR
= 1; break; /* Register Less Than Zero */
340 if ((insn
>> 10) & 4)
345 /* The fmov test was false. Do a nop instead */
346 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
347 regs
->tpc
= regs
->tnpc
;
350 } else if (IR
== 1) {
351 /* Change the instruction into plain fmovq */
352 insn
= (insn
& 0x3e00001f) | 0x81a00060;
358 argp rs1
= NULL
, rs2
= NULL
, rd
= NULL
;
360 /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap
361 * Type field in the %fsr to unimplemented_FPop. Nor does it
362 * use the fp_exception_other trap. Instead it signals an
363 * illegal instruction and leaves the FP trap type field of
364 * the %fsr unchanged.
366 if (!illegal_insn_trap
) {
367 int ftt
= (current_thread_info()->xfsr
[0] >> 14) & 0x7;
368 if (ftt
!= (type
>> 9))
371 current_thread_info()->xfsr
[0] &= ~0x1c000;
372 freg
= ((insn
>> 14) & 0x1f);
373 switch (type
& 0x3) {
374 case 3: if (freg
& 2) {
375 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
378 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
379 case 1: rs1
= (argp
)&f
->regs
[freg
];
380 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
381 if (!(current_thread_info()->fpsaved
[0] & flags
))
385 switch (type
& 0x7) {
386 case 7: FP_UNPACK_QP (QA
, rs1
); break;
387 case 6: FP_UNPACK_DP (DA
, rs1
); break;
388 case 5: FP_UNPACK_SP (SA
, rs1
); break;
390 freg
= (insn
& 0x1f);
391 switch ((type
>> 3) & 0x3) {
392 case 3: if (freg
& 2) {
393 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
396 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
397 case 1: rs2
= (argp
)&f
->regs
[freg
];
398 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
399 if (!(current_thread_info()->fpsaved
[0] & flags
))
403 switch ((type
>> 3) & 0x7) {
404 case 7: FP_UNPACK_QP (QB
, rs2
); break;
405 case 6: FP_UNPACK_DP (DB
, rs2
); break;
406 case 5: FP_UNPACK_SP (SB
, rs2
); break;
408 freg
= ((insn
>> 25) & 0x1f);
409 switch ((type
>> 6) & 0x3) {
410 case 3: if (freg
& 2) {
411 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
414 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
415 case 1: rd
= (argp
)&f
->regs
[freg
];
416 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
417 if (!(current_thread_info()->fpsaved
[0] & FPRS_FEF
)) {
418 current_thread_info()->fpsaved
[0] = FPRS_FEF
;
419 current_thread_info()->gsr
[0] = 0;
421 if (!(current_thread_info()->fpsaved
[0] & flags
)) {
423 memset(f
->regs
, 0, 32*sizeof(u32
));
425 memset(f
->regs
+32, 0, 32*sizeof(u32
));
427 current_thread_info()->fpsaved
[0] |= flags
;
430 switch ((insn
>> 5) & 0x1ff) {
432 case FADDS
: FP_ADD_S (SR
, SA
, SB
); break;
433 case FADDD
: FP_ADD_D (DR
, DA
, DB
); break;
434 case FADDQ
: FP_ADD_Q (QR
, QA
, QB
); break;
436 case FSUBS
: FP_SUB_S (SR
, SA
, SB
); break;
437 case FSUBD
: FP_SUB_D (DR
, DA
, DB
); break;
438 case FSUBQ
: FP_SUB_Q (QR
, QA
, QB
); break;
440 case FMULS
: FP_MUL_S (SR
, SA
, SB
); break;
441 case FSMULD
: FP_CONV (D
, S
, 1, 1, DA
, SA
);
442 FP_CONV (D
, S
, 1, 1, DB
, SB
);
443 case FMULD
: FP_MUL_D (DR
, DA
, DB
); break;
444 case FDMULQ
: FP_CONV (Q
, D
, 2, 1, QA
, DA
);
445 FP_CONV (Q
, D
, 2, 1, QB
, DB
);
446 case FMULQ
: FP_MUL_Q (QR
, QA
, QB
); break;
448 case FDIVS
: FP_DIV_S (SR
, SA
, SB
); break;
449 case FDIVD
: FP_DIV_D (DR
, DA
, DB
); break;
450 case FDIVQ
: FP_DIV_Q (QR
, QA
, QB
); break;
452 case FSQRTS
: FP_SQRT_S (SR
, SB
); break;
453 case FSQRTD
: FP_SQRT_D (DR
, DB
); break;
454 case FSQRTQ
: FP_SQRT_Q (QR
, QB
); break;
456 case FMOVQ
: rd
->q
[0] = rs2
->q
[0]; rd
->q
[1] = rs2
->q
[1]; break;
457 case FABSQ
: rd
->q
[0] = rs2
->q
[0] & 0x7fffffffffffffffUL
; rd
->q
[1] = rs2
->q
[1]; break;
458 case FNEGQ
: rd
->q
[0] = rs2
->q
[0] ^ 0x8000000000000000UL
; rd
->q
[1] = rs2
->q
[1]; break;
460 case FSTOI
: FP_TO_INT_S (IR
, SB
, 32, 1); break;
461 case FDTOI
: FP_TO_INT_D (IR
, DB
, 32, 1); break;
462 case FQTOI
: FP_TO_INT_Q (IR
, QB
, 32, 1); break;
463 case FSTOX
: FP_TO_INT_S (XR
, SB
, 64, 1); break;
464 case FDTOX
: FP_TO_INT_D (XR
, DB
, 64, 1); break;
465 case FQTOX
: FP_TO_INT_Q (XR
, QB
, 64, 1); break;
467 case FITOQ
: IR
= rs2
->s
; FP_FROM_INT_Q (QR
, IR
, 32, int); break;
468 case FXTOQ
: XR
= rs2
->d
; FP_FROM_INT_Q (QR
, XR
, 64, long); break;
469 /* Only Ultra-III generates these */
470 case FXTOS
: XR
= rs2
->d
; FP_FROM_INT_S (SR
, XR
, 64, long); break;
471 case FXTOD
: XR
= rs2
->d
; FP_FROM_INT_D (DR
, XR
, 64, long); break;
472 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
473 case FITOS
: IR
= rs2
->s
; FP_FROM_INT_S (SR
, IR
, 32, int); break;
475 case FITOD
: IR
= rs2
->s
; FP_FROM_INT_D (DR
, IR
, 32, int); break;
477 case FSTOD
: FP_CONV (D
, S
, 1, 1, DR
, SB
); break;
478 case FSTOQ
: FP_CONV (Q
, S
, 2, 1, QR
, SB
); break;
479 case FDTOQ
: FP_CONV (Q
, D
, 2, 1, QR
, DB
); break;
480 case FDTOS
: FP_CONV (S
, D
, 1, 1, SR
, DB
); break;
481 case FQTOS
: FP_CONV (S
, Q
, 1, 2, SR
, QB
); break;
482 case FQTOD
: FP_CONV (D
, Q
, 1, 2, DR
, QB
); break;
486 FP_CMP_Q(XR
, QB
, QA
, 3);
488 (((insn
>> 5) & 0x1ff) == FCMPEQ
||
491 FP_SET_EXCEPTION (FP_EX_INVALID
);
493 if (!FP_INHIBIT_RESULTS
) {
494 switch ((type
>> 6) & 0x7) {
495 case 0: xfsr
= current_thread_info()->xfsr
[0];
496 if (XR
== -1) XR
= 2;
499 case 0: xfsr
&= ~0xc00; xfsr
|= (XR
<< 10); break;
500 case 1: xfsr
&= ~0x300000000UL
; xfsr
|= (XR
<< 32); break;
501 case 2: xfsr
&= ~0xc00000000UL
; xfsr
|= (XR
<< 34); break;
502 case 3: xfsr
&= ~0x3000000000UL
; xfsr
|= (XR
<< 36); break;
504 current_thread_info()->xfsr
[0] = xfsr
;
506 case 1: rd
->s
= IR
; break;
507 case 2: rd
->d
= XR
; break;
508 case 5: FP_PACK_SP (rd
, SR
); break;
509 case 6: FP_PACK_DP (rd
, DR
); break;
510 case 7: FP_PACK_QP (rd
, QR
); break;
515 return record_exception(regs
, _fex
);
517 /* Success and no exceptions detected. */
518 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
519 regs
->tpc
= regs
->tnpc
;