xref: /openbmc/linux/arch/s390/lib/uaccess.c (revision f4c3b83b)
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);
386