powerpc/sstep: Support VSX vector paired storage access instructions
VSX Vector Paired instructions loads/stores an octword (32 bytes) from/to storage into two sequential VSRs. Add emulation support for these new instructions: * Load VSX Vector Paired (lxvp) * Load VSX Vector Paired Indexed (lxvpx) * Prefixed Load VSX Vector Paired (plxvp) * Store VSX Vector Paired (stxvp) * Store VSX Vector Paired Indexed (stxvpx) * Prefixed Store VSX Vector Paired (pstxvp) [kernel test robot reported a build failure] Reported-by: kernel test robot <lkp@intel.com> Suggested-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Signed-off-by: Balamuruhan S <bala24@linux.ibm.com> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20201011050908.72173-4-ravi.bangoria@linux.ibm.com
This commit is contained in:
Родитель
1817de2f14
Коммит
af99da7433
|
@ -32,6 +32,10 @@ extern char system_call_vectored_emulate[];
|
||||||
#define XER_OV32 0x00080000U
|
#define XER_OV32 0x00080000U
|
||||||
#define XER_CA32 0x00040000U
|
#define XER_CA32 0x00040000U
|
||||||
|
|
||||||
|
#ifdef CONFIG_VSX
|
||||||
|
#define VSX_REGISTER_XTP(rd) ((((rd) & 1) << 5) | ((rd) & 0xfe))
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PPC_FPU
|
#ifdef CONFIG_PPC_FPU
|
||||||
/*
|
/*
|
||||||
* Functions in ldstfp.S
|
* Functions in ldstfp.S
|
||||||
|
@ -279,6 +283,19 @@ static nokprobe_inline void do_byte_reverse(void *ptr, int nb)
|
||||||
up[1] = tmp;
|
up[1] = tmp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 32: {
|
||||||
|
unsigned long *up = (unsigned long *)ptr;
|
||||||
|
unsigned long tmp;
|
||||||
|
|
||||||
|
tmp = byterev_8(up[0]);
|
||||||
|
up[0] = byterev_8(up[3]);
|
||||||
|
up[3] = tmp;
|
||||||
|
tmp = byterev_8(up[2]);
|
||||||
|
up[2] = byterev_8(up[1]);
|
||||||
|
up[1] = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
@ -709,6 +726,8 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
|
||||||
reg->d[0] = reg->d[1] = 0;
|
reg->d[0] = reg->d[1] = 0;
|
||||||
|
|
||||||
switch (op->element_size) {
|
switch (op->element_size) {
|
||||||
|
case 32:
|
||||||
|
/* [p]lxvp[x] */
|
||||||
case 16:
|
case 16:
|
||||||
/* whole vector; lxv[x] or lxvl[l] */
|
/* whole vector; lxv[x] or lxvl[l] */
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
|
@ -717,7 +736,7 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
|
||||||
if (IS_LE && (op->vsx_flags & VSX_LDLEFT))
|
if (IS_LE && (op->vsx_flags & VSX_LDLEFT))
|
||||||
rev = !rev;
|
rev = !rev;
|
||||||
if (rev)
|
if (rev)
|
||||||
do_byte_reverse(reg, 16);
|
do_byte_reverse(reg, size);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
/* scalar loads, lxvd2x, lxvdsx */
|
/* scalar loads, lxvd2x, lxvdsx */
|
||||||
|
@ -793,6 +812,20 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg,
|
||||||
size = GETSIZE(op->type);
|
size = GETSIZE(op->type);
|
||||||
|
|
||||||
switch (op->element_size) {
|
switch (op->element_size) {
|
||||||
|
case 32:
|
||||||
|
/* [p]stxvp[x] */
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
|
if (rev) {
|
||||||
|
/* reverse 32 bytes */
|
||||||
|
buf.d[0] = byterev_8(reg->d[3]);
|
||||||
|
buf.d[1] = byterev_8(reg->d[2]);
|
||||||
|
buf.d[2] = byterev_8(reg->d[1]);
|
||||||
|
buf.d[3] = byterev_8(reg->d[0]);
|
||||||
|
reg = &buf;
|
||||||
|
}
|
||||||
|
memcpy(mem, reg, size);
|
||||||
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
/* stxv, stxvx, stxvl, stxvll */
|
/* stxv, stxvx, stxvl, stxvll */
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
|
@ -861,28 +894,43 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op,
|
||||||
bool cross_endian)
|
bool cross_endian)
|
||||||
{
|
{
|
||||||
int reg = op->reg;
|
int reg = op->reg;
|
||||||
u8 mem[16];
|
int i, j, nr_vsx_regs;
|
||||||
union vsx_reg buf;
|
u8 mem[32];
|
||||||
|
union vsx_reg buf[2];
|
||||||
int size = GETSIZE(op->type);
|
int size = GETSIZE(op->type);
|
||||||
|
|
||||||
if (!address_ok(regs, ea, size) || copy_mem_in(mem, ea, size, regs))
|
if (!address_ok(regs, ea, size) || copy_mem_in(mem, ea, size, regs))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
emulate_vsx_load(op, &buf, mem, cross_endian);
|
nr_vsx_regs = size / sizeof(__vector128);
|
||||||
|
emulate_vsx_load(op, buf, mem, cross_endian);
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
if (reg < 32) {
|
if (reg < 32) {
|
||||||
/* FP regs + extensions */
|
/* FP regs + extensions */
|
||||||
if (regs->msr & MSR_FP) {
|
if (regs->msr & MSR_FP) {
|
||||||
load_vsrn(reg, &buf);
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
} else {
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
current->thread.fp_state.fpr[reg][0] = buf.d[0];
|
load_vsrn(reg + i, &buf[j].v);
|
||||||
current->thread.fp_state.fpr[reg][1] = buf.d[1];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (regs->msr & MSR_VEC)
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
load_vsrn(reg, &buf);
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
else
|
current->thread.fp_state.fpr[reg + i][0] = buf[j].d[0];
|
||||||
current->thread.vr_state.vr[reg - 32] = buf.v;
|
current->thread.fp_state.fpr[reg + i][1] = buf[j].d[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (regs->msr & MSR_VEC) {
|
||||||
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
|
load_vsrn(reg + i, &buf[j].v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
|
current->thread.vr_state.vr[reg - 32 + i] = buf[j].v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -893,30 +941,45 @@ static nokprobe_inline int do_vsx_store(struct instruction_op *op,
|
||||||
bool cross_endian)
|
bool cross_endian)
|
||||||
{
|
{
|
||||||
int reg = op->reg;
|
int reg = op->reg;
|
||||||
u8 mem[16];
|
int i, j, nr_vsx_regs;
|
||||||
union vsx_reg buf;
|
u8 mem[32];
|
||||||
|
union vsx_reg buf[2];
|
||||||
int size = GETSIZE(op->type);
|
int size = GETSIZE(op->type);
|
||||||
|
|
||||||
if (!address_ok(regs, ea, size))
|
if (!address_ok(regs, ea, size))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
nr_vsx_regs = size / sizeof(__vector128);
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
if (reg < 32) {
|
if (reg < 32) {
|
||||||
/* FP regs + extensions */
|
/* FP regs + extensions */
|
||||||
if (regs->msr & MSR_FP) {
|
if (regs->msr & MSR_FP) {
|
||||||
store_vsrn(reg, &buf);
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
} else {
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
buf.d[0] = current->thread.fp_state.fpr[reg][0];
|
store_vsrn(reg + i, &buf[j].v);
|
||||||
buf.d[1] = current->thread.fp_state.fpr[reg][1];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (regs->msr & MSR_VEC)
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
store_vsrn(reg, &buf);
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
else
|
buf[j].d[0] = current->thread.fp_state.fpr[reg + i][0];
|
||||||
buf.v = current->thread.vr_state.vr[reg - 32];
|
buf[j].d[1] = current->thread.fp_state.fpr[reg + i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (regs->msr & MSR_VEC) {
|
||||||
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
|
store_vsrn(reg + i, &buf[j].v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < nr_vsx_regs; i++) {
|
||||||
|
j = IS_LE ? nr_vsx_regs - i - 1 : i;
|
||||||
|
buf[j].v = current->thread.vr_state.vr[reg - 32 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
emulate_vsx_store(op, &buf, mem, cross_endian);
|
emulate_vsx_store(op, buf, mem, cross_endian);
|
||||||
return copy_mem_out(mem, ea, size, regs);
|
return copy_mem_out(mem, ea, size, regs);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_VSX */
|
#endif /* CONFIG_VSX */
|
||||||
|
@ -2403,6 +2466,14 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
|
||||||
op->vsx_flags = VSX_SPLAT;
|
op->vsx_flags = VSX_SPLAT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 333: /* lxvpx */
|
||||||
|
if (!cpu_has_feature(CPU_FTR_ARCH_31))
|
||||||
|
return -1;
|
||||||
|
op->reg = VSX_REGISTER_XTP(rd);
|
||||||
|
op->type = MKOP(LOAD_VSX, 0, 32);
|
||||||
|
op->element_size = 32;
|
||||||
|
break;
|
||||||
|
|
||||||
case 364: /* lxvwsx */
|
case 364: /* lxvwsx */
|
||||||
op->reg = rd | ((word & 1) << 5);
|
op->reg = rd | ((word & 1) << 5);
|
||||||
op->type = MKOP(LOAD_VSX, 0, 4);
|
op->type = MKOP(LOAD_VSX, 0, 4);
|
||||||
|
@ -2431,6 +2502,13 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
|
||||||
VSX_CHECK_VEC;
|
VSX_CHECK_VEC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 461: /* stxvpx */
|
||||||
|
if (!cpu_has_feature(CPU_FTR_ARCH_31))
|
||||||
|
return -1;
|
||||||
|
op->reg = VSX_REGISTER_XTP(rd);
|
||||||
|
op->type = MKOP(STORE_VSX, 0, 32);
|
||||||
|
op->element_size = 32;
|
||||||
|
break;
|
||||||
case 524: /* lxsspx */
|
case 524: /* lxsspx */
|
||||||
op->reg = rd | ((word & 1) << 5);
|
op->reg = rd | ((word & 1) << 5);
|
||||||
op->type = MKOP(LOAD_VSX, 0, 4);
|
op->type = MKOP(LOAD_VSX, 0, 4);
|
||||||
|
@ -2672,6 +2750,22 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_VSX
|
#ifdef CONFIG_VSX
|
||||||
|
case 6:
|
||||||
|
if (!cpu_has_feature(CPU_FTR_ARCH_31))
|
||||||
|
return -1;
|
||||||
|
op->ea = dqform_ea(word, regs);
|
||||||
|
op->reg = VSX_REGISTER_XTP(rd);
|
||||||
|
op->element_size = 32;
|
||||||
|
switch (word & 0xf) {
|
||||||
|
case 0: /* lxvp */
|
||||||
|
op->type = MKOP(LOAD_VSX, 0, 32);
|
||||||
|
break;
|
||||||
|
case 1: /* stxvp */
|
||||||
|
op->type = MKOP(STORE_VSX, 0, 32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 61: /* stfdp, lxv, stxsd, stxssp, stxv */
|
case 61: /* stfdp, lxv, stxsd, stxssp, stxv */
|
||||||
switch (word & 7) {
|
switch (word & 7) {
|
||||||
case 0: /* stfdp with LSB of DS field = 0 */
|
case 0: /* stfdp with LSB of DS field = 0 */
|
||||||
|
@ -2805,12 +2899,26 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
|
||||||
case 57: /* pld */
|
case 57: /* pld */
|
||||||
op->type = MKOP(LOAD, PREFIXED, 8);
|
op->type = MKOP(LOAD, PREFIXED, 8);
|
||||||
break;
|
break;
|
||||||
|
#ifdef CONFIG_VSX
|
||||||
|
case 58: /* plxvp */
|
||||||
|
op->reg = VSX_REGISTER_XTP(rd);
|
||||||
|
op->type = MKOP(LOAD_VSX, PREFIXED, 32);
|
||||||
|
op->element_size = 32;
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_VSX */
|
||||||
case 60: /* pstq */
|
case 60: /* pstq */
|
||||||
op->type = MKOP(STORE, PREFIXED, 16);
|
op->type = MKOP(STORE, PREFIXED, 16);
|
||||||
break;
|
break;
|
||||||
case 61: /* pstd */
|
case 61: /* pstd */
|
||||||
op->type = MKOP(STORE, PREFIXED, 8);
|
op->type = MKOP(STORE, PREFIXED, 8);
|
||||||
break;
|
break;
|
||||||
|
#ifdef CONFIG_VSX
|
||||||
|
case 62: /* pstxvp */
|
||||||
|
op->reg = VSX_REGISTER_XTP(rd);
|
||||||
|
op->type = MKOP(STORE_VSX, PREFIXED, 32);
|
||||||
|
op->element_size = 32;
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_VSX */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: /* Type 01 Eight-Byte Register-to-Register */
|
case 1: /* Type 01 Eight-Byte Register-to-Register */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче