1 // SPDX-License-Identifier: GPL-2.0
3 * arch/sparc64/math-emu/math.c
5 * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
6 * Copyright (C) 1999 David S. Miller (davem@redhat.com)
8 * Emulation routines originate from soft-fp package, which is part
9 * of glibc and has appropriate copyrights in it.
12 #include <linux/types.h>
13 #include <linux/sched.h>
14 #include <linux/errno.h>
15 #include <linux/perf_event.h>
17 #include <asm/fpumacro.h>
18 #include <asm/ptrace.h>
19 #include <linux/uaccess.h>
20 #include <asm/cacheflush.h>
22 #include "sfp-util_64.h"
23 #include <math-emu/soft-fp.h>
24 #include <math-emu/single.h>
25 #include <math-emu/double.h>
26 #include <math-emu/quad.h>
46 /* SUBNORMAL - ftt == 2 */
64 #define FXTOS 0x084 /* Only Ultra-III generates this. */
65 #define FXTOD 0x088 /* Only Ultra-III generates this. */
66 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
67 #define FITOS 0x0c4 /* Only Ultra-III generates this. */
69 #define FITOD 0x0c8 /* Only Ultra-III generates this. */
86 #define FSR_TEM_SHIFT 23UL
87 #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
88 #define FSR_AEXC_SHIFT 5UL
89 #define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
90 #define FSR_CEXC_SHIFT 0UL
91 #define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
93 /* All routines returning an exception to raise should detect
94 * such exceptions _before_ rounding to be consistent with
95 * the behavior of the hardware in the implemented cases
96 * (and thus with the recommendations in the V9 architecture
99 * We return 0 if a SIGFPE should be sent, 1 otherwise.
101 static inline int record_exception(struct pt_regs
*regs
, int eflag
)
103 u64 fsr
= current_thread_info()->xfsr
[0];
106 /* Determine if this exception would have generated a trap. */
107 would_trap
= (fsr
& ((long)eflag
<< FSR_TEM_SHIFT
)) != 0UL;
109 /* If trapping, we only want to signal one bit. */
110 if(would_trap
!= 0) {
111 eflag
&= ((fsr
& FSR_TEM_MASK
) >> FSR_TEM_SHIFT
);
112 if((eflag
& (eflag
- 1)) != 0) {
113 if(eflag
& FP_EX_INVALID
)
114 eflag
= FP_EX_INVALID
;
115 else if(eflag
& FP_EX_OVERFLOW
)
116 eflag
= FP_EX_OVERFLOW
;
117 else if(eflag
& FP_EX_UNDERFLOW
)
118 eflag
= FP_EX_UNDERFLOW
;
119 else if(eflag
& FP_EX_DIVZERO
)
120 eflag
= FP_EX_DIVZERO
;
121 else if(eflag
& FP_EX_INEXACT
)
122 eflag
= FP_EX_INEXACT
;
126 /* Set CEXC, here is the rule:
128 * In general all FPU ops will set one and only one
129 * bit in the CEXC field, this is always the case
130 * when the IEEE exception trap is enabled in TEM.
132 fsr
&= ~(FSR_CEXC_MASK
);
133 fsr
|= ((long)eflag
<< FSR_CEXC_SHIFT
);
135 /* Set the AEXC field, rule is:
137 * If a trap would not be generated, the
138 * CEXC just generated is OR'd into the
139 * existing value of AEXC.
142 fsr
|= ((long)eflag
<< FSR_AEXC_SHIFT
);
144 /* If trapping, indicate fault trap type IEEE. */
148 current_thread_info()->xfsr
[0] = fsr
;
150 /* If we will not trap, advance the program counter over
151 * the instruction being handled.
153 if(would_trap
== 0) {
154 regs
->tpc
= regs
->tnpc
;
158 return (would_trap
? 0 : 1);
167 int do_mathemu(struct pt_regs
*regs
, struct fpustate
*f
, bool illegal_insn_trap
)
169 unsigned long pc
= regs
->tpc
;
170 unsigned long tstate
= regs
->tstate
;
173 /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
174 whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
175 non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
176 #define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
178 static u64 zero
[2] = { 0L, 0L };
181 FP_DECL_S(SA
); FP_DECL_S(SB
); FP_DECL_S(SR
);
182 FP_DECL_D(DA
); FP_DECL_D(DB
); FP_DECL_D(DR
);
183 FP_DECL_Q(QA
); FP_DECL_Q(QB
); FP_DECL_Q(QR
);
187 if (tstate
& TSTATE_PRIV
)
188 die_if_kernel("unfinished/unimplemented FPop from kernel", regs
);
189 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS
, 1, regs
, 0);
190 if (test_thread_flag(TIF_32BIT
))
192 if (get_user(insn
, (u32 __user
*) pc
) != -EFAULT
) {
193 if ((insn
& 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
194 switch ((insn
>> 5) & 0x1ff) {
195 /* QUAD - ftt == 3 */
198 case FABSQ
: TYPE(3,3,0,3,0,0,0); break;
199 case FSQRTQ
: TYPE(3,3,1,3,1,0,0); break;
203 case FDIVQ
: TYPE(3,3,1,3,1,3,1); break;
204 case FDMULQ
: TYPE(3,3,1,2,1,2,1); break;
205 case FQTOX
: TYPE(3,2,0,3,1,0,0); break;
206 case FXTOQ
: TYPE(3,3,1,2,0,0,0); break;
207 case FQTOS
: TYPE(3,1,1,3,1,0,0); break;
208 case FQTOD
: TYPE(3,2,1,3,1,0,0); break;
209 case FITOQ
: TYPE(3,3,1,1,0,0,0); break;
210 case FSTOQ
: TYPE(3,3,1,1,1,0,0); break;
211 case FDTOQ
: TYPE(3,3,1,2,1,0,0); break;
212 case FQTOI
: TYPE(3,1,0,3,1,0,0); break;
214 /* We can get either unimplemented or unfinished
215 * for these cases. Pre-Niagara systems generate
216 * unfinished fpop for SUBNORMAL cases, and Niagara
217 * always gives unimplemented fpop for fsqrt{s,d}.
220 unsigned long x
= current_thread_info()->xfsr
[0];
228 unsigned long x
= current_thread_info()->xfsr
[0];
235 /* SUBNORMAL - ftt == 2 */
239 case FDIVD
: TYPE(2,2,1,2,1,2,1); break;
243 case FDIVS
: TYPE(2,1,1,1,1,1,1); break;
244 case FSMULD
: TYPE(2,2,1,1,1,1,1); break;
245 case FSTOX
: TYPE(2,2,0,1,1,0,0); break;
246 case FDTOX
: TYPE(2,2,0,2,1,0,0); break;
247 case FDTOS
: TYPE(2,1,1,2,1,0,0); break;
248 case FSTOD
: TYPE(2,2,1,1,1,0,0); break;
249 case FSTOI
: TYPE(2,1,0,1,1,0,0); break;
250 case FDTOI
: TYPE(2,1,0,2,1,0,0); break;
252 /* Only Ultra-III generates these */
253 case FXTOS
: TYPE(2,1,1,2,0,0,0); break;
254 case FXTOD
: TYPE(2,2,1,2,0,0,0); break;
255 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
256 case FITOS
: TYPE(2,1,1,1,0,0,0); break;
258 case FITOD
: TYPE(2,2,1,1,0,0,0); break;
261 else if ((insn
& 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
263 switch ((insn
>> 5) & 0x1ff) {
264 case FCMPQ
: TYPE(3,0,0,3,1,3,1); break;
265 case FCMPEQ
: TYPE(3,0,0,3,1,3,1); break;
266 /* Now the conditional fmovq support */
271 /* fmovq %fccX, %fY, %fZ */
272 if (!((insn
>> 11) & 3))
273 XR
= current_thread_info()->xfsr
[0] >> 10;
275 XR
= current_thread_info()->xfsr
[0] >> (30 + ((insn
>> 10) & 0x6));
278 switch ((insn
>> 14) & 0x7) {
279 /* case 0: IR = 0; break; */ /* Never */
280 case 1: if (XR
) IR
= 1; break; /* Not Equal */
281 case 2: if (XR
== 1 || XR
== 2) IR
= 1; break; /* Less or Greater */
282 case 3: if (XR
& 1) IR
= 1; break; /* Unordered or Less */
283 case 4: if (XR
== 1) IR
= 1; break; /* Less */
284 case 5: if (XR
& 2) IR
= 1; break; /* Unordered or Greater */
285 case 6: if (XR
== 2) IR
= 1; break; /* Greater */
286 case 7: if (XR
== 3) IR
= 1; break; /* Unordered */
288 if ((insn
>> 14) & 8)
293 /* fmovq %[ix]cc, %fY, %fZ */
294 XR
= regs
->tstate
>> 32;
295 if ((insn
>> 5) & 0x80)
299 freg
= ((XR
>> 2) ^ XR
) & 2;
300 switch ((insn
>> 14) & 0x7) {
301 /* case 0: IR = 0; break; */ /* Never */
302 case 1: if (XR
& 4) IR
= 1; break; /* Equal */
303 case 2: if ((XR
& 4) || freg
) IR
= 1; break; /* Less or Equal */
304 case 3: if (freg
) IR
= 1; break; /* Less */
305 case 4: if (XR
& 5) IR
= 1; break; /* Less or Equal Unsigned */
306 case 5: if (XR
& 1) IR
= 1; break; /* Carry Set */
307 case 6: if (XR
& 8) IR
= 1; break; /* Negative */
308 case 7: if (XR
& 2) IR
= 1; break; /* Overflow Set */
310 if ((insn
>> 14) & 8)
319 freg
= (insn
>> 14) & 0x1f;
323 XR
= regs
->u_regs
[freg
];
324 else if (!test_thread_64bit_stack(regs
->u_regs
[UREG_FP
])) {
325 struct reg_window32 __user
*win32
;
327 win32
= (struct reg_window32 __user
*)((unsigned long)((u32
)regs
->u_regs
[UREG_FP
]));
328 get_user(XR
, &win32
->locals
[freg
- 16]);
330 struct reg_window __user
*win
;
332 win
= (struct reg_window __user
*)(regs
->u_regs
[UREG_FP
] + STACK_BIAS
);
333 get_user(XR
, &win
->locals
[freg
- 16]);
336 switch ((insn
>> 10) & 3) {
337 case 1: if (!XR
) IR
= 1; break; /* Register Zero */
338 case 2: if (XR
<= 0) IR
= 1; break; /* Register Less Than or Equal to Zero */
339 case 3: if (XR
< 0) IR
= 1; break; /* Register Less Than Zero */
341 if ((insn
>> 10) & 4)
346 /* The fmov test was false. Do a nop instead */
347 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
348 regs
->tpc
= regs
->tnpc
;
351 } else if (IR
== 1) {
352 /* Change the instruction into plain fmovq */
353 insn
= (insn
& 0x3e00001f) | 0x81a00060;
359 argp rs1
= NULL
, rs2
= NULL
, rd
= NULL
;
361 /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap
362 * Type field in the %fsr to unimplemented_FPop. Nor does it
363 * use the fp_exception_other trap. Instead it signals an
364 * illegal instruction and leaves the FP trap type field of
365 * the %fsr unchanged.
367 if (!illegal_insn_trap
) {
368 int ftt
= (current_thread_info()->xfsr
[0] >> 14) & 0x7;
369 if (ftt
!= (type
>> 9))
372 current_thread_info()->xfsr
[0] &= ~0x1c000;
373 freg
= ((insn
>> 14) & 0x1f);
374 switch (type
& 0x3) {
375 case 3: if (freg
& 2) {
376 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
379 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
380 case 1: rs1
= (argp
)&f
->regs
[freg
];
381 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
382 if (!(current_thread_info()->fpsaved
[0] & flags
))
386 switch (type
& 0x7) {
387 case 7: FP_UNPACK_QP (QA
, rs1
); break;
388 case 6: FP_UNPACK_DP (DA
, rs1
); break;
389 case 5: FP_UNPACK_SP (SA
, rs1
); break;
391 freg
= (insn
& 0x1f);
392 switch ((type
>> 3) & 0x3) {
393 case 3: if (freg
& 2) {
394 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
397 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
398 case 1: rs2
= (argp
)&f
->regs
[freg
];
399 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
400 if (!(current_thread_info()->fpsaved
[0] & flags
))
404 switch ((type
>> 3) & 0x7) {
405 case 7: FP_UNPACK_QP (QB
, rs2
); break;
406 case 6: FP_UNPACK_DP (DB
, rs2
); break;
407 case 5: FP_UNPACK_SP (SB
, rs2
); break;
409 freg
= ((insn
>> 25) & 0x1f);
410 switch ((type
>> 6) & 0x3) {
411 case 3: if (freg
& 2) {
412 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
415 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
416 case 1: rd
= (argp
)&f
->regs
[freg
];
417 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
418 if (!(current_thread_info()->fpsaved
[0] & FPRS_FEF
)) {
419 current_thread_info()->fpsaved
[0] = FPRS_FEF
;
420 current_thread_info()->gsr
[0] = 0;
422 if (!(current_thread_info()->fpsaved
[0] & flags
)) {
424 memset(f
->regs
, 0, 32*sizeof(u32
));
426 memset(f
->regs
+32, 0, 32*sizeof(u32
));
428 current_thread_info()->fpsaved
[0] |= flags
;
431 switch ((insn
>> 5) & 0x1ff) {
433 case FADDS
: FP_ADD_S (SR
, SA
, SB
); break;
434 case FADDD
: FP_ADD_D (DR
, DA
, DB
); break;
435 case FADDQ
: FP_ADD_Q (QR
, QA
, QB
); break;
437 case FSUBS
: FP_SUB_S (SR
, SA
, SB
); break;
438 case FSUBD
: FP_SUB_D (DR
, DA
, DB
); break;
439 case FSUBQ
: FP_SUB_Q (QR
, QA
, QB
); break;
441 case FMULS
: FP_MUL_S (SR
, SA
, SB
); break;
442 case FSMULD
: FP_CONV (D
, S
, 1, 1, DA
, SA
);
443 FP_CONV (D
, S
, 1, 1, DB
, SB
);
444 case FMULD
: FP_MUL_D (DR
, DA
, DB
); break;
445 case FDMULQ
: FP_CONV (Q
, D
, 2, 1, QA
, DA
);
446 FP_CONV (Q
, D
, 2, 1, QB
, DB
);
447 case FMULQ
: FP_MUL_Q (QR
, QA
, QB
); break;
449 case FDIVS
: FP_DIV_S (SR
, SA
, SB
); break;
450 case FDIVD
: FP_DIV_D (DR
, DA
, DB
); break;
451 case FDIVQ
: FP_DIV_Q (QR
, QA
, QB
); break;
453 case FSQRTS
: FP_SQRT_S (SR
, SB
); break;
454 case FSQRTD
: FP_SQRT_D (DR
, DB
); break;
455 case FSQRTQ
: FP_SQRT_Q (QR
, QB
); break;
457 case FMOVQ
: rd
->q
[0] = rs2
->q
[0]; rd
->q
[1] = rs2
->q
[1]; break;
458 case FABSQ
: rd
->q
[0] = rs2
->q
[0] & 0x7fffffffffffffffUL
; rd
->q
[1] = rs2
->q
[1]; break;
459 case FNEGQ
: rd
->q
[0] = rs2
->q
[0] ^ 0x8000000000000000UL
; rd
->q
[1] = rs2
->q
[1]; break;
461 case FSTOI
: FP_TO_INT_S (IR
, SB
, 32, 1); break;
462 case FDTOI
: FP_TO_INT_D (IR
, DB
, 32, 1); break;
463 case FQTOI
: FP_TO_INT_Q (IR
, QB
, 32, 1); break;
464 case FSTOX
: FP_TO_INT_S (XR
, SB
, 64, 1); break;
465 case FDTOX
: FP_TO_INT_D (XR
, DB
, 64, 1); break;
466 case FQTOX
: FP_TO_INT_Q (XR
, QB
, 64, 1); break;
468 case FITOQ
: IR
= rs2
->s
; FP_FROM_INT_Q (QR
, IR
, 32, int); break;
469 case FXTOQ
: XR
= rs2
->d
; FP_FROM_INT_Q (QR
, XR
, 64, long); break;
470 /* Only Ultra-III generates these */
471 case FXTOS
: XR
= rs2
->d
; FP_FROM_INT_S (SR
, XR
, 64, long); break;
472 case FXTOD
: XR
= rs2
->d
; FP_FROM_INT_D (DR
, XR
, 64, long); break;
473 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
474 case FITOS
: IR
= rs2
->s
; FP_FROM_INT_S (SR
, IR
, 32, int); break;
476 case FITOD
: IR
= rs2
->s
; FP_FROM_INT_D (DR
, IR
, 32, int); break;
478 case FSTOD
: FP_CONV (D
, S
, 1, 1, DR
, SB
); break;
479 case FSTOQ
: FP_CONV (Q
, S
, 2, 1, QR
, SB
); break;
480 case FDTOQ
: FP_CONV (Q
, D
, 2, 1, QR
, DB
); break;
481 case FDTOS
: FP_CONV (S
, D
, 1, 1, SR
, DB
); break;
482 case FQTOS
: FP_CONV (S
, Q
, 1, 2, SR
, QB
); break;
483 case FQTOD
: FP_CONV (D
, Q
, 1, 2, DR
, QB
); break;
487 FP_CMP_Q(XR
, QB
, QA
, 3);
489 (((insn
>> 5) & 0x1ff) == FCMPEQ
||
492 FP_SET_EXCEPTION (FP_EX_INVALID
);
494 if (!FP_INHIBIT_RESULTS
) {
495 switch ((type
>> 6) & 0x7) {
496 case 0: xfsr
= current_thread_info()->xfsr
[0];
497 if (XR
== -1) XR
= 2;
500 case 0: xfsr
&= ~0xc00; xfsr
|= (XR
<< 10); break;
501 case 1: xfsr
&= ~0x300000000UL
; xfsr
|= (XR
<< 32); break;
502 case 2: xfsr
&= ~0xc00000000UL
; xfsr
|= (XR
<< 34); break;
503 case 3: xfsr
&= ~0x3000000000UL
; xfsr
|= (XR
<< 36); break;
505 current_thread_info()->xfsr
[0] = xfsr
;
507 case 1: rd
->s
= IR
; break;
508 case 2: rd
->d
= XR
; break;
509 case 5: FP_PACK_SP (rd
, SR
); break;
510 case 6: FP_PACK_DP (rd
, DR
); break;
511 case 7: FP_PACK_QP (rd
, QR
); break;
516 return record_exception(regs
, _fex
);
518 /* Success and no exceptions detected. */
519 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
520 regs
->tpc
= regs
->tnpc
;