> This patch fixes the handling of VSX alignment faults in little-endian > mode (the current code assumes the processor is in big-endian mode). > > The patch also makes the handlers clear the top 8 bytes of the register > when handling an 8 byte VSX load. > > This is based on 2.6.32. > > Signed-off-by: Neil Campbell <ne...@linux.vnet.ibm.com>
Thanks for this Neil! Acked-by: Michael Neuling <mi...@neuling.org> > Cc: <sta...@kernel.org> > --- > diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c > index a5b632e..f0c624f 100644 > --- a/arch/powerpc/kernel/align.c > +++ b/arch/powerpc/kernel/align.c > @@ -642,10 +642,14 @@ static int emulate_spe(struct pt_regs *regs, unsigned i nt reg, > */ > static int emulate_vsx(unsigned char __user *addr, unsigned int reg, > unsigned int areg, struct pt_regs *regs, > - unsigned int flags, unsigned int length) > + unsigned int flags, unsigned int length, > + unsigned int elsize) > { > char *ptr; > + unsigned long *lptr; > int ret = 0; > + int sw = 0; > + int i, j; > > flush_vsx_to_thread(current); > > @@ -654,19 +658,35 @@ static int emulate_vsx(unsigned char __user *addr, unsi gned int reg, > else > ptr = (char *) ¤t->thread.vr[reg - 32]; > > - if (flags & ST) > - ret = __copy_to_user(addr, ptr, length); > - else { > - if (flags & SPLT){ > - ret = __copy_from_user(ptr, addr, length); > - ptr += length; > + lptr = (unsigned long *) ptr; > + > + if (flags & SW) > + sw = elsize-1; > + > + for (j = 0; j < length; j += elsize) { > + for (i = 0; i < elsize; ++i) { > + if (flags & ST) > + ret |= __put_user(ptr[i^sw], addr + i); > + else > + ret |= __get_user(ptr[i^sw], addr + i); > } > - ret |= __copy_from_user(ptr, addr, length); > + ptr += elsize; > + addr += elsize; > } > - if (flags & U) > - regs->gpr[areg] = regs->dar; > - if (ret) > + > + if (!ret) { > + if (flags & U) > + regs->gpr[areg] = regs->dar; > + > + /* Splat load copies the same data to top and bottom 8 bytes */ > + if (flags & SPLT) > + lptr[1] = lptr[0]; > + /* For 8 byte loads, zero the top 8 bytes */ > + else if (!(flags & ST) && (8 == length)) > + lptr[1] = 0; > + } else > return -EFAULT; > + > return 1; > } > #endif > @@ -767,16 +787,25 @@ int fix_alignment(struct pt_regs *regs) > > #ifdef CONFIG_VSX > if ((instruction & 0xfc00003e) == 0x7c000018) { > - /* Additional register addressing bit (64 VSX vs 32 FPR/GPR */ > + unsigned int elsize; > + > + /* Additional register addressing bit (64 VSX vs 32 FPR/GPR) */ > reg |= (instruction & 0x1) << 5; > /* Simple inline decoder instead of a table */ > + /* VSX has only 8 and 16 byte memory accesses */ > + nb = 8; > if (instruction & 0x200) > nb = 16; > - else if (instruction & 0x080) > - nb = 8; > - else > - nb = 4; > + > + /* Vector stores in little-endian mode swap individual > + elements, so process them separately */ > + elsize = 4; > + if (instruction & 0x80) > + elsize = 8; > + > flags = 0; > + if (regs->msr & MSR_LE) > + flags |= SW; > if (instruction & 0x100) > flags |= ST; > if (instruction & 0x040) > @@ -787,7 +816,7 @@ int fix_alignment(struct pt_regs *regs) > nb = 8; > } > PPC_WARN_EMULATED(vsx); > - return emulate_vsx(addr, reg, areg, regs, flags, nb); > + return emulate_vsx(addr, reg, areg, regs, flags, nb, elsize); > } > #endif > /* A size of 0 indicates an instruction we don't support, with > _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev