Merge tag 'modules-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu...
[linux-2.6-microblaze.git] / arch / s390 / lib / uaccess.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  Standard user space access functions based on mvcp/mvcs and doing
4  *  interesting things in the secondary space mode.
5  *
6  *    Copyright IBM Corp. 2006,2014
7  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
8  *               Gerald Schaefer (gerald.schaefer@de.ibm.com)
9  */
10
11 #include <linux/jump_label.h>
12 #include <linux/uaccess.h>
13 #include <linux/export.h>
14 #include <linux/errno.h>
15 #include <linux/mm.h>
16 #include <asm/mmu_context.h>
17 #include <asm/facility.h>
18
19 #ifdef CONFIG_DEBUG_USER_ASCE
20 void debug_user_asce(void)
21 {
22         unsigned long cr1, cr7;
23
24         __ctl_store(cr1, 1, 1);
25         __ctl_store(cr7, 7, 7);
26         if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce)
27                 return;
28         panic("incorrect ASCE on kernel exit\n"
29               "cr1:    %016lx cr7:  %016lx\n"
30               "kernel: %016llx user: %016llx\n",
31               cr1, cr7, S390_lowcore.kernel_asce, S390_lowcore.user_asce);
32 }
33 #endif /*CONFIG_DEBUG_USER_ASCE */
34
35 #ifndef CONFIG_HAVE_MARCH_Z10_FEATURES
36 static DEFINE_STATIC_KEY_FALSE(have_mvcos);
37
38 static int __init uaccess_init(void)
39 {
40         if (test_facility(27))
41                 static_branch_enable(&have_mvcos);
42         return 0;
43 }
44 early_initcall(uaccess_init);
45
46 static inline int copy_with_mvcos(void)
47 {
48         if (static_branch_likely(&have_mvcos))
49                 return 1;
50         return 0;
51 }
52 #else
53 static inline int copy_with_mvcos(void)
54 {
55         return 1;
56 }
57 #endif
58
59 static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr,
60                                                  unsigned long size)
61 {
62         register unsigned long reg0 asm("0") = 0x81UL;
63         unsigned long tmp1, tmp2;
64
65         tmp1 = -4096UL;
66         asm volatile(
67                 "0: .insn ss,0xc80000000000,0(%0,%2),0(%1),0\n"
68                 "6: jz    4f\n"
69                 "1: algr  %0,%3\n"
70                 "   slgr  %1,%3\n"
71                 "   slgr  %2,%3\n"
72                 "   j     0b\n"
73                 "2: la    %4,4095(%1)\n"/* %4 = ptr + 4095 */
74                 "   nr    %4,%3\n"      /* %4 = (ptr + 4095) & -4096 */
75                 "   slgr  %4,%1\n"
76                 "   clgr  %0,%4\n"      /* copy crosses next page boundary? */
77                 "   jnh   5f\n"
78                 "3: .insn ss,0xc80000000000,0(%4,%2),0(%1),0\n"
79                 "7: slgr  %0,%4\n"
80                 "   j     5f\n"
81                 "4: slgr  %0,%0\n"
82                 "5:\n"
83                 EX_TABLE(0b,2b) EX_TABLE(3b,5b) EX_TABLE(6b,2b) EX_TABLE(7b,5b)
84                 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
85                 : "d" (reg0) : "cc", "memory");
86         return size;
87 }
88
89 static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,
90                                                 unsigned long size)
91 {
92         unsigned long tmp1, tmp2;
93
94         tmp1 = -256UL;
95         asm volatile(
96                 "   sacf  0\n"
97                 "0: mvcp  0(%0,%2),0(%1),%3\n"
98                 "7: jz    5f\n"
99                 "1: algr  %0,%3\n"
100                 "   la    %1,256(%1)\n"
101                 "   la    %2,256(%2)\n"
102                 "2: mvcp  0(%0,%2),0(%1),%3\n"
103                 "8: jnz   1b\n"
104                 "   j     5f\n"
105                 "3: la    %4,255(%1)\n" /* %4 = ptr + 255 */
106                 "   lghi  %3,-4096\n"
107                 "   nr    %4,%3\n"      /* %4 = (ptr + 255) & -4096 */
108                 "   slgr  %4,%1\n"
109                 "   clgr  %0,%4\n"      /* copy crosses next page boundary? */
110                 "   jnh   6f\n"
111                 "4: mvcp  0(%4,%2),0(%1),%3\n"
112                 "9: slgr  %0,%4\n"
113                 "   j     6f\n"
114                 "5: slgr  %0,%0\n"
115                 "6: sacf  768\n"
116                 EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
117                 EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
118                 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
119                 : : "cc", "memory");
120         return size;
121 }
122
123 unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
124 {
125         if (copy_with_mvcos())
126                 return copy_from_user_mvcos(to, from, n);
127         return copy_from_user_mvcp(to, from, n);
128 }
129 EXPORT_SYMBOL(raw_copy_from_user);
130
131 static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x,
132                                                unsigned long size)
133 {
134         register unsigned long reg0 asm("0") = 0x810000UL;
135         unsigned long tmp1, tmp2;
136
137         tmp1 = -4096UL;
138         asm volatile(
139                 "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
140                 "6: jz    4f\n"
141                 "1: algr  %0,%3\n"
142                 "   slgr  %1,%3\n"
143                 "   slgr  %2,%3\n"
144                 "   j     0b\n"
145                 "2: la    %4,4095(%1)\n"/* %4 = ptr + 4095 */
146                 "   nr    %4,%3\n"      /* %4 = (ptr + 4095) & -4096 */
147                 "   slgr  %4,%1\n"
148                 "   clgr  %0,%4\n"      /* copy crosses next page boundary? */
149                 "   jnh   5f\n"
150                 "3: .insn ss,0xc80000000000,0(%4,%1),0(%2),0\n"
151                 "7: slgr  %0,%4\n"
152                 "   j     5f\n"
153                 "4: slgr  %0,%0\n"
154                 "5:\n"
155                 EX_TABLE(0b,2b) EX_TABLE(3b,5b) EX_TABLE(6b,2b) EX_TABLE(7b,5b)
156                 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
157                 : "d" (reg0) : "cc", "memory");
158         return size;
159 }
160
161 static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,
162                                               unsigned long size)
163 {
164         unsigned long tmp1, tmp2;
165
166         tmp1 = -256UL;
167         asm volatile(
168                 "   sacf  0\n"
169                 "0: mvcs  0(%0,%1),0(%2),%3\n"
170                 "7: jz    5f\n"
171                 "1: algr  %0,%3\n"
172                 "   la    %1,256(%1)\n"
173                 "   la    %2,256(%2)\n"
174                 "2: mvcs  0(%0,%1),0(%2),%3\n"
175                 "8: jnz   1b\n"
176                 "   j     5f\n"
177                 "3: la    %4,255(%1)\n" /* %4 = ptr + 255 */
178                 "   lghi  %3,-4096\n"
179                 "   nr    %4,%3\n"      /* %4 = (ptr + 255) & -4096 */
180                 "   slgr  %4,%1\n"
181                 "   clgr  %0,%4\n"      /* copy crosses next page boundary? */
182                 "   jnh   6f\n"
183                 "4: mvcs  0(%4,%1),0(%2),%3\n"
184                 "9: slgr  %0,%4\n"
185                 "   j     6f\n"
186                 "5: slgr  %0,%0\n"
187                 "6: sacf  768\n"
188                 EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
189                 EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
190                 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
191                 : : "cc", "memory");
192         return size;
193 }
194
195 unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
196 {
197         if (copy_with_mvcos())
198                 return copy_to_user_mvcos(to, from, n);
199         return copy_to_user_mvcs(to, from, n);
200 }
201 EXPORT_SYMBOL(raw_copy_to_user);
202
203 static inline unsigned long copy_in_user_mvcos(void __user *to, const void __user *from,
204                                                unsigned long size)
205 {
206         register unsigned long reg0 asm("0") = 0x810081UL;
207         unsigned long tmp1, tmp2;
208
209         tmp1 = -4096UL;
210         /* FIXME: copy with reduced length. */
211         asm volatile(
212                 "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
213                 "   jz    2f\n"
214                 "1: algr  %0,%3\n"
215                 "   slgr  %1,%3\n"
216                 "   slgr  %2,%3\n"
217                 "   j     0b\n"
218                 "2:slgr  %0,%0\n"
219                 "3: \n"
220                 EX_TABLE(0b,3b)
221                 : "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2)
222                 : "d" (reg0) : "cc", "memory");
223         return size;
224 }
225
226 static inline unsigned long copy_in_user_mvc(void __user *to, const void __user *from,
227                                              unsigned long size)
228 {
229         unsigned long tmp1;
230
231         asm volatile(
232                 "   sacf  256\n"
233                 "   aghi  %0,-1\n"
234                 "   jo    5f\n"
235                 "   bras  %3,3f\n"
236                 "0: aghi  %0,257\n"
237                 "1: mvc   0(1,%1),0(%2)\n"
238                 "   la    %1,1(%1)\n"
239                 "   la    %2,1(%2)\n"
240                 "   aghi  %0,-1\n"
241                 "   jnz   1b\n"
242                 "   j     5f\n"
243                 "2: mvc   0(256,%1),0(%2)\n"
244                 "   la    %1,256(%1)\n"
245                 "   la    %2,256(%2)\n"
246                 "3: aghi  %0,-256\n"
247                 "   jnm   2b\n"
248                 "4: ex    %0,1b-0b(%3)\n"
249                 "5: slgr  %0,%0\n"
250                 "6: sacf  768\n"
251                 EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
252                 : "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1)
253                 : : "cc", "memory");
254         return size;
255 }
256
257 unsigned long raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
258 {
259         if (copy_with_mvcos())
260                 return copy_in_user_mvcos(to, from, n);
261         return copy_in_user_mvc(to, from, n);
262 }
263 EXPORT_SYMBOL(raw_copy_in_user);
264
265 static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size)
266 {
267         register unsigned long reg0 asm("0") = 0x810000UL;
268         unsigned long tmp1, tmp2;
269
270         tmp1 = -4096UL;
271         asm volatile(
272                 "0: .insn ss,0xc80000000000,0(%0,%1),0(%4),0\n"
273                 "   jz    4f\n"
274                 "1: algr  %0,%2\n"
275                 "   slgr  %1,%2\n"
276                 "   j     0b\n"
277                 "2: la    %3,4095(%1)\n"/* %4 = to + 4095 */
278                 "   nr    %3,%2\n"      /* %4 = (to + 4095) & -4096 */
279                 "   slgr  %3,%1\n"
280                 "   clgr  %0,%3\n"      /* copy crosses next page boundary? */
281                 "   jnh   5f\n"
282                 "3: .insn ss,0xc80000000000,0(%3,%1),0(%4),0\n"
283                 "   slgr  %0,%3\n"
284                 "   j     5f\n"
285                 "4: slgr  %0,%0\n"
286                 "5:\n"
287                 EX_TABLE(0b,2b) EX_TABLE(3b,5b)
288                 : "+a" (size), "+a" (to), "+a" (tmp1), "=a" (tmp2)
289                 : "a" (empty_zero_page), "d" (reg0) : "cc", "memory");
290         return size;
291 }
292
293 static inline unsigned long clear_user_xc(void __user *to, unsigned long size)
294 {
295         unsigned long tmp1, tmp2;
296
297         asm volatile(
298                 "   sacf  256\n"
299                 "   aghi  %0,-1\n"
300                 "   jo    5f\n"
301                 "   bras  %3,3f\n"
302                 "   xc    0(1,%1),0(%1)\n"
303                 "0: aghi  %0,257\n"
304                 "   la    %2,255(%1)\n" /* %2 = ptr + 255 */
305                 "   srl   %2,12\n"
306                 "   sll   %2,12\n"      /* %2 = (ptr + 255) & -4096 */
307                 "   slgr  %2,%1\n"
308                 "   clgr  %0,%2\n"      /* clear crosses next page boundary? */
309                 "   jnh   5f\n"
310                 "   aghi  %2,-1\n"
311                 "1: ex    %2,0(%3)\n"
312                 "   aghi  %2,1\n"
313                 "   slgr  %0,%2\n"
314                 "   j     5f\n"
315                 "2: xc    0(256,%1),0(%1)\n"
316                 "   la    %1,256(%1)\n"
317                 "3: aghi  %0,-256\n"
318                 "   jnm   2b\n"
319                 "4: ex    %0,0(%3)\n"
320                 "5: slgr  %0,%0\n"
321                 "6: sacf  768\n"
322                 EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
323                 : "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2)
324                 : : "cc", "memory");
325         return size;
326 }
327
328 unsigned long __clear_user(void __user *to, unsigned long size)
329 {
330         if (copy_with_mvcos())
331                         return clear_user_mvcos(to, size);
332         return clear_user_xc(to, size);
333 }
334 EXPORT_SYMBOL(__clear_user);
335
336 static inline unsigned long strnlen_user_srst(const char __user *src,
337                                               unsigned long size)
338 {
339         register unsigned long reg0 asm("0") = 0;
340         unsigned long tmp1, tmp2;
341
342         asm volatile(
343                 "   la    %2,0(%1)\n"
344                 "   la    %3,0(%0,%1)\n"
345                 "   slgr  %0,%0\n"
346                 "   sacf  256\n"
347                 "0: srst  %3,%2\n"
348                 "   jo    0b\n"
349                 "   la    %0,1(%3)\n"   /* strnlen_user results includes \0 */
350                 "   slgr  %0,%1\n"
351                 "1: sacf  768\n"
352                 EX_TABLE(0b,1b)
353                 : "+a" (size), "+a" (src), "=a" (tmp1), "=a" (tmp2)
354                 : "d" (reg0) : "cc", "memory");
355         return size;
356 }
357
358 unsigned long __strnlen_user(const char __user *src, unsigned long size)
359 {
360         if (unlikely(!size))
361                 return 0;
362         return strnlen_user_srst(src, size);
363 }
364 EXPORT_SYMBOL(__strnlen_user);
365
366 long __strncpy_from_user(char *dst, const char __user *src, long size)
367 {
368         size_t done, len, offset, len_str;
369
370         if (unlikely(size <= 0))
371                 return 0;
372         done = 0;
373         do {
374                 offset = (size_t)src & (L1_CACHE_BYTES - 1);
375                 len = min(size - done, L1_CACHE_BYTES - offset);
376                 if (copy_from_user(dst, src, len))
377                         return -EFAULT;
378                 len_str = strnlen(dst, len);
379                 done += len_str;
380                 src += len_str;
381                 dst += len_str;
382         } while ((len_str == len) && (done < size));
383         return done;
384 }
385 EXPORT_SYMBOL(__strncpy_from_user);