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