diff options
author | Chris Metcalf <cmetcalf@ezchip.com> | 2015-04-30 15:12:42 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@ezchip.com> | 2015-04-30 15:23:37 -0400 |
commit | 5bf6c07a1843813d0065feaaecba622d49148d7e (patch) | |
tree | 18118d48ffe123ce6f6d3eaa67dba135adafbc1a | |
parent | 627ae54854edfbf29d5997015c190de22eef497f (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/Kconfig | 2 | ||||
-rw-r--r-- | arch/tile/include/asm/uaccess.h | 66 | ||||
-rw-r--r-- | arch/tile/include/asm/word-at-a-time.h | 36 | ||||
-rw-r--r-- | arch/tile/lib/exports.c | 2 | ||||
-rw-r--r-- | arch/tile/lib/usercopy_32.S | 46 | ||||
-rw-r--r-- | arch/tile/lib/usercopy_64.S | 46 |
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 | /** | 475 | extern long strnlen_user(const char __user *str, long n); |
469 | * strlen_user: - Get the size of a string in user space. | 476 | extern long strlen_user(const char __user *str); |
470 | * @str: The string to measure. | 477 | extern 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 | */ | ||
482 | extern long strnlen_user_asm(const char __user *str, long n); | ||
483 | static 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 | */ | ||
510 | extern long strncpy_from_user_asm(char *dst, const char __user *src, long); | ||
511 | static 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 | } | ||
517 | static 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 | |||
6 | struct 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. */ | ||
10 | static 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. */ | ||
27 | static 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> |
21 | EXPORT_SYMBOL(strnlen_user_asm); | ||
22 | EXPORT_SYMBOL(strncpy_from_user_asm); | ||
23 | EXPORT_SYMBOL(clear_user_asm); | 21 | EXPORT_SYMBOL(clear_user_asm); |
24 | EXPORT_SYMBOL(flush_user_asm); | 22 | EXPORT_SYMBOL(flush_user_asm); |
25 | EXPORT_SYMBOL(finv_user_asm); | 23 | EXPORT_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 | */ | ||
27 | STD_ENTRY(strnlen_user_asm) | ||
28 | { bz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */ | ||
29 | 1: { lb_u r4, r0; addi r1, r1, -1 } | ||
30 | bz r4, 2f | ||
31 | { bnzt r1, 1b; addi r0, r0, 1 } | ||
32 | 2: { sub r0, r0, r3; jrp lr } | ||
33 | STD_ENDPROC(strnlen_user_asm) | ||
34 | .pushsection .fixup,"ax" | ||
35 | strnlen_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 | */ | ||
49 | STD_ENTRY(strncpy_from_user_asm) | ||
50 | { bz r2, 2f; move r3, r0 } | ||
51 | 1: { 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 } | ||
56 | 2: 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" | ||
60 | strncpy_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 | */ | ||
27 | STD_ENTRY(strnlen_user_asm) | ||
28 | { beqz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */ | ||
29 | 1: { ld1u r4, r0; addi r1, r1, -1 } | ||
30 | beqz r4, 2f | ||
31 | { bnezt r1, 1b; addi r0, r0, 1 } | ||
32 | 2: { sub r0, r0, r3; jrp lr } | ||
33 | STD_ENDPROC(strnlen_user_asm) | ||
34 | .pushsection .fixup,"ax" | ||
35 | strnlen_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 | */ | ||
49 | STD_ENTRY(strncpy_from_user_asm) | ||
50 | { beqz r2, 2f; move r3, r0 } | ||
51 | 1: { 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 } | ||
56 | 2: 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" | ||
60 | strncpy_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. |