162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Optimized string functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * S390 version 662306a36Sopenharmony_ci * Copyright IBM Corp. 2004 762306a36Sopenharmony_ci * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define IN_ARCH_STRING_C 1 1162306a36Sopenharmony_ci#ifndef __NO_FORTIFY 1262306a36Sopenharmony_ci# define __NO_FORTIFY 1362306a36Sopenharmony_ci#endif 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Helper functions to find the end of a string 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic inline char *__strend(const char *s) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci unsigned long e = 0; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci asm volatile( 2762306a36Sopenharmony_ci " lghi 0,0\n" 2862306a36Sopenharmony_ci "0: srst %[e],%[s]\n" 2962306a36Sopenharmony_ci " jo 0b\n" 3062306a36Sopenharmony_ci : [e] "+&a" (e), [s] "+&a" (s) 3162306a36Sopenharmony_ci : 3262306a36Sopenharmony_ci : "cc", "memory", "0"); 3362306a36Sopenharmony_ci return (char *)e; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic inline char *__strnend(const char *s, size_t n) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci const char *p = s + n; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci asm volatile( 4162306a36Sopenharmony_ci " lghi 0,0\n" 4262306a36Sopenharmony_ci "0: srst %[p],%[s]\n" 4362306a36Sopenharmony_ci " jo 0b\n" 4462306a36Sopenharmony_ci : [p] "+&d" (p), [s] "+&a" (s) 4562306a36Sopenharmony_ci : 4662306a36Sopenharmony_ci : "cc", "memory", "0"); 4762306a36Sopenharmony_ci return (char *)p; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** 5162306a36Sopenharmony_ci * strlen - Find the length of a string 5262306a36Sopenharmony_ci * @s: The string to be sized 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * returns the length of @s 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRLEN 5762306a36Sopenharmony_cisize_t strlen(const char *s) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return __strend(s) - s; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ciEXPORT_SYMBOL(strlen); 6262306a36Sopenharmony_ci#endif 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/** 6562306a36Sopenharmony_ci * strnlen - Find the length of a length-limited string 6662306a36Sopenharmony_ci * @s: The string to be sized 6762306a36Sopenharmony_ci * @n: The maximum number of bytes to search 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * returns the minimum of the length of @s and @n 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRNLEN 7262306a36Sopenharmony_cisize_t strnlen(const char *s, size_t n) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci return __strnend(s, n) - s; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL(strnlen); 7762306a36Sopenharmony_ci#endif 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * strcpy - Copy a %NUL terminated string 8162306a36Sopenharmony_ci * @dest: Where to copy the string to 8262306a36Sopenharmony_ci * @src: Where to copy the string from 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * returns a pointer to @dest 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRCPY 8762306a36Sopenharmony_cichar *strcpy(char *dest, const char *src) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci char *ret = dest; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci asm volatile( 9262306a36Sopenharmony_ci " lghi 0,0\n" 9362306a36Sopenharmony_ci "0: mvst %[dest],%[src]\n" 9462306a36Sopenharmony_ci " jo 0b\n" 9562306a36Sopenharmony_ci : [dest] "+&a" (dest), [src] "+&a" (src) 9662306a36Sopenharmony_ci : 9762306a36Sopenharmony_ci : "cc", "memory", "0"); 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL(strcpy); 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * strncpy - Copy a length-limited, %NUL-terminated string 10562306a36Sopenharmony_ci * @dest: Where to copy the string to 10662306a36Sopenharmony_ci * @src: Where to copy the string from 10762306a36Sopenharmony_ci * @n: The maximum number of bytes to copy 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * The result is not %NUL-terminated if the source exceeds 11062306a36Sopenharmony_ci * @n bytes. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRNCPY 11362306a36Sopenharmony_cichar *strncpy(char *dest, const char *src, size_t n) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci size_t len = __strnend(src, n) - src; 11662306a36Sopenharmony_ci memset(dest + len, 0, n - len); 11762306a36Sopenharmony_ci memcpy(dest, src, len); 11862306a36Sopenharmony_ci return dest; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ciEXPORT_SYMBOL(strncpy); 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * strcat - Append one %NUL-terminated string to another 12562306a36Sopenharmony_ci * @dest: The string to be appended to 12662306a36Sopenharmony_ci * @src: The string to append to it 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * returns a pointer to @dest 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRCAT 13162306a36Sopenharmony_cichar *strcat(char *dest, const char *src) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned long dummy = 0; 13462306a36Sopenharmony_ci char *ret = dest; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci asm volatile( 13762306a36Sopenharmony_ci " lghi 0,0\n" 13862306a36Sopenharmony_ci "0: srst %[dummy],%[dest]\n" 13962306a36Sopenharmony_ci " jo 0b\n" 14062306a36Sopenharmony_ci "1: mvst %[dummy],%[src]\n" 14162306a36Sopenharmony_ci " jo 1b\n" 14262306a36Sopenharmony_ci : [dummy] "+&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src) 14362306a36Sopenharmony_ci : 14462306a36Sopenharmony_ci : "cc", "memory", "0"); 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL(strcat); 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/** 15162306a36Sopenharmony_ci * strlcat - Append a length-limited, %NUL-terminated string to another 15262306a36Sopenharmony_ci * @dest: The string to be appended to 15362306a36Sopenharmony_ci * @src: The string to append to it 15462306a36Sopenharmony_ci * @n: The size of the destination buffer. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRLCAT 15762306a36Sopenharmony_cisize_t strlcat(char *dest, const char *src, size_t n) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci size_t dsize = __strend(dest) - dest; 16062306a36Sopenharmony_ci size_t len = __strend(src) - src; 16162306a36Sopenharmony_ci size_t res = dsize + len; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (dsize < n) { 16462306a36Sopenharmony_ci dest += dsize; 16562306a36Sopenharmony_ci n -= dsize; 16662306a36Sopenharmony_ci if (len >= n) 16762306a36Sopenharmony_ci len = n - 1; 16862306a36Sopenharmony_ci dest[len] = '\0'; 16962306a36Sopenharmony_ci memcpy(dest, src, len); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci return res; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL(strlcat); 17462306a36Sopenharmony_ci#endif 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * strncat - Append a length-limited, %NUL-terminated string to another 17862306a36Sopenharmony_ci * @dest: The string to be appended to 17962306a36Sopenharmony_ci * @src: The string to append to it 18062306a36Sopenharmony_ci * @n: The maximum numbers of bytes to copy 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * returns a pointer to @dest 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Note that in contrast to strncpy, strncat ensures the result is 18562306a36Sopenharmony_ci * terminated. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRNCAT 18862306a36Sopenharmony_cichar *strncat(char *dest, const char *src, size_t n) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci size_t len = __strnend(src, n) - src; 19162306a36Sopenharmony_ci char *p = __strend(dest); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci p[len] = '\0'; 19462306a36Sopenharmony_ci memcpy(p, src, len); 19562306a36Sopenharmony_ci return dest; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ciEXPORT_SYMBOL(strncat); 19862306a36Sopenharmony_ci#endif 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/** 20162306a36Sopenharmony_ci * strcmp - Compare two strings 20262306a36Sopenharmony_ci * @s1: One string 20362306a36Sopenharmony_ci * @s2: Another string 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * returns 0 if @s1 and @s2 are equal, 20662306a36Sopenharmony_ci * < 0 if @s1 is less than @s2 20762306a36Sopenharmony_ci * > 0 if @s1 is greater than @s2 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRCMP 21062306a36Sopenharmony_ciint strcmp(const char *s1, const char *s2) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int ret = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci asm volatile( 21562306a36Sopenharmony_ci " lghi 0,0\n" 21662306a36Sopenharmony_ci "0: clst %[s1],%[s2]\n" 21762306a36Sopenharmony_ci " jo 0b\n" 21862306a36Sopenharmony_ci " je 1f\n" 21962306a36Sopenharmony_ci " ic %[ret],0(%[s1])\n" 22062306a36Sopenharmony_ci " ic 0,0(%[s2])\n" 22162306a36Sopenharmony_ci " sr %[ret],0\n" 22262306a36Sopenharmony_ci "1:" 22362306a36Sopenharmony_ci : [ret] "+&d" (ret), [s1] "+&a" (s1), [s2] "+&a" (s2) 22462306a36Sopenharmony_ci : 22562306a36Sopenharmony_ci : "cc", "memory", "0"); 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL(strcmp); 22962306a36Sopenharmony_ci#endif 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline int clcle(const char *s1, unsigned long l1, 23262306a36Sopenharmony_ci const char *s2, unsigned long l2) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci union register_pair r1 = { .even = (unsigned long)s1, .odd = l1, }; 23562306a36Sopenharmony_ci union register_pair r3 = { .even = (unsigned long)s2, .odd = l2, }; 23662306a36Sopenharmony_ci int cc; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci asm volatile( 23962306a36Sopenharmony_ci "0: clcle %[r1],%[r3],0\n" 24062306a36Sopenharmony_ci " jo 0b\n" 24162306a36Sopenharmony_ci " ipm %[cc]\n" 24262306a36Sopenharmony_ci " srl %[cc],28\n" 24362306a36Sopenharmony_ci : [cc] "=&d" (cc), [r1] "+&d" (r1.pair), [r3] "+&d" (r3.pair) 24462306a36Sopenharmony_ci : 24562306a36Sopenharmony_ci : "cc", "memory"); 24662306a36Sopenharmony_ci return cc; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * strstr - Find the first substring in a %NUL terminated string 25162306a36Sopenharmony_ci * @s1: The string to be searched 25262306a36Sopenharmony_ci * @s2: The string to search for 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci#ifdef __HAVE_ARCH_STRSTR 25562306a36Sopenharmony_cichar *strstr(const char *s1, const char *s2) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci int l1, l2; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci l2 = __strend(s2) - s2; 26062306a36Sopenharmony_ci if (!l2) 26162306a36Sopenharmony_ci return (char *) s1; 26262306a36Sopenharmony_ci l1 = __strend(s1) - s1; 26362306a36Sopenharmony_ci while (l1-- >= l2) { 26462306a36Sopenharmony_ci int cc; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci cc = clcle(s1, l2, s2, l2); 26762306a36Sopenharmony_ci if (!cc) 26862306a36Sopenharmony_ci return (char *) s1; 26962306a36Sopenharmony_ci s1++; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci return NULL; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ciEXPORT_SYMBOL(strstr); 27462306a36Sopenharmony_ci#endif 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * memchr - Find a character in an area of memory. 27862306a36Sopenharmony_ci * @s: The memory area 27962306a36Sopenharmony_ci * @c: The byte to search for 28062306a36Sopenharmony_ci * @n: The size of the area. 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * returns the address of the first occurrence of @c, or %NULL 28362306a36Sopenharmony_ci * if @c is not found 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci#ifdef __HAVE_ARCH_MEMCHR 28662306a36Sopenharmony_civoid *memchr(const void *s, int c, size_t n) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci const void *ret = s + n; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci asm volatile( 29162306a36Sopenharmony_ci " lgr 0,%[c]\n" 29262306a36Sopenharmony_ci "0: srst %[ret],%[s]\n" 29362306a36Sopenharmony_ci " jo 0b\n" 29462306a36Sopenharmony_ci " jl 1f\n" 29562306a36Sopenharmony_ci " la %[ret],0\n" 29662306a36Sopenharmony_ci "1:" 29762306a36Sopenharmony_ci : [ret] "+&a" (ret), [s] "+&a" (s) 29862306a36Sopenharmony_ci : [c] "d" (c) 29962306a36Sopenharmony_ci : "cc", "memory", "0"); 30062306a36Sopenharmony_ci return (void *) ret; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ciEXPORT_SYMBOL(memchr); 30362306a36Sopenharmony_ci#endif 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/** 30662306a36Sopenharmony_ci * memcmp - Compare two areas of memory 30762306a36Sopenharmony_ci * @s1: One area of memory 30862306a36Sopenharmony_ci * @s2: Another area of memory 30962306a36Sopenharmony_ci * @n: The size of the area. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci#ifdef __HAVE_ARCH_MEMCMP 31262306a36Sopenharmony_ciint memcmp(const void *s1, const void *s2, size_t n) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int ret; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = clcle(s1, n, s2, n); 31762306a36Sopenharmony_ci if (ret) 31862306a36Sopenharmony_ci ret = ret == 1 ? -1 : 1; 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ciEXPORT_SYMBOL(memcmp); 32262306a36Sopenharmony_ci#endif 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * memscan - Find a character in an area of memory. 32662306a36Sopenharmony_ci * @s: The memory area 32762306a36Sopenharmony_ci * @c: The byte to search for 32862306a36Sopenharmony_ci * @n: The size of the area. 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * returns the address of the first occurrence of @c, or 1 byte past 33162306a36Sopenharmony_ci * the area if @c is not found 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci#ifdef __HAVE_ARCH_MEMSCAN 33462306a36Sopenharmony_civoid *memscan(void *s, int c, size_t n) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci const void *ret = s + n; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci asm volatile( 33962306a36Sopenharmony_ci " lgr 0,%[c]\n" 34062306a36Sopenharmony_ci "0: srst %[ret],%[s]\n" 34162306a36Sopenharmony_ci " jo 0b\n" 34262306a36Sopenharmony_ci : [ret] "+&a" (ret), [s] "+&a" (s) 34362306a36Sopenharmony_ci : [c] "d" (c) 34462306a36Sopenharmony_ci : "cc", "memory", "0"); 34562306a36Sopenharmony_ci return (void *)ret; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ciEXPORT_SYMBOL(memscan); 34862306a36Sopenharmony_ci#endif 349