xref: /openbmc/linux/arch/s390/lib/string.c (revision 1edad85b)
1 /*
2  *  arch/s390/lib/string.c
3  *    Optimized string functions
4  *
5  *  S390 version
6  *    Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
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/module.h>
14 
15 /*
16  * Helper functions to find the end of a string
17  */
18 static inline char *__strend(const char *s)
19 {
20 	register unsigned long r0 asm("0") = 0;
21 
22 	asm volatile ("0: srst  %0,%1\n"
23 		      "   jo    0b"
24 		      : "+d" (r0), "+a" (s) :  : "cc" );
25 	return (char *) r0;
26 }
27 
28 static inline char *__strnend(const char *s, size_t n)
29 {
30 	register unsigned long r0 asm("0") = 0;
31 	const char *p = s + n;
32 
33 	asm volatile ("0: srst  %0,%1\n"
34 		      "   jo    0b"
35 		      : "+d" (p), "+a" (s) : "d" (r0) : "cc" );
36 	return (char *) p;
37 }
38 
39 /**
40  * strlen - Find the length of a string
41  * @s: The string to be sized
42  *
43  * returns the length of @s
44  */
45 size_t strlen(const char *s)
46 {
47 #if __GNUC__ < 4
48 	return __strend(s) - s;
49 #else
50 	return __builtin_strlen(s);
51 #endif
52 }
53 EXPORT_SYMBOL(strlen);
54 
55 /**
56  * strnlen - Find the length of a length-limited string
57  * @s: The string to be sized
58  * @n: The maximum number of bytes to search
59  *
60  * returns the minimum of the length of @s and @n
61  */
62 size_t strnlen(const char * s, size_t n)
63 {
64 	return __strnend(s, n) - s;
65 }
66 EXPORT_SYMBOL(strnlen);
67 
68 /**
69  * strcpy - Copy a %NUL terminated string
70  * @dest: Where to copy the string to
71  * @src: Where to copy the string from
72  *
73  * returns a pointer to @dest
74  */
75 char *strcpy(char *dest, const char *src)
76 {
77 #if __GNUC__ < 4
78 	register int r0 asm("0") = 0;
79 	char *ret = dest;
80 
81 	asm volatile ("0: mvst  %0,%1\n"
82 		      "   jo    0b"
83 		      : "+&a" (dest), "+&a" (src) : "d" (r0)
84 		      : "cc", "memory" );
85 	return ret;
86 #else
87 	return __builtin_strcpy(dest, src);
88 #endif
89 }
90 EXPORT_SYMBOL(strcpy);
91 
92 /**
93  * strlcpy - Copy a %NUL terminated string into a sized buffer
94  * @dest: Where to copy the string to
95  * @src: Where to copy the string from
96  * @size: size of destination buffer
97  *
98  * Compatible with *BSD: the result is always a valid
99  * NUL-terminated string that fits in the buffer (unless,
100  * of course, the buffer size is zero). It does not pad
101  * out the result like strncpy() does.
102  */
103 size_t strlcpy(char *dest, const char *src, size_t size)
104 {
105 	size_t ret = __strend(src) - src;
106 
107 	if (size) {
108 		size_t len = (ret >= size) ? size-1 : ret;
109 		dest[len] = '\0';
110 		__builtin_memcpy(dest, src, len);
111 	}
112 	return ret;
113 }
114 EXPORT_SYMBOL(strlcpy);
115 
116 /**
117  * strncpy - Copy a length-limited, %NUL-terminated string
118  * @dest: Where to copy the string to
119  * @src: Where to copy the string from
120  * @n: The maximum number of bytes to copy
121  *
122  * The result is not %NUL-terminated if the source exceeds
123  * @n bytes.
124  */
125 char *strncpy(char *dest, const char *src, size_t n)
126 {
127 	size_t len = __strnend(src, n) - src;
128 	__builtin_memset(dest + len, 0, n - len);
129 	__builtin_memcpy(dest, src, len);
130 	return dest;
131 }
132 EXPORT_SYMBOL(strncpy);
133 
134 /**
135  * strcat - Append one %NUL-terminated string to another
136  * @dest: The string to be appended to
137  * @src: The string to append to it
138  *
139  * returns a pointer to @dest
140  */
141 char *strcat(char *dest, const char *src)
142 {
143 	register int r0 asm("0") = 0;
144 	unsigned long dummy;
145 	char *ret = dest;
146 
147 	asm volatile ("0: srst  %0,%1\n"
148 		      "   jo    0b\n"
149 		      "1: mvst  %0,%2\n"
150 		      "   jo    1b"
151 		      : "=&a" (dummy), "+a" (dest), "+a" (src)
152 		      : "d" (r0), "0" (0UL) : "cc", "memory" );
153 	return ret;
154 }
155 EXPORT_SYMBOL(strcat);
156 
157 /**
158  * strlcat - Append a length-limited, %NUL-terminated string to another
159  * @dest: The string to be appended to
160  * @src: The string to append to it
161  * @n: The size of the destination buffer.
162  */
163 size_t strlcat(char *dest, const char *src, size_t n)
164 {
165 	size_t dsize = __strend(dest) - dest;
166 	size_t len = __strend(src) - src;
167 	size_t res = dsize + len;
168 
169 	if (dsize < n) {
170 		dest += dsize;
171 		n -= dsize;
172 		if (len >= n)
173 			len = n - 1;
174 		dest[len] = '\0';
175 		__builtin_memcpy(dest, src, len);
176 	}
177 	return res;
178 }
179 EXPORT_SYMBOL(strlcat);
180 
181 /**
182  * strncat - Append a length-limited, %NUL-terminated string to another
183  * @dest: The string to be appended to
184  * @src: The string to append to it
185  * @n: The maximum numbers of bytes to copy
186  *
187  * returns a pointer to @dest
188  *
189  * Note that in contrast to strncpy, strncat ensures the result is
190  * terminated.
191  */
192 char *strncat(char *dest, const char *src, size_t n)
193 {
194 	size_t len = __strnend(src, n) - src;
195 	char *p = __strend(dest);
196 
197 	p[len] = '\0';
198 	__builtin_memcpy(p, src, len);
199 	return dest;
200 }
201 EXPORT_SYMBOL(strncat);
202 
203 /**
204  * strcmp - Compare two strings
205  * @cs: One string
206  * @ct: Another string
207  *
208  * returns   0 if @cs and @ct are equal,
209  *         < 0 if @cs is less than @ct
210  *         > 0 if @cs is greater than @ct
211  */
212 int strcmp(const char *cs, const char *ct)
213 {
214 	register int r0 asm("0") = 0;
215 	int ret = 0;
216 
217 	asm volatile ("0: clst %2,%3\n"
218 		      "   jo   0b\n"
219 		      "   je   1f\n"
220 		      "   ic   %0,0(%2)\n"
221 		      "   ic   %1,0(%3)\n"
222 		      "   sr   %0,%1\n"
223 		      "1:"
224 		      : "+d" (ret), "+d" (r0), "+a" (cs), "+a" (ct)
225 		      : : "cc" );
226 	return ret;
227 }
228 EXPORT_SYMBOL(strcmp);
229 
230 /**
231  * strrchr - Find the last occurrence of a character in a string
232  * @s: The string to be searched
233  * @c: The character to search for
234  */
235 char * strrchr(const char * s, int c)
236 {
237        size_t len = __strend(s) - s;
238 
239        if (len)
240 	       do {
241 		       if (s[len] == (char) c)
242 			       return (char *) s + len;
243 	       } while (--len > 0);
244        return NULL;
245 }
246 EXPORT_SYMBOL(strrchr);
247 
248 /**
249  * strstr - Find the first substring in a %NUL terminated string
250  * @s1: The string to be searched
251  * @s2: The string to search for
252  */
253 char * strstr(const char * s1,const char * s2)
254 {
255 	int l1, l2;
256 
257 	l2 = __strend(s2) - s2;
258 	if (!l2)
259 		return (char *) s1;
260 	l1 = __strend(s1) - s1;
261 	while (l1-- >= l2) {
262 		register unsigned long r2 asm("2") = (unsigned long) s1;
263 		register unsigned long r3 asm("3") = (unsigned long) l2;
264 		register unsigned long r4 asm("4") = (unsigned long) s2;
265 		register unsigned long r5 asm("5") = (unsigned long) l2;
266 		int cc;
267 
268 		asm volatile ("0: clcle %1,%3,0\n"
269 			      "   jo    0b\n"
270 			      "   ipm   %0\n"
271 			      "   srl   %0,28"
272 			      : "=&d" (cc), "+a" (r2), "+a" (r3),
273 			        "+a" (r4), "+a" (r5) : : "cc" );
274 		if (!cc)
275 			return (char *) s1;
276 		s1++;
277 	}
278 	return NULL;
279 }
280 EXPORT_SYMBOL(strstr);
281 
282 /**
283  * memchr - Find a character in an area of memory.
284  * @s: The memory area
285  * @c: The byte to search for
286  * @n: The size of the area.
287  *
288  * returns the address of the first occurrence of @c, or %NULL
289  * if @c is not found
290  */
291 void *memchr(const void *s, int c, size_t n)
292 {
293 	register int r0 asm("0") = (char) c;
294 	const void *ret = s + n;
295 
296 	asm volatile ("0: srst  %0,%1\n"
297 		      "   jo    0b\n"
298 		      "   jl	1f\n"
299 		      "   la    %0,0\n"
300 		      "1:"
301 		      : "+a" (ret), "+&a" (s) : "d" (r0) : "cc" );
302 	return (void *) ret;
303 }
304 EXPORT_SYMBOL(memchr);
305 
306 /**
307  * memcmp - Compare two areas of memory
308  * @cs: One area of memory
309  * @ct: Another area of memory
310  * @count: The size of the area.
311  */
312 int memcmp(const void *cs, const void *ct, size_t n)
313 {
314 	register unsigned long r2 asm("2") = (unsigned long) cs;
315 	register unsigned long r3 asm("3") = (unsigned long) n;
316 	register unsigned long r4 asm("4") = (unsigned long) ct;
317 	register unsigned long r5 asm("5") = (unsigned long) n;
318 	int ret;
319 
320 	asm volatile ("0: clcle %1,%3,0\n"
321 		      "   jo    0b\n"
322 		      "   ipm   %0\n"
323 		      "   srl   %0,28"
324 		      : "=&d" (ret), "+a" (r2), "+a" (r3), "+a" (r4), "+a" (r5)
325 		      : : "cc" );
326 	if (ret)
327 		ret = *(char *) r2 - *(char *) r4;
328 	return ret;
329 }
330 EXPORT_SYMBOL(memcmp);
331 
332 /**
333  * memscan - Find a character in an area of memory.
334  * @s: The memory area
335  * @c: The byte to search for
336  * @n: The size of the area.
337  *
338  * returns the address of the first occurrence of @c, or 1 byte past
339  * the area if @c is not found
340  */
341 void *memscan(void *s, int c, size_t n)
342 {
343 	register int r0 asm("0") = (char) c;
344 	const void *ret = s + n;
345 
346 	asm volatile ("0: srst  %0,%1\n"
347 		      "   jo    0b\n"
348 		      : "+a" (ret), "+&a" (s) : "d" (r0) : "cc" );
349 	return (void *) ret;
350 }
351 EXPORT_SYMBOL(memscan);
352 
353 /**
354  * memcpy - Copy one area of memory to another
355  * @dest: Where to copy to
356  * @src: Where to copy from
357  * @n: The size of the area.
358  *
359  * returns a pointer to @dest
360  */
361 void *memcpy(void *dest, const void *src, size_t n)
362 {
363 	return __builtin_memcpy(dest, src, n);
364 }
365 EXPORT_SYMBOL(memcpy);
366 
367 /**
368  * memset - Fill a region of memory with the given value
369  * @s: Pointer to the start of the area.
370  * @c: The byte to fill the area with
371  * @n: The size of the area.
372  *
373  * returns a pointer to @s
374  */
375 void *memset(void *s, int c, size_t n)
376 {
377 	char *xs;
378 
379 	if (c == 0)
380 		return __builtin_memset(s, 0, n);
381 
382 	xs = (char *) s;
383 	if (n > 0)
384 		do {
385 			*xs++ = c;
386 		} while (--n > 0);
387 	return s;
388 }
389 EXPORT_SYMBOL(memset);
390