diff options
| -rw-r--r-- | arch/powerpc/kernel/align.c | 63 |
1 files changed, 46 insertions, 17 deletions
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 3839839f83c7..b876e989220b 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 int reg, | |||
| 642 | */ | 642 | */ |
| 643 | static int emulate_vsx(unsigned char __user *addr, unsigned int reg, | 643 | static int emulate_vsx(unsigned char __user *addr, unsigned int reg, |
| 644 | unsigned int areg, struct pt_regs *regs, | 644 | unsigned int areg, struct pt_regs *regs, |
| 645 | unsigned int flags, unsigned int length) | 645 | unsigned int flags, unsigned int length, |
| 646 | unsigned int elsize) | ||
| 646 | { | 647 | { |
| 647 | char *ptr; | 648 | char *ptr; |
| 649 | unsigned long *lptr; | ||
| 648 | int ret = 0; | 650 | int ret = 0; |
| 651 | int sw = 0; | ||
| 652 | int i, j; | ||
| 649 | 653 | ||
| 650 | flush_vsx_to_thread(current); | 654 | flush_vsx_to_thread(current); |
| 651 | 655 | ||
| @@ -654,19 +658,35 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg, | |||
| 654 | else | 658 | else |
| 655 | ptr = (char *) ¤t->thread.vr[reg - 32]; | 659 | ptr = (char *) ¤t->thread.vr[reg - 32]; |
| 656 | 660 | ||
| 657 | if (flags & ST) | 661 | lptr = (unsigned long *) ptr; |
| 658 | ret = __copy_to_user(addr, ptr, length); | 662 | |
| 659 | else { | 663 | if (flags & SW) |
| 660 | if (flags & SPLT){ | 664 | sw = elsize-1; |
| 661 | ret = __copy_from_user(ptr, addr, length); | 665 | |
| 662 | ptr += length; | 666 | for (j = 0; j < length; j += elsize) { |
| 667 | for (i = 0; i < elsize; ++i) { | ||
| 668 | if (flags & ST) | ||
| 669 | ret |= __put_user(ptr[i^sw], addr + i); | ||
| 670 | else | ||
| 671 | ret |= __get_user(ptr[i^sw], addr + i); | ||
| 663 | } | 672 | } |
| 664 | ret |= __copy_from_user(ptr, addr, length); | 673 | ptr += elsize; |
| 674 | addr += elsize; | ||
| 665 | } | 675 | } |
| 666 | if (flags & U) | 676 | |
| 667 | regs->gpr[areg] = regs->dar; | 677 | if (!ret) { |
| 668 | if (ret) | 678 | if (flags & U) |
| 679 | regs->gpr[areg] = regs->dar; | ||
| 680 | |||
| 681 | /* Splat load copies the same data to top and bottom 8 bytes */ | ||
| 682 | if (flags & SPLT) | ||
| 683 | lptr[1] = lptr[0]; | ||
| 684 | /* For 8 byte loads, zero the top 8 bytes */ | ||
| 685 | else if (!(flags & ST) && (8 == length)) | ||
| 686 | lptr[1] = 0; | ||
| 687 | } else | ||
| 669 | return -EFAULT; | 688 | return -EFAULT; |
| 689 | |||
| 670 | return 1; | 690 | return 1; |
| 671 | } | 691 | } |
| 672 | #endif | 692 | #endif |
| @@ -767,16 +787,25 @@ int fix_alignment(struct pt_regs *regs) | |||
| 767 | 787 | ||
| 768 | #ifdef CONFIG_VSX | 788 | #ifdef CONFIG_VSX |
| 769 | if ((instruction & 0xfc00003e) == 0x7c000018) { | 789 | if ((instruction & 0xfc00003e) == 0x7c000018) { |
| 770 | /* Additional register addressing bit (64 VSX vs 32 FPR/GPR */ | 790 | unsigned int elsize; |
| 791 | |||
| 792 | /* Additional register addressing bit (64 VSX vs 32 FPR/GPR) */ | ||
| 771 | reg |= (instruction & 0x1) << 5; | 793 | reg |= (instruction & 0x1) << 5; |
| 772 | /* Simple inline decoder instead of a table */ | 794 | /* Simple inline decoder instead of a table */ |
| 795 | /* VSX has only 8 and 16 byte memory accesses */ | ||
| 796 | nb = 8; | ||
| 773 | if (instruction & 0x200) | 797 | if (instruction & 0x200) |
| 774 | nb = 16; | 798 | nb = 16; |
| 775 | else if (instruction & 0x080) | 799 | |
| 776 | nb = 8; | 800 | /* Vector stores in little-endian mode swap individual |
| 777 | else | 801 | elements, so process them separately */ |
| 778 | nb = 4; | 802 | elsize = 4; |
| 803 | if (instruction & 0x80) | ||
| 804 | elsize = 8; | ||
| 805 | |||
| 779 | flags = 0; | 806 | flags = 0; |
| 807 | if (regs->msr & MSR_LE) | ||
| 808 | flags |= SW; | ||
| 780 | if (instruction & 0x100) | 809 | if (instruction & 0x100) |
| 781 | flags |= ST; | 810 | flags |= ST; |
| 782 | if (instruction & 0x040) | 811 | if (instruction & 0x040) |
| @@ -787,7 +816,7 @@ int fix_alignment(struct pt_regs *regs) | |||
| 787 | nb = 8; | 816 | nb = 8; |
| 788 | } | 817 | } |
| 789 | PPC_WARN_ALIGNMENT(vsx, regs); | 818 | PPC_WARN_ALIGNMENT(vsx, regs); |
| 790 | return emulate_vsx(addr, reg, areg, regs, flags, nb); | 819 | return emulate_vsx(addr, reg, areg, regs, flags, nb, elsize); |
| 791 | } | 820 | } |
| 792 | #endif | 821 | #endif |
| 793 | /* A size of 0 indicates an instruction we don't support, with | 822 | /* A size of 0 indicates an instruction we don't support, with |
