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