mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 08:02:07 +00:00
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: Chris Metcalf <cmetcalf@ezchip.com>
This commit is contained in:
parent
627ae54854
commit
5bf6c07a18
@ -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
|
||||
|
@ -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
arch/tile/include/asm/word-at-a-time.h
Normal file
36
arch/tile/include/asm/word-at-a-time.h
Normal file
@ -0,0 +1,36 @@
|
||||
#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 */
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{ beqz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */
|
||||
1: { ld1u r4, r0; addi r1, r1, -1 }
|
||||
beqz r4, 2f
|
||||
{ bnezt 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 8
|
||||
.quad 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)
|
||||
{ beqz r2, 2f; move r3, r0 }
|
||||
1: { ld1u r4, r1; addi r1, r1, 1; addi r2, r2, -1 }
|
||||
{ st1 r0, r4; addi r0, r0, 1 }
|
||||
beqz r4, 2f
|
||||
bnezt 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 8
|
||||
.quad 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…
Reference in New Issue
Block a user