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