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