aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2012-05-27 23:03:47 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-28 00:00:07 -0400
commit1629372caaaf7ef744d3b983be56b99468a68ff8 (patch)
tree8d3cc2b69ba7b2626b2fe1b3de3ce2a19e45f524 /arch
parent69ea6405980f217557b6a58f70ff60d8d88519a5 (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/Kconfig2
-rw-r--r--arch/powerpc/include/asm/uaccess.h41
-rw-r--r--arch/powerpc/include/asm/word-at-a-time.h41
-rw-r--r--arch/powerpc/kernel/ppc_ksyms.c2
-rw-r--r--arch/powerpc/lib/string.S45
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
139config EARLY_PRINTK 141config 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
456extern int __strncpy_from_user(char *dst, const char __user *src, long count); 458extern long strncpy_from_user(char *dst, const char __user *src, long count);
457 459extern __must_check long strlen_user(const char __user *str);
458static inline long strncpy_from_user(char *dst, const char __user *src, 460extern __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 */
472extern 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 */
482static 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
11struct 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 */
18static 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
26static 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
34static 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
86EXPORT_SYMBOL(__copy_tofrom_user); 86EXPORT_SYMBOL(__copy_tofrom_user);
87EXPORT_SYMBOL(__clear_user); 87EXPORT_SYMBOL(__clear_user);
88EXPORT_SYMBOL(__strncpy_from_user);
89EXPORT_SYMBOL(__strnlen_user);
90EXPORT_SYMBOL(copy_page); 88EXPORT_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
1701: 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
1752: addi r6,r6,1
1763: subf r3,r3,r6
177 blr
17899: 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
1920: mtctr r6 /* ctr = min(len, top - str) */
1931: 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
20399: li r3,0 /* bad address, return 0 */
204 blr
205
206 .section __ex_table,"a"
207 PPC_LONG 1b,99b