powerpc: Emulate load/store floating point as integer word instructions

This adds emulation for the lfiwax, lfiwzx and stfiwx instructions.
This necessitated adding a new flag to indicate whether a floating
point or an integer conversion was needed for LOAD_FP and STORE_FP,
so this moves the size field in op->type up 4 bits.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
Paul Mackerras 2017-08-30 16:34:09 +10:00 committed by Michael Ellerman
parent 31bfdb036f
commit d2b65ac652
2 changed files with 48 additions and 17 deletions

View file

@ -68,6 +68,7 @@ enum instruction_type {
#define SIGNEXT 0x20 #define SIGNEXT 0x20
#define UPDATE 0x40 /* matches bit in opcode 31 instructions */ #define UPDATE 0x40 /* matches bit in opcode 31 instructions */
#define BYTEREV 0x80 #define BYTEREV 0x80
#define FPCONV 0x100
/* Barrier type field, ORed in with type */ /* Barrier type field, ORed in with type */
#define BARRIER_MASK 0xe0 #define BARRIER_MASK 0xe0
@ -93,8 +94,8 @@ enum instruction_type {
#define VSX_CHECK_VEC 8 /* check MSR_VEC not MSR_VSX for reg >= 32 */ #define VSX_CHECK_VEC 8 /* check MSR_VEC not MSR_VSX for reg >= 32 */
/* Size field in type word */ /* Size field in type word */
#define SIZE(n) ((n) << 8) #define SIZE(n) ((n) << 12)
#define GETSIZE(w) ((w) >> 8) #define GETSIZE(w) ((w) >> 12)
#define MKOP(t, f, s) ((t) | (f) | SIZE(s)) #define MKOP(t, f, s) ((t) | (f) | SIZE(s))

View file

@ -457,19 +457,23 @@ NOKPROBE_SYMBOL(write_mem);
* These access either the real FP register or the image in the * These access either the real FP register or the image in the
* thread_struct, depending on regs->msr & MSR_FP. * thread_struct, depending on regs->msr & MSR_FP.
*/ */
static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs, static int do_fp_load(struct instruction_op *op, unsigned long ea,
bool cross_endian) struct pt_regs *regs, bool cross_endian)
{ {
int err; int err, rn, nb;
union { union {
int i;
unsigned int u;
float f; float f;
double d[2]; double d[2];
unsigned long l[2]; unsigned long l[2];
u8 b[2 * sizeof(double)]; u8 b[2 * sizeof(double)];
} u; } u;
nb = GETSIZE(op->type);
if (!address_ok(regs, ea, nb)) if (!address_ok(regs, ea, nb))
return -EFAULT; return -EFAULT;
rn = op->reg;
err = copy_mem_in(u.b, ea, nb, regs); err = copy_mem_in(u.b, ea, nb, regs);
if (err) if (err)
return err; return err;
@ -479,8 +483,14 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs,
do_byte_reverse(&u.b[8], 8); do_byte_reverse(&u.b[8], 8);
} }
preempt_disable(); preempt_disable();
if (nb == 4) if (nb == 4) {
conv_sp_to_dp(&u.f, &u.d[0]); if (op->type & FPCONV)
conv_sp_to_dp(&u.f, &u.d[0]);
else if (op->type & SIGNEXT)
u.l[0] = u.i;
else
u.l[0] = u.u;
}
if (regs->msr & MSR_FP) if (regs->msr & MSR_FP)
put_fpr(rn, &u.d[0]); put_fpr(rn, &u.d[0]);
else else
@ -498,25 +508,33 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs,
} }
NOKPROBE_SYMBOL(do_fp_load); NOKPROBE_SYMBOL(do_fp_load);
static int do_fp_store(int rn, unsigned long ea, int nb, struct pt_regs *regs, static int do_fp_store(struct instruction_op *op, unsigned long ea,
bool cross_endian) struct pt_regs *regs, bool cross_endian)
{ {
int rn, nb;
union { union {
unsigned int u;
float f; float f;
double d[2]; double d[2];
unsigned long l[2]; unsigned long l[2];
u8 b[2 * sizeof(double)]; u8 b[2 * sizeof(double)];
} u; } u;
nb = GETSIZE(op->type);
if (!address_ok(regs, ea, nb)) if (!address_ok(regs, ea, nb))
return -EFAULT; return -EFAULT;
rn = op->reg;
preempt_disable(); preempt_disable();
if (regs->msr & MSR_FP) if (regs->msr & MSR_FP)
get_fpr(rn, &u.d[0]); get_fpr(rn, &u.d[0]);
else else
u.l[0] = current->thread.TS_FPR(rn); u.l[0] = current->thread.TS_FPR(rn);
if (nb == 4) if (nb == 4) {
conv_dp_to_sp(&u.d[0], &u.f); if (op->type & FPCONV)
conv_dp_to_sp(&u.d[0], &u.f);
else
u.u = u.l[0];
}
if (nb == 16) { if (nb == 16) {
rn |= 1; rn |= 1;
if (regs->msr & MSR_FP) if (regs->msr & MSR_FP)
@ -2049,7 +2067,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
#ifdef CONFIG_PPC_FPU #ifdef CONFIG_PPC_FPU
case 535: /* lfsx */ case 535: /* lfsx */
case 567: /* lfsux */ case 567: /* lfsux */
op->type = MKOP(LOAD_FP, u, 4); op->type = MKOP(LOAD_FP, u | FPCONV, 4);
break; break;
case 599: /* lfdx */ case 599: /* lfdx */
@ -2059,7 +2077,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
case 663: /* stfsx */ case 663: /* stfsx */
case 695: /* stfsux */ case 695: /* stfsux */
op->type = MKOP(STORE_FP, u, 4); op->type = MKOP(STORE_FP, u | FPCONV, 4);
break; break;
case 727: /* stfdx */ case 727: /* stfdx */
@ -2072,9 +2090,21 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
op->type = MKOP(LOAD_FP, 0, 16); op->type = MKOP(LOAD_FP, 0, 16);
break; break;
case 855: /* lfiwax */
op->type = MKOP(LOAD_FP, SIGNEXT, 4);
break;
case 887: /* lfiwzx */
op->type = MKOP(LOAD_FP, 0, 4);
break;
case 919: /* stfdpx */ case 919: /* stfdpx */
op->type = MKOP(STORE_FP, 0, 16); op->type = MKOP(STORE_FP, 0, 16);
break; break;
case 983: /* stfiwx */
op->type = MKOP(STORE_FP, 0, 4);
break;
#endif /* __powerpc64 */ #endif /* __powerpc64 */
#endif /* CONFIG_PPC_FPU */ #endif /* CONFIG_PPC_FPU */
@ -2352,7 +2382,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
#ifdef CONFIG_PPC_FPU #ifdef CONFIG_PPC_FPU
case 48: /* lfs */ case 48: /* lfs */
case 49: /* lfsu */ case 49: /* lfsu */
op->type = MKOP(LOAD_FP, u, 4); op->type = MKOP(LOAD_FP, u | FPCONV, 4);
op->ea = dform_ea(instr, regs); op->ea = dform_ea(instr, regs);
break; break;
@ -2364,7 +2394,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
case 52: /* stfs */ case 52: /* stfs */
case 53: /* stfsu */ case 53: /* stfsu */
op->type = MKOP(STORE_FP, u, 4); op->type = MKOP(STORE_FP, u | FPCONV, 4);
op->ea = dform_ea(instr, regs); op->ea = dform_ea(instr, regs);
break; break;
@ -2792,7 +2822,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op)
*/ */
if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_FP)) if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_FP))
return 0; return 0;
err = do_fp_load(op->reg, ea, size, regs, cross_endian); err = do_fp_load(op, ea, regs, cross_endian);
break; break;
#endif #endif
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
@ -2862,7 +2892,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op)
case STORE_FP: case STORE_FP:
if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_FP)) if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_FP))
return 0; return 0;
err = do_fp_store(op->reg, ea, size, regs, cross_endian); err = do_fp_store(op, ea, regs, cross_endian);
break; break;
#endif #endif
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC