powerpc: Emulate load/store floating double pair instructions
authorPaul Mackerras <paulus@ozlabs.org>
Wed, 30 Aug 2017 04:12:35 +0000 (14:12 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 1 Sep 2017 06:39:53 +0000 (16:39 +1000)
This adds lfdp[x] and stfdp[x] to the set of instructions that
analyse_instr() and emulate_step() understand.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/lib/sstep.c

index 0de41f29e0b06a90afccbbd2bf78bd44fdc4df82..44a8ce062747c4cb470077c7d4d27dcb3f1f34e4 100644 (file)
@@ -415,9 +415,9 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs)
        int err;
        union {
                float f;
-               double d;
-               unsigned long l;
-               u8 b[sizeof(double)];
+               double d[2];
+               unsigned long l[2];
+               u8 b[2 * sizeof(double)];
        } u;
 
        if (!address_ok(regs, ea, nb))
@@ -427,11 +427,19 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs)
                return err;
        preempt_disable();
        if (nb == 4)
-               conv_sp_to_dp(&u.f, &u.d);
+               conv_sp_to_dp(&u.f, &u.d[0]);
        if (regs->msr & MSR_FP)
-               put_fpr(rn, &u.d);
+               put_fpr(rn, &u.d[0]);
        else
-               current->thread.TS_FPR(rn) = u.l;
+               current->thread.TS_FPR(rn) = u.l[0];
+       if (nb == 16) {
+               /* lfdp */
+               rn |= 1;
+               if (regs->msr & MSR_FP)
+                       put_fpr(rn, &u.d[1]);
+               else
+                       current->thread.TS_FPR(rn) = u.l[1];
+       }
        preempt_enable();
        return 0;
 }
@@ -441,20 +449,27 @@ static int do_fp_store(int rn, unsigned long ea, int nb, struct pt_regs *regs)
 {
        union {
                float f;
-               double d;
-               unsigned long l;
-               u8 b[sizeof(double)];
+               double d[2];
+               unsigned long l[2];
+               u8 b[2 * sizeof(double)];
        } u;
 
        if (!address_ok(regs, ea, nb))
                return -EFAULT;
        preempt_disable();
        if (regs->msr & MSR_FP)
-               get_fpr(rn, &u.d);
+               get_fpr(rn, &u.d[0]);
        else
-               u.l = current->thread.TS_FPR(rn);
+               u.l[0] = current->thread.TS_FPR(rn);
        if (nb == 4)
-               conv_dp_to_sp(&u.d, &u.f);
+               conv_dp_to_sp(&u.d[0], &u.f);
+       if (nb == 16) {
+               rn |= 1;
+               if (regs->msr & MSR_FP)
+                       get_fpr(rn, &u.d[1]);
+               else
+                       u.l[1] = current->thread.TS_FPR(rn);
+       }
        preempt_enable();
        return copy_mem_out(u.b, ea, nb);
 }
@@ -1938,7 +1953,17 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
                case 759:       /* stfdux */
                        op->type = MKOP(STORE_FP, u, 8);
                        break;
-#endif
+
+#ifdef __powerpc64__
+               case 791:       /* lfdpx */
+                       op->type = MKOP(LOAD_FP, 0, 16);
+                       break;
+
+               case 919:       /* stfdpx */
+                       op->type = MKOP(STORE_FP, 0, 16);
+                       break;
+#endif /* __powerpc64 */
+#endif /* CONFIG_PPC_FPU */
 
 #ifdef __powerpc64__
                case 660:       /* stdbrx */
@@ -1956,7 +1981,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
                        op->val = byterev_4(regs->gpr[rd]);
                        break;
 
-               case 725:
+               case 725:       /* stswi */
                        if (rb == 0)
                                rb = 32;        /* # bytes to store */
                        op->type = MKOP(STORE_MULTI, 0, rb);
@@ -2246,9 +2271,14 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
 #endif
 
 #ifdef CONFIG_VSX
-       case 57:        /* lxsd, lxssp */
+       case 57:        /* lfdp, lxsd, lxssp */
                op->ea = dsform_ea(instr, regs);
                switch (instr & 3) {
+               case 0:         /* lfdp */
+                       if (rd & 1)
+                               break;          /* reg must be even */
+                       op->type = MKOP(LOAD_FP, 0, 16);
+                       break;
                case 2:         /* lxsd */
                        op->reg = rd + 32;
                        op->type = MKOP(LOAD_VSX, 0, 8);
@@ -2283,8 +2313,14 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
 #endif
 
 #ifdef CONFIG_VSX
-       case 61:        /* lxv, stxsd, stxssp, stxv */
+       case 61:        /* stfdp, lxv, stxsd, stxssp, stxv */
                switch (instr & 7) {
+               case 0:         /* stfdp with LSB of DS field = 0 */
+               case 4:         /* stfdp with LSB of DS field = 1 */
+                       op->ea = dsform_ea(instr, regs);
+                       op->type = MKOP(STORE_FP, 0, 16);
+                       break;
+
                case 1:         /* lxv */
                        op->ea = dqform_ea(instr, regs);
                        if (instr & 8)