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>
15 #include <asm/fpumacro.h>
16 #include <asm/ptrace.h>
17 #include <asm/uaccess.h>
19 #include "sfp-util_64.h"
20 #include <math-emu/soft-fp.h>
21 #include <math-emu/single.h>
22 #include <math-emu/double.h>
23 #include <math-emu/quad.h>
43 /* SUBNORMAL - ftt == 2 */
61 #define FXTOS 0x084 /* Only Ultra-III generates this. */
62 #define FXTOD 0x088 /* Only Ultra-III generates this. */
63 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
64 #define FITOS 0x0c4 /* Only Ultra-III generates this. */
66 #define FITOD 0x0c8 /* Only Ultra-III generates this. */
83 #define FSR_TEM_SHIFT 23UL
84 #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
85 #define FSR_AEXC_SHIFT 5UL
86 #define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
87 #define FSR_CEXC_SHIFT 0UL
88 #define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
90 /* All routines returning an exception to raise should detect
91 * such exceptions _before_ rounding to be consistent with
92 * the behavior of the hardware in the implemented cases
93 * (and thus with the recommendations in the V9 architecture
96 * We return 0 if a SIGFPE should be sent, 1 otherwise.
98 static inline int record_exception(struct pt_regs
*regs
, int eflag
)
100 u64 fsr
= current_thread_info()->xfsr
[0];
103 /* Determine if this exception would have generated a trap. */
104 would_trap
= (fsr
& ((long)eflag
<< FSR_TEM_SHIFT
)) != 0UL;
106 /* If trapping, we only want to signal one bit. */
107 if(would_trap
!= 0) {
108 eflag
&= ((fsr
& FSR_TEM_MASK
) >> FSR_TEM_SHIFT
);
109 if((eflag
& (eflag
- 1)) != 0) {
110 if(eflag
& FP_EX_INVALID
)
111 eflag
= FP_EX_INVALID
;
112 else if(eflag
& FP_EX_OVERFLOW
)
113 eflag
= FP_EX_OVERFLOW
;
114 else if(eflag
& FP_EX_UNDERFLOW
)
115 eflag
= FP_EX_UNDERFLOW
;
116 else if(eflag
& FP_EX_DIVZERO
)
117 eflag
= FP_EX_DIVZERO
;
118 else if(eflag
& FP_EX_INEXACT
)
119 eflag
= FP_EX_INEXACT
;
123 /* Set CEXC, here is the rule:
125 * In general all FPU ops will set one and only one
126 * bit in the CEXC field, this is always the case
127 * when the IEEE exception trap is enabled in TEM.
129 fsr
&= ~(FSR_CEXC_MASK
);
130 fsr
|= ((long)eflag
<< FSR_CEXC_SHIFT
);
132 /* Set the AEXC field, rule is:
134 * If a trap would not be generated, the
135 * CEXC just generated is OR'd into the
136 * existing value of AEXC.
139 fsr
|= ((long)eflag
<< FSR_AEXC_SHIFT
);
141 /* If trapping, indicate fault trap type IEEE. */
145 current_thread_info()->xfsr
[0] = fsr
;
147 /* If we will not trap, advance the program counter over
148 * the instruction being handled.
150 if(would_trap
== 0) {
151 regs
->tpc
= regs
->tnpc
;
155 return (would_trap
? 0 : 1);
164 int do_mathemu(struct pt_regs
*regs
, struct fpustate
*f
)
166 unsigned long pc
= regs
->tpc
;
167 unsigned long tstate
= regs
->tstate
;
170 /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
171 whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
172 non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
173 #define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
175 static u64 zero
[2] = { 0L, 0L };
178 FP_DECL_S(SA
); FP_DECL_S(SB
); FP_DECL_S(SR
);
179 FP_DECL_D(DA
); FP_DECL_D(DB
); FP_DECL_D(DR
);
180 FP_DECL_Q(QA
); FP_DECL_Q(QB
); FP_DECL_Q(QR
);
184 if (tstate
& TSTATE_PRIV
)
185 die_if_kernel("unfinished/unimplemented FPop from kernel", regs
);
186 if (test_thread_flag(TIF_32BIT
))
188 if (get_user(insn
, (u32 __user
*) pc
) != -EFAULT
) {
189 if ((insn
& 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
190 switch ((insn
>> 5) & 0x1ff) {
191 /* QUAD - ftt == 3 */
194 case FABSQ
: TYPE(3,3,0,3,0,0,0); break;
195 case FSQRTQ
: TYPE(3,3,1,3,1,0,0); break;
199 case FDIVQ
: TYPE(3,3,1,3,1,3,1); break;
200 case FDMULQ
: TYPE(3,3,1,2,1,2,1); break;
201 case FQTOX
: TYPE(3,2,0,3,1,0,0); break;
202 case FXTOQ
: TYPE(3,3,1,2,0,0,0); break;
203 case FQTOS
: TYPE(3,1,1,3,1,0,0); break;
204 case FQTOD
: TYPE(3,2,1,3,1,0,0); break;
205 case FITOQ
: TYPE(3,3,1,1,0,0,0); break;
206 case FSTOQ
: TYPE(3,3,1,1,1,0,0); break;
207 case FDTOQ
: TYPE(3,3,1,2,1,0,0); break;
208 case FQTOI
: TYPE(3,1,0,3,1,0,0); break;
210 /* We can get either unimplemented or unfinished
211 * for these cases. Pre-Niagara systems generate
212 * unfinished fpop for SUBNORMAL cases, and Niagara
213 * always gives unimplemented fpop for fsqrt{s,d}.
216 unsigned long x
= current_thread_info()->xfsr
[0];
224 unsigned long x
= current_thread_info()->xfsr
[0];
231 /* SUBNORMAL - ftt == 2 */
235 case FDIVD
: TYPE(2,2,1,2,1,2,1); break;
239 case FDIVS
: TYPE(2,1,1,1,1,1,1); break;
240 case FSMULD
: TYPE(2,2,1,1,1,1,1); break;
241 case FSTOX
: TYPE(2,2,0,1,1,0,0); break;
242 case FDTOX
: TYPE(2,2,0,2,1,0,0); break;
243 case FDTOS
: TYPE(2,1,1,2,1,0,0); break;
244 case FSTOD
: TYPE(2,2,1,1,1,0,0); break;
245 case FSTOI
: TYPE(2,1,0,1,1,0,0); break;
246 case FDTOI
: TYPE(2,1,0,2,1,0,0); break;
248 /* Only Ultra-III generates these */
249 case FXTOS
: TYPE(2,1,1,2,0,0,0); break;
250 case FXTOD
: TYPE(2,2,1,2,0,0,0); break;
251 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
252 case FITOS
: TYPE(2,1,1,1,0,0,0); break;
254 case FITOD
: TYPE(2,2,1,1,0,0,0); break;
257 else if ((insn
& 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
259 switch ((insn
>> 5) & 0x1ff) {
260 case FCMPQ
: TYPE(3,0,0,3,1,3,1); break;
261 case FCMPEQ
: TYPE(3,0,0,3,1,3,1); break;
262 /* Now the conditional fmovq support */
267 /* fmovq %fccX, %fY, %fZ */
268 if (!((insn
>> 11) & 3))
269 XR
= current_thread_info()->xfsr
[0] >> 10;
271 XR
= current_thread_info()->xfsr
[0] >> (30 + ((insn
>> 10) & 0x6));
274 switch ((insn
>> 14) & 0x7) {
275 /* case 0: IR = 0; break; */ /* Never */
276 case 1: if (XR
) IR
= 1; break; /* Not Equal */
277 case 2: if (XR
== 1 || XR
== 2) IR
= 1; break; /* Less or Greater */
278 case 3: if (XR
& 1) IR
= 1; break; /* Unordered or Less */
279 case 4: if (XR
== 1) IR
= 1; break; /* Less */
280 case 5: if (XR
& 2) IR
= 1; break; /* Unordered or Greater */
281 case 6: if (XR
== 2) IR
= 1; break; /* Greater */
282 case 7: if (XR
== 3) IR
= 1; break; /* Unordered */
284 if ((insn
>> 14) & 8)
289 /* fmovq %[ix]cc, %fY, %fZ */
290 XR
= regs
->tstate
>> 32;
291 if ((insn
>> 5) & 0x80)
295 freg
= ((XR
>> 2) ^ XR
) & 2;
296 switch ((insn
>> 14) & 0x7) {
297 /* case 0: IR = 0; break; */ /* Never */
298 case 1: if (XR
& 4) IR
= 1; break; /* Equal */
299 case 2: if ((XR
& 4) || freg
) IR
= 1; break; /* Less or Equal */
300 case 3: if (freg
) IR
= 1; break; /* Less */
301 case 4: if (XR
& 5) IR
= 1; break; /* Less or Equal Unsigned */
302 case 5: if (XR
& 1) IR
= 1; break; /* Carry Set */
303 case 6: if (XR
& 8) IR
= 1; break; /* Negative */
304 case 7: if (XR
& 2) IR
= 1; break; /* Overflow Set */
306 if ((insn
>> 14) & 8)
315 freg
= (insn
>> 14) & 0x1f;
319 XR
= regs
->u_regs
[freg
];
320 else if (test_thread_flag(TIF_32BIT
)) {
321 struct reg_window32 __user
*win32
;
323 win32
= (struct reg_window32 __user
*)((unsigned long)((u32
)regs
->u_regs
[UREG_FP
]));
324 get_user(XR
, &win32
->locals
[freg
- 16]);
326 struct reg_window __user
*win
;
328 win
= (struct reg_window __user
*)(regs
->u_regs
[UREG_FP
] + STACK_BIAS
);
329 get_user(XR
, &win
->locals
[freg
- 16]);
332 switch ((insn
>> 10) & 3) {
333 case 1: if (!XR
) IR
= 1; break; /* Register Zero */
334 case 2: if (XR
<= 0) IR
= 1; break; /* Register Less Than or Equal to Zero */
335 case 3: if (XR
< 0) IR
= 1; break; /* Register Less Than Zero */
337 if ((insn
>> 10) & 4)
342 /* The fmov test was false. Do a nop instead */
343 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
344 regs
->tpc
= regs
->tnpc
;
347 } else if (IR
== 1) {
348 /* Change the instruction into plain fmovq */
349 insn
= (insn
& 0x3e00001f) | 0x81a00060;
355 argp rs1
= NULL
, rs2
= NULL
, rd
= NULL
;
357 freg
= (current_thread_info()->xfsr
[0] >> 14) & 0xf;
358 if (freg
!= (type
>> 9))
360 current_thread_info()->xfsr
[0] &= ~0x1c000;
361 freg
= ((insn
>> 14) & 0x1f);
362 switch (type
& 0x3) {
363 case 3: if (freg
& 2) {
364 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
367 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
368 case 1: rs1
= (argp
)&f
->regs
[freg
];
369 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
370 if (!(current_thread_info()->fpsaved
[0] & flags
))
374 switch (type
& 0x7) {
375 case 7: FP_UNPACK_QP (QA
, rs1
); break;
376 case 6: FP_UNPACK_DP (DA
, rs1
); break;
377 case 5: FP_UNPACK_SP (SA
, rs1
); break;
379 freg
= (insn
& 0x1f);
380 switch ((type
>> 3) & 0x3) {
381 case 3: if (freg
& 2) {
382 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
385 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
386 case 1: rs2
= (argp
)&f
->regs
[freg
];
387 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
388 if (!(current_thread_info()->fpsaved
[0] & flags
))
392 switch ((type
>> 3) & 0x7) {
393 case 7: FP_UNPACK_QP (QB
, rs2
); break;
394 case 6: FP_UNPACK_DP (DB
, rs2
); break;
395 case 5: FP_UNPACK_SP (SB
, rs2
); break;
397 freg
= ((insn
>> 25) & 0x1f);
398 switch ((type
>> 6) & 0x3) {
399 case 3: if (freg
& 2) {
400 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
403 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
404 case 1: rd
= (argp
)&f
->regs
[freg
];
405 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
406 if (!(current_thread_info()->fpsaved
[0] & FPRS_FEF
)) {
407 current_thread_info()->fpsaved
[0] = FPRS_FEF
;
408 current_thread_info()->gsr
[0] = 0;
410 if (!(current_thread_info()->fpsaved
[0] & flags
)) {
412 memset(f
->regs
, 0, 32*sizeof(u32
));
414 memset(f
->regs
+32, 0, 32*sizeof(u32
));
416 current_thread_info()->fpsaved
[0] |= flags
;
419 switch ((insn
>> 5) & 0x1ff) {
421 case FADDS
: FP_ADD_S (SR
, SA
, SB
); break;
422 case FADDD
: FP_ADD_D (DR
, DA
, DB
); break;
423 case FADDQ
: FP_ADD_Q (QR
, QA
, QB
); break;
425 case FSUBS
: FP_SUB_S (SR
, SA
, SB
); break;
426 case FSUBD
: FP_SUB_D (DR
, DA
, DB
); break;
427 case FSUBQ
: FP_SUB_Q (QR
, QA
, QB
); break;
429 case FMULS
: FP_MUL_S (SR
, SA
, SB
); break;
430 case FSMULD
: FP_CONV (D
, S
, 1, 1, DA
, SA
);
431 FP_CONV (D
, S
, 1, 1, DB
, SB
);
432 case FMULD
: FP_MUL_D (DR
, DA
, DB
); break;
433 case FDMULQ
: FP_CONV (Q
, D
, 2, 1, QA
, DA
);
434 FP_CONV (Q
, D
, 2, 1, QB
, DB
);
435 case FMULQ
: FP_MUL_Q (QR
, QA
, QB
); break;
437 case FDIVS
: FP_DIV_S (SR
, SA
, SB
); break;
438 case FDIVD
: FP_DIV_D (DR
, DA
, DB
); break;
439 case FDIVQ
: FP_DIV_Q (QR
, QA
, QB
); break;
441 case FSQRTS
: FP_SQRT_S (SR
, SB
); break;
442 case FSQRTD
: FP_SQRT_D (DR
, DB
); break;
443 case FSQRTQ
: FP_SQRT_Q (QR
, QB
); break;
445 case FMOVQ
: rd
->q
[0] = rs2
->q
[0]; rd
->q
[1] = rs2
->q
[1]; break;
446 case FABSQ
: rd
->q
[0] = rs2
->q
[0] & 0x7fffffffffffffffUL
; rd
->q
[1] = rs2
->q
[1]; break;
447 case FNEGQ
: rd
->q
[0] = rs2
->q
[0] ^ 0x8000000000000000UL
; rd
->q
[1] = rs2
->q
[1]; break;
449 case FSTOI
: FP_TO_INT_S (IR
, SB
, 32, 1); break;
450 case FDTOI
: FP_TO_INT_D (IR
, DB
, 32, 1); break;
451 case FQTOI
: FP_TO_INT_Q (IR
, QB
, 32, 1); break;
452 case FSTOX
: FP_TO_INT_S (XR
, SB
, 64, 1); break;
453 case FDTOX
: FP_TO_INT_D (XR
, DB
, 64, 1); break;
454 case FQTOX
: FP_TO_INT_Q (XR
, QB
, 64, 1); break;
456 case FITOQ
: IR
= rs2
->s
; FP_FROM_INT_Q (QR
, IR
, 32, int); break;
457 case FXTOQ
: XR
= rs2
->d
; FP_FROM_INT_Q (QR
, XR
, 64, long); break;
458 /* Only Ultra-III generates these */
459 case FXTOS
: XR
= rs2
->d
; FP_FROM_INT_S (SR
, XR
, 64, long); break;
460 case FXTOD
: XR
= rs2
->d
; FP_FROM_INT_D (DR
, XR
, 64, long); break;
461 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
462 case FITOS
: IR
= rs2
->s
; FP_FROM_INT_S (SR
, IR
, 32, int); break;
464 case FITOD
: IR
= rs2
->s
; FP_FROM_INT_D (DR
, IR
, 32, int); break;
466 case FSTOD
: FP_CONV (D
, S
, 1, 1, DR
, SB
); break;
467 case FSTOQ
: FP_CONV (Q
, S
, 2, 1, QR
, SB
); break;
468 case FDTOQ
: FP_CONV (Q
, D
, 2, 1, QR
, DB
); break;
469 case FDTOS
: FP_CONV (S
, D
, 1, 1, SR
, DB
); break;
470 case FQTOS
: FP_CONV (S
, Q
, 1, 2, SR
, QB
); break;
471 case FQTOD
: FP_CONV (D
, Q
, 1, 2, DR
, QB
); break;
475 FP_CMP_Q(XR
, QB
, QA
, 3);
477 (((insn
>> 5) & 0x1ff) == FCMPEQ
||
480 FP_SET_EXCEPTION (FP_EX_INVALID
);
482 if (!FP_INHIBIT_RESULTS
) {
483 switch ((type
>> 6) & 0x7) {
484 case 0: xfsr
= current_thread_info()->xfsr
[0];
485 if (XR
== -1) XR
= 2;
488 case 0: xfsr
&= ~0xc00; xfsr
|= (XR
<< 10); break;
489 case 1: xfsr
&= ~0x300000000UL
; xfsr
|= (XR
<< 32); break;
490 case 2: xfsr
&= ~0xc00000000UL
; xfsr
|= (XR
<< 34); break;
491 case 3: xfsr
&= ~0x3000000000UL
; xfsr
|= (XR
<< 36); break;
493 current_thread_info()->xfsr
[0] = xfsr
;
495 case 1: rd
->s
= IR
; break;
496 case 2: rd
->d
= XR
; break;
497 case 5: FP_PACK_SP (rd
, SR
); break;
498 case 6: FP_PACK_DP (rd
, DR
); break;
499 case 7: FP_PACK_QP (rd
, QR
); break;
504 return record_exception(regs
, _fex
);
506 /* Success and no exceptions detected. */
507 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
508 regs
->tpc
= regs
->tnpc
;