aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/math-emu/math.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/math-emu/math.c')
-rw-r--r--arch/powerpc/math-emu/math.c151
1 files changed, 62 insertions, 89 deletions
diff --git a/arch/powerpc/math-emu/math.c b/arch/powerpc/math-emu/math.c
index 381306bb159..ab151f04050 100644
--- a/arch/powerpc/math-emu/math.c
+++ b/arch/powerpc/math-emu/math.c
@@ -7,12 +7,27 @@
#include <asm/uaccess.h>
#include <asm/reg.h>
+#include <asm/switch_to.h>
-#include "sfp-machine.h"
-#include "double.h"
+#include <asm/sfp-machine.h>
+#include <math-emu/double.h>
#define FLOATFUNC(x) extern int x(void *, void *, void *, void *)
+/* The instructions list which may be not implemented by a hardware FPU */
+FLOATFUNC(fre);
+FLOATFUNC(frsqrtes);
+FLOATFUNC(fsqrt);
+FLOATFUNC(fsqrts);
+FLOATFUNC(mtfsf);
+FLOATFUNC(mtfsfi);
+
+#ifdef CONFIG_MATH_EMULATION_HW_UNIMPLEMENTED
+#undef FLOATFUNC(x)
+#define FLOATFUNC(x) static inline int x(void *op1, void *op2, void *op3, \
+ void *op4) { }
+#endif
+
FLOATFUNC(fadd);
FLOATFUNC(fadds);
FLOATFUNC(fdiv);
@@ -42,8 +57,6 @@ FLOATFUNC(mcrfs);
FLOATFUNC(mffs);
FLOATFUNC(mtfsb0);
FLOATFUNC(mtfsb1);
-FLOATFUNC(mtfsf);
-FLOATFUNC(mtfsfi);
FLOATFUNC(lfd);
FLOATFUNC(lfs);
@@ -61,8 +74,6 @@ FLOATFUNC(fneg);
FLOATFUNC(fres);
FLOATFUNC(frsqrte);
FLOATFUNC(fsel);
-FLOATFUNC(fsqrt);
-FLOATFUNC(fsqrts);
#define OP31 0x1f /* 31 */
@@ -97,6 +108,7 @@ FLOATFUNC(fsqrts);
#define FSQRTS 0x016 /* 22 */
#define FRES 0x018 /* 24 */
#define FMULS 0x019 /* 25 */
+#define FRSQRTES 0x01a /* 26 */
#define FMSUBS 0x01c /* 28 */
#define FMADDS 0x01d /* 29 */
#define FNMSUBS 0x01e /* 30 */
@@ -109,6 +121,7 @@ FLOATFUNC(fsqrts);
#define FADD 0x015 /* 21 */
#define FSQRT 0x016 /* 22 */
#define FSEL 0x017 /* 23 */
+#define FRE 0x018 /* 24 */
#define FMUL 0x019 /* 25 */
#define FRSQRTE 0x01a /* 26 */
#define FMSUB 0x01c /* 28 */
@@ -150,7 +163,6 @@ FLOATFUNC(fsqrts);
#define XEU 15
#define XFLB 10
-#ifdef CONFIG_MATH_EMULATION
static int
record_exception(struct pt_regs *regs, int eflag)
{
@@ -168,6 +180,8 @@ record_exception(struct pt_regs *regs, int eflag)
fpscr |= FPSCR_ZX;
if (eflag & EFLAG_INEXACT)
fpscr |= FPSCR_XX;
+ if (eflag & EFLAG_INVALID)
+ fpscr |= FPSCR_VX;
if (eflag & EFLAG_VXSNAN)
fpscr |= FPSCR_VXSNAN;
if (eflag & EFLAG_VXISI)
@@ -188,7 +202,7 @@ record_exception(struct pt_regs *regs, int eflag)
fpscr |= FPSCR_VXCVI;
}
- fpscr &= ~(FPSCR_VX);
+// fpscr &= ~(FPSCR_VX);
if (fpscr & (FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI |
FPSCR_VXZDZ | FPSCR_VXIMZ | FPSCR_VXVC |
FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI))
@@ -206,7 +220,6 @@ record_exception(struct pt_regs *regs, int eflag)
return (fpscr & FPSCR_FEX) ? 1 : 0;
}
-#endif /* CONFIG_MATH_EMULATION */
int
do_mathemu(struct pt_regs *regs)
@@ -216,56 +229,13 @@ do_mathemu(struct pt_regs *regs)
signed short sdisp;
u32 insn = 0;
int idx = 0;
-#ifdef CONFIG_MATH_EMULATION
int (*func)(void *, void *, void *, void *);
int type = 0;
int eflag, trap;
-#endif
if (get_user(insn, (u32 *)pc))
return -EFAULT;
-#ifndef CONFIG_MATH_EMULATION
- switch (insn >> 26) {
- case LFD:
- idx = (insn >> 16) & 0x1f;
- sdisp = (insn & 0xffff);
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
- lfd(op0, op1, op2, op3);
- break;
- case LFDU:
- idx = (insn >> 16) & 0x1f;
- sdisp = (insn & 0xffff);
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
- lfd(op0, op1, op2, op3);
- regs->gpr[idx] = (unsigned long)op1;
- break;
- case STFD:
- idx = (insn >> 16) & 0x1f;
- sdisp = (insn & 0xffff);
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
- stfd(op0, op1, op2, op3);
- break;
- case STFDU:
- idx = (insn >> 16) & 0x1f;
- sdisp = (insn & 0xffff);
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
- stfd(op0, op1, op2, op3);
- regs->gpr[idx] = (unsigned long)op1;
- break;
- case OP63:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)&current->thread.fpr[(insn >> 11) & 0x1f];
- fmr(op0, op1, op2, op3);
- break;
- default:
- goto illegal;
- }
-#else /* CONFIG_MATH_EMULATION */
switch (insn >> 26) {
case LFS: func = lfs; type = D; break;
case LFSU: func = lfs; type = DU; break;
@@ -297,9 +267,10 @@ do_mathemu(struct pt_regs *regs)
case FDIVS: func = fdivs; type = AB; break;
case FSUBS: func = fsubs; type = AB; break;
case FADDS: func = fadds; type = AB; break;
- case FSQRTS: func = fsqrts; type = AB; break;
- case FRES: func = fres; type = AB; break;
+ case FSQRTS: func = fsqrts; type = XB; break;
+ case FRES: func = fres; type = XB; break;
case FMULS: func = fmuls; type = AC; break;
+ case FRSQRTES: func = frsqrtes;type = XB; break;
case FMSUBS: func = fmsubs; type = ABC; break;
case FMADDS: func = fmadds; type = ABC; break;
case FNMSUBS: func = fnmsubs; type = ABC; break;
@@ -315,10 +286,11 @@ do_mathemu(struct pt_regs *regs)
case FDIV: func = fdiv; type = AB; break;
case FSUB: func = fsub; type = AB; break;
case FADD: func = fadd; type = AB; break;
- case FSQRT: func = fsqrt; type = AB; break;
+ case FSQRT: func = fsqrt; type = XB; break;
+ case FRE: func = fre; type = XB; break;
case FSEL: func = fsel; type = ABC; break;
case FMUL: func = fmul; type = AC; break;
- case FRSQRTE: func = frsqrte; type = AB; break;
+ case FRSQRTE: func = frsqrte; type = XB; break;
case FMSUB: func = fmsub; type = ABC; break;
case FMADD: func = fmadd; type = ABC; break;
case FNMSUB: func = fnmsub; type = ABC; break;
@@ -356,28 +328,28 @@ do_mathemu(struct pt_regs *regs)
switch (type) {
case AB:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)&current->thread.fpr[(insn >> 16) & 0x1f];
- op2 = (void *)&current->thread.fpr[(insn >> 11) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)&current->thread.TS_FPR((insn >> 16) & 0x1f);
+ op2 = (void *)&current->thread.TS_FPR((insn >> 11) & 0x1f);
break;
case AC:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)&current->thread.fpr[(insn >> 16) & 0x1f];
- op2 = (void *)&current->thread.fpr[(insn >> 6) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)&current->thread.TS_FPR((insn >> 16) & 0x1f);
+ op2 = (void *)&current->thread.TS_FPR((insn >> 6) & 0x1f);
break;
case ABC:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)&current->thread.fpr[(insn >> 16) & 0x1f];
- op2 = (void *)&current->thread.fpr[(insn >> 11) & 0x1f];
- op3 = (void *)&current->thread.fpr[(insn >> 6) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)&current->thread.TS_FPR((insn >> 16) & 0x1f);
+ op2 = (void *)&current->thread.TS_FPR((insn >> 11) & 0x1f);
+ op3 = (void *)&current->thread.TS_FPR((insn >> 6) & 0x1f);
break;
case D:
idx = (insn >> 16) & 0x1f;
sdisp = (insn & 0xffff);
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
break;
@@ -387,50 +359,45 @@ do_mathemu(struct pt_regs *regs)
goto illegal;
sdisp = (insn & 0xffff);
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
op1 = (void *)(regs->gpr[idx] + sdisp);
break;
case X:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
break;
case XA:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)&current->thread.fpr[(insn >> 16) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)&current->thread.TS_FPR((insn >> 16) & 0x1f);
break;
case XB:
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)&current->thread.fpr[(insn >> 11) & 0x1f];
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)&current->thread.TS_FPR((insn >> 11) & 0x1f);
break;
case XE:
idx = (insn >> 16) & 0x1f;
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- if (!idx) {
- if (((insn >> 1) & 0x3ff) == STFIWX)
- op1 = (void *)(regs->gpr[(insn >> 11) & 0x1f]);
- else
- goto illegal;
- } else {
- op1 = (void *)(regs->gpr[idx] + regs->gpr[(insn >> 11) & 0x1f]);
- }
-
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)((idx ? regs->gpr[idx] : 0)
+ + regs->gpr[(insn >> 11) & 0x1f]);
break;
case XEU:
idx = (insn >> 16) & 0x1f;
- op0 = (void *)&current->thread.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)((idx ? regs->gpr[idx] : 0)
+ if (!idx)
+ goto illegal;
+ op0 = (void *)&current->thread.TS_FPR((insn >> 21) & 0x1f);
+ op1 = (void *)(regs->gpr[idx]
+ regs->gpr[(insn >> 11) & 0x1f]);
break;
case XCR:
op0 = (void *)&regs->ccr;
op1 = (void *)((insn >> 23) & 0x7);
- op2 = (void *)&current->thread.fpr[(insn >> 16) & 0x1f];
- op3 = (void *)&current->thread.fpr[(insn >> 11) & 0x1f];
+ op2 = (void *)&current->thread.TS_FPR((insn >> 16) & 0x1f);
+ op3 = (void *)&current->thread.TS_FPR((insn >> 11) & 0x1f);
break;
case XCRL:
@@ -450,13 +417,20 @@ do_mathemu(struct pt_regs *regs)
case XFLB:
op0 = (void *)((insn >> 17) & 0xff);
- op1 = (void *)&current->thread.fpr[(insn >> 11) & 0x1f];
+ op1 = (void *)&current->thread.TS_FPR((insn >> 11) & 0x1f);
break;
default:
goto illegal;
}
+ /*
+ * If we support a HW FPU, we need to ensure the FP state
+ * is flushed into the thread_struct before attempting
+ * emulation
+ */
+ flush_fp_to_thread(current);
+
eflag = func(op0, op1, op2, op3);
if (insn & 1) {
@@ -477,7 +451,6 @@ do_mathemu(struct pt_regs *regs)
default:
break;
}
-#endif /* CONFIG_MATH_EMULATION */
regs->nip += 4;
return 0;