s390/string: provide asm lib functions for memcpy and memcmp

Our memcpy and memcmp variants were implemented by calling the corresponding
gcc builtin variants.
However gcc is free to replace a call to __builtin_memcmp with a call to memcmp
which, when called, will result in an endless recursion within memcmp.
So let's provide asm variants and also fix the variants that are used for
uncompressing the kernel image.
In addition remove all other occurences of builtin function calls.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Heiko Carstens 2012-08-14 13:20:20 +02:00 committed by Martin Schwidefsky
parent 68d9884dbc
commit 535c611ddd
7 changed files with 212 additions and 80 deletions

View File

@ -71,34 +71,37 @@ void *memset(void *s, int c, size_t n)
{ {
char *xs; char *xs;
if (c == 0) xs = s;
return __builtin_memset(s, 0, n); while (n--)
*xs++ = c;
xs = (char *) s;
if (n > 0)
do {
*xs++ = c;
} while (--n > 0);
return s; return s;
} }
void *memcpy(void *__dest, __const void *__src, size_t __n) void *memcpy(void *dest, const void *src, size_t n)
{ {
return __builtin_memcpy(__dest, __src, __n); const char *s = src;
char *d = dest;
while (n--)
*d++ = *s++;
return dest;
} }
void *memmove(void *__dest, __const void *__src, size_t __n) void *memmove(void *dest, const void *src, size_t n)
{ {
char *d; const char *s = src;
const char *s; char *d = dest;
if (__dest <= __src) if (d <= s) {
return __builtin_memcpy(__dest, __src, __n); while (n--)
d = __dest + __n; *d++ = *s++;
s = __src + __n; } else {
while (__n--) d += n;
*--d = *--s; s += n;
return __dest; while (n--)
*--d = *--s;
}
return dest;
} }
static void error(char *x) static void error(char *x)

View File

@ -96,7 +96,6 @@ static inline char *strcat(char *dst, const char *src)
static inline char *strcpy(char *dst, const char *src) static inline char *strcpy(char *dst, const char *src)
{ {
#if __GNUC__ < 4
register int r0 asm("0") = 0; register int r0 asm("0") = 0;
char *ret = dst; char *ret = dst;
@ -106,14 +105,10 @@ static inline char *strcpy(char *dst, const char *src)
: "+&a" (dst), "+&a" (src) : "d" (r0) : "+&a" (dst), "+&a" (src) : "d" (r0)
: "cc", "memory"); : "cc", "memory");
return ret; return ret;
#else
return __builtin_strcpy(dst, src);
#endif
} }
static inline size_t strlen(const char *s) static inline size_t strlen(const char *s)
{ {
#if __GNUC__ < 4
register unsigned long r0 asm("0") = 0; register unsigned long r0 asm("0") = 0;
const char *tmp = s; const char *tmp = s;
@ -122,9 +117,6 @@ static inline size_t strlen(const char *s)
" jo 0b" " jo 0b"
: "+d" (r0), "+a" (tmp) : : "cc"); : "+d" (r0), "+a" (tmp) : : "cc");
return r0 - (unsigned long) s; return r0 - (unsigned long) s;
#else
return __builtin_strlen(s);
#endif
} }
static inline size_t strnlen(const char * s, size_t n) static inline size_t strnlen(const char * s, size_t n)

View File

@ -8,3 +8,5 @@ EXPORT_SYMBOL(_mcount);
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
EXPORT_SYMBOL(sie64a); EXPORT_SYMBOL(sie64a);
#endif #endif
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memset);

View File

@ -4,6 +4,7 @@
lib-y += delay.o string.o uaccess_std.o uaccess_pt.o lib-y += delay.o string.o uaccess_std.o uaccess_pt.o
obj-y += usercopy.o obj-y += usercopy.o
obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o mem32.o
obj-$(CONFIG_64BIT) += mem64.o
lib-$(CONFIG_64BIT) += uaccess_mvcos.o lib-$(CONFIG_64BIT) += uaccess_mvcos.o
lib-$(CONFIG_SMP) += spinlock.o lib-$(CONFIG_SMP) += spinlock.o

92
arch/s390/lib/mem32.S Normal file
View File

