]>
Commit | Line | Data |
---|---|---|
80e6ae8f GKH |
1 | From bb7f20b1c639606def3b91f4e4aca6daeee5d80a Mon Sep 17 00:00:00 2001 |
2 | From: Neil Campbell <neilc@linux.vnet.ibm.com> | |
3 | Date: Mon, 14 Dec 2009 04:08:57 +0000 | |
4 | Subject: powerpc: Handle VSX alignment faults correctly in little-endian mode | |
5 | ||
6 | From: Neil Campbell <neilc@linux.vnet.ibm.com> | |
7 | ||
8 | commit bb7f20b1c639606def3b91f4e4aca6daeee5d80a upstream. | |
9 | ||
10 | This patch fixes the handling of VSX alignment faults in little-endian | |
11 | mode (the current code assumes the processor is in big-endian mode). | |
12 | ||
13 | The patch also makes the handlers clear the top 8 bytes of the register | |
14 | when handling an 8 byte VSX load. | |
15 | ||
16 | This is based on 2.6.32. | |
17 | ||
18 | Signed-off-by: Neil Campbell <neilc@linux.vnet.ibm.com> | |
19 | Acked-by: Michael Neuling <mikey@neuling.org> | |
20 | Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> | |
21 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
22 | ||
23 | --- | |
24 | arch/powerpc/kernel/align.c | 63 ++++++++++++++++++++++++++++++++------------ | |
25 | 1 file changed, 46 insertions(+), 17 deletions(-) | |
26 | ||
27 | --- a/arch/powerpc/kernel/align.c | |
28 | +++ b/arch/powerpc/kernel/align.c | |
29 | @@ -642,10 +642,14 @@ static int emulate_spe(struct pt_regs *r | |
30 | */ | |
31 | static int emulate_vsx(unsigned char __user *addr, unsigned int reg, | |
32 | unsigned int areg, struct pt_regs *regs, | |
33 | - unsigned int flags, unsigned int length) | |
34 | + unsigned int flags, unsigned int length, | |
35 | + unsigned int elsize) | |
36 | { | |
37 | char *ptr; | |
38 | + unsigned long *lptr; | |
39 | int ret = 0; | |
40 | + int sw = 0; | |
41 | + int i, j; | |
42 | ||
43 | flush_vsx_to_thread(current); | |
44 | ||
45 | @@ -654,19 +658,35 @@ static int emulate_vsx(unsigned char __u | |
46 | else | |
47 | ptr = (char *) ¤t->thread.vr[reg - 32]; | |
48 | ||
49 | - if (flags & ST) | |
50 | - ret = __copy_to_user(addr, ptr, length); | |
51 | - else { | |
52 | - if (flags & SPLT){ | |
53 | - ret = __copy_from_user(ptr, addr, length); | |
54 | - ptr += length; | |
55 | + lptr = (unsigned long *) ptr; | |
56 | + | |
57 | + if (flags & SW) | |
58 | + sw = elsize-1; | |
59 | + | |
60 | + for (j = 0; j < length; j += elsize) { | |
61 | + for (i = 0; i < elsize; ++i) { | |
62 | + if (flags & ST) | |
63 | + ret |= __put_user(ptr[i^sw], addr + i); | |
64 | + else | |
65 | + ret |= __get_user(ptr[i^sw], addr + i); | |
66 | } | |
67 | - ret |= __copy_from_user(ptr, addr, length); | |
68 | + ptr += elsize; | |
69 | + addr += elsize; | |
70 | } | |
71 | - if (flags & U) | |
72 | - regs->gpr[areg] = regs->dar; | |
73 | - if (ret) | |
74 | + | |
75 | + if (!ret) { | |
76 | + if (flags & U) | |
77 | + regs->gpr[areg] = regs->dar; | |
78 | + | |
79 | + /* Splat load copies the same data to top and bottom 8 bytes */ | |
80 | + if (flags & SPLT) | |
81 | + lptr[1] = lptr[0]; | |
82 | + /* For 8 byte loads, zero the top 8 bytes */ | |
83 | + else if (!(flags & ST) && (8 == length)) | |
84 | + lptr[1] = 0; | |
85 | + } else | |
86 | return -EFAULT; | |
87 | + | |
88 | return 1; | |
89 | } | |
90 | #endif | |
91 | @@ -767,16 +787,25 @@ int fix_alignment(struct pt_regs *regs) | |
92 | ||
93 | #ifdef CONFIG_VSX | |
94 | if ((instruction & 0xfc00003e) == 0x7c000018) { | |
95 | - /* Additional register addressing bit (64 VSX vs 32 FPR/GPR */ | |
96 | + unsigned int elsize; | |
97 | + | |
98 | + /* Additional register addressing bit (64 VSX vs 32 FPR/GPR) */ | |
99 | reg |= (instruction & 0x1) << 5; | |
100 | /* Simple inline decoder instead of a table */ | |
101 | + /* VSX has only 8 and 16 byte memory accesses */ | |
102 | + nb = 8; | |
103 | if (instruction & 0x200) | |
104 | nb = 16; | |
105 | - else if (instruction & 0x080) | |
106 | - nb = 8; | |
107 | - else | |
108 | - nb = 4; | |
109 | + | |
110 | + /* Vector stores in little-endian mode swap individual | |
111 | + elements, so process them separately */ | |
112 | + elsize = 4; | |
113 | + if (instruction & 0x80) | |
114 | + elsize = 8; | |
115 | + | |
116 | flags = 0; | |
117 | + if (regs->msr & MSR_LE) | |
118 | + flags |= SW; | |
119 | if (instruction & 0x100) | |
120 | flags |= ST; | |
121 | if (instruction & 0x040) | |
122 | @@ -787,7 +816,7 @@ int fix_alignment(struct pt_regs *regs) | |
123 | nb = 8; | |
124 | } | |
125 | PPC_WARN_EMULATED(vsx); | |
126 | - return emulate_vsx(addr, reg, areg, regs, flags, nb); | |
127 | + return emulate_vsx(addr, reg, areg, regs, flags, nb, elsize); | |
128 | } | |
129 | #endif | |
130 | /* A size of 0 indicates an instruction we don't support, with |