ARM: 7449/1: use generic strnlen_user and strncpy_from_user functions
authorWill Deacon <will.deacon@arm.com>
Fri, 6 Jul 2012 14:45:39 +0000 (15:45 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 9 Jul 2012 16:41:11 +0000 (17:41 +0100)
This patch implements the word-at-a-time interface for ARM using the
same algorithm as x86. We use the fls macro from ARMv5 onwards, where
we have a clz instruction available which saves us a mov instruction
when targetting Thumb-2. For older CPUs, we use the magic 0x0ff0001
constant. Big-endian configurations make use of the implementation from
asm-generic.

With this implemented, we can replace our byte-at-a-time strnlen_user
and strncpy_from_user functions with the optimised generic versions.

Reviewed-by: Nicolas Pitre <nico@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/Kconfig
arch/arm/include/asm/uaccess.h
arch/arm/include/asm/word-at-a-time.h [new file with mode: 0644]
arch/arm/kernel/armksyms.c
arch/arm/lib/Makefile
arch/arm/lib/strncpy_from_user.S [deleted file]
arch/arm/lib/strnlen_user.S [deleted file]

index 8c9d264f2108814aad76bc255bd1b00af9f06e12..574561a66d752e232ad14a516d6f857bbda73cde 100644 (file)
@@ -46,6 +46,8 @@ config ARM
        select GENERIC_SMP_IDLE_THREAD
        select KTIME_SCALAR
        select GENERIC_CLOCKEVENTS_BROADCAST if SMP
+       select GENERIC_STRNCPY_FROM_USER
+       select GENERIC_STRNLEN_USER
        help
          The ARM series is a line of low-power-consumption RISC chip designs
          licensed by ARM Ltd and targeted at embedded applications and
index 71f6536d17ac01d53f638c47edbc4a5589f95838..479a6352e0b5075911e91a4e0b60b7e9443fd4fd 100644 (file)
@@ -189,6 +189,9 @@ static inline void set_fs(mm_segment_t fs)
 
 #define access_ok(type,addr,size)      (__range_ok(addr,size) == 0)
 
+#define user_addr_max() \
+       (segment_eq(get_fs(), USER_DS) ? TASK_SIZE : ~0UL)
+
 /*
  * The "__xxx" versions of the user access functions do not verify the
  * address space - it must have been done previously with a separate
@@ -398,9 +401,6 @@ extern unsigned long __must_check __clear_user_std(void __user *addr, unsigned l
 #define __clear_user(addr,n)           (memset((void __force *)addr, 0, n), 0)
 #endif
 
-extern unsigned long __must_check __strncpy_from_user(char *to, const char __user *from, unsigned long count);
-extern unsigned long __must_check __strnlen_user(const char __user *s, long n);
-
 static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
 {
        if (access_ok(VERIFY_READ, from, n))
@@ -427,24 +427,9 @@ static inline unsigned long __must_check clear_user(void __user *to, unsigned lo
        return n;
 }
 
-static inline long __must_check strncpy_from_user(char *dst, const char __user *src, long count)
-{
-       long res = -EFAULT;
-       if (access_ok(VERIFY_READ, src, 1))
-               res = __strncpy_from_user(dst, src, count);
-       return res;
-}
-
-#define strlen_user(s) strnlen_user(s, ~0UL >> 1)
+extern long strncpy_from_user(char *dest, const char __user *src, long count);
 
-static inline long __must_check strnlen_user(const char __user *s, long n)
-{
-       unsigned long res = 0;
-
-       if (__addr_ok(s))
-               res = __strnlen_user(s, n);
-
-       return res;
-}
+extern __must_check long strlen_user(const char __user *str);
+extern __must_check long strnlen_user(const char __user *str, long n);
 
 #endif /* _ASMARM_UACCESS_H */
diff --git a/arch/arm/include/asm/word-at-a-time.h b/arch/arm/include/asm/word-at-a-time.h
new file mode 100644 (file)
index 0000000..74b2d45
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __ASM_ARM_WORD_AT_A_TIME_H
+#define __ASM_ARM_WORD_AT_A_TIME_H
+
+#ifndef __ARMEB__
+
+/*
+ * Little-endian word-at-a-time zero byte handling.
+ * Heavily based on the x86 algorithm.
+ */
+#include <linux/kernel.h>
+
+struct word_at_a_time {
+       const unsigned long one_bits, high_bits;
+};
+
+#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
+
+static inline unsigned long has_zero(unsigned long a, unsigned long *bits,
+                                    const struct word_at_a_time *c)
+{
+       unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits;
+       *bits = mask;
+       return mask;
+}
+
+#define prep_zero_mask(a, bits, c) (bits)
+
+static inline unsigned long create_zero_mask(unsigned long bits)
+{
+       bits = (bits - 1) & ~bits;
+       return bits >> 7;
+}
+
+static inline unsigned long find_zero(unsigned long mask)
+{
+       unsigned long ret;
+
+#if __LINUX_ARM_ARCH__ >= 5
+       /* We have clz available. */
+       ret = fls(mask) >> 3;
+#else
+       /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */
+       ret = (0x0ff0001 + mask) >> 23;
+       /* Fix the 1 for 00 case */
+       ret &= mask;
+#endif
+
+       return ret;
+}
+
+#else  /* __ARMEB__ */
+#include <asm-generic/word-at-a-time.h>
+#endif
+
+#endif /* __ASM_ARM_WORD_AT_A_TIME_H */
index b57c75e0b01f975defe734de3cce7c6ecfb34dda..c3dff6abc89dd046b607428ddadf4e582e9e8c00 100644 (file)
@@ -87,10 +87,6 @@ EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(memchr);
 EXPORT_SYMBOL(__memzero);
 
-       /* user mem (segment) */
-EXPORT_SYMBOL(__strnlen_user);
-EXPORT_SYMBOL(__strncpy_from_user);
-
 #ifdef CONFIG_MMU
 EXPORT_SYMBOL(copy_page);
 
index 992769ae2599f1f5c050847311fabd789d308c52..d5060dab6e52af6651f57ef80fbab88dbfba0c7f 100644 (file)
@@ -8,7 +8,6 @@ lib-y           := backtrace.o changebit.o csumipv6.o csumpartial.o   \
                   csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
                   delay.o findbit.o memchr.o memcpy.o                \
                   memmove.o memset.o memzero.o setbit.o              \
-                  strncpy_from_user.o strnlen_user.o                 \
                   strchr.o strrchr.o                                 \
                   testchangebit.o testclearbit.o testsetbit.o        \
                   ashldi3.o ashrdi3.o lshrdi3.o muldi3.o             \
diff --git a/arch/arm/lib/strncpy_from_user.S b/arch/arm/lib/strncpy_from_user.S
deleted file mode 100644 (file)
index f202d7b..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *  linux/arch/arm/lib/strncpy_from_user.S
- *
- *  Copyright (C) 1995-2000 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-#include <asm/errno.h>
-
-       .text
-       .align  5
-
-/*
- * Copy a string from user space to kernel space.
- *  r0 = dst, r1 = src, r2 = byte length
- * returns the number of characters copied (strlen of copied string),
- *  -EFAULT on exception, or "len" if we fill the whole buffer
- */
-ENTRY(__strncpy_from_user)
-       mov     ip, r1
-1:     subs    r2, r2, #1
-       ldrusr  r3, r1, 1, pl
-       bmi     2f
-       strb    r3, [r0], #1
-       teq     r3, #0
-       bne     1b
-       sub     r1, r1, #1      @ take NUL character out of count
-2:     sub     r0, r1, ip
-       mov     pc, lr
-ENDPROC(__strncpy_from_user)
-
-       .pushsection .fixup,"ax"
-       .align  0
-9001:  mov     r3, #0
-       strb    r3, [r0, #0]    @ null terminate
-       mov     r0, #-EFAULT
-       mov     pc, lr
-       .popsection
-
diff --git a/arch/arm/lib/strnlen_user.S b/arch/arm/lib/strnlen_user.S
deleted file mode 100644 (file)
index 0ecbb45..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  linux/arch/arm/lib/strnlen_user.S
- *
- *  Copyright (C) 1995-2000 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-#include <asm/errno.h>
-
-       .text
-       .align  5
-
-/* Prototype: unsigned long __strnlen_user(const char *str, long n)
- * Purpose  : get length of a string in user memory
- * Params   : str - address of string in user memory
- * Returns  : length of string *including terminator*
- *           or zero on exception, or n + 1 if too long
- */
-ENTRY(__strnlen_user)
-       mov     r2, r0
-1:
-       ldrusr  r3, r0, 1
-       teq     r3, #0
-       beq     2f
-       subs    r1, r1, #1
-       bne     1b
-       add     r0, r0, #1
-2:     sub     r0, r0, r2
-       mov     pc, lr
-ENDPROC(__strnlen_user)
-
-       .pushsection .fixup,"ax"
-       .align  0
-9001:  mov     r0, #0
-       mov     pc, lr
-       .popsection