mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ARM: 7449/1: use generic strnlen_user and strncpy_from_user functions
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>
This commit is contained in:
		
							parent
							
								
									4295b898f5
								
							
						
					
					
						commit
						8c56cc8be5
					
				
					 7 changed files with 63 additions and 109 deletions
				
			
		| 
						 | 
					@ -46,6 +46,8 @@ config ARM
 | 
				
			||||||
	select GENERIC_SMP_IDLE_THREAD
 | 
						select GENERIC_SMP_IDLE_THREAD
 | 
				
			||||||
	select KTIME_SCALAR
 | 
						select KTIME_SCALAR
 | 
				
			||||||
	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
 | 
						select GENERIC_CLOCKEVENTS_BROADCAST if SMP
 | 
				
			||||||
 | 
						select GENERIC_STRNCPY_FROM_USER
 | 
				
			||||||
 | 
						select GENERIC_STRNLEN_USER
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  The ARM series is a line of low-power-consumption RISC chip designs
 | 
						  The ARM series is a line of low-power-consumption RISC chip designs
 | 
				
			||||||
	  licensed by ARM Ltd and targeted at embedded applications and
 | 
						  licensed by ARM Ltd and targeted at embedded applications and
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 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
 | 
					 * The "__xxx" versions of the user access functions do not verify the
 | 
				
			||||||
 * address space - it must have been done previously with a separate
 | 
					 * 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)
 | 
					#define __clear_user(addr,n)		(memset((void __force *)addr, 0, n), 0)
 | 
				
			||||||
#endif
 | 
					#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)
 | 
					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))
 | 
						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;
 | 
						return n;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline long __must_check strncpy_from_user(char *dst, const char __user *src, long count)
 | 
					extern long strncpy_from_user(char *dest, 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 __must_check long strlen_user(const char __user *str);
 | 
				
			||||||
 | 
					extern __must_check long strnlen_user(const char __user *str, long n);
 | 
				
			||||||
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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _ASMARM_UACCESS_H */
 | 
					#endif /* _ASMARM_UACCESS_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										55
									
								
								arch/arm/include/asm/word-at-a-time.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								arch/arm/include/asm/word-at-a-time.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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 */
 | 
				
			||||||
| 
						 | 
					@ -87,10 +87,6 @@ EXPORT_SYMBOL(memmove);
 | 
				
			||||||
EXPORT_SYMBOL(memchr);
 | 
					EXPORT_SYMBOL(memchr);
 | 
				
			||||||
EXPORT_SYMBOL(__memzero);
 | 
					EXPORT_SYMBOL(__memzero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* user mem (segment) */
 | 
					 | 
				
			||||||
EXPORT_SYMBOL(__strnlen_user);
 | 
					 | 
				
			||||||
EXPORT_SYMBOL(__strncpy_from_user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef CONFIG_MMU
 | 
					#ifdef CONFIG_MMU
 | 
				
			||||||
EXPORT_SYMBOL(copy_page);
 | 
					EXPORT_SYMBOL(copy_page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@ lib-y		:= backtrace.o changebit.o csumipv6.o csumpartial.o   \
 | 
				
			||||||
		   csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
 | 
							   csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
 | 
				
			||||||
		   delay.o findbit.o memchr.o memcpy.o		      \
 | 
							   delay.o findbit.o memchr.o memcpy.o		      \
 | 
				
			||||||
		   memmove.o memset.o memzero.o setbit.o              \
 | 
							   memmove.o memset.o memzero.o setbit.o              \
 | 
				
			||||||
		   strncpy_from_user.o strnlen_user.o                 \
 | 
					 | 
				
			||||||
		   strchr.o strrchr.o                                 \
 | 
							   strchr.o strrchr.o                                 \
 | 
				
			||||||
		   testchangebit.o testclearbit.o testsetbit.o        \
 | 
							   testchangebit.o testclearbit.o testsetbit.o        \
 | 
				
			||||||
		   ashldi3.o ashrdi3.o lshrdi3.o muldi3.o             \
 | 
							   ashldi3.o ashrdi3.o lshrdi3.o muldi3.o             \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in a new issue