Commit d955189a authored by Paul Mackerras's avatar Paul Mackerras Committed by Michael Ellerman

powerpc: Handle opposite-endian processes in emulation code

This adds code to the load and store emulation code to byte-swap
the data appropriately when the process being emulated is set to
the opposite endianness to that of the kernel.

This also enables the emulation for the multiple-register loads
and stores (lmw, stmw, lswi, stswi, lswx, stswx) to work for
little-endian.  In little-endian mode, the partial word at the
end of a transfer for lsw*/stsw* (when the byte count is not a
multiple of 4) is loaded/stored at the least-significant end of
the register.  Additionally, this fixes a bug in the previous
code in that it could call read_mem/write_mem with a byte count
that was not 1, 2, 4 or 8.

Note that this only works correctly on processors with "true"
little-endian mode, such as IBM POWER processors from POWER6 on, not
the so-called "PowerPC" little-endian mode that uses address swizzling
as implemented on the old 32-bit 603, 604, 740/750, 74xx CPUs.
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent b9da9c8a
...@@ -153,7 +153,8 @@ void emulate_update_regs(struct pt_regs *reg, struct instruction_op *op); ...@@ -153,7 +153,8 @@ void emulate_update_regs(struct pt_regs *reg, struct instruction_op *op);
extern int emulate_step(struct pt_regs *regs, unsigned int instr); extern int emulate_step(struct pt_regs *regs, unsigned int instr);
extern void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, extern void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
const void *mem); const void *mem, bool cross_endian);
extern void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg, extern void emulate_vsx_store(struct instruction_op *op,
void *mem); const union vsx_reg *reg, void *mem,
bool cross_endian);
extern int emulate_dcbz(unsigned long ea, struct pt_regs *regs); extern int emulate_dcbz(unsigned long ea, struct pt_regs *regs);
...@@ -217,6 +217,33 @@ static nokprobe_inline unsigned long byterev_8(unsigned long x) ...@@ -217,6 +217,33 @@ static nokprobe_inline unsigned long byterev_8(unsigned long x)
} }
#endif #endif
static nokprobe_inline void do_byte_reverse(void *ptr, int nb)
{
switch (nb) {
case 2:
*(u16 *)ptr = byterev_2(*(u16 *)ptr);
break;
case 4:
*(u32 *)ptr = byterev_4(*(u32 *)ptr);
break;
#ifdef __powerpc64__
case 8:
*(unsigned long *)ptr = byterev_8(*(unsigned long *)ptr);
break;
case 16: {
unsigned long *up = (unsigned long *)ptr;
unsigned long tmp;
tmp = byterev_8(up[0]);
up[0] = byterev_8(up[1]);
up[1] = tmp;
break;
}
#endif
default:
WARN_ON_ONCE(1);
}
}
static nokprobe_inline int read_mem_aligned(unsigned long *dest, static nokprobe_inline int read_mem_aligned(unsigned long *dest,
unsigned long ea, int nb, unsigned long ea, int nb,
struct pt_regs *regs) struct pt_regs *regs)
...@@ -430,7 +457,8 @@ NOKPROBE_SYMBOL(write_mem); ...@@ -430,7 +457,8 @@ 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(int rn, unsigned long ea, int nb, struct pt_regs *regs,
bool cross_endian)
{ {
int err; int err;
union { union {
...@@ -445,6 +473,11 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs) ...@@ -445,6 +473,11 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs)
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;
if (unlikely(cross_endian)) {
do_byte_reverse(u.b, min(nb, 8));
if (nb == 16)
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]); conv_sp_to_dp(&u.f, &u.d[0]);
...@@ -465,7 +498,8 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs) ...@@ -465,7 +498,8 @@ 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(int rn, unsigned long ea, int nb, struct pt_regs *regs,
bool cross_endian)
{ {
union { union {
float f; float f;
...@@ -491,6 +525,11 @@ static int do_fp_store(int rn, unsigned long ea, int nb, struct pt_regs *regs) ...@@ -491,6 +525,11 @@ static int do_fp_store(int rn, unsigned long ea, int nb, struct pt_regs *regs)
u.l[1] = current->thread.TS_FPR(rn); u.l[1] = current->thread.TS_FPR(rn);
} }
preempt_enable(); preempt_enable();
if (unlikely(cross_endian)) {
do_byte_reverse(u.b, min(nb, 8));
if (nb == 16)
do_byte_reverse(&u.b[8], 8);
}
return copy_mem_out(u.b, ea, nb, regs); return copy_mem_out(u.b, ea, nb, regs);
} }
NOKPROBE_SYMBOL(do_fp_store); NOKPROBE_SYMBOL(do_fp_store);
...@@ -499,7 +538,8 @@ NOKPROBE_SYMBOL(do_fp_store); ...@@ -499,7 +538,8 @@ NOKPROBE_SYMBOL(do_fp_store);
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
/* For Altivec/VMX, no need to worry about alignment */ /* For Altivec/VMX, no need to worry about alignment */
static nokprobe_inline int do_vec_load(int rn, unsigned long ea, static nokprobe_inline int do_vec_load(int rn, unsigned long ea,
int size, struct pt_regs *regs) int size, struct pt_regs *regs,
bool cross_endian)
{ {
int err; int err;
union { union {
...@@ -514,7 +554,8 @@ static nokprobe_inline int do_vec_load(int rn, unsigned long ea, ...@@ -514,7 +554,8 @@ static nokprobe_inline int do_vec_load(int rn, unsigned long ea,
err = copy_mem_in(&u.b[ea & 0xf], ea, size, regs); err = copy_mem_in(&u.b[ea & 0xf], ea, size, regs);
if (err) if (err)
return err; return err;
if (unlikely(cross_endian))
do_byte_reverse(&u.b[ea & 0xf], size);
preempt_disable(); preempt_disable();
if (regs->msr & MSR_VEC) if (regs->msr & MSR_VEC)
put_vr(rn, &u.v); put_vr(rn, &u.v);
...@@ -525,7 +566,8 @@ static nokprobe_inline int do_vec_load(int rn, unsigned long ea, ...@@ -525,7 +566,8 @@ static nokprobe_inline int do_vec_load(int rn, unsigned long ea,
} }
static nokprobe_inline int do_vec_store(int rn, unsigned long ea, static nokprobe_inline int do_vec_store(int rn, unsigned long ea,
int size, struct pt_regs *regs) int size, struct pt_regs *regs,
bool cross_endian)
{ {
union { union {
__vector128 v; __vector128 v;
...@@ -543,49 +585,60 @@ static nokprobe_inline int do_vec_store(int rn, unsigned long ea, ...@@ -543,49 +585,60 @@ static nokprobe_inline int do_vec_store(int rn, unsigned long ea,
else else
u.v = current->thread.vr_state.vr[rn]; u.v = current->thread.vr_state.vr[rn];
preempt_enable(); preempt_enable();
if (unlikely(cross_endian))
do_byte_reverse(&u.b[ea & 0xf], size);
return copy_mem_out(&u.b[ea & 0xf], ea, size, regs); return copy_mem_out(&u.b[ea & 0xf], ea, size, regs);
} }
#endif /* CONFIG_ALTIVEC */ #endif /* CONFIG_ALTIVEC */
#ifdef __powerpc64__ #ifdef __powerpc64__
static nokprobe_inline int emulate_lq(struct pt_regs *regs, unsigned long ea, static nokprobe_inline int emulate_lq(struct pt_regs *regs, unsigned long ea,
int reg) int reg, bool cross_endian)
{ {
int err; int err;
if (!address_ok(regs, ea, 16)) if (!address_ok(regs, ea, 16))
return -EFAULT; return -EFAULT;
/* if aligned, should be atomic */ /* if aligned, should be atomic */
if ((ea & 0xf) == 0) if ((ea & 0xf) == 0) {
return do_lq(ea, &regs->gpr[reg]); err = do_lq(ea, &regs->gpr[reg]);
} else {
err = read_mem(&regs->gpr[reg + IS_LE], ea, 8, regs); err = read_mem(&regs->gpr[reg + IS_LE], ea, 8, regs);
if (!err) if (!err)
err = read_mem(&regs->gpr[reg + IS_BE], ea + 8, 8, regs); err = read_mem(&regs->gpr[reg + IS_BE], ea + 8, 8, regs);
}
if (!err && unlikely(cross_endian))
do_byte_reverse(&regs->gpr[reg], 16);
return err; return err;
} }
static nokprobe_inline int emulate_stq(struct pt_regs *regs, unsigned long ea, static nokprobe_inline int emulate_stq(struct pt_regs *regs, unsigned long ea,
int reg) int reg, bool cross_endian)
{ {
int err; int err;
unsigned long vals[2];
if (!address_ok(regs, ea, 16)) if (!address_ok(regs, ea, 16))
return -EFAULT; return -EFAULT;
vals[0] = regs->gpr[reg];
vals[1] = regs->gpr[reg + 1];
if (unlikely(cross_endian))
do_byte_reverse(vals, 16);
/* if aligned, should be atomic */ /* if aligned, should be atomic */
if ((ea & 0xf) == 0) if ((ea & 0xf) == 0)
return do_stq(ea, regs->gpr[reg], regs->gpr[reg + 1]); return do_stq(ea, vals[0], vals[1]);
err = write_mem(regs->gpr[reg + IS_LE], ea, 8, regs); err = write_mem(vals[IS_LE], ea, 8, regs);
if (!err) if (!err)
err = write_mem(regs->gpr[reg + IS_BE], ea + 8, 8, regs); err = write_mem(vals[IS_BE], ea + 8, 8, regs);
return err; return err;
} }
#endif /* __powerpc64 */ #endif /* __powerpc64 */
#ifdef CONFIG_VSX #ifdef CONFIG_VSX
void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
const void *mem) const void *mem, bool rev)
{ {
int size, read_size; int size, read_size;
int i, j; int i, j;
...@@ -602,19 +655,18 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, ...@@ -602,19 +655,18 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
if (size == 0) if (size == 0)
break; break;
memcpy(reg, mem, size); memcpy(reg, mem, size);
if (IS_LE && (op->vsx_flags & VSX_LDLEFT)) { if (IS_LE && (op->vsx_flags & VSX_LDLEFT))
/* reverse 16 bytes */ rev = !rev;
unsigned long tmp; if (rev)
tmp = byterev_8(reg->d[0]); do_byte_reverse(reg, 16);
reg->d[0] = byterev_8(reg->d[1]);
reg->d[1] = tmp;
}
break; break;
case 8: case 8:
/* scalar loads, lxvd2x, lxvdsx */ /* scalar loads, lxvd2x, lxvdsx */
read_size = (size >= 8) ? 8 : size; read_size = (size >= 8) ? 8 : size;
i = IS_LE ? 8 : 8 - read_size; i = IS_LE ? 8 : 8 - read_size;
memcpy(&reg->b[i], mem, read_size); memcpy(&reg->b[i], mem, read_size);
if (rev)
do_byte_reverse(&reg->b[i], 8);
if (size < 8) { if (size < 8) {
if (op->type & SIGNEXT) { if (op->type & SIGNEXT) {
/* size == 4 is the only case here */ /* size == 4 is the only case here */
...@@ -626,9 +678,10 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, ...@@ -626,9 +678,10 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
preempt_enable(); preempt_enable();
} }
} else { } else {
if (size == 16) if (size == 16) {
reg->d[IS_BE] = *(unsigned long *)(mem + 8); unsigned long v = *(unsigned long *)(mem + 8);
else if (op->vsx_flags & VSX_SPLAT) reg->d[IS_BE] = !rev ? v : byterev_8(v);
} else if (op->vsx_flags & VSX_SPLAT)
reg->d[IS_BE] = reg->d[IS_LE]; reg->d[IS_BE] = reg->d[IS_LE];
} }
break; break;
...@@ -637,7 +690,7 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, ...@@ -637,7 +690,7 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
wp = mem; wp = mem;
for (j = 0; j < size / 4; ++j) { for (j = 0; j < size / 4; ++j) {
i = IS_LE ? 3 - j : j; i = IS_LE ? 3 - j : j;
reg->w[i] = *wp++; reg->w[i] = !rev ? *wp++ : byterev_4(*wp++);
} }
if (op->vsx_flags & VSX_SPLAT) { if (op->vsx_flags & VSX_SPLAT) {
u32 val = reg->w[IS_LE ? 3 : 0]; u32 val = reg->w[IS_LE ? 3 : 0];
...@@ -652,7 +705,7 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, ...@@ -652,7 +705,7 @@ void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
hp = mem; hp = mem;
for (j = 0; j < size / 2; ++j) { for (j = 0; j < size / 2; ++j) {
i = IS_LE ? 7 - j : j; i = IS_LE ? 7 - j : j;
reg->h[i] = *hp++; reg->h[i] = !rev ? *hp++ : byterev_2(*hp++);
} }
break; break;
case 1: case 1:
...@@ -669,7 +722,7 @@ EXPORT_SYMBOL_GPL(emulate_vsx_load); ...@@ -669,7 +722,7 @@ EXPORT_SYMBOL_GPL(emulate_vsx_load);
NOKPROBE_SYMBOL(emulate_vsx_load); NOKPROBE_SYMBOL(emulate_vsx_load);
void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg, void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg,
void *mem) void *mem, bool rev)
{ {
int size, write_size; int size, write_size;
int i, j; int i, j;
...@@ -685,7 +738,9 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg, ...@@ -685,7 +738,9 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg,
/* stxv, stxvx, stxvl, stxvll */ /* stxv, stxvx, stxvl, stxvll */
if (size == 0) if (size == 0)
break; break;
if (IS_LE && (op->vsx_flags & VSX_LDLEFT)) { if (IS_LE && (op->vsx_flags & VSX_LDLEFT))
rev = !rev;
if (rev) {
/* reverse 16 bytes */ /* reverse 16 bytes */
buf.d[0] = byterev_8(reg->d[1]); buf.d[0] = byterev_8(reg->d[1]);
buf.d[1] = byterev_8(reg->d[0]); buf.d[1] = byterev_8(reg->d[0]);
...@@ -707,13 +762,18 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg, ...@@ -707,13 +762,18 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg,
memcpy(mem, &reg->b[i], write_size); memcpy(mem, &reg->b[i], write_size);
if (size == 16) if (size == 16)
memcpy(mem + 8, &reg->d[IS_BE], 8); memcpy(mem + 8, &reg->d[IS_BE], 8);
if (unlikely(rev)) {
do_byte_reverse(mem, write_size);
if (size == 16)
do_byte_reverse(mem + 8, 8);
}
break; break;
case 4: case 4:
/* stxvw4x */ /* stxvw4x */
wp = mem; wp = mem;
for (j = 0; j < size / 4; ++j) { for (j = 0; j < size / 4; ++j) {
i = IS_LE ? 3 - j : j; i = IS_LE ? 3 - j : j;
*wp++ = reg->w[i]; *wp++ = !rev ? reg->w[i] : byterev_4(reg->w[i]);
} }
break; break;
case 2: case 2:
...@@ -721,7 +781,7 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg, ...@@ -721,7 +781,7 @@ void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg,
hp = mem; hp = mem;
for (j = 0; j < size / 2; ++j) { for (j = 0; j < size / 2; ++j) {
i = IS_LE ? 7 - j : j; i = IS_LE ? 7 - j : j;
*hp++ = reg->h[i]; *hp++ = !rev ? reg->h[i] : byterev_2(reg->h[i]);
} }
break; break;
case 1: case 1:
...@@ -738,7 +798,8 @@ EXPORT_SYMBOL_GPL(emulate_vsx_store); ...@@ -738,7 +798,8 @@ EXPORT_SYMBOL_GPL(emulate_vsx_store);
NOKPROBE_SYMBOL(emulate_vsx_store); NOKPROBE_SYMBOL(emulate_vsx_store);
static nokprobe_inline int do_vsx_load(struct instruction_op *op, static nokprobe_inline int do_vsx_load(struct instruction_op *op,
unsigned long ea, struct pt_regs *regs) unsigned long ea, struct pt_regs *regs,
bool cross_endian)
{ {
int reg = op->reg; int reg = op->reg;
u8 mem[16]; u8 mem[16];
...@@ -748,7 +809,7 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op, ...@@ -748,7 +809,7 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op,
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); emulate_vsx_load(op, &buf, mem, cross_endian);
preempt_disable(); preempt_disable();
if (reg < 32) { if (reg < 32) {
/* FP regs + extensions */ /* FP regs + extensions */
...@@ -769,7 +830,8 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op, ...@@ -769,7 +830,8 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op,
} }
static nokprobe_inline int do_vsx_store(struct instruction_op *op, static nokprobe_inline int do_vsx_store(struct instruction_op *op,
unsigned long ea, struct pt_regs *regs) unsigned long ea, struct pt_regs *regs,
bool cross_endian)
{ {
int reg = op->reg; int reg = op->reg;
u8 mem[16]; u8 mem[16];
...@@ -795,7 +857,7 @@ static nokprobe_inline int do_vsx_store(struct instruction_op *op, ...@@ -795,7 +857,7 @@ static nokprobe_inline int do_vsx_store(struct instruction_op *op,
buf.v = current->thread.vr_state.vr[reg - 32]; buf.v = current->thread.vr_state.vr[reg - 32];
} }
preempt_enable(); preempt_enable();
emulate_vsx_store(op, &buf, mem); 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 */
...@@ -2619,6 +2681,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2619,6 +2681,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
unsigned int cr; unsigned int cr;
int i, rd, nb; int i, rd, nb;
unsigned long ea; unsigned long ea;
bool cross_endian;
r = analyse_instr(&op, regs, instr); r = analyse_instr(&op, regs, instr);
if (r < 0) if (r < 0)
...@@ -2631,6 +2694,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2631,6 +2694,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
err = 0; err = 0;
size = GETSIZE(op.type); size = GETSIZE(op.type);
type = op.type & INSTR_TYPE_MASK; type = op.type & INSTR_TYPE_MASK;
cross_endian = (regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE);
ea = op.ea; ea = op.ea;
if (OP_IS_LOAD_STORE(type) || type == CACHEOP) if (OP_IS_LOAD_STORE(type) || type == CACHEOP)
...@@ -2746,7 +2810,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2746,7 +2810,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
case LOAD: case LOAD:
#ifdef __powerpc64__ #ifdef __powerpc64__
if (size == 16) { if (size == 16) {
err = emulate_lq(regs, ea, op.reg); err = emulate_lq(regs, ea, op.reg, cross_endian);
goto ldst_done; goto ldst_done;
} }
#endif #endif
...@@ -2754,7 +2818,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2754,7 +2818,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
if (!err) { if (!err) {
if (op.type & SIGNEXT) if (op.type & SIGNEXT)
do_signext(&regs->gpr[op.reg], size); do_signext(&regs->gpr[op.reg], size);
if (op.type & BYTEREV) if ((op.type & BYTEREV) == (cross_endian ? 0 : BYTEREV))
do_byterev(&regs->gpr[op.reg], size); do_byterev(&regs->gpr[op.reg], size);
} }
goto ldst_done; goto ldst_done;
...@@ -2769,14 +2833,14 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2769,14 +2833,14 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
*/ */
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); err = do_fp_load(op.reg, ea, size, regs, cross_endian);
goto ldst_done; goto ldst_done;
#endif #endif
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
case LOAD_VMX: case LOAD_VMX:
if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_VEC)) if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_VEC))
return 0; return 0;
err = do_vec_load(op.reg, ea, size, regs); err = do_vec_load(op.reg, ea, size, regs, cross_endian);
goto ldst_done; goto ldst_done;
#endif #endif
#ifdef CONFIG_VSX #ifdef CONFIG_VSX
...@@ -2791,23 +2855,26 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2791,23 +2855,26 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
msrbit = MSR_VEC; msrbit = MSR_VEC;
if (!(regs->msr & MSR_PR) && !(regs->msr & msrbit)) if (!(regs->msr & MSR_PR) && !(regs->msr & msrbit))
return 0; return 0;
err = do_vsx_load(&op, ea, regs); err = do_vsx_load(&op, ea, regs, cross_endian);
goto ldst_done; goto ldst_done;
} }
#endif #endif
case LOAD_MULTI: case LOAD_MULTI:
if (regs->msr & MSR_LE) if (!address_ok(regs, ea, size))
return 0; return -EFAULT;
rd = op.reg; rd = op.reg;
for (i = 0; i < size; i += 4) { for (i = 0; i < size; i += 4) {
unsigned int v32 = 0;
nb = size - i; nb = size - i;
if (nb > 4) if (nb > 4)
nb = 4; nb = 4;
err = read_mem(&regs->gpr[rd], ea, nb, regs); err = copy_mem_in((u8 *) &v32, ea, nb, regs);
if (err) if (err)
return 0; return 0;
if (nb < 4) /* left-justify last bytes */ if (unlikely(cross_endian))
regs->gpr[rd] <<= 32 - 8 * nb; v32 = byterev_4(v32);
regs->gpr[rd] = v32;
ea += 4; ea += 4;
++rd; ++rd;
} }
...@@ -2816,7 +2883,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2816,7 +2883,7 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
case STORE: case STORE:
#ifdef __powerpc64__ #ifdef __powerpc64__
if (size == 16) { if (size == 16) {
err = emulate_stq(regs, ea, op.reg); err = emulate_stq(regs, ea, op.reg, cross_endian);
goto ldst_done; goto ldst_done;
} }
#endif #endif
...@@ -2827,6 +2894,8 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2827,6 +2894,8 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
err = handle_stack_update(ea, regs); err = handle_stack_update(ea, regs);
goto ldst_done; goto ldst_done;
} }
if (unlikely(cross_endian))
do_byterev(&op.val, size);
err = write_mem(op.val, ea, size, regs); err = write_mem(op.val, ea, size, regs);
goto ldst_done; goto ldst_done;
...@@ -2834,14 +2903,14 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2834,14 +2903,14 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
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); err = do_fp_store(op.reg, ea, size, regs, cross_endian);
goto ldst_done; goto ldst_done;
#endif #endif
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
case STORE_VMX: case STORE_VMX:
if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_VEC)) if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_VEC))
return 0; return 0;
err = do_vec_store(op.reg, ea, size, regs); err = do_vec_store(op.reg, ea, size, regs, cross_endian);
goto ldst_done; goto ldst_done;
#endif #endif
#ifdef CONFIG_VSX #ifdef CONFIG_VSX
...@@ -2856,22 +2925,23 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2856,22 +2925,23 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
msrbit = MSR_VEC; msrbit = MSR_VEC;
if (!(regs->msr & MSR_PR) && !(regs->msr & msrbit)) if (!(regs->msr & MSR_PR) && !(regs->msr & msrbit))
return 0; return 0;
err = do_vsx_store(&op, ea, regs); err = do_vsx_store(&op, ea, regs, cross_endian);
goto ldst_done; goto ldst_done;
} }
#endif #endif
case STORE_MULTI: case STORE_MULTI:
if (regs->msr & MSR_LE) if (!address_ok(regs, ea, size))
return 0; return -EFAULT;
rd = op.reg; rd = op.reg;
for (i = 0; i < size; i += 4) { for (i = 0; i < size; i += 4) {
val = regs->gpr[rd]; unsigned int v32 = regs->gpr[rd];
nb = size - i; nb = size - i;
if (nb > 4) if (nb > 4)
nb = 4; nb = 4;
else if (unlikely(cross_endian))
val >>= 32 - 8 * nb; v32 = byterev_4(v32);
err = write_mem(val, ea, nb, regs); err = copy_mem_out((u8 *) &v32, ea, nb, regs);
if (err) if (err)
return 0; return 0;
ea += 4; ea += 4;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment