powerpc: Implement load_unaligned_zeropad
authorAnton Blanchard <anton@samba.org>
Thu, 18 Sep 2014 23:40:19 +0000 (09:40 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 30 Sep 2014 04:59:12 +0000 (14:59 +1000)
Implement a bi-arch and bi-endian version of load_unaligned_zeropad.

Since the fallback case is so rare, a userspace test harness was used
to test this on ppc64le, ppc64 and ppc32:

http://ozlabs.org/~anton/junkcode/test_load_unaligned_zeropad.c

It uses mprotect to force a SEGV across a page boundary, and a SEGV
handler to lookup the exception tables and run the fixup routine.
It also compares the result against a normal load.

Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/word-at-a-time.h

index 9a5c928bb3c64ea8cb021da323c46b1d59dc6446..07cc121e8a7978a8bdd3c84cfe2aab572273dc4d 100644 (file)
@@ -116,4 +116,44 @@ static inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits,
 
 #endif
 
+static inline unsigned long load_unaligned_zeropad(const void *addr)
+{
+       unsigned long ret, offset, tmp;
+
+       asm(
+       "1:     " PPC_LL "%[ret], 0(%[addr])\n"
+       "2:\n"
+       ".section .fixup,\"ax\"\n"
+       "3:     "
+#ifdef __powerpc64__
+       "clrrdi         %[tmp], %[addr], 3\n\t"
+       "clrlsldi       %[offset], %[addr], 61, 3\n\t"
+       "ld             %[ret], 0(%[tmp])\n\t"
+#ifdef __BIG_ENDIAN__
+       "sld            %[ret], %[ret], %[offset]\n\t"
+#else
+       "srd            %[ret], %[ret], %[offset]\n\t"
+#endif
+#else
+       "clrrwi         %[tmp], %[addr], 2\n\t"
+       "clrlslwi       %[offset], %[addr], 30, 3\n\t"
+       "lwz            %[ret], 0(%[tmp])\n\t"
+#ifdef __BIG_ENDIAN__
+       "slw            %[ret], %[ret], %[offset]\n\t"
+#else
+       "srw            %[ret], %[ret], %[offset]\n\t"
+#endif
+#endif
+       "b      2b\n"
+       ".previous\n"
+       ".section __ex_table,\"a\"\n\t"
+               PPC_LONG_ALIGN "\n\t"
+               PPC_LONG "1b,3b\n"
+       ".previous"
+       : [tmp] "=&b" (tmp), [offset] "=&r" (offset), [ret] "=&r" (ret)
+       : [addr] "b" (addr), "m" (*(unsigned long *)addr));
+
+       return ret;
+}
+
 #endif /* _ASM_WORD_AT_A_TIME_H */