powerpc/64: Copy as much as possible in __copy_tofrom_user
authorPaul Mackerras <paulus@ozlabs.org>
Fri, 3 Aug 2018 10:13:06 +0000 (20:13 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 7 Aug 2018 14:32:36 +0000 (00:32 +1000)
In __copy_tofrom_user, if we encounter an exception on a store, we
stop copying and return the number of bytes not copied.  However,
if the store is wider than one byte and is to an unaligned address,
it is possible that the store operand overlaps a page boundary
and the exception occurred on the latter part of the store operand,
meaning that it would be possible to copy a few more bytes.  Since
copy_to_user is generally expected to copy as much as possible,
it would be better to copy those extra few bytes.  This adds code
to do that.  Since this edge case is not performance-critical,
the code has been written to be compact rather than as fast as
possible.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/lib/copyuser_64.S

index 2197a35097c5f721d8ca5e573f799c5aa0461a82..96c514bee66b6a58d294942a77ca9b6115e2e9c6 100644 (file)
@@ -379,8 +379,8 @@ stex;       stb     r0,0(r3)
        blr
 
 /*
- * exception handlers for stores: we just need to work
- * out how many bytes weren't copied
+ * exception handlers for stores: we need to work out how many bytes
+ * weren't copied, and we may need to copy some more.
  * Note that the number of bytes of instructions for adjusting r3 needs
  * to equal the amount of the adjustment, due to the trick of using
  * .Lst_exc - r3_offset as the handler address.
@@ -400,10 +400,27 @@ stex;     stb     r0,0(r3)
        /* adjust by 4 */
        addi    r3,r3,4
 .Lst_exc:
-       ld      r6,-24(r1)
-       ld      r5,-8(r1)
-       add     r6,r6,r5
-       subf    r3,r3,r6        /* #bytes not copied in r3 */
+       ld      r6,-24(r1)      /* original destination pointer */
+       ld      r4,-16(r1)      /* original source pointer */
+       ld      r5,-8(r1)       /* original number of bytes */
+       add     r7,r6,r5
+       /*
+        * If the destination pointer isn't 8-byte aligned,
+        * we may have got the exception as a result of a
+        * store that overlapped a page boundary, so we may be
+        * able to copy a few more bytes.
+        */
+17:    andi.   r0,r3,7
+       beq     19f
+       subf    r8,r6,r3        /* #bytes copied */
+100:   EX_TABLE(100b,19f)
+       lbzx    r0,r8,r4
+100:   EX_TABLE(100b,19f)
+       stb     r0,0(r3)
+       addi    r3,r3,1
+       cmpld   r3,r7
+       blt     17b
+19:    subf    r3,r3,r7        /* #bytes not copied in r3 */
        blr
 
 /*