1 /* $Id: math.c,v 1.11 1999/12/20 05:02:25 davem Exp $
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>
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;
209 /* SUBNORMAL - ftt == 2 */
210 case FSQRTS
: TYPE(2,1,1,1,1,0,0); break;
211 case FSQRTD
: TYPE(2,2,1,2,1,0,0); break;
215 case FDIVD
: TYPE(2,2,1,2,1,2,1); break;
219 case FDIVS
: TYPE(2,1,1,1,1,1,1); break;
220 case FSMULD
: TYPE(2,2,1,1,1,1,1); break;
221 case FSTOX
: TYPE(2,2,0,1,1,0,0); break;
222 case FDTOX
: TYPE(2,2,0,2,1,0,0); break;
223 case FDTOS
: TYPE(2,1,1,2,1,0,0); break;
224 case FSTOD
: TYPE(2,2,1,1,1,0,0); break;
225 case FSTOI
: TYPE(2,1,0,1,1,0,0); break;
226 case FDTOI
: TYPE(2,1,0,2,1,0,0); break;
228 /* Only Ultra-III generates these */
229 case FXTOS
: TYPE(2,1,1,2,0,0,0); break;
230 case FXTOD
: TYPE(2,2,1,2,0,0,0); break;
231 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
232 case FITOS
: TYPE(2,1,1,1,0,0,0); break;
234 case FITOD
: TYPE(2,2,1,1,0,0,0); break;
237 else if ((insn
& 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
239 switch ((insn
>> 5) & 0x1ff) {
240 case FCMPQ
: TYPE(3,0,0,3,1,3,1); break;
241 case FCMPEQ
: TYPE(3,0,0,3,1,3,1); break;
242 /* Now the conditional fmovq support */
247 /* fmovq %fccX, %fY, %fZ */
248 if (!((insn
>> 11) & 3))
249 XR
= current_thread_info()->xfsr
[0] >> 10;
251 XR
= current_thread_info()->xfsr
[0] >> (30 + ((insn
>> 10) & 0x6));
254 switch ((insn
>> 14) & 0x7) {
255 /* case 0: IR = 0; break; */ /* Never */
256 case 1: if (XR
) IR
= 1; break; /* Not Equal */
257 case 2: if (XR
== 1 || XR
== 2) IR
= 1; break; /* Less or Greater */
258 case 3: if (XR
& 1) IR
= 1; break; /* Unordered or Less */
259 case 4: if (XR
== 1) IR
= 1; break; /* Less */
260 case 5: if (XR
& 2) IR
= 1; break; /* Unordered or Greater */
261 case 6: if (XR
== 2) IR
= 1; break; /* Greater */
262 case 7: if (XR
== 3) IR
= 1; break; /* Unordered */
264 if ((insn
>> 14) & 8)
269 /* fmovq %[ix]cc, %fY, %fZ */
270 XR
= regs
->tstate
>> 32;
271 if ((insn
>> 5) & 0x80)
275 freg
= ((XR
>> 2) ^ XR
) & 2;
276 switch ((insn
>> 14) & 0x7) {
277 /* case 0: IR = 0; break; */ /* Never */
278 case 1: if (XR
& 4) IR
= 1; break; /* Equal */
279 case 2: if ((XR
& 4) || freg
) IR
= 1; break; /* Less or Equal */
280 case 3: if (freg
) IR
= 1; break; /* Less */
281 case 4: if (XR
& 5) IR
= 1; break; /* Less or Equal Unsigned */
282 case 5: if (XR
& 1) IR
= 1; break; /* Carry Set */
283 case 6: if (XR
& 8) IR
= 1; break; /* Negative */
284 case 7: if (XR
& 2) IR
= 1; break; /* Overflow Set */
286 if ((insn
>> 14) & 8)
295 freg
= (insn
>> 14) & 0x1f;
299 XR
= regs
->u_regs
[freg
];
300 else if (test_thread_flag(TIF_32BIT
)) {
301 struct reg_window32 __user
*win32
;
303 win32
= (struct reg_window32 __user
*)((unsigned long)((u32
)regs
->u_regs
[UREG_FP
]));
304 get_user(XR
, &win32
->locals
[freg
- 16]);
306 struct reg_window __user
*win
;
308 win
= (struct reg_window __user
*)(regs
->u_regs
[UREG_FP
] + STACK_BIAS
);
309 get_user(XR
, &win
->locals
[freg
- 16]);
312 switch ((insn
>> 10) & 3) {
313 case 1: if (!XR
) IR
= 1; break; /* Register Zero */
314 case 2: if (XR
<= 0) IR
= 1; break; /* Register Less Than or Equal to Zero */
315 case 3: if (XR
< 0) IR
= 1; break; /* Register Less Than Zero */
317 if ((insn
>> 10) & 4)
322 /* The fmov test was false. Do a nop instead */
323 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
324 regs
->tpc
= regs
->tnpc
;
327 } else if (IR
== 1) {
328 /* Change the instruction into plain fmovq */
329 insn
= (insn
& 0x3e00001f) | 0x81a00060;
335 argp rs1
= NULL
, rs2
= NULL
, rd
= NULL
;
337 freg
= (current_thread_info()->xfsr
[0] >> 14) & 0xf;
338 if (freg
!= (type
>> 9))
340 current_thread_info()->xfsr
[0] &= ~0x1c000;
341 freg
= ((insn
>> 14) & 0x1f);
342 switch (type
& 0x3) {
343 case 3: if (freg
& 2) {
344 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
347 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
348 case 1: rs1
= (argp
)&f
->regs
[freg
];
349 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
350 if (!(current_thread_info()->fpsaved
[0] & flags
))
354 switch (type
& 0x7) {
355 case 7: FP_UNPACK_QP (QA
, rs1
); break;
356 case 6: FP_UNPACK_DP (DA
, rs1
); break;
357 case 5: FP_UNPACK_SP (SA
, rs1
); break;
359 freg
= (insn
& 0x1f);
360 switch ((type
>> 3) & 0x3) {
361 case 3: if (freg
& 2) {
362 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
365 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
366 case 1: rs2
= (argp
)&f
->regs
[freg
];
367 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
368 if (!(current_thread_info()->fpsaved
[0] & flags
))
372 switch ((type
>> 3) & 0x7) {
373 case 7: FP_UNPACK_QP (QB
, rs2
); break;
374 case 6: FP_UNPACK_DP (DB
, rs2
); break;
375 case 5: FP_UNPACK_SP (SB
, rs2
); break;
377 freg
= ((insn
>> 25) & 0x1f);
378 switch ((type
>> 6) & 0x3) {
379 case 3: if (freg
& 2) {
380 current_thread_info()->xfsr
[0] |= (6 << 14) /* invalid_fp_register */;
383 case 2: freg
= ((freg
& 1) << 5) | (freg
& 0x1e);
384 case 1: rd
= (argp
)&f
->regs
[freg
];
385 flags
= (freg
< 32) ? FPRS_DL
: FPRS_DU
;
386 if (!(current_thread_info()->fpsaved
[0] & FPRS_FEF
)) {
387 current_thread_info()->fpsaved
[0] = FPRS_FEF
;
388 current_thread_info()->gsr
[0] = 0;
390 if (!(current_thread_info()->fpsaved
[0] & flags
)) {
392 memset(f
->regs
, 0, 32*sizeof(u32
));
394 memset(f
->regs
+32, 0, 32*sizeof(u32
));
396 current_thread_info()->fpsaved
[0] |= flags
;
399 switch ((insn
>> 5) & 0x1ff) {
401 case FADDS
: FP_ADD_S (SR
, SA
, SB
); break;
402 case FADDD
: FP_ADD_D (DR
, DA
, DB
); break;
403 case FADDQ
: FP_ADD_Q (QR
, QA
, QB
); break;
405 case FSUBS
: FP_SUB_S (SR
, SA
, SB
); break;
406 case FSUBD
: FP_SUB_D (DR
, DA
, DB
); break;
407 case FSUBQ
: FP_SUB_Q (QR
, QA
, QB
); break;
409 case FMULS
: FP_MUL_S (SR
, SA
, SB
); break;
410 case FSMULD
: FP_CONV (D
, S
, 1, 1, DA
, SA
);
411 FP_CONV (D
, S
, 1, 1, DB
, SB
);
412 case FMULD
: FP_MUL_D (DR
, DA
, DB
); break;
413 case FDMULQ
: FP_CONV (Q
, D
, 2, 1, QA
, DA
);
414 FP_CONV (Q
, D
, 2, 1, QB
, DB
);
415 case FMULQ
: FP_MUL_Q (QR
, QA
, QB
); break;
417 case FDIVS
: FP_DIV_S (SR
, SA
, SB
); break;
418 case FDIVD
: FP_DIV_D (DR
, DA
, DB
); break;
419 case FDIVQ
: FP_DIV_Q (QR
, QA
, QB
); break;
421 case FSQRTS
: FP_SQRT_S (SR
, SB
); break;
422 case FSQRTD
: FP_SQRT_D (DR
, DB
); break;
423 case FSQRTQ
: FP_SQRT_Q (QR
, QB
); break;
425 case FMOVQ
: rd
->q
[0] = rs2
->q
[0]; rd
->q
[1] = rs2
->q
[1]; break;
426 case FABSQ
: rd
->q
[0] = rs2
->q
[0] & 0x7fffffffffffffffUL
; rd
->q
[1] = rs2
->q
[1]; break;
427 case FNEGQ
: rd
->q
[0] = rs2
->q
[0] ^ 0x8000000000000000UL
; rd
->q
[1] = rs2
->q
[1]; break;
429 case FSTOI
: FP_TO_INT_S (IR
, SB
, 32, 1); break;
430 case FDTOI
: FP_TO_INT_D (IR
, DB
, 32, 1); break;
431 case FQTOI
: FP_TO_INT_Q (IR
, QB
, 32, 1); break;
432 case FSTOX
: FP_TO_INT_S (XR
, SB
, 64, 1); break;
433 case FDTOX
: FP_TO_INT_D (XR
, DB
, 64, 1); break;
434 case FQTOX
: FP_TO_INT_Q (XR
, QB
, 64, 1); break;
436 case FITOQ
: IR
= rs2
->s
; FP_FROM_INT_Q (QR
, IR
, 32, int); break;
437 case FXTOQ
: XR
= rs2
->d
; FP_FROM_INT_Q (QR
, XR
, 64, long); break;
438 /* Only Ultra-III generates these */
439 case FXTOS
: XR
= rs2
->d
; FP_FROM_INT_S (SR
, XR
, 64, long); break;
440 case FXTOD
: XR
= rs2
->d
; FP_FROM_INT_D (DR
, XR
, 64, long); break;
441 #if 0 /* Optimized inline in sparc64/kernel/entry.S */
442 case FITOS
: IR
= rs2
->s
; FP_FROM_INT_S (SR
, IR
, 32, int); break;
444 case FITOD
: IR
= rs2
->s
; FP_FROM_INT_D (DR
, IR
, 32, int); break;
446 case FSTOD
: FP_CONV (D
, S
, 1, 1, DR
, SB
); break;
447 case FSTOQ
: FP_CONV (Q
, S
, 2, 1, QR
, SB
); break;
448 case FDTOQ
: FP_CONV (Q
, D
, 2, 1, QR
, DB
); break;
449 case FDTOS
: FP_CONV (S
, D
, 1, 1, SR
, DB
); break;
450 case FQTOS
: FP_CONV (S
, Q
, 1, 2, SR
, QB
); break;
451 case FQTOD
: FP_CONV (D
, Q
, 1, 2, DR
, QB
); break;
455 FP_CMP_Q(XR
, QB
, QA
, 3);
457 (((insn
>> 5) & 0x1ff) == FCMPEQ
||
460 FP_SET_EXCEPTION (FP_EX_INVALID
);
462 if (!FP_INHIBIT_RESULTS
) {
463 switch ((type
>> 6) & 0x7) {
464 case 0: xfsr
= current_thread_info()->xfsr
[0];
465 if (XR
== -1) XR
= 2;
468 case 0: xfsr
&= ~0xc00; xfsr
|= (XR
<< 10); break;
469 case 1: xfsr
&= ~0x300000000UL
; xfsr
|= (XR
<< 32); break;
470 case 2: xfsr
&= ~0xc00000000UL
; xfsr
|= (XR
<< 34); break;
471 case 3: xfsr
&= ~0x3000000000UL
; xfsr
|= (XR
<< 36); break;
473 current_thread_info()->xfsr
[0] = xfsr
;
475 case 1: rd
->s
= IR
; break;
476 case 2: rd
->d
= XR
; break;
477 case 5: FP_PACK_SP (rd
, SR
); break;
478 case 6: FP_PACK_DP (rd
, DR
); break;
479 case 7: FP_PACK_QP (rd
, QR
); break;
484 return record_exception(regs
, _fex
);
486 /* Success and no exceptions detected. */
487 current_thread_info()->xfsr
[0] &= ~(FSR_CEXC_MASK
);
488 regs
->tpc
= regs
->tnpc
;