1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Optimized string functions 4 * 5 * S390 version 6 * Copyright IBM Corp. 2004 7 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 8 */ 9 10 #define IN_ARCH_STRING_C 1 11 12 #include <linux/types.h> 13 #include <linux/string.h> 14 #include <linux/export.h> 15 16 /* 17 * Helper functions to find the end of a string 18 */ 19 static inline char *__strend(const char *s) 20 { 21 unsigned long e = 0; 22 23 asm volatile( 24 " lghi 0,0\n" 25 "0: srst %[e],%[s]\n" 26 " jo 0b\n" 27 : [e] "+&a" (e), [s] "+&a" (s) 28 : 29 : "cc", "memory", "0"); 30 return (char *)e; 31 } 32 33 static inline char *__strnend(const char *s, size_t n) 34 { 35 const char *p = s + n; 36 37 asm volatile( 38 " lghi 0,0\n" 39 "0: srst %[p],%[s]\n" 40 " jo 0b\n" 41 : [p] "+&d" (p), [s] "+&a" (s) 42 : 43 : "cc", "memory", "0"); 44 return (char *)p; 45 } 46 47 /** 48 * strlen - Find the length of a string 49 * @s: The string to be sized 50 * 51 * returns the length of @s 52 */ 53 #ifdef __HAVE_ARCH_STRLEN 54 size_t strlen(const char *s) 55 { 56 return __strend(s) - s; 57 } 58 EXPORT_SYMBOL(strlen); 59 #endif 60 61 /** 62 * strnlen - Find the length of a length-limited string 63 * @s: The string to be sized 64 * @n: The maximum number of bytes to search 65 * 66 * returns the minimum of the length of @s and @n 67 */ 68 #ifdef __HAVE_ARCH_STRNLEN 69 size_t strnlen(const char *s, size_t n) 70 { 71 return __strnend(s, n) - s; 72 } 73 EXPORT_SYMBOL(strnlen); 74 #endif 75 76 /** 77 * strcpy - Copy a %NUL terminated string 78 * @dest: Where to copy the string to 79 * @src: Where to copy the string from 80 * 81 * returns a pointer to @dest 82 */ 83 #ifdef __HAVE_ARCH_STRCPY 84 char *strcpy(char *dest, const char *src) 85 { 86 char *ret = dest; 87 88 asm volatile( 89 " lghi 0,0\n" 90 "0: mvst %[dest],%[src]\n" 91 " jo 0b\n" 92 : [dest] "+&a" (dest), [src] "+&a" (src) 93 : 94 : "cc", "memory", "0"); 95 return ret; 96 } 97 EXPORT_SYMBOL(strcpy); 98 #endif 99 100 /** 101 * strncpy - Copy a length-limited, %NUL-terminated string 102 * @dest: Where to copy the string to 103 * @src: Where to copy the string from 104 * @n: The maximum number of bytes to copy 105 * 106 * The result is not %NUL-terminated if the source exceeds 107 * @n bytes. 108 */ 109 #ifdef __HAVE_ARCH_STRNCPY 110 char *strncpy(char *dest, const char *src, size_t n) 111 { 112 size_t len = __strnend(src, n) - src; 113 memset(dest + len, 0, n - len); 114 memcpy(dest, src, len); 115 return dest; 116 } 117 EXPORT_SYMBOL(strncpy); 118 #endif 119 120 /** 121 * strcat - Append one %NUL-terminated string to another 122 * @dest: The string to be appended to 123 * @src: The string to append to it 124 * 125 * returns a pointer to @dest 126 */ 127 #ifdef __HAVE_ARCH_STRCAT 128 char *strcat(char *dest, const char *src) 129 { 130 unsigned long dummy = 0; 131 char *ret = dest; 132 133 asm volatile( 134 " lghi 0,0\n" 135 "0: srst %[dummy],%[dest]\n" 136 " jo 0b\n" 137 "1: mvst %[dummy],%[src]\n" 138 " jo 1b\n" 139 : [dummy] "+&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src) 140 : 141 : "cc", "memory", "0"); 142 return ret; 143 } 144 EXPORT_SYMBOL(strcat); 145 #endif 146 147 /** 148 * strlcat - Append a length-limited, %NUL-terminated string to another 149 * @dest: The string to be appended to 150 * @src: The string to append to it 151 * @n: The size of the destination buffer. 152 */ 153 #ifdef __HAVE_ARCH_STRLCAT 154 size_t strlcat(char *dest, const char *src, size_t n) 155 { 156 size_t dsize = __strend(dest) - dest; 157 size_t len = __strend(src) - src; 158 size_t res = dsize + len; 159 160 if (dsize < n) { 161 dest += dsize; 162 n -= dsize; 163 if (len >= n) 164 len = n - 1; 165 dest[len] = '\0'; 166 memcpy(dest, src, len); 167 } 168 return res; 169 } 170 EXPORT_SYMBOL(strlcat); 171 #endif 172 173 /** 174 * strncat - Append a length-limited, %NUL-terminated string to another 175 * @dest: The string to be appended to 176 * @src: The string to append to it 177 * @n: The maximum numbers of bytes to copy 178 * 179 * returns a pointer to @dest 180 * 181 * Note that in contrast to strncpy, strncat ensures the result is 182 * terminated. 183 */ 184 #ifdef __HAVE_ARCH_STRNCAT 185 char *strncat(char *dest, const char *src, size_t n) 186 { 187 size_t len = __strnend(src, n) - src; 188 char *p = __strend(dest); 189 190 p[len] = '\0'; 191 memcpy(p, src, len); 192 return dest; 193 } 194 EXPORT_SYMBOL(strncat); 195 #endif 196 197 /** 198 * strcmp - Compare two strings 199 * @s1: One string 200 * @s2: Another string 201 * 202 * returns 0 if @s1 and @s2 are equal, 203 * < 0 if @s1 is less than @s2 204 * > 0 if @s1 is greater than @s2 205 */ 206 #ifdef __HAVE_ARCH_STRCMP 207 int strcmp(const char *s1, const char *s2) 208 { 209 int ret = 0; 210 211 asm volatile( 212 " lghi 0,0\n" 213 "0: clst %[s1],%[s2]\n" 214 " jo 0b\n" 215 " je 1f\n" 216 " ic %[ret],0(%[s1])\n" 217 " ic 0,0(%[s2])\n" 218 " sr %[ret],0\n" 219 "1:" 220 : [ret] "+&d" (ret), [s1] "+&a" (s1), [s2] "+&a" (s2) 221 : 222 : "cc", "memory", "0"); 223 return ret; 224 } 225 EXPORT_SYMBOL(strcmp); 226 #endif 227 228 static inline int clcle(const char *s1, unsigned long l1, 229 const char *s2, unsigned long l2) 230 { 231 union register_pair r1 = { .even = (unsigned long)s1, .odd = l1, }; 232 union register_pair r3 = { .even = (unsigned long)s2, .odd = l2, }; 233 int cc; 234 235 asm volatile( 236 "0: clcle %[r1],%[r3],0\n" 237 " jo 0b\n" 238 " ipm %[cc]\n" 239 " srl %[cc],28\n" 240 : [cc] "=&d" (cc), [r1] "+&d" (r1.pair), [r3] "+&d" (r3.pair) 241 : 242 : "cc", "memory"); 243 return cc; 244 } 245 246 /** 247 * strstr - Find the first substring in a %NUL terminated string 248 * @s1: The string to be searched 249 * @s2: The string to search for 250 */ 251 #ifdef __HAVE_ARCH_STRSTR 252 char *strstr(const char *s1, const char *s2) 253 { 254 int l1, l2; 255 256 l2 = __strend(s2) - s2; 257 if (!l2) 258 return (char *) s1; 259 l1 = __strend(s1) - s1; 260 while (l1-- >= l2) { 261 int cc; 262 263 cc = clcle(s1, l2, s2, l2); 264 if (!cc) 265 return (char *) s1; 266 s1++; 267 } 268 return NULL; 269 } 270 EXPORT_SYMBOL(strstr); 271 #endif 272 273 /** 274 * memchr - Find a character in an area of memory. 275 * @s: The memory area 276 * @c: The byte to search for 277 * @n: The size of the area. 278 * 279 * returns the address of the first occurrence of @c, or %NULL 280 * if @c is not found 281 */ 282 #ifdef __HAVE_ARCH_MEMCHR 283 void *memchr(const void *s, int c, size_t n) 284 { 285 const void *ret = s + n; 286 287 asm volatile( 288 " lgr 0,%[c]\n" 289 "0: srst %[ret],%[s]\n" 290 " jo 0b\n" 291 " jl 1f\n" 292 " la %[ret],0\n" 293 "1:" 294 : [ret] "+&a" (ret), [s] "+&a" (s) 295 : [c] "d" (c) 296 : "cc", "memory", "0"); 297 return (void *) ret; 298 } 299 EXPORT_SYMBOL(memchr); 300 #endif 301 302 /** 303 * memcmp - Compare two areas of memory 304 * @s1: One area of memory 305 * @s2: Another area of memory 306 * @n: The size of the area. 307 */ 308 #ifdef __HAVE_ARCH_MEMCMP 309 int memcmp(const void *s1, const void *s2, size_t n) 310 { 311 int ret; 312 313 ret = clcle(s1, n, s2, n); 314 if (ret) 315 ret = ret == 1 ? -1 : 1; 316 return ret; 317 } 318 EXPORT_SYMBOL(memcmp); 319 #endif 320 321 /** 322 * memscan - Find a character in an area of memory. 323 * @s: The memory area 324 * @c: The byte to search for 325 * @n: The size of the area. 326 * 327 * returns the address of the first occurrence of @c, or 1 byte past 328 * the area if @c is not found 329 */ 330 #ifdef __HAVE_ARCH_MEMSCAN 331 void *memscan(void *s, int c, size_t n) 332 { 333 const void *ret = s + n; 334 335 asm volatile( 336 " lgr 0,%[c]\n" 337 "0: srst %[ret],%[s]\n" 338 " jo 0b\n" 339 : [ret] "+&a" (ret), [s] "+&a" (s) 340 : [c] "d" (c) 341 : "cc", "memory", "0"); 342 return (void *)ret; 343 } 344 EXPORT_SYMBOL(memscan); 345 #endif 346