xref: /linux/arch/s390/lib/string.c (revision 7f9f879243d6cf5d2d60d12065e93189cc343387)
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 	ssize_t len = __strend(s) - s;
266 
267 	do {
268 		if (s[len] == (char)c)
269 			return (char *)s + len;
270 	} while (--len >= 0);
271 	return NULL;
272 }
273 EXPORT_SYMBOL(strrchr);
274 #endif
275 
276 static inline int clcle(const char *s1, unsigned long l1,
277 			const char *s2, unsigned long l2)
278 {
279 	union register_pair r1 = { .even = (unsigned long)s1, .odd = l1, };
280 	union register_pair r3 = { .even = (unsigned long)s2, .odd = l2, };
281 	int cc;
282 
283 	asm volatile(
284 		"0:	clcle	%[r1],%[r3],0\n"
285 		"	jo	0b\n"
286 		"	ipm	%[cc]\n"
287 		"	srl	%[cc],28\n"
288 		: [cc] "=&d" (cc), [r1] "+&d" (r1.pair), [r3] "+&d" (r3.pair)
289 		:
290 		: "cc", "memory");
291 	return cc;
292 }
293 
294 /**
295  * strstr - Find the first substring in a %NUL terminated string
296  * @s1: The string to be searched
297  * @s2: The string to search for
298  */
299 #ifdef __HAVE_ARCH_STRSTR
300 char *strstr(const char *s1, const char *s2)
301 {
302 	int l1, l2;
303 
304 	l2 = __strend(s2) - s2;
305 	if (!l2)
306 		return (char *) s1;
307 	l1 = __strend(s1) - s1;
308 	while (l1-- >= l2) {
309 		int cc;
310 
311 		cc = clcle(s1, l2, s2, l2);
312 		if (!cc)
313 			return (char *) s1;
314 		s1++;
315 	}
316 	return NULL;
317 }
318 EXPORT_SYMBOL(strstr);
319 #endif
320 
321 /**
322  * memchr - 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 %NULL
328  * if @c is not found
329  */
330 #ifdef __HAVE_ARCH_MEMCHR
331 void *memchr(const 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 		"	jl	1f\n"
340 		"	la	%[ret],0\n"
341 		"1:"
342 		: [ret] "+&a" (ret), [s] "+&a" (s)
343 		: [c] "d" (c)
344 		: "cc", "memory", "0");
345 	return (void *) ret;
346 }
347 EXPORT_SYMBOL(memchr);
348 #endif
349 
350 /**
351  * memcmp - Compare two areas of memory
352  * @s1: One area of memory
353  * @s2: Another area of memory
354  * @n: The size of the area.
355  */
356 #ifdef __HAVE_ARCH_MEMCMP
357 int memcmp(const void *s1, const void *s2, size_t n)
358 {
359 	int ret;
360 
361 	ret = clcle(s1, n, s2, n);
362 	if (ret)
363 		ret = ret == 1 ? -1 : 1;
364 	return ret;
365 }
366 EXPORT_SYMBOL(memcmp);
367 #endif
368 
369 /**
370  * memscan - Find a character in an area of memory.
371  * @s: The memory area
372  * @c: The byte to search for
373  * @n: The size of the area.
374  *
375  * returns the address of the first occurrence of @c, or 1 byte past
376  * the area if @c is not found
377  */
378 #ifdef __HAVE_ARCH_MEMSCAN
379 void *memscan(void *s, int c, size_t n)
380 {
381 	const void *ret = s + n;
382 
383 	asm volatile(
384 		"	lgr	0,%[c]\n"
385 		"0:	srst	%[ret],%[s]\n"
386 		"	jo	0b\n"
387 		: [ret] "+&a" (ret), [s] "+&a" (s)
388 		: [c] "d" (c)
389 		: "cc", "memory", "0");
390 	return (void *)ret;
391 }
392 EXPORT_SYMBOL(memscan);
393 #endif
394