1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
4  * Copyright (C) 2008-2009 PetaLogix
5  * Copyright (C) 2006 Atmark Techno, Inc.
6  */
7 
8 #ifndef _ASM_MICROBLAZE_UACCESS_H
9 #define _ASM_MICROBLAZE_UACCESS_H
10 
11 #include <linux/kernel.h>
12 
13 #include <asm/mmu.h>
14 #include <asm/page.h>
15 #include <linux/pgtable.h>
16 #include <asm/extable.h>
17 #include <linux/string.h>
18 
19 /*
20  * On Microblaze the fs value is actually the top of the corresponding
21  * address space.
22  *
23  * The fs value determines whether argument validity checking should be
24  * performed or not. If get_fs() == USER_DS, checking is performed, with
25  * get_fs() == KERNEL_DS, checking is bypassed.
26  *
27  * For historical reasons, these macros are grossly misnamed.
28  *
29  * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal.
30  */
31 # define MAKE_MM_SEG(s)       ((mm_segment_t) { (s) })
32 
33 #  define KERNEL_DS	MAKE_MM_SEG(0xFFFFFFFF)
34 #  define USER_DS	MAKE_MM_SEG(TASK_SIZE - 1)
35 
36 # define get_fs()	(current_thread_info()->addr_limit)
37 # define set_fs(val)	(current_thread_info()->addr_limit = (val))
38 
39 # define uaccess_kernel()	(get_fs().seg == KERNEL_DS.seg)
40 
41 static inline int access_ok(const void __user *addr, unsigned long size)
42 {
43 	if (!size)
44 		goto ok;
45 
46 	if ((get_fs().seg < ((unsigned long)addr)) ||
47 			(get_fs().seg < ((unsigned long)addr + size - 1))) {
48 		pr_devel("ACCESS fail at 0x%08x (size 0x%x), seg 0x%08x\n",
49 			(__force u32)addr, (u32)size,
50 			(u32)get_fs().seg);
51 		return 0;
52 	}
53 ok:
54 	pr_devel("ACCESS OK at 0x%08x (size 0x%x), seg 0x%08x\n",
55 			(__force u32)addr, (u32)size,
56 			(u32)get_fs().seg);
57 	return 1;
58 }
59 
60 # define __FIXUP_SECTION	".section .fixup,\"ax\"\n"
61 # define __EX_TABLE_SECTION	".section __ex_table,\"a\"\n"
62 
63 extern unsigned long __copy_tofrom_user(void __user *to,
64 		const void __user *from, unsigned long size);
65 
66 /* Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. */
67 static inline unsigned long __must_check __clear_user(void __user *to,
68 							unsigned long n)
69 {
70 	/* normal memset with two words to __ex_table */
71 	__asm__ __volatile__ (				\
72 			"1:	sb	r0, %1, r0;"	\
73 			"	addik	%0, %0, -1;"	\
74 			"	bneid	%0, 1b;"	\
75 			"	addik	%1, %1, 1;"	\
76 			"2:			"	\
77 			__EX_TABLE_SECTION		\
78 			".word	1b,2b;"			\
79 			".previous;"			\
80 		: "=r"(n), "=r"(to)			\
81 		: "0"(n), "1"(to)
82 	);
83 	return n;
84 }
85 
86 static inline unsigned long __must_check clear_user(void __user *to,
87 							unsigned long n)
88 {
89 	might_fault();
90 	if (unlikely(!access_ok(to, n)))
91 		return n;
92 
93 	return __clear_user(to, n);
94 }
95 
96 /* put_user and get_user macros */
97 extern long __user_bad(void);
98 
99 #define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\
100 ({								\
101 	__asm__ __volatile__ (					\
102 			"1:"	insn	" %1, %2, r0;"		\
103 			"	addk	%0, r0, r0;"		\
104 			"2:			"		\
105 			__FIXUP_SECTION				\
106 			"3:	brid	2b;"			\
107 			"	addik	%0, r0, %3;"		\
108 			".previous;"				\
109 			__EX_TABLE_SECTION			\
110 			".word	1b,3b;"				\
111 			".previous;"				\
112 		: "=&r"(__gu_err), "=r"(__gu_val)		\
113 		: "r"(__gu_ptr), "i"(-EFAULT)			\
114 	);							\
115 })
116 
117 /**
118  * get_user: - Get a simple variable from user space.
119  * @x:   Variable to store result.
120  * @ptr: Source address, in user space.
121  *
122  * Context: User context only. This function may sleep if pagefaults are
123  *          enabled.
124  *
125  * This macro copies a single simple variable from user space to kernel
126  * space.  It supports simple types like char and int, but not larger
127  * data types like structures or arrays.
128  *
129  * @ptr must have pointer-to-simple-variable type, and the result of
130  * dereferencing @ptr must be assignable to @x without a cast.
131  *
132  * Returns zero on success, or -EFAULT on error.
133  * On error, the variable @x is set to zero.
134  */
135 #define get_user(x, ptr) ({				\
136 	const typeof(*(ptr)) __user *__gu_ptr = (ptr);	\
137 	access_ok(__gu_ptr, sizeof(*__gu_ptr)) ?	\
138 		__get_user(x, __gu_ptr) : -EFAULT;	\
139 })
140 
141 #define __get_user(x, ptr)						\
142 ({									\
143 	unsigned long __gu_val = 0;					\
144 	long __gu_err;							\
145 	switch (sizeof(*(ptr))) {					\
146 	case 1:								\
147 		__get_user_asm("lbu", (ptr), __gu_val, __gu_err);	\
148 		break;							\
149 	case 2:								\
150 		__get_user_asm("lhu", (ptr), __gu_val, __gu_err);	\
151 		break;							\
152 	case 4:								\
153 		__get_user_asm("lw", (ptr), __gu_val, __gu_err);	\
154 		break;							\
155 	case 8:								\
156 		__gu_err = __copy_from_user(&__gu_val, ptr, 8);		\
157 		if (__gu_err)						\
158 			__gu_err = -EFAULT;				\
159 		break;							\
160 	default:							\
161 		/* __gu_val = 0; __gu_err = -EINVAL;*/ __gu_err = __user_bad();\
162 	}								\
163 	x = (__force __typeof__(*(ptr))) __gu_val;			\
164 	__gu_err;							\
165 })
166 
167 
168 #define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\
169 ({								\
170 	__asm__ __volatile__ (					\
171 			"1:"	insn	" %1, %2, r0;"		\
172 			"	addk	%0, r0, r0;"		\
173 			"2:			"		\
174 			__FIXUP_SECTION				\
175 			"3:	brid	2b;"			\
176 			"	addik	%0, r0, %3;"		\
177 			".previous;"				\
178 			__EX_TABLE_SECTION			\
179 			".word	1b,3b;"				\
180 			".previous;"				\
181 		: "=&r"(__gu_err)				\
182 		: "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\
183 	);							\
184 })
185 
186 #define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err)		\
187 ({								\
188 	__asm__ __volatile__ ("	lwi	%0, %1, 0;"		\
189 			"1:	swi	%0, %2, 0;"		\
190 			"	lwi	%0, %1, 4;"		\
191 			"2:	swi	%0, %2, 4;"		\
192 			"	addk	%0, r0, r0;"		\
193 			"3:			"		\
194 			__FIXUP_SECTION				\
195 			"4:	brid	3b;"			\
196 			"	addik	%0, r0, %3;"		\
197 			".previous;"				\
198 			__EX_TABLE_SECTION			\
199 			".word	1b,4b,2b,4b;"			\
200 			".previous;"				\
201 		: "=&r"(__gu_err)				\
202 		: "r"(&__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\
203 		);						\
204 })
205 
206 /**
207  * put_user: - Write a simple value into user space.
208  * @x:   Value to copy to user space.
209  * @ptr: Destination address, in user space.
210  *
211  * Context: User context only. This function may sleep if pagefaults are
212  *          enabled.
213  *
214  * This macro copies a single simple value from kernel space to user
215  * space.  It supports simple types like char and int, but not larger
216  * data types like structures or arrays.
217  *
218  * @ptr must have pointer-to-simple-variable type, and @x must be assignable
219  * to the result of dereferencing @ptr.
220  *
221  * Returns zero on success, or -EFAULT on error.
222  */
223 #define put_user(x, ptr)						\
224 	__put_user_check((x), (ptr), sizeof(*(ptr)))
225 
226 #define __put_user_check(x, ptr, size)					\
227 ({									\
228 	typeof(*(ptr)) volatile __pu_val = x;				\
229 	typeof(*(ptr)) __user *__pu_addr = (ptr);			\
230 	int __pu_err = 0;						\
231 									\
232 	if (access_ok(__pu_addr, size)) {			\
233 		switch (size) {						\
234 		case 1:							\
235 			__put_user_asm("sb", __pu_addr, __pu_val,	\
236 				       __pu_err);			\
237 			break;						\
238 		case 2:							\
239 			__put_user_asm("sh", __pu_addr, __pu_val,	\
240 				       __pu_err);			\
241 			break;						\
242 		case 4:							\
243 			__put_user_asm("sw", __pu_addr, __pu_val,	\
244 				       __pu_err);			\
245 			break;						\
246 		case 8:							\
247 			__put_user_asm_8(__pu_addr, __pu_val, __pu_err);\
248 			break;						\
249 		default:						\
250 			__pu_err = __user_bad();			\
251 			break;						\
252 		}							\
253 	} else {							\
254 		__pu_err = -EFAULT;					\
255 	}								\
256 	__pu_err;							\
257 })
258 
259 #define __put_user(x, ptr)						\
260 ({									\
261 	__typeof__(*(ptr)) volatile __gu_val = (x);			\
262 	long __gu_err = 0;						\
263 	switch (sizeof(__gu_val)) {					\
264 	case 1:								\
265 		__put_user_asm("sb", (ptr), __gu_val, __gu_err);	\
266 		break;							\
267 	case 2:								\
268 		__put_user_asm("sh", (ptr), __gu_val, __gu_err);	\
269 		break;							\
270 	case 4:								\
271 		__put_user_asm("sw", (ptr), __gu_val, __gu_err);	\
272 		break;							\
273 	case 8:								\
274 		__put_user_asm_8((ptr), __gu_val, __gu_err);		\
275 		break;							\
276 	default:							\
277 		/*__gu_err = -EINVAL;*/	__gu_err = __user_bad();	\
278 	}								\
279 	__gu_err;							\
280 })
281 
282 static inline unsigned long
283 raw_copy_from_user(void *to, const void __user *from, unsigned long n)
284 {
285 	return __copy_tofrom_user((__force void __user *)to, from, n);
286 }
287 
288 static inline unsigned long
289 raw_copy_to_user(void __user *to, const void *from, unsigned long n)
290 {
291 	return __copy_tofrom_user(to, (__force const void __user *)from, n);
292 }
293 #define INLINE_COPY_FROM_USER
294 #define INLINE_COPY_TO_USER
295 
296 /*
297  * Copy a null terminated string from userspace.
298  */
299 extern int __strncpy_user(char *to, const char __user *from, int len);
300 
301 static inline long
302 strncpy_from_user(char *dst, const char __user *src, long count)
303 {
304 	if (!access_ok(src, 1))
305 		return -EFAULT;
306 	return __strncpy_user(dst, src, count);
307 }
308 
309 /*
310  * Return the size of a string (including the ending 0)
311  *
312  * Return 0 on exception, a value greater than N if too long
313  */
314 extern int __strnlen_user(const char __user *sstr, int len);
315 
316 static inline long strnlen_user(const char __user *src, long n)
317 {
318 	if (!access_ok(src, 1))
319 		return 0;
320 	return __strnlen_user(src, n);
321 }
322 
323 #endif /* _ASM_MICROBLAZE_UACCESS_H */
324