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 |
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 == S390_lowcore.kernel_asce.val && cr7.val == S390_lowcore.user_asce.val) |
25 | return; |
26 | panic(fmt: "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 | S390_lowcore.kernel_asce.val, S390_lowcore.user_asce.val); |
31 | } |
32 | #endif /*CONFIG_DEBUG_ENTRY */ |
33 | |
34 | static unsigned long raw_copy_from_user_key(void *to, const void __user *from, |
35 | unsigned long size, unsigned long key) |
36 | { |
37 | unsigned long rem; |
38 | union oac spec = { |
39 | .oac2.key = key, |
40 | .oac2.as = PSW_BITS_AS_SECONDARY, |
41 | .oac2.k = 1, |
42 | .oac2.a = 1, |
43 | }; |
44 | |
45 | asm volatile( |
46 | " lr 0,%[spec]\n" |
47 | "0: mvcos 0(%[to]),0(%[from]),%[size]\n" |
48 | "1: jz 5f\n" |
49 | " algr %[size],%[val]\n" |
50 | " slgr %[from],%[val]\n" |
51 | " slgr %[to],%[val]\n" |
52 | " j 0b\n" |
53 | "2: la %[rem],4095(%[from])\n" /* rem = from + 4095 */ |
54 | " nr %[rem],%[val]\n" /* rem = (from + 4095) & -4096 */ |
55 | " slgr %[rem],%[from]\n" |
56 | " clgr %[size],%[rem]\n" /* copy crosses next page boundary? */ |
57 | " jnh 6f\n" |
58 | "3: mvcos 0(%[to]),0(%[from]),%[rem]\n" |
59 | "4: slgr %[size],%[rem]\n" |
60 | " j 6f\n" |
61 | "5: slgr %[size],%[size]\n" |
62 | "6:\n" |
63 | EX_TABLE(0b, 2b) |
64 | EX_TABLE(1b, 2b) |
65 | EX_TABLE(3b, 6b) |
66 | EX_TABLE(4b, 6b) |
67 | : [size] "+&a" (size), [from] "+&a" (from), [to] "+&a" (to), [rem] "=&a" (rem) |
68 | : [val] "a" (-4096UL), [spec] "d" (spec.val) |
69 | : "cc" , "memory" , "0" ); |
70 | return size; |
71 | } |
72 | |
73 | unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n) |
74 | { |
75 | return raw_copy_from_user_key(to, from, size: n, key: 0); |
76 | } |
77 | EXPORT_SYMBOL(raw_copy_from_user); |
78 | |
79 | unsigned long _copy_from_user_key(void *to, const void __user *from, |
80 | unsigned long n, unsigned long key) |
81 | { |
82 | unsigned long res = n; |
83 | |
84 | might_fault(); |
85 | if (!should_fail_usercopy()) { |
86 | instrument_copy_from_user_before(to, from, n); |
87 | res = raw_copy_from_user_key(to, from, size: n, key); |
88 | instrument_copy_from_user_after(to, from, n, left: res); |
89 | } |
90 | if (unlikely(res)) |
91 | memset(to + (n - res), 0, res); |
92 | return res; |
93 | } |
94 | EXPORT_SYMBOL(_copy_from_user_key); |
95 | |
96 | static unsigned long raw_copy_to_user_key(void __user *to, const void *from, |
97 | unsigned long size, unsigned long key) |
98 | { |
99 | unsigned long rem; |
100 | union oac spec = { |
101 | .oac1.key = key, |
102 | .oac1.as = PSW_BITS_AS_SECONDARY, |
103 | .oac1.k = 1, |
104 | .oac1.a = 1, |
105 | }; |
106 | |
107 | asm volatile( |
108 | " lr 0,%[spec]\n" |
109 | "0: mvcos 0(%[to]),0(%[from]),%[size]\n" |
110 | "1: jz 5f\n" |
111 | " algr %[size],%[val]\n" |
112 | " slgr %[to],%[val]\n" |
113 | " slgr %[from],%[val]\n" |
114 | " j 0b\n" |
115 | "2: la %[rem],4095(%[to])\n" /* rem = to + 4095 */ |
116 | " nr %[rem],%[val]\n" /* rem = (to + 4095) & -4096 */ |
117 | " slgr %[rem],%[to]\n" |
118 | " clgr %[size],%[rem]\n" /* copy crosses next page boundary? */ |
119 | " jnh 6f\n" |
120 | "3: mvcos 0(%[to]),0(%[from]),%[rem]\n" |
121 | "4: slgr %[size],%[rem]\n" |
122 | " j 6f\n" |
123 | "5: slgr %[size],%[size]\n" |
124 | "6:\n" |
125 | EX_TABLE(0b, 2b) |
126 | EX_TABLE(1b, 2b) |
127 | EX_TABLE(3b, 6b) |
128 | EX_TABLE(4b, 6b) |
129 | : [size] "+&a" (size), [to] "+&a" (to), [from] "+&a" (from), [rem] "=&a" (rem) |
130 | : [val] "a" (-4096UL), [spec] "d" (spec.val) |
131 | : "cc" , "memory" , "0" ); |
132 | return size; |
133 | } |
134 | |
135 | unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n) |
136 | { |
137 | return raw_copy_to_user_key(to, from, size: n, key: 0); |
138 | } |
139 | EXPORT_SYMBOL(raw_copy_to_user); |
140 | |
141 | unsigned long _copy_to_user_key(void __user *to, const void *from, |
142 | unsigned long n, unsigned long key) |
143 | { |
144 | might_fault(); |
145 | if (should_fail_usercopy()) |
146 | return n; |
147 | instrument_copy_to_user(to, from, n); |
148 | return raw_copy_to_user_key(to, from, size: n, key); |
149 | } |
150 | EXPORT_SYMBOL(_copy_to_user_key); |
151 | |
152 | unsigned long __clear_user(void __user *to, unsigned long size) |
153 | { |
154 | unsigned long rem; |
155 | union oac spec = { |
156 | .oac1.as = PSW_BITS_AS_SECONDARY, |
157 | .oac1.a = 1, |
158 | }; |
159 | |
160 | asm volatile( |
161 | " lr 0,%[spec]\n" |
162 | "0: mvcos 0(%[to]),0(%[zeropg]),%[size]\n" |
163 | "1: jz 5f\n" |
164 | " algr %[size],%[val]\n" |
165 | " slgr %[to],%[val]\n" |
166 | " j 0b\n" |
167 | "2: la %[rem],4095(%[to])\n" /* rem = to + 4095 */ |
168 | " nr %[rem],%[val]\n" /* rem = (to + 4095) & -4096 */ |
169 | " slgr %[rem],%[to]\n" |
170 | " clgr %[size],%[rem]\n" /* copy crosses next page boundary? */ |
171 | " jnh 6f\n" |
172 | "3: mvcos 0(%[to]),0(%[zeropg]),%[rem]\n" |
173 | "4: slgr %[size],%[rem]\n" |
174 | " j 6f\n" |
175 | "5: slgr %[size],%[size]\n" |
176 | "6:\n" |
177 | EX_TABLE(0b, 2b) |
178 | EX_TABLE(1b, 2b) |
179 | EX_TABLE(3b, 6b) |
180 | EX_TABLE(4b, 6b) |
181 | : [size] "+&a" (size), [to] "+&a" (to), [rem] "=&a" (rem) |
182 | : [val] "a" (-4096UL), [zeropg] "a" (empty_zero_page), [spec] "d" (spec.val) |
183 | : "cc" , "memory" , "0" ); |
184 | return size; |
185 | } |
186 | EXPORT_SYMBOL(__clear_user); |
187 | |