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/uaccess.h>
12 #include <linux/export.h>
13 #include <linux/mm.h>
14 #include <asm/asm-extable.h>
15 #include <asm/ctlreg.h>
16
17 #ifdef CONFIG_DEBUG_ENTRY
debug_user_asce(int exit)18 void debug_user_asce(int exit)
19 {
20 struct ctlreg cr1, cr7;
21
22 local_ctl_store(1, &cr1);
23 local_ctl_store(7, &cr7);
24 if (cr1.val == get_lowcore()->kernel_asce.val && cr7.val == get_lowcore()->user_asce.val)
25 return;
26 panic("incorrect ASCE on kernel %s\n"
27 "cr1: %016lx cr7: %016lx\n"
28 "kernel: %016lx user: %016lx\n",
29 exit ? "exit" : "entry", cr1.val, cr7.val,
30 get_lowcore()->kernel_asce.val, get_lowcore()->user_asce.val);
31 }
32 #endif /*CONFIG_DEBUG_ENTRY */
33
34 union oac {
35 unsigned int val;
36 struct {
37 struct {
38 unsigned short key : 4;
39 unsigned short : 4;
40 unsigned short as : 2;
41 unsigned short : 4;
42 unsigned short k : 1;
43 unsigned short a : 1;
44 } oac1;
45 struct {
46 unsigned short key : 4;
47 unsigned short : 4;
48 unsigned short as : 2;
49 unsigned short : 4;
50 unsigned short k : 1;
51 unsigned short a : 1;
52 } oac2;
53 };
54 };
55
56 static uaccess_kmsan_or_inline __must_check unsigned long
raw_copy_from_user_key(void * to,const void __user * from,unsigned long size,unsigned long key)57 raw_copy_from_user_key(void *to, const void __user *from, unsigned long size, unsigned long key)
58 {
59 unsigned long osize;
60 union oac spec = {
61 .oac2.key = key,
62 .oac2.as = PSW_BITS_AS_SECONDARY,
63 .oac2.k = 1,
64 .oac2.a = 1,
65 };
66 int cc;
67
68 while (1) {
69 osize = size;
70 asm_inline volatile(
71 " lr %%r0,%[spec]\n"
72 "0: mvcos %[to],%[from],%[size]\n"
73 "1: nopr %%r7\n"
74 CC_IPM(cc)
75 EX_TABLE_UA_MVCOS_FROM(0b, 0b)
76 EX_TABLE_UA_MVCOS_FROM(1b, 0b)
77 : CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char *)to)
78 : [spec] "d" (spec.val), [from] "Q" (*(const char __user *)from)
79 : CC_CLOBBER_LIST("memory", "0"));
80 if (CC_TRANSFORM(cc) == 0)
81 return osize - size;
82 size -= 4096;
83 to += 4096;
84 from += 4096;
85 }
86 }
87
_copy_from_user_key(void * to,const void __user * from,unsigned long n,unsigned long key)88 unsigned long _copy_from_user_key(void *to, const void __user *from,
89 unsigned long n, unsigned long key)
90 {
91 unsigned long res = n;
92
93 might_fault();
94 if (!should_fail_usercopy()) {
95 instrument_copy_from_user_before(to, from, n);
96 res = raw_copy_from_user_key(to, from, n, key);
97 instrument_copy_from_user_after(to, from, n, res);
98 }
99 if (unlikely(res))
100 memset(to + (n - res), 0, res);
101 return res;
102 }
103 EXPORT_SYMBOL(_copy_from_user_key);
104
105 static uaccess_kmsan_or_inline __must_check unsigned long
raw_copy_to_user_key(void __user * to,const void * from,unsigned long size,unsigned long key)106 raw_copy_to_user_key(void __user *to, const void *from, unsigned long size, unsigned long key)
107 {
108 unsigned long osize;
109 union oac spec = {
110 .oac1.key = key,
111 .oac1.as = PSW_BITS_AS_SECONDARY,
112 .oac1.k = 1,
113 .oac1.a = 1,
114 };
115 int cc;
116
117 while (1) {
118 osize = size;
119 asm_inline volatile(
120 " lr %%r0,%[spec]\n"
121 "0: mvcos %[to],%[from],%[size]\n"
122 "1: nopr %%r7\n"
123 CC_IPM(cc)
124 EX_TABLE_UA_MVCOS_TO(0b, 0b)
125 EX_TABLE_UA_MVCOS_TO(1b, 0b)
126 : CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char __user *)to)
127 : [spec] "d" (spec.val), [from] "Q" (*(const char *)from)
128 : CC_CLOBBER_LIST("memory", "0"));
129 if (CC_TRANSFORM(cc) == 0)
130 return osize - size;
131 size -= 4096;
132 to += 4096;
133 from += 4096;
134 }
135 }
136
_copy_to_user_key(void __user * to,const void * from,unsigned long n,unsigned long key)137 unsigned long _copy_to_user_key(void __user *to, const void *from,
138 unsigned long n, unsigned long key)
139 {
140 might_fault();
141 if (should_fail_usercopy())
142 return n;
143 instrument_copy_to_user(to, from, n);
144 return raw_copy_to_user_key(to, from, n, key);
145 }
146 EXPORT_SYMBOL(_copy_to_user_key);
147