@ -0,0 +1,92 @@
/*
* String handling functions.
*
* Copyright IBM Corp. 2012
*/
#include <linux/linkage.h>
/*
* memset implementation
*
* This code corresponds to the C construct below. We do distinguish
* between clearing (c == 0) and setting a memory array (c != 0) simply
* because nearly all memset invocations in the kernel clear memory and
* the xc instruction is preferred in such cases.
*
* void *memset(void *s, int c, size_t n)
* {
* if (likely(c == 0))
* return __builtin_memset(s, 0, n);
* return __builtin_memset(s, c, n);
* }
*/
ENTRY(memset)
basr %r5,%r0
.Lmemset_base:
ltr %r4,%r4
bzr %r14
ltr %r3,%r3
jnz .Lmemset_fill
ahi %r4,-1
lr %r3,%r4
srl %r3,8
ltr %r3,%r3
lr %r1,%r2
je .Lmemset_clear_rest
.Lmemset_clear_loop:
xc 0(256,%r1),0(%r1)
la %r1,256(%r1)
brct %r3,.Lmemset_clear_loop
.Lmemset_clear_rest:
ex %r4,.Lmemset_xc-.Lmemset_base(%r5)
br %r14
.Lmemset_fill:
stc %r3,0(%r2)
chi %r4,1
lr %r1,%r2
ber %r14
ahi %r4,-2
lr %r3,%r4
srl %r3,8
ltr %r3,%r3
je .Lmemset_fill_rest
.Lmemset_fill_loop:
mvc 1(256,%r1),0(%r1)
la %r1,256(%r1)
brct %r3,.Lmemset_fill_loop
.Lmemset_fill_rest:
ex %r4,.Lmemset_mvc-.Lmemset_base(%r5)
br %r14
.Lmemset_xc:
xc 0(1,%r1),0(%r1)
.Lmemset_mvc:
mvc 1(1,%r1),0(%r1)
/*
* memcpy implementation
*
* void *memcpy(void *dest, const void *src, size_t n)
*/
ENTRY(memcpy)
basr %r5,%r0
.Lmemcpy_base:
ltr %r4,%r4
bzr %r14
ahi %r4,-1
lr %r0,%r4
srl %r0,8
ltr %r0,%r0
lr %r1,%r2
jnz .Lmemcpy_loop
.Lmemcpy_rest:
ex %r4,.Lmemcpy_mvc-.Lmemcpy_base(%r5)
br %r14
.Lmemcpy_loop:
mvc 0(256,%r1),0(%r3)
la %r1,256(%r1)
la %r3,256(%r3)
brct %r0,.Lmemcpy_loop
j .Lmemcpy_rest
.Lmemcpy_mvc:
mvc 0(1,%r1),0(%r3)

88
arch/s390/lib/mem64.S Normal file
View File

@ -0,0 +1,88 @@
/*
* String handling functions.
*
* Copyright IBM Corp. 2012
*/
#include <linux/linkage.h>
/*
* memset implementation
*
* This code corresponds to the C construct below. We do distinguish
* between clearing (c == 0) and setting a memory array (c != 0) simply
* because nearly all memset invocations in the kernel clear memory and
* the xc instruction is preferred in such cases.
*
* void *memset(void *s, int c, size_t n)
* {
* if (likely(c == 0))
* return __builtin_memset(s, 0, n);
* return __builtin_memset(s, c, n);
* }
*/
ENTRY(memset)
ltgr %r4,%r4
bzr %r14
ltgr %r3,%r3
jnz .Lmemset_fill
aghi %r4,-1
srlg %r3,%r4,8
ltgr %r3,%r3
lgr %r1,%r2
jz .Lmemset_clear_rest
.Lmemset_clear_loop:
xc 0(256,%r1),0(%r1)
la %r1,256(%r1)
brctg %r3,.Lmemset_clear_loop
.Lmemset_clear_rest:
larl %r3,.Lmemset_xc
ex %r4,0(%r3)
br %r14
.Lmemset_fill:
stc %r3,0(%r2)
cghi %r4,1
lgr %r1,%r2
ber %r14
aghi %r4,-2
srlg %r3,%r4,8
ltgr %r3,%r3
jz .Lmemset_fill_rest
.Lmemset_fill_loop:
mvc 1(256,%r1),0(%r1)
la %r1,256(%r1)
brctg %r3,.Lmemset_fill_loop
.Lmemset_fill_rest:
larl %r3,.Lmemset_mvc
ex %r4,0(%r3)
br %r14
.Lmemset_xc:
xc 0(1,%r1),0(%r1)
.Lmemset_mvc:
mvc 1(1,%r1),0(%r1)
/*
* memcpy implementation
*
* void *memcpy(void *dest, const void *src, size_t n)
*/
ENTRY(memcpy)
ltgr %r4,%r4
bzr %r14
aghi %r4,-1
srlg %r5,%r4,8
ltgr %r5,%r5
lgr %r1,%r2
jnz .Lmemcpy_loop
.Lmemcpy_rest:
larl %r5,.Lmemcpy_mvc
ex %r4,0(%r5)
br %r14
.Lmemcpy_loop:
mvc 0(256,%r1),0(%r3)
la %r1,256(%r1)
la %r3,256(%r3)
brctg %r5,.Lmemcpy_loop
j .Lmemcpy_rest
.Lmemcpy_mvc:
mvc 0(1,%r1),0(%r3)

