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>
This commit is contained in:
parent
69ea640598
commit
1629372caa
|
@ -135,6 +135,8 @@ config PPC
|
|||
select GENERIC_CMOS_UPDATE
|
||||
select GENERIC_TIME_VSYSCALL
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_STRNCPY_FROM_USER
|
||||
select GENERIC_STRNLEN_USER
|
||||
|
||||
config EARLY_PRINTK
|
||||
bool
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
|
||||
#define segment_eq(a, b) ((a).seg == (b).seg)
|
||||
|
||||
#define user_addr_max() (get_fs().seg)
|
||||
|
||||
#ifdef __powerpc64__
|
||||
/*
|
||||
* 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)
|
|||
return size;
|
||||
}
|
||||
|
||||
extern int __strncpy_from_user(char *dst, const char __user *src, long count);
|
||||
|
||||
static inline long strncpy_from_user(char *dst, const char __user *src,
|
||||
long count)
|
||||
{
|
||||
might_sleep();
|
||||
if (likely(access_ok(VERIFY_READ, src, 1)))
|
||||
return __strncpy_from_user(dst, src, count);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the size of a string (including the ending 0)
|
||||
*
|
||||
* Return 0 for error
|
||||
*/
|
||||
extern int __strnlen_user(const char __user *str, long len, unsigned long top);
|
||||
|
||||
/*
|
||||
* Returns the length of the string at str (including the null byte),
|
||||
* or 0 if we hit a page we can't access,
|
||||
* or something > len if we didn't find a null byte.
|
||||
*
|
||||
* The `top' parameter to __strnlen_user is to make sure that
|
||||
* we can never overflow from the user area into kernel space.
|
||||
*/
|
||||
static inline int strnlen_user(const char __user *str, long len)
|
||||
{
|
||||
unsigned long top = current->thread.fs.seg;
|
||||
|
||||
if ((unsigned long)str > top)
|
||||
return 0;
|
||||
return __strnlen_user(str, len, top);
|
||||
}
|
||||
|
||||
#define strlen_user(str) strnlen_user((str), 0x7ffffffe)
|
||||
extern long strncpy_from_user(char *dst, const char __user *src, long count);
|
||||
extern __must_check long strlen_user(const char __user *str);
|
||||
extern __must_check long strnlen_user(const char __user *str, long n);
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __KERNEL__ */
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef _ASM_WORD_AT_A_TIME_H
|
||||
#define _ASM_WORD_AT_A_TIME_H
|
||||
|
||||
/*
|
||||
* Word-at-a-time interfaces for PowerPC.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/asm-compat.h>
|
||||
|
||||
struct word_at_a_time {
|
||||
const unsigned long high_bits, low_bits;
|
||||
};
|
||||
|
||||
#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0xfe) + 1, REPEAT_BYTE(0x7f) }
|
||||
|
||||
/* Bit set in the bytes that have a zero */
|
||||
static inline long prep_zero_mask(unsigned long val, unsigned long rhs, const struct word_at_a_time *c)
|
||||
{
|
||||
unsigned long mask = (val & c->low_bits) + c->low_bits;
|
||||
return ~(mask | rhs);
|
||||
}
|
||||
|
||||
#define create_zero_mask(mask) (mask)
|
||||
|
||||
static inline long find_zero(unsigned long mask)
|
||||
{
|
||||
long leading_zero_bits;
|
||||
|
||||
asm (PPC_CNTLZL "%0,%1" : "=r" (leading_zero_bits) : "r" (mask));
|
||||
return leading_zero_bits >> 3;
|
||||
}
|
||||
|
||||
static inline bool has_zero(unsigned long val, unsigned long *data, const struct word_at_a_time *c)
|
||||
{
|
||||
unsigned long rhs = val | c->low_bits;
|
||||
*data = rhs;
|
||||
return (val + c->high_bits) & ~rhs;
|
||||
}
|
||||
|
||||
#endif /* _ASM_WORD_AT_A_TIME_H */
|
|
@ -85,8 +85,6 @@ EXPORT_SYMBOL(csum_tcpudp_magic);
|
|||
|
||||
EXPORT_SYMBOL(__copy_tofrom_user);
|
||||
EXPORT_SYMBOL(__clear_user);
|
||||
EXPORT_SYMBOL(__strncpy_from_user);
|
||||
EXPORT_SYMBOL(__strnlen_user);
|
||||
EXPORT_SYMBOL(copy_page);
|
||||
|
||||
#if defined(CONFIG_PCI) && defined(CONFIG_PPC32)
|
||||
|
|
|
@ -160,48 +160,3 @@ _GLOBAL(__clear_user)
|
|||
PPC_LONG 1b,91b
|
||||
PPC_LONG 8b,92b
|
||||
.text
|
||||
|
||||
_GLOBAL(__strncpy_from_user)
|
||||
addi r6,r3,-1
|
||||
addi r4,r4,-1
|
||||
cmpwi 0,r5,0
|
||||
beq 2f
|
||||
mtctr r5
|
||||
1: lbzu r0,1(r4)
|
||||
cmpwi 0,r0,0
|
||||
stbu r0,1(r6)
|
||||
bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */
|
||||
beq 3f
|
||||
2: addi r6,r6,1
|
||||
3: subf r3,r3,r6
|
||||
blr
|
||||
99: li r3,-EFAULT
|
||||
blr
|
||||
|
||||
.section __ex_table,"a"
|
||||
PPC_LONG 1b,99b
|
||||
.text
|
||||
|
||||
/* r3 = str, r4 = len (> 0), r5 = top (highest addr) */
|
||||
_GLOBAL(__strnlen_user)
|
||||
addi r7,r3,-1
|
||||
subf r6,r7,r5 /* top+1 - str */
|
||||
cmplw 0,r4,r6
|
||||
bge 0f
|
||||
mr r6,r4
|
||||
0: mtctr r6 /* ctr = min(len, top - str) */
|
||||
1: lbzu r0,1(r7) /* get next byte */
|
||||
cmpwi 0,r0,0
|
||||
bdnzf 2,1b /* loop if --ctr != 0 && byte != 0 */
|
||||
addi r7,r7,1
|
||||
subf r3,r3,r7 /* number of bytes we have looked at */
|
||||
beqlr /* return if we found a 0 byte */
|
||||
cmpw 0,r3,r4 /* did we look at all len bytes? */
|
||||
blt 99f /* if not, must have hit top */
|
||||
addi r3,r4,1 /* return len + 1 to indicate no null found */
|
||||
blr
|
||||
99: li r3,0 /* bad address, return 0 */
|
||||
blr
|
||||
|
||||
.section __ex_table,"a"
|
||||
PPC_LONG 1b,99b
|
||||
|
|
Loading…
Reference in New Issue