Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 */
23static 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"
31 : [e] "+&a" (e), [s] "+&a" (s)
32 :
33 : "cc", "memory", "0");
34 return (char *)e;
35}
36
37static 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"
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
58size_t strlen(const char *s)
59{
60 return __strend(s) - s;
61}
62EXPORT_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
73size_t strnlen(const char *s, size_t n)
74{
75 return __strnend(s, n) - s;
76}
77EXPORT_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
88char *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"
99 : [dummy] "+&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src)
100 :
101 : "cc", "memory", "0");
102 return ret;
103}
104EXPORT_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
114size_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}
130EXPORT_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
142char *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}
151EXPORT_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
164int 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}
182EXPORT_SYMBOL(strcmp);
183#endif
184
185static 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
208char *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}
226EXPORT_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
239void *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}
255EXPORT_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
265int 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}
274EXPORT_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
287void *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"
295 : [ret] "+&a" (ret), [s] "+&a" (s)
296 : [c] "d" (c)
297 : "cc", "memory", "0");
298 return (void *)ret;
299}
300EXPORT_SYMBOL(memscan);
301#endif