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 * strlcpy - Copy a %NUL terminated string into a sized buffer 102 * @dest: Where to copy the string to 103 * @src: Where to copy the string from 104 * @size: size of destination buffer 105 * 106 * Compatible with *BSD: the result is always a valid 107 * NUL-terminated string that fits in the buffer (unless, 108 * of course, the buffer size is zero). It does not pad 109 * out the result like strncpy() does. 110 */ 111 #ifdef __HAVE_ARCH_STRLCPY 112 size_t strlcpy(char *dest, const char *src, size_t size) 113 { 114 size_t ret = __strend(src) - src; 115 116 if (size) { 117 size_t len = (ret >= size) ? size-1 : ret; 118 dest[len] = '\0'; 119 memcpy(dest, src, len); 120 } 121 return ret; 122 } 123 EXPORT_SYMBOL(strlcpy); 124 #endif 125 126 /** 127 * strncpy - Copy a length-limited, %NUL-terminated string 128 * @dest: Where to copy the string to 129 * @src: Where to copy the string from 130 * @n: The maximum number of bytes to copy 131 * 132 * The result is not %NUL-terminated if the source exceeds 133 * @n bytes. 134 */ 135 #ifdef __HAVE_ARCH_STRNCPY 136 char *strncpy(char *dest, const char *src, size_t n) 137 { 138 size_t len = __strnend(src, n) - src; 139 memset(dest + len, 0, n - len); 140 memcpy(dest, src, len); 141 return dest; 142 } 143 EXPORT_SYMBOL(strncpy); 144 #endif 145 146 /** 147 * strcat - Append one %NUL-terminated string to another 148 * @dest: The string to be appended to 149 * @src: The string to append to it 150 * 151 * returns a pointer to @dest 152 */ 153 #ifdef __HAVE_ARCH_STRCAT 154 char *strcat(char *dest, const char *src) 155 { 156 unsigned long dummy = 0; 157 char *ret = dest; 158 159 asm volatile( 160 " lghi 0,0\n" 161 "0: srst %[dummy],%[dest]\n" 162 " jo 0b\n" 163 "1: mvst %[dummy],%[src]\n" 164 " jo 1b\n" 165 : [dummy] "+&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src) 166 : 167 : "cc", "memory", "0"); 168 return ret; 169 } 170 EXPORT_SYMBOL(strcat); 171 #endif 172 173 /** 174 * strlcat - 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 size of the destination buffer. 178 */ 179 #ifdef __HAVE_ARCH_STRLCAT 180 size_t strlcat(char *dest, const char *src, size_t n) 181 { 182 size_t dsize = __strend(dest) - dest; 183 size_t len = __strend(src) - src; 184 size_t res = dsize + len; 185 186 if (dsize < n) { 187 dest += dsize; 188 n -= dsize; 189 if (len >= n) 190 len = n - 1; 191 dest[len] = '\0'; 192 memcpy(dest, src, len); 193 } 194 return res; 195 } 196 EXPORT_SYMBOL(strlcat); 197 #endif 198 199 /** 200 * strncat - Append a length-limited, %NUL-terminated string to another 201 * @dest: The string to be appended to 202 * @src: The string to append to it 203 * @n: The maximum numbers of bytes to copy 204 * 205 * returns a pointer to @dest 206 * 207 * Note that in contrast to strncpy, strncat ensures the result is 208 * terminated. 209 */ 210 #ifdef __HAVE_ARCH_STRNCAT 211 char *strncat(char *dest, const char *src, size_t n) 212 { 213 size_t len = __strnend(src, n) - src; 214 char *p = __strend(dest); 215 216 p[len] = '\0'; 217 memcpy(p, src, len); 218 return dest; 219 } 220 EXPORT_SYMBOL(strncat); 221 #endif 222 223 /** 224 * strcmp - Compare two strings 225 * @s1: One string 226 * @s2: Another string 227 * 228 * returns 0 if @s1 and @s2 are equal, 229 * < 0 if @s1 is less than @s2 230 * > 0 if @s1 is greater than @s2 231 */ 232 #ifdef __HAVE_ARCH_STRCMP 233 int strcmp(const char *s1, const char *s2) 234 { 235 int ret = 0; 236 237 asm volatile( 238 " lghi 0,0\n" 239 "0: clst %[s1],%[s2]\n" 240 " jo 0b\n" 241 " je 1f\n" 242 " ic %[ret],0(%[s1])\n" 243 " ic 0,0(%[s2])\n" 244 " sr %[ret],0\n" 245 "1:" 246 : [ret] "+&d" (ret), [s1] "+&a" (s1), [s2] "+&a" (s2) 247 : 248 : "cc", "memory", "0"); 249 return ret; 250 } 251 EXPORT_SYMBOL(strcmp); 252 #endif 253 254 /** 255 * strrchr - Find the last occurrence of a character in a string 256 * @s: The string to be searched 257 * @c: The character to search for 258 */ 259 #ifdef __HAVE_ARCH_STRRCHR 260 char *strrchr(const char *s, int c) 261 { 262 size_t len = __strend(s) - s; 263 264 if (len) 265 do { 266 if (s[len] == (char) c) 267 return (char *) s + len; 268 } while (--len > 0); 269 return NULL; 270 } 271 EXPORT_SYMBOL(strrchr); 272 #endif 273 274 static inline int clcle(const char *s1, unsigned long l1, 275 const char *s2, unsigned long l2) 276 { 277 union register_pair r1 = { .even = (unsigned long)s1, .odd = l1, }; 278 union register_pair r3 = { .even = (unsigned long)s2, .odd = l2, }; 279 int cc; 280 281 asm volatile( 282 "0: clcle %[r1],%[r3],0\n" 283 " jo 0b\n" 284 " ipm %[cc]\n" 285 " srl %[cc],28\n" 286 : [cc] "=&d" (cc), [r1] "+&d" (r1.pair), [r3] "+&d" (r3.pair) 287 : 288 : "cc", "memory"); 289 return cc; 290 } 291 292 /** 293 * strstr - Find the first substring in a %NUL terminated string 294 * @s1: The string to be searched 295 * @s2: The string to search for 296 */ 297 #ifdef __HAVE_ARCH_STRSTR 298 char *strstr(const char *s1, const char *s2) 299 { 300 int l1, l2; 301 302 l2 = __strend(s2) - s2; 303 if (!l2) 304 return (char *) s1; 305 l1 = __strend(s1) - s1; 306 while (l1-- >= l2) { 307 int cc; 308 309 cc = clcle(s1, l2, s2, l2); 310 if (!cc) 311 return (char *) s1; 312 s1++; 313 } 314 return NULL; 315 } 316 EXPORT_SYMBOL(strstr); 317 #endif 318 319 /** 320 * memchr - Find a character in an area of memory. 321 * @s: The memory area 322 * @c: The byte to search for 323 * @n: The size of the area. 324 * 325 * returns the address of the first occurrence of @c, or %NULL 326 * if @c is not found 327 */ 328 #ifdef __HAVE_ARCH_MEMCHR 329 void *memchr(const void *s, int c, size_t n) 330 { 331 const void *ret = s + n; 332 333 asm volatile( 334 " lgr 0,%[c]\n" 335 "0: srst %[ret],%[s]\n" 336 " jo 0b\n" 337 " jl 1f\n" 338 " la %[ret],0\n" 339 "1:" 340 : [ret] "+&a" (ret), [s] "+&a" (s) 341 : [c] "d" (c) 342 : "cc", "memory", "0"); 343 return (void *) ret; 344 } 345 EXPORT_SYMBOL(memchr); 346 #endif 347 348 /** 349 * memcmp - Compare two areas of memory 350 * @s1: One area of memory 351 * @s2: Another area of memory 352 * @n: The size of the area. 353 */ 354 #ifdef __HAVE_ARCH_MEMCMP 355 int memcmp(const void *s1, const void *s2, size_t n) 356 { 357 int ret; 358 359 ret = clcle(s1, n, s2, n); 360 if (ret) 361 ret = ret == 1 ? -1 : 1; 362 return ret; 363 } 364 EXPORT_SYMBOL(memcmp); 365 #endif 366 367 /** 368 * memscan - Find a character in an area of memory. 369 * @s: The memory area 370 * @c: The byte to search for 371 * @n: The size of the area. 372 * 373 * returns the address of the first occurrence of @c, or 1 byte past 374 * the area if @c is not found 375 */ 376 #ifdef __HAVE_ARCH_MEMSCAN 377 void *memscan(void *s, int c, size_t n) 378 { 379 const void *ret = s + n; 380 381 asm volatile( 382 " lgr 0,%[c]\n" 383 "0: srst %[ret],%[s]\n" 384 " jo 0b\n" 385 : [ret] "+&a" (ret), [s] "+&a" (s) 386 : [c] "d" (c) 387 : "cc", "memory", "0"); 388 return (void *)ret; 389 } 390 EXPORT_SYMBOL(memscan); 391 #endif 392