[RFC PATCH 1/2] ARM: use generic strnlen_user and strncpy_from_user functions
Nicolas Pitre
nico at fluxnic.net
Mon Jun 11 22:53:56 EDT 2012
On Fri, 8 Jun 2012, Will Deacon wrote:
> This patch implements the word-at-a-time interface for ARM using the
> same algorithm as x86. Although we have a clz instruction from ARMv5,
> this only saves us one mov instruction when building with Thumb-2 and
> makes no difference when targetting ARM, so we use the magic 0x0ff0001
> constant for all CPUs. For big-endian configurations, we use 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.
>
> Signed-off-by: Will Deacon <will.deacon at arm.com>
With the updated patch comment...
Reviewed-by: Nicolas Pitre <nico at linaro.org>
> ---
> arch/arm/Kconfig | 2 +
> arch/arm/include/asm/uaccess.h | 27 +++------------
> arch/arm/include/asm/word-at-a-time.h | 55 +++++++++++++++++++++++++++++++++
> arch/arm/kernel/armksyms.c | 4 --
> arch/arm/lib/Makefile | 1 -
> arch/arm/lib/strncpy_from_user.S | 43 -------------------------
> arch/arm/lib/strnlen_user.S | 40 ------------------------
> 7 files changed, 63 insertions(+), 109 deletions(-)
> create mode 100644 arch/arm/include/asm/word-at-a-time.h
> delete mode 100644 arch/arm/lib/strncpy_from_user.S
> delete mode 100644 arch/arm/lib/strnlen_user.S
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index b649c59..b236fd9 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -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
> diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
> index 71f6536..479a635 100644
> --- a/arch/arm/include/asm/uaccess.h
> +++ b/arch/arm/include/asm/uaccess.h
> @@ -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
> index 0000000..74b2d45
> --- /dev/null
> +++ b/arch/arm/include/asm/word-at-a-time.h
> @@ -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 */
> diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
> index b57c75e..c3dff6a 100644
> --- a/arch/arm/kernel/armksyms.c
> +++ b/arch/arm/kernel/armksyms.c
> @@ -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);
>
> diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
> index 992769a..d5060da 100644
> --- a/arch/arm/lib/Makefile
> +++ b/arch/arm/lib/Makefile
> @@ -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
> index f202d7b..0000000
> --- a/arch/arm/lib/strncpy_from_user.S
> +++ /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
> index 0ecbb45..0000000
> --- a/arch/arm/lib/strnlen_user.S
> +++ /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
> --
> 1.7.4.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
More information about the linux-arm-kernel
mailing list