diff options
author | Paul Mackerras <paulus@samba.org> | 2012-05-27 23:03:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-28 00:00:07 -0400 |
commit | 1629372caaaf7ef744d3b983be56b99468a68ff8 (patch) | |
tree | 8d3cc2b69ba7b2626b2fe1b3de3ce2a19e45f524 /arch | |
parent | 69ea6405980f217557b6a58f70ff60d8d88519a5 (diff) |
powerpc: Use the new generic strncpy_from_user() and strnlen_user()
This is much the same as for SPARC except that we can do the find_zero()
function more efficiently using the count-leading-zeroes instructions.
Tested on 32-bit and 64-bit PowerPC.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/Kconfig | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/uaccess.h | 41 | ||||
-rw-r--r-- | arch/powerpc/include/asm/word-at-a-time.h | 41 | ||||
-rw-r--r-- | arch/powerpc/kernel/ppc_ksyms.c | 2 | ||||
-rw-r--r-- | arch/powerpc/lib/string.S | 45 |
5 files changed, 48 insertions, 83 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 00b9874e2240..050cb371a69e 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -135,6 +135,8 @@ config PPC | |||
135 | select GENERIC_CMOS_UPDATE | 135 | select GENERIC_CMOS_UPDATE |
136 | select GENERIC_TIME_VSYSCALL | 136 | select GENERIC_TIME_VSYSCALL |
137 | select GENERIC_CLOCKEVENTS | 137 | select GENERIC_CLOCKEVENTS |
138 | select GENERIC_STRNCPY_FROM_USER | ||
139 | select GENERIC_STRNLEN_USER | ||
138 | 140 | ||
139 | config EARLY_PRINTK | 141 | config EARLY_PRINTK |
140 | bool | 142 | bool |
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index bd0fb8495154..17bb40cad5bf 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h | |||
@@ -40,6 +40,8 @@ | |||
40 | 40 | ||
41 | #define segment_eq(a, b) ((a).seg == (b).seg) | 41 | #define segment_eq(a, b) ((a).seg == (b).seg) |
42 | 42 | ||
43 | #define user_addr_max() (get_fs().seg) | ||
44 | |||
43 | #ifdef __powerpc64__ | 45 | #ifdef __powerpc64__ |
44 | /* | 46 | /* |
45 | * This check is sufficient because there is a large enough | 47 | * This check is sufficient because there is a large enough |
@@ -453,42 +455,9 @@ static inline unsigned long clear_user(void __user *addr, unsigned long size) | |||
453 | return size; | 455 | return size; |
454 | } | 456 | } |
455 | 457 | ||
456 | extern int __strncpy_from_user(char *dst, const char __user *src, long count); | 458 | extern long strncpy_from_user(char *dst, const char __user *src, long count); |
457 | 459 | extern __must_check long strlen_user(const char __user *str); | |
458 | static inline long strncpy_from_user(char *dst, const char __user *src, | 460 | extern __must_check long strnlen_user(const char __user *str, long n); |
459 | long count) | ||
460 | { | ||
461 | might_sleep(); | ||
462 | if (likely(access_ok(VERIFY_READ, src, 1))) | ||
463 | return __strncpy_from_user(dst, src, count); | ||
464 | return -EFAULT; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * Return the size of a string (including the ending 0) | ||
469 | * | ||
470 | * Return 0 for error | ||
471 | */ | ||
472 | extern int __strnlen_user(const char __user *str, long len, unsigned long top); | ||
473 | |||
474 | /* | ||
475 | * Returns the length of the string at str (including the null byte), | ||
476 | * or 0 if we hit a page we can't access, | ||
477 | * or something > len if we didn't find a null byte. | ||
478 | * | ||
479 | * The `top' parameter to __strnlen_user is to make sure that | ||
480 | * we can never overflow from the user area into kernel space. | ||
481 | */ | ||
482 | static inline int strnlen_user(const char __user *str, long len) | ||
483 | { | ||
484 | unsigned long top = current->thread.fs.seg; | ||
485 | |||
486 | if ((unsigned long)str > top) | ||
487 | return 0; | ||
488 | return __strnlen_user(str, len, top); | ||
489 | } | ||
490 | |||
491 | #define strlen_user(str) strnlen_user((str), 0x7ffffffe) | ||
492 | 461 | ||
493 | #endif /* __ASSEMBLY__ */ | 462 | #endif /* __ASSEMBLY__ */ |
494 | #endif /* __KERNEL__ */ | 463 | #endif /* __KERNEL__ */ |
diff --git a/arch/powerpc/include/asm/word-at-a-time.h b/arch/powerpc/include/asm/word-at-a-time.h new file mode 100644 index 000000000000..d0b6d4ac6dda --- /dev/null +++ b/arch/powerpc/include/asm/word-at-a-time.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef _ASM_WORD_AT_A_TIME_H | ||
2 | #define _ASM_WORD_AT_A_TIME_H | ||
3 | |||
4 | /* | ||
5 | * Word-at-a-time interfaces for PowerPC. | ||
6 | */ | ||
7 | |||
8 | #include <linux/kernel.h> | ||
9 | #include <asm/asm-compat.h> | ||
10 | |||
11 | struct word_at_a_time { | ||
12 | const unsigned long high_bits, low_bits; | ||
13 | }; | ||
14 | |||
15 | #define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0xfe) + 1, REPEAT_BYTE(0x7f) } | ||
16 | |||
17 | /* Bit set in the bytes that have a zero */ | ||
18 | static inline long prep_zero_mask(unsigned long val, unsigned long rhs, const struct word_at_a_time *c) | ||
19 | { | ||
20 | unsigned long mask = (val & c->low_bits) + c->low_bits; | ||
21 | return ~(mask | rhs); | ||
22 | } | ||
23 | |||
24 | #define create_zero_mask(mask) (mask) | ||
25 | |||
26 | static inline long find_zero(unsigned long mask) | ||
27 | { | ||
28 | long leading_zero_bits; | ||
29 | |||
30 | asm (PPC_CNTLZL "%0,%1" : "=r" (leading_zero_bits) : "r" (mask)); | ||
31 | return leading_zero_bits >> 3; | ||
32 | } | ||
33 | |||
34 | static inline bool has_zero(unsigned long val, unsigned long *data, const struct word_at_a_time *c) | ||
35 | { | ||
36 | unsigned long rhs = val | c->low_bits; | ||
37 | *data = rhs; | ||
38 | return (val + c->high_bits) & ~rhs; | ||
39 | } | ||
40 | |||
41 | #endif /* _ASM_WORD_AT_A_TIME_H */ | ||
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index d1f2aafcbe8c..3e4031581c65 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c | |||
@@ -85,8 +85,6 @@ EXPORT_SYMBOL(csum_tcpudp_magic); | |||
85 | 85 | ||
86 | EXPORT_SYMBOL(__copy_tofrom_user); | 86 | EXPORT_SYMBOL(__copy_tofrom_user); |
87 | EXPORT_SYMBOL(__clear_user); | 87 | EXPORT_SYMBOL(__clear_user); |
88 | EXPORT_SYMBOL(__strncpy_from_user); | ||
89 | EXPORT_SYMBOL(__strnlen_user); | ||
90 | EXPORT_SYMBOL(copy_page); | 88 | EXPORT_SYMBOL(copy_page); |
91 | 89 | ||
92 | #if defined(CONFIG_PCI) && defined(CONFIG_PPC32) | 90 | #if defined(CONFIG_PCI) && defined(CONFIG_PPC32) |
diff --git a/arch/powerpc/lib/string.S b/arch/powerpc/lib/string.S index 455881a5563f..093d6316435c 100644 --- a/arch/powerpc/lib/string.S +++ b/arch/powerpc/lib/string.S | |||
@@ -160,48 +160,3 @@ _GLOBAL(__clear_user) | |||
160 | PPC_LONG 1b,91b | 160 | PPC_LONG 1b,91b |
161 | PPC_LONG 8b,92b | 161 | PPC_LONG 8b,92b |
162 | .text | 162 | .text |
163 | |||
164 | _GLOBAL(__strncpy_from_user) | ||
165 | addi r6,r3,-1 | ||
166 | addi r4,r4,-1 | ||
167 | cmpwi 0,r5,0 | ||
168 | beq 2f | ||
169 | mtctr r5 | ||
170 | 1: lbzu r0,1(r4) | ||
171 | cmpwi 0,r0,0 | ||
172 | stbu r0,1(r6) | ||
173 | bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ | ||
174 | beq 3f | ||
175 | 2: addi r6,r6,1 | ||
176 | 3: subf r3,r3,r6 | ||
177 | blr | ||
178 | 99: li r3,-EFAULT | ||
179 | blr | ||
180 | |||
181 | .section __ex_table,"a" | ||
182 | PPC_LONG 1b,99b | ||
183 | .text | ||
184 | |||
185 | /* r3 = str, r4 = len (> 0), r5 = top (highest addr) */ | ||
186 | _GLOBAL(__strnlen_user) | ||
187 | addi r7,r3,-1 | ||
188 | subf r6,r7,r5 /* top+1 - str */ | ||
189 | cmplw 0,r4,r6 | ||
190 | bge 0f | ||
191 | mr r6,r4 | ||
192 | 0: mtctr r6 /* ctr = min(len, top - str) */ | ||
193 | 1: lbzu r0,1(r7) /* get next byte */ | ||
194 | cmpwi 0,r0,0 | ||
195 | bdnzf 2,1b /* loop if --ctr != 0 && byte != 0 */ | ||
196 | addi r7,r7,1 | ||
197 | subf r3,r3,r7 /* number of bytes we have looked at */ | ||
198 | beqlr /* return if we found a 0 byte */ | ||
199 | cmpw 0,r3,r4 /* did we look at all len bytes? */ | ||
200 | blt 99f /* if not, must have hit top */ | ||
201 | addi r3,r4,1 /* return len + 1 to indicate no null found */ | ||
202 | blr | ||
203 | 99: li r3,0 /* bad address, return 0 */ | ||
204 | blr | ||
205 | |||
206 | .section __ex_table,"a" | ||
207 | PPC_LONG 1b,99b | ||