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