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