2 * Standard user space access functions based on mvcp/mvcs and doing
3 * interesting things in the secondary space mode.
5 * Copyright IBM Corp. 2006,2014
10 #include <linux/jump_label.h>
11 #include <linux/uaccess.h>
12 #include <linux/export.h>
13 #include <linux/errno.h>
15 #include <asm/mmu_context.h>
16 #include <asm/facility.h>
32 static struct static_key have_mvcos = STATIC_KEY_INIT_FALSE;
34 static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr,
37 register unsigned long reg0 asm("0") = 0x81UL;
38 unsigned long tmp1, tmp2;
42 "0: .insn ss,0xc80000000000,0(%0,%2),0(%1),0\n"
48 "2: la %4,4095(%1)\n"/* %4 = ptr + 4095 */
49 " nr %4,%3\n" /* %4 = (ptr + 4095) & -4096 */
51 " "CLR" %0,%4\n" /* copy crosses next page boundary? */
53 "3: .insn ss,0xc80000000000,0(%4,%2),0(%1),0\n"
57 " "ALR" %4,%0\n" /* copy remaining size, subtract 1 */
58 " bras %3,6f\n" /* memset loop */
60 "5: xc 0(256,%2),0(%2)\n"
68 EX_TABLE(0b,2b) EX_TABLE(3b,4b) EX_TABLE(9b,2b) EX_TABLE(10b,4b)
69 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
70 : "d" (reg0) : "cc", "memory");
74 static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,
77 unsigned long tmp1, tmp2;
79 update_primary_asce(current);
83 "0: mvcp 0(%0,%2),0(%1),%3\n"
88 "2: mvcp 0(%0,%2),0(%1),%3\n"
91 "3: la %4,255(%1)\n" /* %4 = ptr + 255 */
93 " nr %4,%3\n" /* %4 = (ptr + 255) & -4096 */
95 " "CLR" %0,%4\n" /* copy crosses next page boundary? */
97 "4: mvcp 0(%4,%2),0(%1),%3\n"
101 " "ALR" %4,%0\n" /* copy remaining size, subtract 1 */
102 " bras %3,7f\n" /* memset loop */
103 " xc 0(1,%2),0(%2)\n"
104 "6: xc 0(256,%2),0(%2)\n"
112 EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,5b)
113 EX_TABLE(10b,3b) EX_TABLE(11b,3b) EX_TABLE(12b,5b)
114 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
119 unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n)
121 if (static_key_false(&have_mvcos))
122 return copy_from_user_mvcos(to, from, n);
123 return copy_from_user_mvcp(to, from, n);
125 EXPORT_SYMBOL(__copy_from_user);
127 static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x,
130 register unsigned long reg0 asm("0") = 0x810000UL;
131 unsigned long tmp1, tmp2;
135 "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
141 "2: la %4,4095(%1)\n"/* %4 = ptr + 4095 */
142 " nr %4,%3\n" /* %4 = (ptr + 4095) & -4096 */
144 " "CLR" %0,%4\n" /* copy crosses next page boundary? */
146 "3: .insn ss,0xc80000000000,0(%4,%1),0(%2),0\n"
151 EX_TABLE(0b,2b) EX_TABLE(3b,5b) EX_TABLE(6b,2b) EX_TABLE(7b,5b)
152 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
153 : "d" (reg0) : "cc", "memory");
157 static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,
160 unsigned long tmp1, tmp2;
162 update_primary_asce(current);
166 "0: mvcs 0(%0,%1),0(%2),%3\n"
171 "2: mvcs 0(%0,%1),0(%2),%3\n"
174 "3: la %4,255(%1)\n" /* %4 = ptr + 255 */
176 " nr %4,%3\n" /* %4 = (ptr + 255) & -4096 */
178 " "CLR" %0,%4\n" /* copy crosses next page boundary? */
180 "4: mvcs 0(%4,%1),0(%2),%3\n"
185 EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
186 EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
187 : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
192 unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n)
194 if (static_key_false(&have_mvcos))
195 return copy_to_user_mvcos(to, from, n);
196 return copy_to_user_mvcs(to, from, n);
198 EXPORT_SYMBOL(__copy_to_user);
200 static inline unsigned long copy_in_user_mvcos(void __user *to, const void __user *from,
203 register unsigned long reg0 asm("0") = 0x810081UL;
204 unsigned long tmp1, tmp2;
207 /* FIXME: copy with reduced length. */
209 "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
218 : "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2)
219 : "d" (reg0) : "cc", "memory");
223 static inline unsigned long copy_in_user_mvc(void __user *to, const void __user *from,
228 update_primary_asce(current);
235 "1: mvc 0(1,%1),0(%2)\n"
241 "2: mvc 0(256,%1),0(%2)\n"
246 "4: ex %0,1b-0b(%3)\n"
249 EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
250 : "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1)
255 unsigned long __copy_in_user(void __user *to, const void __user *from, unsigned long n)
257 if (static_key_false(&have_mvcos))
258 return copy_in_user_mvcos(to, from, n);
259 return copy_in_user_mvc(to, from, n);
261 EXPORT_SYMBOL(__copy_in_user);
263 static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size)
265 register unsigned long reg0 asm("0") = 0x810000UL;
266 unsigned long tmp1, tmp2;
270 "0: .insn ss,0xc80000000000,0(%0,%1),0(%4),0\n"
275 "2: la %3,4095(%1)\n"/* %4 = to + 4095 */
276 " nr %3,%2\n" /* %4 = (to + 4095) & -4096 */
278 " "CLR" %0,%3\n" /* copy crosses next page boundary? */
280 "3: .insn ss,0xc80000000000,0(%3,%1),0(%4),0\n"
285 EX_TABLE(0b,2b) EX_TABLE(3b,5b)
286 : "+a" (size), "+a" (to), "+a" (tmp1), "=a" (tmp2)
287 : "a" (empty_zero_page), "d" (reg0) : "cc", "memory");
291 static inline unsigned long clear_user_xc(void __user *to, unsigned long size)
293 unsigned long tmp1, tmp2;
295 update_primary_asce(current);
301 " xc 0(1,%1),0(%1)\n"
303 " la %2,255(%1)\n" /* %2 = ptr + 255 */
305 " sll %2,12\n" /* %2 = (ptr + 255) & -4096 */
307 " "CLR" %0,%2\n" /* clear crosses next page boundary? */
314 "2: xc 0(256,%1),0(%1)\n"
321 EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
322 : "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2)
327 unsigned long __clear_user(void __user *to, unsigned long size)
329 if (static_key_false(&have_mvcos))
330 return clear_user_mvcos(to, size);
331 return clear_user_xc(to, size);
333 EXPORT_SYMBOL(__clear_user);
335 static inline unsigned long strnlen_user_srst(const char __user *src,
338 register unsigned long reg0 asm("0") = 0;
339 unsigned long tmp1, tmp2;
348 " la %0,1(%3)\n" /* strnlen_user results includes \0 */
352 : "+a" (size), "+a" (src), "=a" (tmp1), "=a" (tmp2)
353 : "d" (reg0) : "cc", "memory");
357 unsigned long __strnlen_user(const char __user *src, unsigned long size)
361 update_primary_asce(current);
362 return strnlen_user_srst(src, size);
364 EXPORT_SYMBOL(__strnlen_user);
366 long __strncpy_from_user(char *dst, const char __user *src, long size)
368 size_t done, len, offset, len_str;
370 if (unlikely(size <= 0))
374 offset = (size_t)src & ~PAGE_MASK;
375 len = min(size - done, PAGE_SIZE - offset);
376 if (copy_from_user(dst, src, len))
378 len_str = strnlen(dst, len);
382 } while ((len_str == len) && (done < size));
385 EXPORT_SYMBOL(__strncpy_from_user);
388 * The "old" uaccess variant without mvcos can be enforced with the
389 * uaccess_primary kernel parameter. This is mainly for debugging purposes.
391 static int uaccess_primary __initdata;
393 static int __init parse_uaccess_pt(char *__unused)
398 early_param("uaccess_primary", parse_uaccess_pt);
400 static int __init uaccess_init(void)
402 if (IS_ENABLED(CONFIG_64BIT) && !uaccess_primary && test_facility(27))
403 static_key_slow_inc(&have_mvcos);
406 early_initcall(uaccess_init);