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

Commit 5bf6c07a authored by Chris Metcalf's avatar Chris Metcalf
Browse files

tile: add <asm/word-at-a-time.h> and enable support functions



This change enables the generic strncpy_from_user() and strnlen_user()
using word-at-a-time.h.  The tile implementation is trivial since
both tilepro and tilegx have SIMD operations that do byte-wise
comparisons against immediate zero for each byte, and return an
0x01 byte in each position where there is a 0x00 byte.

Signed-off-by: default avatarChris Metcalf <cmetcalf@ezchip.com>
parent 627ae548
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ config TILE
	select HAVE_DEBUG_STACKOVERFLOW
	select ARCH_WANT_FRAME_POINTERS
	select HAVE_CONTEXT_TRACKING
	select GENERIC_STRNCPY_FROM_USER
	select GENERIC_STRNLEN_USER

# FIXME: investigate whether we need/want these options.
#	select HAVE_IOREMAP_PROT
+10 −56
Original line number Diff line number Diff line
@@ -64,6 +64,13 @@ static inline int is_arch_mappable_range(unsigned long addr,
#define is_arch_mappable_range(addr, size) 0
#endif

/*
 * Note that using this definition ignores is_arch_mappable_range(),
 * so on tilepro code that uses user_addr_max() is constrained not
 * to reference the tilepro user-interrupt region.
 */
#define user_addr_max() (current_thread_info()->addr_limit.seg)

/*
 * Test whether a block of memory is a valid user space address.
 * Returns 0 if the range is valid, nonzero otherwise.
@@ -465,62 +472,9 @@ copy_in_user(void __user *to, const void __user *from, unsigned long n)
#endif


/**
 * strlen_user: - Get the size of a string in user space.
 * @str: The string to measure.
 *
 * Context: User context only.  This function may sleep.
 *
 * Get the size of a NUL-terminated string in user space.
 *
 * Returns the size of the string INCLUDING the terminating NUL.
 * On exception, returns 0.
 *
 * If there is a limit on the length of a valid string, you may wish to
 * consider using strnlen_user() instead.
 */
extern long strnlen_user_asm(const char __user *str, long n);
static inline long __must_check strnlen_user(const char __user *str, long n)
{
	might_fault();
	return strnlen_user_asm(str, n);
}
#define strlen_user(str) strnlen_user(str, LONG_MAX)

/**
 * strncpy_from_user: - Copy a NUL terminated string from userspace, with less checking.
 * @dst:   Destination address, in kernel space.  This buffer must be at
 *         least @count bytes long.
 * @src:   Source address, in user space.
 * @count: Maximum number of bytes to copy, including the trailing NUL.
 *
 * Copies a NUL-terminated string from userspace to kernel space.
 * Caller must check the specified block with access_ok() before calling
 * this function.
 *
 * On success, returns the length of the string (not including the trailing
 * NUL).
 *
 * If access to userspace fails, returns -EFAULT (some data may have been
 * copied).
 *
 * If @count is smaller than the length of the string, copies @count bytes
 * and returns @count.
 */
extern long strncpy_from_user_asm(char *dst, const char __user *src, long);
static inline long __must_check __strncpy_from_user(
	char *dst, const char __user *src, long count)
{
	might_fault();
	return strncpy_from_user_asm(dst, src, count);
}
static inline long __must_check strncpy_from_user(
	char *dst, const char __user *src, long count)
{
	if (access_ok(VERIFY_READ, src, 1))
		return __strncpy_from_user(dst, src, count);
	return -EFAULT;
}
extern long strnlen_user(const char __user *str, long n);
extern long strlen_user(const char __user *str);
extern long strncpy_from_user(char *dst, const char __user *src, long);

/**
 * clear_user: - Zero a block of memory in user space.
+36 −0
Original line number Diff line number Diff line
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H

#include <asm/byteorder.h>

struct word_at_a_time { /* unused */ };
#define WORD_AT_A_TIME_CONSTANTS {}

/* Generate 0x01 byte values for non-zero bytes using a SIMD instruction. */
static inline unsigned long has_zero(unsigned long val, unsigned long *data,
				     const struct word_at_a_time *c)
{
#ifdef __tilegx__
	unsigned long mask = __insn_v1cmpeqi(val, 0);
#else /* tilepro */
	unsigned long mask = __insn_seqib(val, 0);
#endif
	*data = mask;
	return mask;
}

/* These operations are both nops. */
#define prep_zero_mask(val, data, c) (data)
#define create_zero_mask(data) (data)

/* And this operation just depends on endianness. */
static inline long find_zero(unsigned long mask)
{
#ifdef __BIG_ENDIAN
	return __builtin_clzl(mask) >> 3;
#else
	return __builtin_ctzl(mask) >> 3;
#endif
}

#endif /* _ASM_WORD_AT_A_TIME_H */
+0 −2
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@

/* arch/tile/lib/usercopy.S */
#include <linux/uaccess.h>
EXPORT_SYMBOL(strnlen_user_asm);
EXPORT_SYMBOL(strncpy_from_user_asm);
EXPORT_SYMBOL(clear_user_asm);
EXPORT_SYMBOL(flush_user_asm);
EXPORT_SYMBOL(finv_user_asm);
+0 −46
Original line number Diff line number Diff line
@@ -19,52 +19,6 @@

/* Access user memory, but use MMU to avoid propagating kernel exceptions. */

/*
 * strnlen_user_asm takes the pointer in r0, and the length bound in r1.
 * It returns the length, including the terminating NUL, or zero on exception.
 * If length is greater than the bound, returns one plus the bound.
 */
STD_ENTRY(strnlen_user_asm)
	{ bz r1, 2f; addi r3, r0, -1 }  /* bias down to include NUL */
1:      { lb_u r4, r0; addi r1, r1, -1 }
	bz r4, 2f
	{ bnzt r1, 1b; addi r0, r0, 1 }
2:      { sub r0, r0, r3; jrp lr }
	STD_ENDPROC(strnlen_user_asm)
	.pushsection .fixup,"ax"
strnlen_user_fault:
	{ move r0, zero; jrp lr }
	ENDPROC(strnlen_user_fault)
	.section __ex_table,"a"
	.align 4
	.word 1b, strnlen_user_fault
	.popsection

/*
 * strncpy_from_user_asm takes the kernel target pointer in r0,
 * the userspace source pointer in r1, and the length bound (including
 * the trailing NUL) in r2.  On success, it returns the string length
 * (not including the trailing NUL), or -EFAULT on failure.
 */
STD_ENTRY(strncpy_from_user_asm)
	{ bz r2, 2f; move r3, r0 }
1:	{ lb_u r4, r1; addi r1, r1, 1; addi r2, r2, -1 }
	{ sb r0, r4; addi r0, r0, 1 }
	bz r4, 2f
	bnzt r2, 1b
	{ sub r0, r0, r3; jrp lr }
2:	addi r0, r0, -1   /* don't count the trailing NUL */
	{ sub r0, r0, r3; jrp lr }
	STD_ENDPROC(strncpy_from_user_asm)
	.pushsection .fixup,"ax"
strncpy_from_user_fault:
	{ movei r0, -EFAULT; jrp lr }
	ENDPROC(strncpy_from_user_fault)
	.section __ex_table,"a"
	.align 4
	.word 1b, strncpy_from_user_fault
	.popsection

/*
 * clear_user_asm takes the user target address in r0 and the
 * number of bytes to zero in r1.
Loading