aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@ezchip.com>2015-04-30 15:12:42 -0400
committerChris Metcalf <cmetcalf@ezchip.com>2015-04-30 15:23:37 -0400
commit5bf6c07a1843813d0065feaaecba622d49148d7e (patch)
tree18118d48ffe123ce6f6d3eaa67dba135adafbc1a
parent627ae54854edfbf29d5997015c190de22eef497f (diff)
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>
-rw-r--r--arch/tile/Kconfig2
-rw-r--r--arch/tile/include/asm/uaccess.h66
-rw-r--r--arch/tile/include/asm/word-at-a-time.h36
-rw-r--r--arch/tile/lib/exports.c2
-rw-r--r--arch/tile/lib/usercopy_32.S46
-rw-r--r--arch/tile/lib/usercopy_64.S46
6 files changed, 48 insertions, 150 deletions
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index a07e31b50d3f..8fac40f56ec7 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -28,6 +28,8 @@ config TILE
28 select HAVE_DEBUG_STACKOVERFLOW 28 select HAVE_DEBUG_STACKOVERFLOW
29 select ARCH_WANT_FRAME_POINTERS 29 select ARCH_WANT_FRAME_POINTERS
30 select HAVE_CONTEXT_TRACKING 30 select HAVE_CONTEXT_TRACKING
31 select GENERIC_STRNCPY_FROM_USER
32 select GENERIC_STRNLEN_USER
31 33
32# FIXME: investigate whether we need/want these options. 34# FIXME: investigate whether we need/want these options.
33# select HAVE_IOREMAP_PROT 35# select HAVE_IOREMAP_PROT
diff --git a/arch/tile/include/asm/uaccess.h b/arch/tile/include/asm/uaccess.h
index f41cb53cf645..d598c11a82d9 100644
--- a/arch/tile/include/asm/uaccess.h
+++ b/arch/tile/include/asm/uaccess.h
@@ -65,6 +65,13 @@ static inline int is_arch_mappable_range(unsigned long addr,
65#endif 65#endif
66 66
67/* 67/*
68 * Note that using this definition ignores is_arch_mappable_range(),
69 * so on tilepro code that uses user_addr_max() is constrained not
70 * to reference the tilepro user-interrupt region.
71 */
72#define user_addr_max() (current_thread_info()->addr_limit.seg)
73
74/*
68 * Test whether a block of memory is a valid user space address. 75 * Test whether a block of memory is a valid user space address.
69 * Returns 0 if the range is valid, nonzero otherwise. 76 * Returns 0 if the range is valid, nonzero otherwise.
70 */ 77 */
@@ -465,62 +472,9 @@ copy_in_user(void __user *to, const void __user *from, unsigned long n)
465#endif 472#endif
466 473
467 474
468/** 475extern long strnlen_user(const char __user *str, long n);
469 * strlen_user: - Get the size of a string in user space. 476extern long strlen_user(const char __user *str);
470 * @str: The string to measure. 477extern long strncpy_from_user(char *dst, const char __user *src, long);
471 *
472 * Context: User context only. This function may sleep.
473 *
474 * Get the size of a NUL-terminated string in user space.
475 *
476 * Returns the size of the string INCLUDING the terminating NUL.
477 * On exception, returns 0.
478 *
479 * If there is a limit on the length of a valid string, you may wish to
480 * consider using strnlen_user() instead.
481 */
482extern long strnlen_user_asm(const char __user *str, long n);
483static inline long __must_check strnlen_user(const char __user *str, long n)
484{
485 might_fault();
486 return strnlen_user_asm(str, n);
487}
488#define strlen_user(str) strnlen_user(str, LONG_MAX)
489
490/**
491 * strncpy_from_user: - Copy a NUL terminated string from userspace, with less checking.
492 * @dst: Destination address, in kernel space. This buffer must be at
493 * least @count bytes long.
494 * @src: Source address, in user space.
495 * @count: Maximum number of bytes to copy, including the trailing NUL.
496 *
497 * Copies a NUL-terminated string from userspace to kernel space.
498 * Caller must check the specified block with access_ok() before calling
499 * this function.
500 *
501 * On success, returns the length of the string (not including the trailing
502 * NUL).
503 *
504 * If access to userspace fails, returns -EFAULT (some data may have been
505 * copied).
506 *
507 * If @count is smaller than the length of the string, copies @count bytes
508 * and returns @count.
509 */
510extern long strncpy_from_user_asm(char *dst, const char __user *src, long);
511static inline long __must_check __strncpy_from_user(
512 char *dst, const char __user *src, long count)
513{
514 might_fault();
515 return strncpy_from_user_asm(dst, src, count);
516}
517static inline long __must_check strncpy_from_user(
518 char *dst, const char __user *src, long count)
519{
520 if (access_ok(VERIFY_READ, src, 1))
521 return __strncpy_from_user(dst, src, count);
522 return -EFAULT;
523}
524 478
525/** 479/**
526 * clear_user: - Zero a block of memory in user space. 480 * clear_user: - Zero a block of memory in user space.
diff --git a/arch/tile/include/asm/word-at-a-time.h b/arch/tile/include/asm/word-at-a-time.h
new file mode 100644
index 000000000000..9e5ce0d7b292
--- /dev/null
+++ b/arch/tile/include/asm/word-at-a-time.h
@@ -0,0 +1,36 @@
1#ifndef _ASM_WORD_AT_A_TIME_H
2#define _ASM_WORD_AT_A_TIME_H
3
4#include <asm/byteorder.h>
5
6struct word_at_a_time { /* unused */ };
7#define WORD_AT_A_TIME_CONSTANTS {}
8
9/* Generate 0x01 byte values for non-zero bytes using a SIMD instruction. */
10static inline unsigned long has_zero(unsigned long val, unsigned long *data,
11 const struct word_at_a_time *c)
12{
13#ifdef __tilegx__
14 unsigned long mask = __insn_v1cmpeqi(val, 0);
15#else /* tilepro */
16 unsigned long mask = __insn_seqib(val, 0);
17#endif
18 *data = mask;
19 return mask;
20}
21
22/* These operations are both nops. */
23#define prep_zero_mask(val, data, c) (data)
24#define create_zero_mask(data) (data)
25
26/* And this operation just depends on endianness. */
27static inline long find_zero(unsigned long mask)
28{
29#ifdef __BIG_ENDIAN
30 return __builtin_clzl(mask) >> 3;
31#else
32 return __builtin_ctzl(mask) >> 3;
33#endif
34}
35
36#endif /* _ASM_WORD_AT_A_TIME_H */
diff --git a/arch/tile/lib/exports.c b/arch/tile/lib/exports.c
index 82733c87d67e..16326f288177 100644
--- a/arch/tile/lib/exports.c
+++ b/arch/tile/lib/exports.c
@@ -18,8 +18,6 @@
18 18
19/* arch/tile/lib/usercopy.S */ 19/* arch/tile/lib/usercopy.S */
20#include <linux/uaccess.h> 20#include <linux/uaccess.h>
21EXPORT_SYMBOL(strnlen_user_asm);
22EXPORT_SYMBOL(strncpy_from_user_asm);
23EXPORT_SYMBOL(clear_user_asm); 21EXPORT_SYMBOL(clear_user_asm);
24EXPORT_SYMBOL(flush_user_asm); 22EXPORT_SYMBOL(flush_user_asm);
25EXPORT_SYMBOL(finv_user_asm); 23EXPORT_SYMBOL(finv_user_asm);
diff --git a/arch/tile/lib/usercopy_32.S b/arch/tile/lib/usercopy_32.S
index 1bc162224638..db93ad5fae25 100644
--- a/arch/tile/lib/usercopy_32.S
+++ b/arch/tile/lib/usercopy_32.S
@@ -20,52 +20,6 @@
20/* Access user memory, but use MMU to avoid propagating kernel exceptions. */ 20/* Access user memory, but use MMU to avoid propagating kernel exceptions. */
21 21
22/* 22/*
23 * strnlen_user_asm takes the pointer in r0, and the length bound in r1.
24 * It returns the length, including the terminating NUL, or zero on exception.
25 * If length is greater than the bound, returns one plus the bound.
26 */
27STD_ENTRY(strnlen_user_asm)
28 { bz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */
291: { lb_u r4, r0; addi r1, r1, -1 }
30 bz r4, 2f
31 { bnzt r1, 1b; addi r0, r0, 1 }
322: { sub r0, r0, r3; jrp lr }
33 STD_ENDPROC(strnlen_user_asm)
34 .pushsection .fixup,"ax"
35strnlen_user_fault:
36 { move r0, zero; jrp lr }
37 ENDPROC(strnlen_user_fault)
38 .section __ex_table,"a"
39 .align 4
40 .word 1b, strnlen_user_fault
41 .popsection
42
43/*
44 * strncpy_from_user_asm takes the kernel target pointer in r0,
45 * the userspace source pointer in r1, and the length bound (including
46 * the trailing NUL) in r2. On success, it returns the string length
47 * (not including the trailing NUL), or -EFAULT on failure.
48 */
49STD_ENTRY(strncpy_from_user_asm)
50 { bz r2, 2f; move r3, r0 }
511: { lb_u r4, r1; addi r1, r1, 1; addi r2, r2, -1 }
52 { sb r0, r4; addi r0, r0, 1 }
53 bz r4, 2f
54 bnzt r2, 1b
55 { sub r0, r0, r3; jrp lr }
562: addi r0, r0, -1 /* don't count the trailing NUL */
57 { sub r0, r0, r3; jrp lr }
58 STD_ENDPROC(strncpy_from_user_asm)
59 .pushsection .fixup,"ax"
60strncpy_from_user_fault:
61 { movei r0, -EFAULT; jrp lr }
62 ENDPROC(strncpy_from_user_fault)
63 .section __ex_table,"a"
64 .align 4
65 .word 1b, strncpy_from_user_fault
66 .popsection
67
68/*
69 * clear_user_asm takes the user target address in r0 and the 23 * clear_user_asm takes the user target address in r0 and the
70 * number of bytes to zero in r1. 24 * number of bytes to zero in r1.
71 * It returns the number of uncopiable bytes (hopefully zero) in r0. 25 * It returns the number of uncopiable bytes (hopefully zero) in r0.
diff --git a/arch/tile/lib/usercopy_64.S b/arch/tile/lib/usercopy_64.S
index b3b31a3306f8..9322dc551e91 100644
--- a/arch/tile/lib/usercopy_64.S
+++ b/arch/tile/lib/usercopy_64.S
@@ -20,52 +20,6 @@
20/* Access user memory, but use MMU to avoid propagating kernel exceptions. */ 20/* Access user memory, but use MMU to avoid propagating kernel exceptions. */
21 21
22/* 22/*
23 * strnlen_user_asm takes the pointer in r0, and the length bound in r1.
24 * It returns the length, including the terminating NUL, or zero on exception.
25 * If length is greater than the bound, returns one plus the bound.
26 */
27STD_ENTRY(strnlen_user_asm)
28 { beqz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */
291: { ld1u r4, r0; addi r1, r1, -1 }
30 beqz r4, 2f
31 { bnezt r1, 1b; addi r0, r0, 1 }
322: { sub r0, r0, r3; jrp lr }
33 STD_ENDPROC(strnlen_user_asm)
34 .pushsection .fixup,"ax"
35strnlen_user_fault:
36 { move r0, zero; jrp lr }
37 ENDPROC(strnlen_user_fault)
38 .section __ex_table,"a"
39 .align 8
40 .quad 1b, strnlen_user_fault
41 .popsection
42
43/*
44 * strncpy_from_user_asm takes the kernel target pointer in r0,
45 * the userspace source pointer in r1, and the length bound (including
46 * the trailing NUL) in r2. On success, it returns the string length
47 * (not including the trailing NUL), or -EFAULT on failure.
48 */
49STD_ENTRY(strncpy_from_user_asm)
50 { beqz r2, 2f; move r3, r0 }
511: { ld1u r4, r1; addi r1, r1, 1; addi r2, r2, -1 }
52 { st1 r0, r4; addi r0, r0, 1 }
53 beqz r4, 2f
54 bnezt r2, 1b
55 { sub r0, r0, r3; jrp lr }
562: addi r0, r0, -1 /* don't count the trailing NUL */
57 { sub r0, r0, r3; jrp lr }
58 STD_ENDPROC(strncpy_from_user_asm)
59 .pushsection .fixup,"ax"
60strncpy_from_user_fault:
61 { movei r0, -EFAULT; jrp lr }
62 ENDPROC(strncpy_from_user_fault)
63 .section __ex_table,"a"
64 .align 8
65 .quad 1b, strncpy_from_user_fault
66 .popsection
67
68/*
69 * clear_user_asm takes the user target address in r0 and the 23 * clear_user_asm takes the user target address in r0 and the
70 * number of bytes to zero in r1. 24 * number of bytes to zero in r1.
71 * It returns the number of uncopiable bytes (hopefully zero) in r0. 25 * It returns the number of uncopiable bytes (hopefully zero) in r0.