View File

@ -43,11 +43,7 @@ static inline char *__strnend(const char *s, size_t n)
*/ */
size_t strlen(const char *s) size_t strlen(const char *s)
{ {
#if __GNUC__ < 4
return __strend(s) - s; return __strend(s) - s;
#else
return __builtin_strlen(s);
#endif
} }
EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strlen);
@ -73,7 +69,6 @@ EXPORT_SYMBOL(strnlen);
*/ */
char *strcpy(char *dest, const char *src) char *strcpy(char *dest, const char *src)
{ {
#if __GNUC__ < 4
register int r0 asm("0") = 0; register int r0 asm("0") = 0;
char *ret = dest; char *ret = dest;
@ -82,9 +77,6 @@ char *strcpy(char *dest, const char *src)
: "+&a" (dest), "+&a" (src) : "d" (r0) : "+&a" (dest), "+&a" (src) : "d" (r0)
: "cc", "memory" ); : "cc", "memory" );
return ret; return ret;
#else
return __builtin_strcpy(dest, src);
#endif
} }
EXPORT_SYMBOL(strcpy); EXPORT_SYMBOL(strcpy);
@ -106,7 +98,7 @@ size_t strlcpy(char *dest, const char *src, size_t size)
if (size) { if (size) {
size_t len = (ret >= size) ? size-1 : ret; size_t len = (ret >= size) ? size-1 : ret;
dest[len] = '\0'; dest[len] = '\0';
__builtin_memcpy(dest, src, len); memcpy(dest, src, len);
} }
return ret; return ret;
} }
@ -124,8 +116,8 @@ EXPORT_SYMBOL(strlcpy);
char *strncpy(char *dest, const char *src, size_t n) char *strncpy(char *dest, const char *src, size_t n)
{ {
size_t len = __strnend(src, n) - src; size_t len = __strnend(src, n) - src;
__builtin_memset(dest + len, 0, n - len); memset(dest + len, 0, n - len);
__builtin_memcpy(dest, src, len); memcpy(dest, src, len);
return dest; return dest;
} }
EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strncpy);
@ -171,7 +163,7 @@ size_t strlcat(char *dest, const char *src, size_t n)
if (len >= n) if (len >= n)
len = n - 1; len = n - 1;
dest[len] = '\0'; dest[len] = '\0';
__builtin_memcpy(dest, src, len); memcpy(dest, src, len);
} }
return res; return res;
} }
@ -194,7 +186,7 @@ char *strncat(char *dest, const char *src, size_t n)
char *p = __strend(dest); char *p = __strend(dest);
p[len] = '\0'; p[len] = '\0';
__builtin_memcpy(p, src, len); memcpy(p, src, len);
return dest; return dest;
} }
EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strncat);
@ -348,41 +340,3 @@ void *memscan(void *s, int c, size_t n)
return (void *) ret; return (void *) ret;
} }
EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(memscan);
/**
* memcpy - Copy one area of memory to another
* @dest: Where to copy to
* @src: Where to copy from
* @n: The size of the area.
*
* returns a pointer to @dest
*/
void *memcpy(void *dest, const void *src, size_t n)
{
return __builtin_memcpy(dest, src, n);
}
EXPORT_SYMBOL(memcpy);
/**
* memset - Fill a region of memory with the given value
* @s: Pointer to the start of the area.
* @c: The byte to fill the area with
* @n: The size of the area.
*
* returns a pointer to @s
*/
void *memset(void *s, int c, size_t n)
{
char *xs;
if (c == 0)
return __builtin_memset(s, 0, n);
xs = (char *) s;
if (n > 0)
do {
*xs++ = c;
} while (--n > 0);
return s;
}
EXPORT_SYMBOL(memset);