• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 # define user_addr_max() get_fs().seg
39 
40 # define uaccess_kernel()	(get_fs().seg == KERNEL_DS.seg)
41 
__access_ok(unsigned long addr,unsigned long size)42 static inline int __access_ok(unsigned long addr, unsigned long size)
43 {
44 	unsigned long limit = user_addr_max();
45 
46 	return (size <= limit) && (addr <= (limit - size));
47 }
48 #define access_ok(addr, size) __access_ok((unsigned long)addr, size)
49 
50 # define __FIXUP_SECTION	".section .fixup,\"ax\"\n"
51 # define __EX_TABLE_SECTION	".section __ex_table,\"a\"\n"
52 
53 extern unsigned long __copy_tofrom_user(void __user *to,
54 		const void __user *from, unsigned long size);
55 
56 /* Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. */
__clear_user(void __user * to,unsigned long n)57 static inline unsigned long __must_check __clear_user(void __user *to,
58 							unsigned long n)
59 {
60 	/* normal memset with two words to __ex_table */
61 	__asm__ __volatile__ (				\
62 			"1:	sb	r0, %1, r0;"	\
63 			"	addik	%0, %0, -1;"	\
64 			"	bneid	%0, 1b;"	\
65 			"	addik	%1, %1, 1;"	\
66 			"2:			"	\
67 			__EX_TABLE_SECTION		\
68 			".word	1b,2b;"			\
69 			".previous;"			\
70 		: "=r"(n), "=r"(to)			\
71 		: "0"(n), "1"(to)
72 	);
73 	return n;
74 }
75 
clear_user(void __user * to,unsigned long n)76 static inline unsigned long __must_check clear_user(void __user *to,
77 							unsigned long n)
78 {
79 	might_fault();
80 	if (unlikely(!access_ok(to, n)))
81 		return n;
82 
83 	return __clear_user(to, n);
84 }
85 
86 /* put_user and get_user macros */
87 extern long __user_bad(void);
88 
89 #define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\
90 ({								\
91 	__asm__ __volatile__ (					\
92 			"1:"	insn	" %1, %2, r0;"		\
93 			"	addk	%0, r0, r0;"		\
94 			"2:			"		\
95 			__FIXUP_SECTION				\
96 			"3:	brid	2b;"			\
97 			"	addik	%0, r0, %3;"		\
98 			".previous;"				\
99 			__EX_TABLE_SECTION			\
100 			".word	1b,3b;"				\
101 			".previous;"				\
102 		: "=&r"(__gu_err), "=r"(__gu_val)		\
103 		: "r"(__gu_ptr), "i"(-EFAULT)			\
104 	);							\
105 })
106 
107 /**
108  * get_user: - Get a simple variable from user space.
109  * @x:   Variable to store result.
110  * @ptr: Source address, in user space.
111  *
112  * Context: User context only. This function may sleep if pagefaults are
113  *          enabled.
114  *
115  * This macro copies a single simple variable from user space to kernel
116  * space.  It supports simple types like char and int, but not larger
117  * data types like structures or arrays.
118  *
119  * @ptr must have pointer-to-simple-variable type, and the result of
120  * dereferencing @ptr must be assignable to @x without a cast.
121  *
122  * Returns zero on success, or -EFAULT on error.
123  * On error, the variable @x is set to zero.
124  */
125 #define get_user(x, ptr) ({				\
126 	const typeof(*(ptr)) __user *__gu_ptr = (ptr);	\
127 	access_ok(__gu_ptr, sizeof(*__gu_ptr)) ?	\
128 		__get_user(x, __gu_ptr) : -EFAULT;	\
129 })
130 
131 #define __get_user(x, ptr)						\
132 ({									\
133 	long __gu_err;							\
134 	switch (sizeof(*(ptr))) {					\
135 	case 1:								\
136 		__get_user_asm("lbu", (ptr), x, __gu_err);		\
137 		break;							\
138 	case 2:								\
139 		__get_user_asm("lhu", (ptr), x, __gu_err);		\
140 		break;							\
141 	case 4:								\
142 		__get_user_asm("lw", (ptr), x, __gu_err);		\
143 		break;							\
144 	case 8: {							\
145 		__u64 __x = 0;						\
146 		__gu_err = raw_copy_from_user(&__x, ptr, 8) ?		\
147 							-EFAULT : 0;	\
148 		(x) = (typeof(x))(typeof((x) - (x)))__x;		\
149 		break;							\
150 	}								\
151 	default:							\
152 		/* __gu_val = 0; __gu_err = -EINVAL;*/ __gu_err = __user_bad();\
153 	}								\
154 	__gu_err;							\
155 })
156 
157 
158 #define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\
159 ({								\
160 	__asm__ __volatile__ (					\
161 			"1:"	insn	" %1, %2, r0;"		\
162 			"	addk	%0, r0, r0;"		\
163 			"2:			"		\
164 			__FIXUP_SECTION				\
165 			"3:	brid	2b;"			\
166 			"	addik	%0, r0, %3;"		\
167 			".previous;"				\
168 			__EX_TABLE_SECTION			\
169 			".word	1b,3b;"				\
170 			".previous;"				\
171 		: "=&r"(__gu_err)				\
172 		: "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\
173 	);							\
174 })
175 
176 #define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err)		\
177 ({								\
178 	__asm__ __volatile__ ("	lwi	%0, %1, 0;"		\
179 			"1:	swi	%0, %2, 0;"		\
180 			"	lwi	%0, %1, 4;"		\
181 			"2:	swi	%0, %2, 4;"		\
182 			"	addk	%0, r0, r0;"		\
183 			"3:			"		\
184 			__FIXUP_SECTION				\
185 			"4:	brid	3b;"			\
186 			"	addik	%0, r0, %3;"		\
187 			".previous;"				\
188 			__EX_TABLE_SECTION			\
189 			".word	1b,4b,2b,4b;"			\
190 			".previous;"				\
191 		: "=&r"(__gu_err)				\
192 		: "r"(&__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\
193 		);						\
194 })
195 
196 /**
197  * put_user: - Write a simple value into user space.
198  * @x:   Value to copy to user space.
199  * @ptr: Destination address, in user space.
200  *
201  * Context: User context only. This function may sleep if pagefaults are
202  *          enabled.
203  *
204  * This macro copies a single simple value from kernel space to user
205  * space.  It supports simple types like char and int, but not larger
206  * data types like structures or arrays.
207  *
208  * @ptr must have pointer-to-simple-variable type, and @x must be assignable
209  * to the result of dereferencing @ptr.
210  *
211  * Returns zero on success, or -EFAULT on error.
212  */
213 #define put_user(x, ptr)						\
214 	__put_user_check((x), (ptr), sizeof(*(ptr)))
215 
216 #define __put_user_check(x, ptr, size)					\
217 ({									\
218 	typeof(*(ptr)) volatile __pu_val = x;				\
219 	typeof(*(ptr)) __user *__pu_addr = (ptr);			\
220 	int __pu_err = 0;						\
221 									\
222 	if (access_ok(__pu_addr, size)) {			\
223 		switch (size) {						\
224 		case 1:							\
225 			__put_user_asm("sb", __pu_addr, __pu_val,	\
226 				       __pu_err);			\
227 			break;						\
228 		case 2:							\
229 			__put_user_asm("sh", __pu_addr, __pu_val,	\
230 				       __pu_err);			\
231 			break;						\
232 		case 4:							\
233 			__put_user_asm("sw", __pu_addr, __pu_val,	\
234 				       __pu_err);			\
235 			break;						\
236 		case 8:							\
237 			__put_user_asm_8(__pu_addr, __pu_val, __pu_err);\
238 			break;						\
239 		default:						\
240 			__pu_err = __user_bad();			\
241 			break;						\
242 		}							\
243 	} else {							\
244 		__pu_err = -EFAULT;					\
245 	}								\
246 	__pu_err;							\
247 })
248 
249 #define __put_user(x, ptr)						\
250 ({									\
251 	__typeof__(*(ptr)) volatile __gu_val = (x);			\
252 	long __gu_err = 0;						\
253 	switch (sizeof(__gu_val)) {					\
254 	case 1:								\
255 		__put_user_asm("sb", (ptr), __gu_val, __gu_err);	\
256 		break;							\
257 	case 2:								\
258 		__put_user_asm("sh", (ptr), __gu_val, __gu_err);	\
259 		break;							\
260 	case 4:								\
261 		__put_user_asm("sw", (ptr), __gu_val, __gu_err);	\
262 		break;							\
263 	case 8:								\
264 		__put_user_asm_8((ptr), __gu_val, __gu_err);		\
265 		break;							\
266 	default:							\
267 		/*__gu_err = -EINVAL;*/	__gu_err = __user_bad();	\
268 	}								\
269 	__gu_err;							\
270 })
271 
272 static inline unsigned long
raw_copy_from_user(void * to,const void __user * from,unsigned long n)273 raw_copy_from_user(void *to, const void __user *from, unsigned long n)
274 {
275 	return __copy_tofrom_user((__force void __user *)to, from, n);
276 }
277 
278 static inline unsigned long
raw_copy_to_user(void __user * to,const void * from,unsigned long n)279 raw_copy_to_user(void __user *to, const void *from, unsigned long n)
280 {
281 	return __copy_tofrom_user(to, (__force const void __user *)from, n);
282 }
283 #define INLINE_COPY_FROM_USER
284 #define INLINE_COPY_TO_USER
285 
286 /*
287  * Copy a null terminated string from userspace.
288  */
289 __must_check long strncpy_from_user(char *dst, const char __user *src,
290 				    long count);
291 
292 /*
293  * Return the size of a string (including the ending 0)
294  *
295  * Return 0 on exception, a value greater than N if too long
296  */
297 __must_check long strnlen_user(const char __user *sstr, long len);
298 
299 #endif /* _ASM_MICROBLAZE_UACCESS_H */
300