Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 12a0ef7b authored by Will Deacon's avatar Will Deacon Committed by Catalin Marinas
Browse files

arm64: use generic strnlen_user and strncpy_from_user functions



This patch implements the word-at-a-time interface for arm64 using the
same algorithm as ARM. We use the fls64 macro, which expands to a clz
instruction via a compiler builtin. 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.

Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 71586276
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@ config ARM64
	select GENERIC_IRQ_SHOW
	select GENERIC_SCHED_CLOCK
	select GENERIC_SMP_IDLE_THREAD
	select GENERIC_STRNCPY_FROM_USER
	select GENERIC_STRNLEN_USER
	select GENERIC_TIME_VSYSCALL
	select HARDIRQS_SW_RESEND
	select HAVE_ARCH_TRACEHOOK
+4 −21
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ static inline void set_fs(mm_segment_t fs)
})

#define access_ok(type, addr, size)	__range_ok(addr, size)
#define user_addr_max			get_fs

/*
 * The "__xxx" versions of the user access functions do not verify the address
@@ -240,9 +241,6 @@ extern unsigned long __must_check __copy_to_user(void __user *to, const void *fr
extern unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n);
extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);

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))
@@ -276,24 +274,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 /* __ASM_UACCESS_H */
+54 −0
Original line number Diff line number Diff line
/*
 * Based on arch/arm/lib/strncpy_from_user.S
 *
 * Copyright (C) 1995-2000 Russell King
 * Copyright (C) 2012 ARM Ltd.
 * Copyright (C) 2013 ARM Ltd.
 *
 * 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
@@ -16,35 +13,42 @@
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __ASM_WORD_AT_A_TIME_H
#define __ASM_WORD_AT_A_TIME_H

#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/errno.h>
#ifndef __AARCH64EB__

	.text
	.align	5
#include <linux/kernel.h>

/*
 * Copy a string from user space to kernel space.
 *  x0 = dst, x1 = src, x2 = 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	x4, x1
1:	subs	x2, x2, #1
	bmi	2f
USER(9f, ldrb	w3, [x1], #1	)
	strb	w3, [x0], #1
	cbnz	w3, 1b
	sub	x1, x1, #1	// take NUL character out of count
2:	sub	x0, x1, x4
	ret
ENDPROC(__strncpy_from_user)

	.section .fixup,"ax"
	.align	0
9:	strb	wzr, [x0]	// null terminate
	mov	x0, #-EFAULT
	ret
	.previous
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)
{
	return fls64(mask) >> 3;
}

#else	/* __AARCH64EB__ */
#include <asm-generic/word-at-a-time.h>
#endif

#endif /* __ASM_WORD_AT_A_TIME_H */
+1 −4
Original line number Diff line number Diff line
@@ -29,13 +29,10 @@

#include <asm/checksum.h>

	/* user mem (segment) */
EXPORT_SYMBOL(__strnlen_user);
EXPORT_SYMBOL(__strncpy_from_user);

EXPORT_SYMBOL(copy_page);
EXPORT_SYMBOL(clear_page);

	/* user mem (segment) */
EXPORT_SYMBOL(__copy_from_user);
EXPORT_SYMBOL(__copy_to_user);
EXPORT_SYMBOL(__clear_user);
+3 −5
Original line number Diff line number Diff line
lib-y		:= bitops.o delay.o					\
		   strncpy_from_user.o strnlen_user.o clear_user.o	\
		   copy_from_user.o copy_to_user.o copy_in_user.o	\
		   copy_page.o clear_page.o				\
		   memchr.o memcpy.o memmove.o memset.o			\
lib-y		:= bitops.o clear_user.o delay.o copy_from_user.o	\
		   copy_to_user.o copy_in_user.o copy_page.o		\
		   clear_page.o memchr.o memcpy.o memmove.o memset.o	\
		   strchr.o strrchr.o
Loading