diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-10-30 01:19:13 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2014-10-31 02:09:04 -0400 |
commit | 325e4114043469e5f9923d902b4d30bcc2be8163 (patch) | |
tree | f70b8aab18045f1e0edecce809ba02cbd5475e6b /arch | |
parent | 808be31426af57af22268ef0fcb42617beb3d15b (diff) |
powerpc/powernv: Properly fix LPC debugfs endianness
Endian is hard, especially when I designed a stupid FW interface, and
I should know better... oh well, this is attempt #2 at fixing this
properly. This time it seems to work with all access sizes and I
can run my flashing tool (which exercises all sort of access sizes
and types to access the SPI controller in the BMC) just fine.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
CC: stable@vger.kernel.org
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-lpc.c | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/opal-lpc.c b/arch/powerpc/platforms/powernv/opal-lpc.c index ad4b31df779a..e4169d68cb32 100644 --- a/arch/powerpc/platforms/powernv/opal-lpc.c +++ b/arch/powerpc/platforms/powernv/opal-lpc.c | |||
@@ -216,14 +216,54 @@ static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf, | |||
216 | &data, len); | 216 | &data, len); |
217 | if (rc) | 217 | if (rc) |
218 | return -ENXIO; | 218 | return -ENXIO; |
219 | |||
220 | /* | ||
221 | * Now there is some trickery with the data returned by OPAL | ||
222 | * as it's the desired data right justified in a 32-bit BE | ||
223 | * word. | ||
224 | * | ||
225 | * This is a very bad interface and I'm to blame for it :-( | ||
226 | * | ||
227 | * So we can't just apply a 32-bit swap to what comes from OPAL, | ||
228 | * because user space expects the *bytes* to be in their proper | ||
229 | * respective positions (ie, LPC position). | ||
230 | * | ||
231 | * So what we really want to do here is to shift data right | ||
232 | * appropriately on a LE kernel. | ||
233 | * | ||
234 | * IE. If the LPC transaction has bytes B0, B1, B2 and B3 in that | ||
235 | * order, we have in memory written to by OPAL at the "data" | ||
236 | * pointer: | ||
237 | * | ||
238 | * Bytes: OPAL "data" LE "data" | ||
239 | * 32-bit: B0 B1 B2 B3 B0B1B2B3 B3B2B1B0 | ||
240 | * 16-bit: B0 B1 0000B0B1 B1B00000 | ||
241 | * 8-bit: B0 000000B0 B0000000 | ||
242 | * | ||
243 | * So a BE kernel will have the leftmost of the above in the MSB | ||
244 | * and rightmost in the LSB and can just then "cast" the u32 "data" | ||
245 | * down to the appropriate quantity and write it. | ||
246 | * | ||
247 | * However, an LE kernel can't. It doesn't need to swap because a | ||
248 | * load from data followed by a store to user are going to preserve | ||
249 | * the byte ordering which is the wire byte order which is what the | ||
250 | * user wants, but in order to "crop" to the right size, we need to | ||
251 | * shift right first. | ||
252 | */ | ||
219 | switch(len) { | 253 | switch(len) { |
220 | case 4: | 254 | case 4: |
221 | rc = __put_user((u32)data, (u32 __user *)ubuf); | 255 | rc = __put_user((u32)data, (u32 __user *)ubuf); |
222 | break; | 256 | break; |
223 | case 2: | 257 | case 2: |
258 | #ifdef __LITTLE_ENDIAN__ | ||
259 | data >>= 16; | ||
260 | #endif | ||
224 | rc = __put_user((u16)data, (u16 __user *)ubuf); | 261 | rc = __put_user((u16)data, (u16 __user *)ubuf); |
225 | break; | 262 | break; |
226 | default: | 263 | default: |
264 | #ifdef __LITTLE_ENDIAN__ | ||
265 | data >>= 24; | ||
266 | #endif | ||
227 | rc = __put_user((u8)data, (u8 __user *)ubuf); | 267 | rc = __put_user((u8)data, (u8 __user *)ubuf); |
228 | break; | 268 | break; |
229 | } | 269 | } |
@@ -263,12 +303,31 @@ static ssize_t lpc_debug_write(struct file *filp, const char __user *ubuf, | |||
263 | else if (todo > 1 && (pos & 1) == 0) | 303 | else if (todo > 1 && (pos & 1) == 0) |
264 | len = 2; | 304 | len = 2; |
265 | } | 305 | } |
306 | |||
307 | /* | ||
308 | * Similarly to the read case, we have some trickery here but | ||
309 | * it's different to handle. We need to pass the value to OPAL in | ||
310 | * a register whose layout depends on the access size. We want | ||
311 | * to reproduce the memory layout of the user, however we aren't | ||
312 | * doing a load from user and a store to another memory location | ||
313 | * which would achieve that. Here we pass the value to OPAL via | ||
314 | * a register which is expected to contain the "BE" interpretation | ||
315 | * of the byte sequence. IE: for a 32-bit access, byte 0 should be | ||
316 | * in the MSB. So here we *do* need to byteswap on LE. | ||
317 | * | ||
318 | * User bytes: LE "data" OPAL "data" | ||
319 | * 32-bit: B0 B1 B2 B3 B3B2B1B0 B0B1B2B3 | ||
320 | * 16-bit: B0 B1 0000B1B0 0000B0B1 | ||
321 | * 8-bit: B0 000000B0 000000B0 | ||
322 | */ | ||
266 | switch(len) { | 323 | switch(len) { |
267 | case 4: | 324 | case 4: |
268 | rc = __get_user(data, (u32 __user *)ubuf); | 325 | rc = __get_user(data, (u32 __user *)ubuf); |
326 | data = cpu_to_be32(data); | ||
269 | break; | 327 | break; |
270 | case 2: | 328 | case 2: |
271 | rc = __get_user(data, (u16 __user *)ubuf); | 329 | rc = __get_user(data, (u16 __user *)ubuf); |
330 | data = cpu_to_be16(data); | ||
272 | break; | 331 | break; |
273 | default: | 332 | default: |
274 | rc = __get_user(data, (u8 __user *)ubuf); | 333 | rc = __get_user(data, (u8 __user *)ubuf); |