xref: /linux/arch/s390/lib/string.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *    Optimized string functions
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  S390 version
6a53c8fabSHeiko Carstens  *    Copyright IBM Corp. 2004
71da177e4SLinus Torvalds  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #define IN_ARCH_STRING_C 1
11*cfecea6eSKees Cook #ifndef __NO_FORTIFY
12*cfecea6eSKees Cook # define __NO_FORTIFY
13*cfecea6eSKees Cook #endif
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/types.h>
16d3217967SPaul Gortmaker #include <linux/string.h>
17d3217967SPaul Gortmaker #include <linux/export.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /*
201da177e4SLinus Torvalds  * Helper functions to find the end of a string
211da177e4SLinus Torvalds  */
__strend(const char * s)221da177e4SLinus Torvalds static inline char *__strend(const char *s)
231da177e4SLinus Torvalds {
248cf23c8eSHeiko Carstens 	unsigned long e = 0;
251da177e4SLinus Torvalds 
268cf23c8eSHeiko Carstens 	asm volatile(
278cf23c8eSHeiko Carstens 		"	lghi	0,0\n"
288cf23c8eSHeiko Carstens 		"0:	srst	%[e],%[s]\n"
298cf23c8eSHeiko Carstens 		"	jo	0b\n"
308cf23c8eSHeiko Carstens 		: [e] "+&a" (e), [s] "+&a" (s)
318cf23c8eSHeiko Carstens 		:
328cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
338cf23c8eSHeiko Carstens 	return (char *)e;
341da177e4SLinus Torvalds }
351da177e4SLinus Torvalds 
__strnend(const char * s,size_t n)361da177e4SLinus Torvalds static inline char *__strnend(const char *s, size_t n)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds 	const char *p = s + n;
391da177e4SLinus Torvalds 
408cf23c8eSHeiko Carstens 	asm volatile(
418cf23c8eSHeiko Carstens 		"	lghi	0,0\n"
428cf23c8eSHeiko Carstens 		"0:	srst	%[p],%[s]\n"
438cf23c8eSHeiko Carstens 		"	jo	0b\n"
448cf23c8eSHeiko Carstens 		: [p] "+&d" (p), [s] "+&a" (s)
458cf23c8eSHeiko Carstens 		:
468cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
471da177e4SLinus Torvalds 	return (char *)p;
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /**
511da177e4SLinus Torvalds  * strlen - Find the length of a string
521da177e4SLinus Torvalds  * @s: The string to be sized
531da177e4SLinus Torvalds  *
541da177e4SLinus Torvalds  * returns the length of @s
551da177e4SLinus Torvalds  */
567e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRLEN
strlen(const char * s)571da177e4SLinus Torvalds size_t strlen(const char *s)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	return __strend(s) - s;
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds EXPORT_SYMBOL(strlen);
627e0d92f0SVasily Gorbik #endif
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /**
651da177e4SLinus Torvalds  * strnlen - Find the length of a length-limited string
661da177e4SLinus Torvalds  * @s: The string to be sized
671da177e4SLinus Torvalds  * @n: The maximum number of bytes to search
681da177e4SLinus Torvalds  *
691da177e4SLinus Torvalds  * returns the minimum of the length of @s and @n
701da177e4SLinus Torvalds  */
717e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRNLEN
strnlen(const char * s,size_t n)721da177e4SLinus Torvalds size_t strnlen(const char *s, size_t n)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	return __strnend(s, n) - s;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds EXPORT_SYMBOL(strnlen);
777e0d92f0SVasily Gorbik #endif
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds /**
801da177e4SLinus Torvalds  * strcpy - Copy a %NUL terminated string
811da177e4SLinus Torvalds  * @dest: Where to copy the string to
821da177e4SLinus Torvalds  * @src: Where to copy the string from
831da177e4SLinus Torvalds  *
841da177e4SLinus Torvalds  * returns a pointer to @dest
851da177e4SLinus Torvalds  */
867e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRCPY
strcpy(char * dest,const char * src)871da177e4SLinus Torvalds char *strcpy(char *dest, const char *src)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	char *ret = dest;
901da177e4SLinus Torvalds 
918cf23c8eSHeiko Carstens 	asm volatile(
928cf23c8eSHeiko Carstens 		"	lghi	0,0\n"
938cf23c8eSHeiko Carstens 		"0:	mvst	%[dest],%[src]\n"
948cf23c8eSHeiko Carstens 		"	jo	0b\n"
958cf23c8eSHeiko Carstens 		: [dest] "+&a" (dest), [src] "+&a" (src)
968cf23c8eSHeiko Carstens 		:
978cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
981da177e4SLinus Torvalds 	return ret;
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds EXPORT_SYMBOL(strcpy);
1017e0d92f0SVasily Gorbik #endif
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds /**
1041da177e4SLinus Torvalds  * strncpy - Copy a length-limited, %NUL-terminated string
1051da177e4SLinus Torvalds  * @dest: Where to copy the string to
1061da177e4SLinus Torvalds  * @src: Where to copy the string from
1071da177e4SLinus Torvalds  * @n: The maximum number of bytes to copy
1081da177e4SLinus Torvalds  *
1091da177e4SLinus Torvalds  * The result is not %NUL-terminated if the source exceeds
1101da177e4SLinus Torvalds  * @n bytes.
1111da177e4SLinus Torvalds  */
1127e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRNCPY
strncpy(char * dest,const char * src,size_t n)1131da177e4SLinus Torvalds char *strncpy(char *dest, const char *src, size_t n)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds 	size_t len = __strnend(src, n) - src;
116535c611dSHeiko Carstens 	memset(dest + len, 0, n - len);
117535c611dSHeiko Carstens 	memcpy(dest, src, len);
1181da177e4SLinus Torvalds 	return dest;
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds EXPORT_SYMBOL(strncpy);
1217e0d92f0SVasily Gorbik #endif
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds /**
1241da177e4SLinus Torvalds  * strcat - Append one %NUL-terminated string to another
1251da177e4SLinus Torvalds  * @dest: The string to be appended to
1261da177e4SLinus Torvalds  * @src: The string to append to it
1271da177e4SLinus Torvalds  *
1281da177e4SLinus Torvalds  * returns a pointer to @dest
1291da177e4SLinus Torvalds  */
1307e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRCAT
strcat(char * dest,const char * src)1311da177e4SLinus Torvalds char *strcat(char *dest, const char *src)
1321da177e4SLinus Torvalds {
1338cf23c8eSHeiko Carstens 	unsigned long dummy = 0;
1341da177e4SLinus Torvalds 	char *ret = dest;
1351da177e4SLinus Torvalds 
1368cf23c8eSHeiko Carstens 	asm volatile(
1378cf23c8eSHeiko Carstens 		"	lghi	0,0\n"
1388cf23c8eSHeiko Carstens 		"0:	srst	%[dummy],%[dest]\n"
1391da177e4SLinus Torvalds 		"	jo	0b\n"
1408cf23c8eSHeiko Carstens 		"1:	mvst	%[dummy],%[src]\n"
1418cf23c8eSHeiko Carstens 		"	jo	1b\n"
142a0ae5cd2SHeiko Carstens 		: [dummy] "+&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src)
1438cf23c8eSHeiko Carstens 		:
1448cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
1451da177e4SLinus Torvalds 	return ret;
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds EXPORT_SYMBOL(strcat);
1487e0d92f0SVasily Gorbik #endif
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds /**
1511da177e4SLinus Torvalds  * strlcat - Append a length-limited, %NUL-terminated string to another
1521da177e4SLinus Torvalds  * @dest: The string to be appended to
1531da177e4SLinus Torvalds  * @src: The string to append to it
1541da177e4SLinus Torvalds  * @n: The size of the destination buffer.
1551da177e4SLinus Torvalds  */
1567e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRLCAT
strlcat(char * dest,const char * src,size_t n)1571da177e4SLinus Torvalds size_t strlcat(char *dest, const char *src, size_t n)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	size_t dsize = __strend(dest) - dest;
1601da177e4SLinus Torvalds 	size_t len = __strend(src) - src;
1611da177e4SLinus Torvalds 	size_t res = dsize + len;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	if (dsize < n) {
1641da177e4SLinus Torvalds 		dest += dsize;
1651da177e4SLinus Torvalds 		n -= dsize;
1661da177e4SLinus Torvalds 		if (len >= n)
1671da177e4SLinus Torvalds 			len = n - 1;
1681da177e4SLinus Torvalds 		dest[len] = '\0';
169535c611dSHeiko Carstens 		memcpy(dest, src, len);
1701da177e4SLinus Torvalds 	}
1711da177e4SLinus Torvalds 	return res;
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds EXPORT_SYMBOL(strlcat);
1747e0d92f0SVasily Gorbik #endif
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds /**
1771da177e4SLinus Torvalds  * strncat - Append a length-limited, %NUL-terminated string to another
1781da177e4SLinus Torvalds  * @dest: The string to be appended to
1791da177e4SLinus Torvalds  * @src: The string to append to it
1801da177e4SLinus Torvalds  * @n: The maximum numbers of bytes to copy
1811da177e4SLinus Torvalds  *
1821da177e4SLinus Torvalds  * returns a pointer to @dest
1831da177e4SLinus Torvalds  *
1841da177e4SLinus Torvalds  * Note that in contrast to strncpy, strncat ensures the result is
1851da177e4SLinus Torvalds  * terminated.
1861da177e4SLinus Torvalds  */
1877e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRNCAT
strncat(char * dest,const char * src,size_t n)1881da177e4SLinus Torvalds char *strncat(char *dest, const char *src, size_t n)
1891da177e4SLinus Torvalds {
1901da177e4SLinus Torvalds 	size_t len = __strnend(src, n) - src;
1911da177e4SLinus Torvalds 	char *p = __strend(dest);
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	p[len] = '\0';
194535c611dSHeiko Carstens 	memcpy(p, src, len);
1951da177e4SLinus Torvalds 	return dest;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds EXPORT_SYMBOL(strncat);
1987e0d92f0SVasily Gorbik #endif
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds /**
2011da177e4SLinus Torvalds  * strcmp - Compare two strings
20249913f1fSHeiko Carstens  * @s1: One string
20349913f1fSHeiko Carstens  * @s2: Another string
2041da177e4SLinus Torvalds  *
20549913f1fSHeiko Carstens  * returns   0 if @s1 and @s2 are equal,
20649913f1fSHeiko Carstens  *	   < 0 if @s1 is less than @s2
20749913f1fSHeiko Carstens  *	   > 0 if @s1 is greater than @s2
2081da177e4SLinus Torvalds  */
2097e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRCMP
strcmp(const char * s1,const char * s2)21049913f1fSHeiko Carstens int strcmp(const char *s1, const char *s2)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds 	int ret = 0;
2131da177e4SLinus Torvalds 
2148cf23c8eSHeiko Carstens 	asm volatile(
2158cf23c8eSHeiko Carstens 		"	lghi	0,0\n"
2168cf23c8eSHeiko Carstens 		"0:	clst	%[s1],%[s2]\n"
2171da177e4SLinus Torvalds 		"	jo	0b\n"
2181da177e4SLinus Torvalds 		"	je	1f\n"
2198cf23c8eSHeiko Carstens 		"	ic	%[ret],0(%[s1])\n"
2208cf23c8eSHeiko Carstens 		"	ic	0,0(%[s2])\n"
2218cf23c8eSHeiko Carstens 		"	sr	%[ret],0\n"
2221da177e4SLinus Torvalds 		"1:"
2238cf23c8eSHeiko Carstens 		: [ret] "+&d" (ret), [s1] "+&a" (s1), [s2] "+&a" (s2)
2248cf23c8eSHeiko Carstens 		:
2258cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
2261da177e4SLinus Torvalds 	return ret;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds EXPORT_SYMBOL(strcmp);
2297e0d92f0SVasily Gorbik #endif
2301da177e4SLinus Torvalds 
clcle(const char * s1,unsigned long l1,const char * s2,unsigned long l2)231db7f5eefSHeiko Carstens static inline int clcle(const char *s1, unsigned long l1,
232e2efc424SChristian Borntraeger 			const char *s2, unsigned long l2)
233db7f5eefSHeiko Carstens {
2348cf23c8eSHeiko Carstens 	union register_pair r1 = { .even = (unsigned long)s1, .odd = l1, };
2358cf23c8eSHeiko Carstens 	union register_pair r3 = { .even = (unsigned long)s2, .odd = l2, };
236db7f5eefSHeiko Carstens 	int cc;
237db7f5eefSHeiko Carstens 
2388cf23c8eSHeiko Carstens 	asm volatile(
2398cf23c8eSHeiko Carstens 		"0:	clcle	%[r1],%[r3],0\n"
240db7f5eefSHeiko Carstens 		"	jo	0b\n"
2418cf23c8eSHeiko Carstens 		"	ipm	%[cc]\n"
2428cf23c8eSHeiko Carstens 		"	srl	%[cc],28\n"
2438cf23c8eSHeiko Carstens 		: [cc] "=&d" (cc), [r1] "+&d" (r1.pair), [r3] "+&d" (r3.pair)
2448cf23c8eSHeiko Carstens 		:
2458cf23c8eSHeiko Carstens 		: "cc", "memory");
246db7f5eefSHeiko Carstens 	return cc;
247db7f5eefSHeiko Carstens }
248db7f5eefSHeiko Carstens 
2491da177e4SLinus Torvalds /**
2501da177e4SLinus Torvalds  * strstr - Find the first substring in a %NUL terminated string
2511da177e4SLinus Torvalds  * @s1: The string to be searched
2521da177e4SLinus Torvalds  * @s2: The string to search for
2531da177e4SLinus Torvalds  */
2547e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_STRSTR
strstr(const char * s1,const char * s2)2551da177e4SLinus Torvalds char *strstr(const char *s1, const char *s2)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	int l1, l2;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	l2 = __strend(s2) - s2;
2601da177e4SLinus Torvalds 	if (!l2)
2611da177e4SLinus Torvalds 		return (char *) s1;
2621da177e4SLinus Torvalds 	l1 = __strend(s1) - s1;
2631da177e4SLinus Torvalds 	while (l1-- >= l2) {
264e2efc424SChristian Borntraeger 		int cc;
2651da177e4SLinus Torvalds 
266e2efc424SChristian Borntraeger 		cc = clcle(s1, l2, s2, l2);
2671da177e4SLinus Torvalds 		if (!cc)
2681da177e4SLinus Torvalds 			return (char *) s1;
2691da177e4SLinus Torvalds 		s1++;
2701da177e4SLinus Torvalds 	}
271d2c993d8SHeiko Carstens 	return NULL;
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds EXPORT_SYMBOL(strstr);
2747e0d92f0SVasily Gorbik #endif
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds /**
2771da177e4SLinus Torvalds  * memchr - Find a character in an area of memory.
2781da177e4SLinus Torvalds  * @s: The memory area
2791da177e4SLinus Torvalds  * @c: The byte to search for
2801da177e4SLinus Torvalds  * @n: The size of the area.
2811da177e4SLinus Torvalds  *
2821da177e4SLinus Torvalds  * returns the address of the first occurrence of @c, or %NULL
2831da177e4SLinus Torvalds  * if @c is not found
2841da177e4SLinus Torvalds  */
2857e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_MEMCHR
memchr(const void * s,int c,size_t n)2861da177e4SLinus Torvalds void *memchr(const void *s, int c, size_t n)
2871da177e4SLinus Torvalds {
2881da177e4SLinus Torvalds 	const void *ret = s + n;
2891da177e4SLinus Torvalds 
2908cf23c8eSHeiko Carstens 	asm volatile(
2918cf23c8eSHeiko Carstens 		"	lgr	0,%[c]\n"
2928cf23c8eSHeiko Carstens 		"0:	srst	%[ret],%[s]\n"
2931da177e4SLinus Torvalds 		"	jo	0b\n"
2941da177e4SLinus Torvalds 		"	jl	1f\n"
2958cf23c8eSHeiko Carstens 		"	la	%[ret],0\n"
2961da177e4SLinus Torvalds 		"1:"
2978cf23c8eSHeiko Carstens 		: [ret] "+&a" (ret), [s] "+&a" (s)
2988cf23c8eSHeiko Carstens 		: [c] "d" (c)
2998cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
3001da177e4SLinus Torvalds 	return (void *) ret;
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds EXPORT_SYMBOL(memchr);
3037e0d92f0SVasily Gorbik #endif
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds /**
3061da177e4SLinus Torvalds  * memcmp - Compare two areas of memory
30749913f1fSHeiko Carstens  * @s1: One area of memory
30849913f1fSHeiko Carstens  * @s2: Another area of memory
3094aa32ee3SJulian Wiedmann  * @n: The size of the area.
3101da177e4SLinus Torvalds  */
3117e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_MEMCMP
memcmp(const void * s1,const void * s2,size_t n)31249913f1fSHeiko Carstens int memcmp(const void *s1, const void *s2, size_t n)
3131da177e4SLinus Torvalds {
314e2efc424SChristian Borntraeger 	int ret;
3151da177e4SLinus Torvalds 
31649913f1fSHeiko Carstens 	ret = clcle(s1, n, s2, n);
3171da177e4SLinus Torvalds 	if (ret)
318e2efc424SChristian Borntraeger 		ret = ret == 1 ? -1 : 1;
3191da177e4SLinus Torvalds 	return ret;
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds EXPORT_SYMBOL(memcmp);
3227e0d92f0SVasily Gorbik #endif
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds /**
3251da177e4SLinus Torvalds  * memscan - Find a character in an area of memory.
3261da177e4SLinus Torvalds  * @s: The memory area
3271da177e4SLinus Torvalds  * @c: The byte to search for
3281da177e4SLinus Torvalds  * @n: The size of the area.
3291da177e4SLinus Torvalds  *
3301da177e4SLinus Torvalds  * returns the address of the first occurrence of @c, or 1 byte past
3311da177e4SLinus Torvalds  * the area if @c is not found
3321da177e4SLinus Torvalds  */
3337e0d92f0SVasily Gorbik #ifdef __HAVE_ARCH_MEMSCAN
memscan(void * s,int c,size_t n)3341da177e4SLinus Torvalds void *memscan(void *s, int c, size_t n)
3351da177e4SLinus Torvalds {
3361da177e4SLinus Torvalds 	const void *ret = s + n;
3371da177e4SLinus Torvalds 
3388cf23c8eSHeiko Carstens 	asm volatile(
3398cf23c8eSHeiko Carstens 		"	lgr	0,%[c]\n"
3408cf23c8eSHeiko Carstens 		"0:	srst	%[ret],%[s]\n"
3411da177e4SLinus Torvalds 		"	jo	0b\n"
3428cf23c8eSHeiko Carstens 		: [ret] "+&a" (ret), [s] "+&a" (s)
3438cf23c8eSHeiko Carstens 		: [c] "d" (c)
3448cf23c8eSHeiko Carstens 		: "cc", "memory", "0");
3451da177e4SLinus Torvalds 	return (void *)ret;
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds EXPORT_SYMBOL(memscan);
3487e0d92f0SVasily Gorbik #endif
349