1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2c6557e7fSMartin Schwidefsky /*
3c6557e7fSMartin Schwidefsky * S390 version
4a53c8fabSHeiko Carstens * Copyright IBM Corp. 1999, 2000
5c6557e7fSMartin Schwidefsky * Author(s): Hartmut Penner (hp@de.ibm.com),
6c6557e7fSMartin Schwidefsky * Martin Schwidefsky (schwidefsky@de.ibm.com)
7c6557e7fSMartin Schwidefsky *
8c6557e7fSMartin Schwidefsky * Derived from "include/asm-i386/uaccess.h"
9c6557e7fSMartin Schwidefsky */
10c6557e7fSMartin Schwidefsky #ifndef __S390_UACCESS_H
11c6557e7fSMartin Schwidefsky #define __S390_UACCESS_H
12c6557e7fSMartin Schwidefsky
13c6557e7fSMartin Schwidefsky /*
14c6557e7fSMartin Schwidefsky * User space memory access functions
15c6557e7fSMartin Schwidefsky */
16d09a307fSHeiko Carstens #include <asm/asm-extable.h>
17b5a882fcSHeiko Carstens #include <asm/processor.h>
18a0616cdeSDavid Howells #include <asm/ctl_reg.h>
19e70f1d59SAl Viro #include <asm/extable.h>
200aaba41bSMartin Schwidefsky #include <asm/facility.h>
2112700c17SArnd Bergmann #include <asm-generic/access_ok.h>
22c6557e7fSMartin Schwidefsky
2356e62a73SSven Schnelle void debug_user_asce(int exit);
24062e5279SHeiko Carstens
2537096003SAl Viro unsigned long __must_check
2637096003SAl Viro raw_copy_from_user(void *to, const void __user *from, unsigned long n);
27c6557e7fSMartin Schwidefsky
2837096003SAl Viro unsigned long __must_check
2937096003SAl Viro raw_copy_to_user(void __user *to, const void *from, unsigned long n);
30c6557e7fSMartin Schwidefsky
3101eb42afSVasily Gorbik #ifndef CONFIG_KASAN
3237096003SAl Viro #define INLINE_COPY_FROM_USER
3337096003SAl Viro #define INLINE_COPY_TO_USER
3401eb42afSVasily Gorbik #endif
356c1e3e79SGerald Schaefer
361a82f6abSJanis Schoetterl-Glausch unsigned long __must_check
371a82f6abSJanis Schoetterl-Glausch _copy_from_user_key(void *to, const void __user *from, unsigned long n, unsigned long key);
381a82f6abSJanis Schoetterl-Glausch
391a82f6abSJanis Schoetterl-Glausch static __always_inline unsigned long __must_check
copy_from_user_key(void * to,const void __user * from,unsigned long n,unsigned long key)401a82f6abSJanis Schoetterl-Glausch copy_from_user_key(void *to, const void __user *from, unsigned long n, unsigned long key)
411a82f6abSJanis Schoetterl-Glausch {
420e3c3b90SAl Viro if (check_copy_size(to, n, false))
431a82f6abSJanis Schoetterl-Glausch n = _copy_from_user_key(to, from, n, key);
441a82f6abSJanis Schoetterl-Glausch return n;
451a82f6abSJanis Schoetterl-Glausch }
461a82f6abSJanis Schoetterl-Glausch
471a82f6abSJanis Schoetterl-Glausch unsigned long __must_check
481a82f6abSJanis Schoetterl-Glausch _copy_to_user_key(void __user *to, const void *from, unsigned long n, unsigned long key);
491a82f6abSJanis Schoetterl-Glausch
501a82f6abSJanis Schoetterl-Glausch static __always_inline unsigned long __must_check
copy_to_user_key(void __user * to,const void * from,unsigned long n,unsigned long key)511a82f6abSJanis Schoetterl-Glausch copy_to_user_key(void __user *to, const void *from, unsigned long n, unsigned long key)
521a82f6abSJanis Schoetterl-Glausch {
530e3c3b90SAl Viro if (check_copy_size(from, n, true))
541a82f6abSJanis Schoetterl-Glausch n = _copy_to_user_key(to, from, n, key);
551a82f6abSJanis Schoetterl-Glausch return n;
561a82f6abSJanis Schoetterl-Glausch }
571a82f6abSJanis Schoetterl-Glausch
58012a224eSNico Boehr union oac {
59012a224eSNico Boehr unsigned int val;
60012a224eSNico Boehr struct {
61012a224eSNico Boehr struct {
62012a224eSNico Boehr unsigned short key : 4;
63012a224eSNico Boehr unsigned short : 4;
64012a224eSNico Boehr unsigned short as : 2;
65012a224eSNico Boehr unsigned short : 4;
66012a224eSNico Boehr unsigned short k : 1;
67012a224eSNico Boehr unsigned short a : 1;
68012a224eSNico Boehr } oac1;
69012a224eSNico Boehr struct {
70012a224eSNico Boehr unsigned short key : 4;
71012a224eSNico Boehr unsigned short : 4;
72012a224eSNico Boehr unsigned short as : 2;
73012a224eSNico Boehr unsigned short : 4;
74012a224eSNico Boehr unsigned short k : 1;
75012a224eSNico Boehr unsigned short a : 1;
76012a224eSNico Boehr } oac2;
77012a224eSNico Boehr };
78012a224eSNico Boehr };
79012a224eSNico Boehr
80a0e3a44bSHeiko Carstens int __noreturn __put_user_bad(void);
81a0e3a44bSHeiko Carstens
82454ede3fSHeiko Carstens #define __put_user_asm(to, from, size) \
83c9ca7841SHeiko Carstens ({ \
84454ede3fSHeiko Carstens union oac __oac_spec = { \
85454ede3fSHeiko Carstens .oac1.as = PSW_BITS_AS_SECONDARY, \
86454ede3fSHeiko Carstens .oac1.a = 1, \
87454ede3fSHeiko Carstens }; \
88c9ca7841SHeiko Carstens int __rc; \
89c9ca7841SHeiko Carstens \
90c9ca7841SHeiko Carstens asm volatile( \
91012a224eSNico Boehr " lr 0,%[spec]\n" \
92dbb8864bSHeiko Carstens "0: mvcos %[_to],%[_from],%[_size]\n" \
93dbb8864bSHeiko Carstens "1: xr %[rc],%[rc]\n" \
94c9ca7841SHeiko Carstens "2:\n" \
95454ede3fSHeiko Carstens EX_TABLE_UA_STORE(0b, 2b, %[rc]) \
96454ede3fSHeiko Carstens EX_TABLE_UA_STORE(1b, 2b, %[rc]) \
97dbb8864bSHeiko Carstens : [rc] "=&d" (__rc), [_to] "+Q" (*(to)) \
98dbb8864bSHeiko Carstens : [_size] "d" (size), [_from] "Q" (*(from)), \
99454ede3fSHeiko Carstens [spec] "d" (__oac_spec.val) \
100dbb8864bSHeiko Carstens : "cc", "0"); \
101c9ca7841SHeiko Carstens __rc; \
102c9ca7841SHeiko Carstens })
103c9ca7841SHeiko Carstens
__put_user_fn(void * x,void __user * ptr,unsigned long size)104062795fcSChristian Borntraeger static __always_inline int __put_user_fn(void *x, void __user *ptr, unsigned long size)
105dc4aace1SHeiko Carstens {
106dc4aace1SHeiko Carstens int rc;
107dc4aace1SHeiko Carstens
108dc4aace1SHeiko Carstens switch (size) {
109dc4aace1SHeiko Carstens case 1:
110012a224eSNico Boehr rc = __put_user_asm((unsigned char __user *)ptr,
111dc4aace1SHeiko Carstens (unsigned char *)x,
112012a224eSNico Boehr size);
113dc4aace1SHeiko Carstens break;
114dc4aace1SHeiko Carstens case 2:
115012a224eSNico Boehr rc = __put_user_asm((unsigned short __user *)ptr,
116dc4aace1SHeiko Carstens (unsigned short *)x,
117012a224eSNico Boehr size);
118dc4aace1SHeiko Carstens break;
119dc4aace1SHeiko Carstens case 4:
120012a224eSNico Boehr rc = __put_user_asm((unsigned int __user *)ptr,
121dc4aace1SHeiko Carstens (unsigned int *)x,
122012a224eSNico Boehr size);
123dc4aace1SHeiko Carstens break;
124dc4aace1SHeiko Carstens case 8:
125012a224eSNico Boehr rc = __put_user_asm((unsigned long __user *)ptr,
126dc4aace1SHeiko Carstens (unsigned long *)x,
127012a224eSNico Boehr size);
128dc4aace1SHeiko Carstens break;
129db527397SHeiko Carstens default:
130db527397SHeiko Carstens __put_user_bad();
131db527397SHeiko Carstens break;
1320b925159SHeiko Carstens }
133dc4aace1SHeiko Carstens return rc;
134dc4aace1SHeiko Carstens }
135dc4aace1SHeiko Carstens
136a0e3a44bSHeiko Carstens int __noreturn __get_user_bad(void);
137a0e3a44bSHeiko Carstens
138454ede3fSHeiko Carstens #define __get_user_asm(to, from, size) \
139454ede3fSHeiko Carstens ({ \
140454ede3fSHeiko Carstens union oac __oac_spec = { \
141454ede3fSHeiko Carstens .oac2.as = PSW_BITS_AS_SECONDARY, \
142454ede3fSHeiko Carstens .oac2.a = 1, \
143454ede3fSHeiko Carstens }; \
144454ede3fSHeiko Carstens int __rc; \
145454ede3fSHeiko Carstens \
146454ede3fSHeiko Carstens asm volatile( \
147454ede3fSHeiko Carstens " lr 0,%[spec]\n" \
148454ede3fSHeiko Carstens "0: mvcos 0(%[_to]),%[_from],%[_size]\n" \
149454ede3fSHeiko Carstens "1: xr %[rc],%[rc]\n" \
150454ede3fSHeiko Carstens "2:\n" \
151454ede3fSHeiko Carstens EX_TABLE_UA_LOAD_MEM(0b, 2b, %[rc], %[_to], %[_ksize]) \
152454ede3fSHeiko Carstens EX_TABLE_UA_LOAD_MEM(1b, 2b, %[rc], %[_to], %[_ksize]) \
153454ede3fSHeiko Carstens : [rc] "=&d" (__rc), "=Q" (*(to)) \
154454ede3fSHeiko Carstens : [_size] "d" (size), [_from] "Q" (*(from)), \
155454ede3fSHeiko Carstens [spec] "d" (__oac_spec.val), [_to] "a" (to), \
156454ede3fSHeiko Carstens [_ksize] "K" (size) \
157454ede3fSHeiko Carstens : "cc", "0"); \
158454ede3fSHeiko Carstens __rc; \
159454ede3fSHeiko Carstens })
160454ede3fSHeiko Carstens
__get_user_fn(void * x,const void __user * ptr,unsigned long size)161062795fcSChristian Borntraeger static __always_inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size)
162dc4aace1SHeiko Carstens {
163dc4aace1SHeiko Carstens int rc;
164dc4aace1SHeiko Carstens
165dc4aace1SHeiko Carstens switch (size) {
166dc4aace1SHeiko Carstens case 1:
167012a224eSNico Boehr rc = __get_user_asm((unsigned char *)x,
168dc4aace1SHeiko Carstens (unsigned char __user *)ptr,
169012a224eSNico Boehr size);
170dc4aace1SHeiko Carstens break;
171dc4aace1SHeiko Carstens case 2:
172012a224eSNico Boehr rc = __get_user_asm((unsigned short *)x,
173dc4aace1SHeiko Carstens (unsigned short __user *)ptr,
174012a224eSNico Boehr size);
175dc4aace1SHeiko Carstens break;
176dc4aace1SHeiko Carstens case 4:
177012a224eSNico Boehr rc = __get_user_asm((unsigned int *)x,
178dc4aace1SHeiko Carstens (unsigned int __user *)ptr,
179012a224eSNico Boehr size);
180dc4aace1SHeiko Carstens break;
181dc4aace1SHeiko Carstens case 8:
182012a224eSNico Boehr rc = __get_user_asm((unsigned long *)x,
183dc4aace1SHeiko Carstens (unsigned long __user *)ptr,
184012a224eSNico Boehr size);
185dc4aace1SHeiko Carstens break;
186db527397SHeiko Carstens default:
187db527397SHeiko Carstens __get_user_bad();
188db527397SHeiko Carstens break;
1890b925159SHeiko Carstens }
190dc4aace1SHeiko Carstens return rc;
191dc4aace1SHeiko Carstens }
192c9ca7841SHeiko Carstens
193c6557e7fSMartin Schwidefsky /*
194c6557e7fSMartin Schwidefsky * These are the main single-value transfer routines. They automatically
195c6557e7fSMartin Schwidefsky * use the right size if we just have the right pointer type.
196c6557e7fSMartin Schwidefsky */
197c6557e7fSMartin Schwidefsky #define __put_user(x, ptr) \
198c6557e7fSMartin Schwidefsky ({ \
199c6557e7fSMartin Schwidefsky __typeof__(*(ptr)) __x = (x); \
200c6557e7fSMartin Schwidefsky int __pu_err = -EFAULT; \
201d144182eSHeiko Carstens \
202c6557e7fSMartin Schwidefsky __chk_user_ptr(ptr); \
203c6557e7fSMartin Schwidefsky switch (sizeof(*(ptr))) { \
204c6557e7fSMartin Schwidefsky case 1: \
205c6557e7fSMartin Schwidefsky case 2: \
206c6557e7fSMartin Schwidefsky case 4: \
207c6557e7fSMartin Schwidefsky case 8: \
208d144182eSHeiko Carstens __pu_err = __put_user_fn(&__x, ptr, sizeof(*(ptr))); \
209c6557e7fSMartin Schwidefsky break; \
210c6557e7fSMartin Schwidefsky default: \
211c6557e7fSMartin Schwidefsky __put_user_bad(); \
212c6557e7fSMartin Schwidefsky break; \
213c6557e7fSMartin Schwidefsky } \
214ee64baf4SHeiko Carstens __builtin_expect(__pu_err, 0); \
215c6557e7fSMartin Schwidefsky })
216c6557e7fSMartin Schwidefsky
217c6557e7fSMartin Schwidefsky #define put_user(x, ptr) \
218c6557e7fSMartin Schwidefsky ({ \
219dab4079dSHeiko Carstens might_fault(); \
220c6557e7fSMartin Schwidefsky __put_user(x, ptr); \
221c6557e7fSMartin Schwidefsky })
222c6557e7fSMartin Schwidefsky
223c6557e7fSMartin Schwidefsky #define __get_user(x, ptr) \
224c6557e7fSMartin Schwidefsky ({ \
225c6557e7fSMartin Schwidefsky int __gu_err = -EFAULT; \
226d144182eSHeiko Carstens \
227c6557e7fSMartin Schwidefsky __chk_user_ptr(ptr); \
228c6557e7fSMartin Schwidefsky switch (sizeof(*(ptr))) { \
229c6557e7fSMartin Schwidefsky case 1: { \
230454ede3fSHeiko Carstens unsigned char __x; \
231d144182eSHeiko Carstens \
232d144182eSHeiko Carstens __gu_err = __get_user_fn(&__x, ptr, sizeof(*(ptr))); \
233c6557e7fSMartin Schwidefsky (x) = *(__force __typeof__(*(ptr)) *)&__x; \
234c6557e7fSMartin Schwidefsky break; \
235c6557e7fSMartin Schwidefsky }; \
236c6557e7fSMartin Schwidefsky case 2: { \
237454ede3fSHeiko Carstens unsigned short __x; \
238d144182eSHeiko Carstens \
239d144182eSHeiko Carstens __gu_err = __get_user_fn(&__x, ptr, sizeof(*(ptr))); \
240c6557e7fSMartin Schwidefsky (x) = *(__force __typeof__(*(ptr)) *)&__x; \
241c6557e7fSMartin Schwidefsky break; \
242c6557e7fSMartin Schwidefsky }; \
243c6557e7fSMartin Schwidefsky case 4: { \
244454ede3fSHeiko Carstens unsigned int __x; \
245d144182eSHeiko Carstens \
246d144182eSHeiko Carstens __gu_err = __get_user_fn(&__x, ptr, sizeof(*(ptr))); \
247c6557e7fSMartin Schwidefsky (x) = *(__force __typeof__(*(ptr)) *)&__x; \
248c6557e7fSMartin Schwidefsky break; \
249c6557e7fSMartin Schwidefsky }; \
250c6557e7fSMartin Schwidefsky case 8: { \
251454ede3fSHeiko Carstens unsigned long __x; \
252d144182eSHeiko Carstens \
253d144182eSHeiko Carstens __gu_err = __get_user_fn(&__x, ptr, sizeof(*(ptr))); \
254c6557e7fSMartin Schwidefsky (x) = *(__force __typeof__(*(ptr)) *)&__x; \
255c6557e7fSMartin Schwidefsky break; \
256c6557e7fSMartin Schwidefsky }; \
257c6557e7fSMartin Schwidefsky default: \
258c6557e7fSMartin Schwidefsky __get_user_bad(); \
259c6557e7fSMartin Schwidefsky break; \
260c6557e7fSMartin Schwidefsky } \
261ee64baf4SHeiko Carstens __builtin_expect(__gu_err, 0); \
262c6557e7fSMartin Schwidefsky })
263c6557e7fSMartin Schwidefsky
264c6557e7fSMartin Schwidefsky #define get_user(x, ptr) \
265c6557e7fSMartin Schwidefsky ({ \
266dab4079dSHeiko Carstens might_fault(); \
267c6557e7fSMartin Schwidefsky __get_user(x, ptr); \
268c6557e7fSMartin Schwidefsky })
269c6557e7fSMartin Schwidefsky
270c6557e7fSMartin Schwidefsky /*
271c6557e7fSMartin Schwidefsky * Copy a null terminated string from userspace.
272c6557e7fSMartin Schwidefsky */
273e93a1cb8SHeiko Carstens long __must_check strncpy_from_user(char *dst, const char __user *src, long count);
2744f41c2b4SHeiko Carstens
275e93a1cb8SHeiko Carstens long __must_check strnlen_user(const char __user *src, long count);
276c6557e7fSMartin Schwidefsky
277c6557e7fSMartin Schwidefsky /*
278c6557e7fSMartin Schwidefsky * Zero Userspace
279c6557e7fSMartin Schwidefsky */
280211deca6SHeiko Carstens unsigned long __must_check __clear_user(void __user *to, unsigned long size);
281c6557e7fSMartin Schwidefsky
clear_user(void __user * to,unsigned long n)282211deca6SHeiko Carstens static inline unsigned long __must_check clear_user(void __user *to, unsigned long n)
283c6557e7fSMartin Schwidefsky {
284dab4079dSHeiko Carstens might_fault();
2854f41c2b4SHeiko Carstens return __clear_user(to, n);
286c6557e7fSMartin Schwidefsky }
287c6557e7fSMartin Schwidefsky
288cb2cceaeSJosh Poimboeuf void *s390_kernel_write(void *dst, const void *src, size_t size);
289a0616cdeSDavid Howells
290110a6dbbSHeiko Carstens int __noreturn __put_kernel_bad(void);
291110a6dbbSHeiko Carstens
292110a6dbbSHeiko Carstens #define __put_kernel_asm(val, to, insn) \
293110a6dbbSHeiko Carstens ({ \
294110a6dbbSHeiko Carstens int __rc; \
295110a6dbbSHeiko Carstens \
296110a6dbbSHeiko Carstens asm volatile( \
29779a74dacSHeiko Carstens "0: " insn " %[_val],%[_to]\n" \
29879a74dacSHeiko Carstens "1: xr %[rc],%[rc]\n" \
299110a6dbbSHeiko Carstens "2:\n" \
300454ede3fSHeiko Carstens EX_TABLE_UA_STORE(0b, 2b, %[rc]) \
301454ede3fSHeiko Carstens EX_TABLE_UA_STORE(1b, 2b, %[rc]) \
30279a74dacSHeiko Carstens : [rc] "=d" (__rc), [_to] "+Q" (*(to)) \
30379a74dacSHeiko Carstens : [_val] "d" (val) \
304110a6dbbSHeiko Carstens : "cc"); \
305110a6dbbSHeiko Carstens __rc; \
306110a6dbbSHeiko Carstens })
307110a6dbbSHeiko Carstens
308110a6dbbSHeiko Carstens #define __put_kernel_nofault(dst, src, type, err_label) \
309110a6dbbSHeiko Carstens do { \
310454ede3fSHeiko Carstens unsigned long __x = (unsigned long)(*((type *)(src))); \
311110a6dbbSHeiko Carstens int __pk_err; \
312110a6dbbSHeiko Carstens \
313110a6dbbSHeiko Carstens switch (sizeof(type)) { \
314110a6dbbSHeiko Carstens case 1: \
315110a6dbbSHeiko Carstens __pk_err = __put_kernel_asm(__x, (type *)(dst), "stc"); \
316110a6dbbSHeiko Carstens break; \
317110a6dbbSHeiko Carstens case 2: \
318110a6dbbSHeiko Carstens __pk_err = __put_kernel_asm(__x, (type *)(dst), "sth"); \
319110a6dbbSHeiko Carstens break; \
320110a6dbbSHeiko Carstens case 4: \
321110a6dbbSHeiko Carstens __pk_err = __put_kernel_asm(__x, (type *)(dst), "st"); \
322110a6dbbSHeiko Carstens break; \
323110a6dbbSHeiko Carstens case 8: \
324110a6dbbSHeiko Carstens __pk_err = __put_kernel_asm(__x, (type *)(dst), "stg"); \
325110a6dbbSHeiko Carstens break; \
326110a6dbbSHeiko Carstens default: \
327110a6dbbSHeiko Carstens __pk_err = __put_kernel_bad(); \
328110a6dbbSHeiko Carstens break; \
329110a6dbbSHeiko Carstens } \
330110a6dbbSHeiko Carstens if (unlikely(__pk_err)) \
331110a6dbbSHeiko Carstens goto err_label; \
332110a6dbbSHeiko Carstens } while (0)
333110a6dbbSHeiko Carstens
334110a6dbbSHeiko Carstens int __noreturn __get_kernel_bad(void);
335110a6dbbSHeiko Carstens
336110a6dbbSHeiko Carstens #define __get_kernel_asm(val, from, insn) \
337110a6dbbSHeiko Carstens ({ \
338110a6dbbSHeiko Carstens int __rc; \
339110a6dbbSHeiko Carstens \
340110a6dbbSHeiko Carstens asm volatile( \
34179a74dacSHeiko Carstens "0: " insn " %[_val],%[_from]\n" \
34279a74dacSHeiko Carstens "1: xr %[rc],%[rc]\n" \
343110a6dbbSHeiko Carstens "2:\n" \
344454ede3fSHeiko Carstens EX_TABLE_UA_LOAD_REG(0b, 2b, %[rc], %[_val]) \
345454ede3fSHeiko Carstens EX_TABLE_UA_LOAD_REG(1b, 2b, %[rc], %[_val]) \
346454ede3fSHeiko Carstens : [rc] "=d" (__rc), [_val] "=d" (val) \
34779a74dacSHeiko Carstens : [_from] "Q" (*(from)) \
348110a6dbbSHeiko Carstens : "cc"); \
349110a6dbbSHeiko Carstens __rc; \
350110a6dbbSHeiko Carstens })
351110a6dbbSHeiko Carstens
352110a6dbbSHeiko Carstens #define __get_kernel_nofault(dst, src, type, err_label) \
353110a6dbbSHeiko Carstens do { \
354110a6dbbSHeiko Carstens int __gk_err; \
355110a6dbbSHeiko Carstens \
356110a6dbbSHeiko Carstens switch (sizeof(type)) { \
357110a6dbbSHeiko Carstens case 1: { \
358454ede3fSHeiko Carstens unsigned char __x; \
359110a6dbbSHeiko Carstens \
360110a6dbbSHeiko Carstens __gk_err = __get_kernel_asm(__x, (type *)(src), "ic"); \
361110a6dbbSHeiko Carstens *((type *)(dst)) = (type)__x; \
362110a6dbbSHeiko Carstens break; \
363110a6dbbSHeiko Carstens }; \
364110a6dbbSHeiko Carstens case 2: { \
365454ede3fSHeiko Carstens unsigned short __x; \
366110a6dbbSHeiko Carstens \
367110a6dbbSHeiko Carstens __gk_err = __get_kernel_asm(__x, (type *)(src), "lh"); \
368110a6dbbSHeiko Carstens *((type *)(dst)) = (type)__x; \
369110a6dbbSHeiko Carstens break; \
370110a6dbbSHeiko Carstens }; \
371110a6dbbSHeiko Carstens case 4: { \
372454ede3fSHeiko Carstens unsigned int __x; \
373110a6dbbSHeiko Carstens \
374110a6dbbSHeiko Carstens __gk_err = __get_kernel_asm(__x, (type *)(src), "l"); \
375110a6dbbSHeiko Carstens *((type *)(dst)) = (type)__x; \
376110a6dbbSHeiko Carstens break; \
377110a6dbbSHeiko Carstens }; \
378110a6dbbSHeiko Carstens case 8: { \
379454ede3fSHeiko Carstens unsigned long __x; \
380110a6dbbSHeiko Carstens \
381110a6dbbSHeiko Carstens __gk_err = __get_kernel_asm(__x, (type *)(src), "lg"); \
382110a6dbbSHeiko Carstens *((type *)(dst)) = (type)__x; \
383110a6dbbSHeiko Carstens break; \
384110a6dbbSHeiko Carstens }; \
385110a6dbbSHeiko Carstens default: \
386110a6dbbSHeiko Carstens __gk_err = __get_kernel_bad(); \
387110a6dbbSHeiko Carstens break; \
388110a6dbbSHeiko Carstens } \
389110a6dbbSHeiko Carstens if (unlikely(__gk_err)) \
390110a6dbbSHeiko Carstens goto err_label; \
391110a6dbbSHeiko Carstens } while (0)
392110a6dbbSHeiko Carstens
3934148575aSHeiko Carstens void __cmpxchg_user_key_called_with_bad_pointer(void);
3944148575aSHeiko Carstens
395739ad2e4SJanis Schoetterl-Glausch #define CMPXCHG_USER_KEY_MAX_LOOPS 128
396739ad2e4SJanis Schoetterl-Glausch
__cmpxchg_user_key(unsigned long address,void * uval,__uint128_t old,__uint128_t new,unsigned long key,int size)3974148575aSHeiko Carstens static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
3984148575aSHeiko Carstens __uint128_t old, __uint128_t new,
3994148575aSHeiko Carstens unsigned long key, int size)
4004148575aSHeiko Carstens {
4014148575aSHeiko Carstens int rc = 0;
4024148575aSHeiko Carstens
4034148575aSHeiko Carstens switch (size) {
4044148575aSHeiko Carstens case 1: {
40551098f0eSJanis Schoetterl-Glausch unsigned int prev, shift, mask, _old, _new;
406739ad2e4SJanis Schoetterl-Glausch unsigned long count;
4074148575aSHeiko Carstens
4084148575aSHeiko Carstens shift = (3 ^ (address & 3)) << 3;
4094148575aSHeiko Carstens address ^= address & 3;
410*b33d59fbSHeiko Carstens _old = ((unsigned int)old & 0xff) << shift;
411*b33d59fbSHeiko Carstens _new = ((unsigned int)new & 0xff) << shift;
41251098f0eSJanis Schoetterl-Glausch mask = ~(0xff << shift);
4134148575aSHeiko Carstens asm volatile(
4144148575aSHeiko Carstens " spka 0(%[key])\n"
4154148575aSHeiko Carstens " sacf 256\n"
416739ad2e4SJanis Schoetterl-Glausch " llill %[count],%[max_loops]\n"
4174148575aSHeiko Carstens "0: l %[prev],%[address]\n"
4184148575aSHeiko Carstens "1: nr %[prev],%[mask]\n"
41951098f0eSJanis Schoetterl-Glausch " xilf %[mask],0xffffffff\n"
42051098f0eSJanis Schoetterl-Glausch " or %[new],%[prev]\n"
42151098f0eSJanis Schoetterl-Glausch " or %[prev],%[tmp]\n"
42251098f0eSJanis Schoetterl-Glausch "2: lr %[tmp],%[prev]\n"
42351098f0eSJanis Schoetterl-Glausch "3: cs %[prev],%[new],%[address]\n"
42451098f0eSJanis Schoetterl-Glausch "4: jnl 5f\n"
4254148575aSHeiko Carstens " xr %[tmp],%[prev]\n"
42651098f0eSJanis Schoetterl-Glausch " xr %[new],%[tmp]\n"
4274148575aSHeiko Carstens " nr %[tmp],%[mask]\n"
428739ad2e4SJanis Schoetterl-Glausch " jnz 5f\n"
429739ad2e4SJanis Schoetterl-Glausch " brct %[count],2b\n"
43051098f0eSJanis Schoetterl-Glausch "5: sacf 768\n"
4314148575aSHeiko Carstens " spka %[default_key]\n"
43251098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev])
43351098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev])
43451098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev])
43551098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev])
4364148575aSHeiko Carstens : [rc] "+&d" (rc),
4374148575aSHeiko Carstens [prev] "=&d" (prev),
43851098f0eSJanis Schoetterl-Glausch [address] "+Q" (*(int *)address),
43951098f0eSJanis Schoetterl-Glausch [tmp] "+&d" (_old),
44051098f0eSJanis Schoetterl-Glausch [new] "+&d" (_new),
441739ad2e4SJanis Schoetterl-Glausch [mask] "+&d" (mask),
442739ad2e4SJanis Schoetterl-Glausch [count] "=a" (count)
443739ad2e4SJanis Schoetterl-Glausch : [key] "%[count]" (key << 4),
444739ad2e4SJanis Schoetterl-Glausch [default_key] "J" (PAGE_DEFAULT_KEY),
445739ad2e4SJanis Schoetterl-Glausch [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS)
4464148575aSHeiko Carstens : "memory", "cc");
4474148575aSHeiko Carstens *(unsigned char *)uval = prev >> shift;
448739ad2e4SJanis Schoetterl-Glausch if (!count)
449739ad2e4SJanis Schoetterl-Glausch rc = -EAGAIN;
4504148575aSHeiko Carstens return rc;
4514148575aSHeiko Carstens }
4524148575aSHeiko Carstens case 2: {
45351098f0eSJanis Schoetterl-Glausch unsigned int prev, shift, mask, _old, _new;
454739ad2e4SJanis Schoetterl-Glausch unsigned long count;
4554148575aSHeiko Carstens
4564148575aSHeiko Carstens shift = (2 ^ (address & 2)) << 3;
4574148575aSHeiko Carstens address ^= address & 2;
458*b33d59fbSHeiko Carstens _old = ((unsigned int)old & 0xffff) << shift;
459*b33d59fbSHeiko Carstens _new = ((unsigned int)new & 0xffff) << shift;
46051098f0eSJanis Schoetterl-Glausch mask = ~(0xffff << shift);
4614148575aSHeiko Carstens asm volatile(
4624148575aSHeiko Carstens " spka 0(%[key])\n"
4634148575aSHeiko Carstens " sacf 256\n"
464739ad2e4SJanis Schoetterl-Glausch " llill %[count],%[max_loops]\n"
4654148575aSHeiko Carstens "0: l %[prev],%[address]\n"
4664148575aSHeiko Carstens "1: nr %[prev],%[mask]\n"
46751098f0eSJanis Schoetterl-Glausch " xilf %[mask],0xffffffff\n"
46851098f0eSJanis Schoetterl-Glausch " or %[new],%[prev]\n"
46951098f0eSJanis Schoetterl-Glausch " or %[prev],%[tmp]\n"
47051098f0eSJanis Schoetterl-Glausch "2: lr %[tmp],%[prev]\n"
47151098f0eSJanis Schoetterl-Glausch "3: cs %[prev],%[new],%[address]\n"
47251098f0eSJanis Schoetterl-Glausch "4: jnl 5f\n"
4734148575aSHeiko Carstens " xr %[tmp],%[prev]\n"
47451098f0eSJanis Schoetterl-Glausch " xr %[new],%[tmp]\n"
4754148575aSHeiko Carstens " nr %[tmp],%[mask]\n"
476739ad2e4SJanis Schoetterl-Glausch " jnz 5f\n"
477739ad2e4SJanis Schoetterl-Glausch " brct %[count],2b\n"
47851098f0eSJanis Schoetterl-Glausch "5: sacf 768\n"
4794148575aSHeiko Carstens " spka %[default_key]\n"
48051098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev])
48151098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev])
48251098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev])
48351098f0eSJanis Schoetterl-Glausch EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev])
4844148575aSHeiko Carstens : [rc] "+&d" (rc),
4854148575aSHeiko Carstens [prev] "=&d" (prev),
48651098f0eSJanis Schoetterl-Glausch [address] "+Q" (*(int *)address),
48751098f0eSJanis Schoetterl-Glausch [tmp] "+&d" (_old),
48851098f0eSJanis Schoetterl-Glausch [new] "+&d" (_new),
489739ad2e4SJanis Schoetterl-Glausch [mask] "+&d" (mask),
490739ad2e4SJanis Schoetterl-Glausch [count] "=a" (count)
491739ad2e4SJanis Schoetterl-Glausch : [key] "%[count]" (key << 4),
492739ad2e4SJanis Schoetterl-Glausch [default_key] "J" (PAGE_DEFAULT_KEY),
493739ad2e4SJanis Schoetterl-Glausch [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS)
4944148575aSHeiko Carstens : "memory", "cc");
4954148575aSHeiko Carstens *(unsigned short *)uval = prev >> shift;
496739ad2e4SJanis Schoetterl-Glausch if (!count)
497739ad2e4SJanis Schoetterl-Glausch rc = -EAGAIN;
4984148575aSHeiko Carstens return rc;
4994148575aSHeiko Carstens }
5004148575aSHeiko Carstens case 4: {
5014148575aSHeiko Carstens unsigned int prev = old;
5024148575aSHeiko Carstens
5034148575aSHeiko Carstens asm volatile(
5044148575aSHeiko Carstens " spka 0(%[key])\n"
5054148575aSHeiko Carstens " sacf 256\n"
5064148575aSHeiko Carstens "0: cs %[prev],%[new],%[address]\n"
5074148575aSHeiko Carstens "1: sacf 768\n"
5084148575aSHeiko Carstens " spka %[default_key]\n"
5094148575aSHeiko Carstens EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev])
5104148575aSHeiko Carstens EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev])
5114148575aSHeiko Carstens : [rc] "+&d" (rc),
5124148575aSHeiko Carstens [prev] "+&d" (prev),
5134148575aSHeiko Carstens [address] "+Q" (*(int *)address)
5144148575aSHeiko Carstens : [new] "d" ((unsigned int)new),
5154148575aSHeiko Carstens [key] "a" (key << 4),
5164148575aSHeiko Carstens [default_key] "J" (PAGE_DEFAULT_KEY)
5174148575aSHeiko Carstens : "memory", "cc");
5184148575aSHeiko Carstens *(unsigned int *)uval = prev;
5194148575aSHeiko Carstens return rc;
5204148575aSHeiko Carstens }
5214148575aSHeiko Carstens case 8: {
5224148575aSHeiko Carstens unsigned long prev = old;
5234148575aSHeiko Carstens
5244148575aSHeiko Carstens asm volatile(
5254148575aSHeiko Carstens " spka 0(%[key])\n"
5264148575aSHeiko Carstens " sacf 256\n"
5274148575aSHeiko Carstens "0: csg %[prev],%[new],%[address]\n"
5284148575aSHeiko Carstens "1: sacf 768\n"
5294148575aSHeiko Carstens " spka %[default_key]\n"
5304148575aSHeiko Carstens EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev])
5314148575aSHeiko Carstens EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev])
5324148575aSHeiko Carstens : [rc] "+&d" (rc),
5334148575aSHeiko Carstens [prev] "+&d" (prev),
5344148575aSHeiko Carstens [address] "+QS" (*(long *)address)
5354148575aSHeiko Carstens : [new] "d" ((unsigned long)new),
5364148575aSHeiko Carstens [key] "a" (key << 4),
5374148575aSHeiko Carstens [default_key] "J" (PAGE_DEFAULT_KEY)
5384148575aSHeiko Carstens : "memory", "cc");
5394148575aSHeiko Carstens *(unsigned long *)uval = prev;
5404148575aSHeiko Carstens return rc;
5414148575aSHeiko Carstens }
5424148575aSHeiko Carstens case 16: {
5434148575aSHeiko Carstens __uint128_t prev = old;
5444148575aSHeiko Carstens
5454148575aSHeiko Carstens asm volatile(
5464148575aSHeiko Carstens " spka 0(%[key])\n"
5474148575aSHeiko Carstens " sacf 256\n"
5484148575aSHeiko Carstens "0: cdsg %[prev],%[new],%[address]\n"
5494148575aSHeiko Carstens "1: sacf 768\n"
5504148575aSHeiko Carstens " spka %[default_key]\n"
5514148575aSHeiko Carstens EX_TABLE_UA_LOAD_REGPAIR(0b, 1b, %[rc], %[prev])
5524148575aSHeiko Carstens EX_TABLE_UA_LOAD_REGPAIR(1b, 1b, %[rc], %[prev])
5534148575aSHeiko Carstens : [rc] "+&d" (rc),
5544148575aSHeiko Carstens [prev] "+&d" (prev),
5554148575aSHeiko Carstens [address] "+QS" (*(__int128_t *)address)
5564148575aSHeiko Carstens : [new] "d" (new),
5574148575aSHeiko Carstens [key] "a" (key << 4),
5584148575aSHeiko Carstens [default_key] "J" (PAGE_DEFAULT_KEY)
5594148575aSHeiko Carstens : "memory", "cc");
5604148575aSHeiko Carstens *(__uint128_t *)uval = prev;
5614148575aSHeiko Carstens return rc;
5624148575aSHeiko Carstens }
5634148575aSHeiko Carstens }
5644148575aSHeiko Carstens __cmpxchg_user_key_called_with_bad_pointer();
5654148575aSHeiko Carstens return rc;
5664148575aSHeiko Carstens }
5674148575aSHeiko Carstens
5684148575aSHeiko Carstens /**
5694148575aSHeiko Carstens * cmpxchg_user_key() - cmpxchg with user space target, honoring storage keys
5704148575aSHeiko Carstens * @ptr: User space address of value to compare to @old and exchange with
5714148575aSHeiko Carstens * @new. Must be aligned to sizeof(*@ptr).
5724148575aSHeiko Carstens * @uval: Address where the old value of *@ptr is written to.
5734148575aSHeiko Carstens * @old: Old value. Compared to the content pointed to by @ptr in order to
5744148575aSHeiko Carstens * determine if the exchange occurs. The old value read from *@ptr is
5754148575aSHeiko Carstens * written to *@uval.
5764148575aSHeiko Carstens * @new: New value to place at *@ptr.
5774148575aSHeiko Carstens * @key: Access key to use for checking storage key protection.
5784148575aSHeiko Carstens *
5794148575aSHeiko Carstens * Perform a cmpxchg on a user space target, honoring storage key protection.
5804148575aSHeiko Carstens * @key alone determines how key checking is performed, neither
5814148575aSHeiko Carstens * storage-protection-override nor fetch-protection-override apply.
5824148575aSHeiko Carstens * The caller must compare *@uval and @old to determine if values have been
5834148575aSHeiko Carstens * exchanged. In case of an exception *@uval is set to zero.
5844148575aSHeiko Carstens *
5854148575aSHeiko Carstens * Return: 0: cmpxchg executed
5864148575aSHeiko Carstens * -EFAULT: an exception happened when trying to access *@ptr
587739ad2e4SJanis Schoetterl-Glausch * -EAGAIN: maxed out number of retries (byte and short only)
5884148575aSHeiko Carstens */
5894148575aSHeiko Carstens #define cmpxchg_user_key(ptr, uval, old, new, key) \
5904148575aSHeiko Carstens ({ \
5914148575aSHeiko Carstens __typeof__(ptr) __ptr = (ptr); \
5924148575aSHeiko Carstens __typeof__(uval) __uval = (uval); \
5934148575aSHeiko Carstens \
5944148575aSHeiko Carstens BUILD_BUG_ON(sizeof(*(__ptr)) != sizeof(*(__uval))); \
5954148575aSHeiko Carstens might_fault(); \
5964148575aSHeiko Carstens __chk_user_ptr(__ptr); \
5974148575aSHeiko Carstens __cmpxchg_user_key((unsigned long)(__ptr), (void *)(__uval), \
5984148575aSHeiko Carstens (old), (new), (key), sizeof(*(__ptr))); \
5994148575aSHeiko Carstens })
6004148575aSHeiko Carstens
601c6557e7fSMartin Schwidefsky #endif /* __S390_UACCESS_H */
602