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