xref: /linux/arch/s390/lib/string.c (revision fcab107abe1ab5be9dbe874baa722372da8f4f73)
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  */
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\n"
31 		: [e] "+&a" (e), [s] "+&a" (s)
32 		:
33 		: "cc", "memory", "0");
34 	return (char *)e;
35 }
36 
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\n"
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
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
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
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\n"
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
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
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
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 
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
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
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
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
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\n"
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