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