1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef __M68K_UACCESS_H
3 #define __M68K_UACCESS_H
8 * User space memory access functions
10 #include <linux/compiler.h>
11 #include <linux/types.h>
12 #include <asm/segment.h>
13 #include <asm/extable.h>
15 /* We let the MMU do all checking */
16 static inline int access_ok(const void __user *addr,
20 * XXX: for !CONFIG_CPU_HAS_ADDRESS_SPACES this really needs to check
27 * Not all varients of the 68k family support the notion of address spaces.
28 * The traditional 680x0 parts do, and they use the sfc/dfc registers and
29 * the "moves" instruction to access user space from kernel space. Other
30 * family members like ColdFire don't support this, and only have a single
31 * address space, and use the usual "move" instruction for user space access.
33 * Outside of this difference the user space access functions are the same.
34 * So lets keep the code simple and just define in what we need to use.
36 #ifdef CONFIG_CPU_HAS_ADDRESS_SPACES
42 #define __put_user_asm(res, x, ptr, bwl, reg, err) \
44 "1: "MOVES"."#bwl" %2,%1\n" \
46 " .section .fixup,\"ax\"\n" \
48 "10: moveq.l %3,%0\n" \
52 " .section __ex_table,\"a\"\n" \
57 : "+d" (res), "=m" (*(ptr)) \
58 : #reg (x), "i" (err))
60 #define __put_user_asm8(res, x, ptr) \
62 const void *__pu_ptr = (const void __force *)(ptr); \
65 "1: "MOVES".l %2,(%1)+\n" \
66 "2: "MOVES".l %R2,(%1)\n" \
68 " .section .fixup,\"ax\"\n" \
74 " .section __ex_table,\"a\"\n" \
80 : "+d" (res), "+a" (__pu_ptr) \
81 : "r" (x), "i" (-EFAULT) \
86 * These are the main single-value transfer routines. They automatically
87 * use the right size if we just have the right pointer type.
90 #define __put_user(x, ptr) \
92 typeof(*(ptr)) __pu_val = (x); \
94 __chk_user_ptr(ptr); \
95 switch (sizeof (*(ptr))) { \
97 __put_user_asm(__pu_err, __pu_val, ptr, b, d, -EFAULT); \
100 __put_user_asm(__pu_err, __pu_val, ptr, w, r, -EFAULT); \
103 __put_user_asm(__pu_err, __pu_val, ptr, l, r, -EFAULT); \
106 __put_user_asm8(__pu_err, __pu_val, ptr); \
113 #define put_user(x, ptr) __put_user(x, ptr)
116 #define __get_user_asm(res, x, ptr, type, bwl, reg, err) ({ \
119 "1: "MOVES"."#bwl" %2,%1\n" \
121 " .section .fixup,\"ax\"\n" \
123 "10: move.l %3,%0\n" \
128 " .section __ex_table,\"a\"\n" \
132 : "+d" (res), "=&" #reg (__gu_val) \
133 : "m" (*(ptr)), "i" (err)); \
134 (x) = (__force typeof(*(ptr)))(__force unsigned long)__gu_val; \
137 #define __get_user_asm8(res, x, ptr) \
139 const void *__gu_ptr = (const void __force *)(ptr); \
142 __typeof__(*(ptr)) t; \
146 "1: "MOVES".l (%2)+,%1\n" \
147 "2: "MOVES".l (%2),%R1\n" \
149 " .section .fixup,\"ax\"\n" \
151 "10: move.l %3,%0\n" \
157 " .section __ex_table,\"a\"\n" \
162 : "+d" (res), "=&r" (__gu_val.l), \
169 #define __get_user(x, ptr) \
172 __chk_user_ptr(ptr); \
173 switch (sizeof(*(ptr))) { \
175 __get_user_asm(__gu_err, x, ptr, u8, b, d, -EFAULT); \
178 __get_user_asm(__gu_err, x, ptr, u16, w, r, -EFAULT); \
181 __get_user_asm(__gu_err, x, ptr, u32, l, r, -EFAULT); \
184 __get_user_asm8(__gu_err, x, ptr); \
191 #define get_user(x, ptr) __get_user(x, ptr)
193 unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long n);
194 unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long n);
201 #define ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
203 "1: "MOVES"."#s1" (%2)+,%3\n" \
204 " move."#s1" %3,(%1)+\n" \
205 " .ifnc \""#s2"\",\"\"\n" \
206 "2: "MOVES"."#s2" (%2)+,%3\n" \
207 " move."#s2" %3,(%1)+\n" \
208 " .ifnc \""#s3"\",\"\"\n" \
209 "3: "MOVES"."#s3" (%2)+,%3\n" \
210 " move."#s3" %3,(%1)+\n" \
214 " .section __ex_table,\"a\"\n" \
217 " .ifnc \""#s2"\",\"\"\n" \
219 " .ifnc \""#s3"\",\"\"\n" \
225 " .section .fixup,\"ax\"\n" \
227 "10: addq.l #"#n1",%0\n" \
228 " .ifnc \""#s2"\",\"\"\n" \
229 "20: addq.l #"#n2",%0\n" \
230 " .ifnc \""#s3"\",\"\"\n" \
231 "30: addq.l #"#n3",%0\n" \
236 : "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp) \
239 #define ___constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
240 ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)
241 #define __constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3) \
242 ___constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, \
243 __suffix##n1, __suffix##n2, __suffix##n3)
245 static __always_inline unsigned long
246 __constant_copy_from_user(void *to, const void __user *from, unsigned long n)
248 unsigned long res = 0, tmp;
252 __constant_copy_from_user_asm(res, to, from, tmp, 1, 0, 0);
255 __constant_copy_from_user_asm(res, to, from, tmp, 2, 0, 0);
258 __constant_copy_from_user_asm(res, to, from, tmp, 2, 1, 0);
261 __constant_copy_from_user_asm(res, to, from, tmp, 4, 0, 0);
264 __constant_copy_from_user_asm(res, to, from, tmp, 4, 1, 0);
267 __constant_copy_from_user_asm(res, to, from, tmp, 4, 2, 0);
270 __constant_copy_from_user_asm(res, to, from, tmp, 4, 2, 1);
273 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 0);
276 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 1);
279 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 2);
282 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 4);
285 /* we limit the inlined version to 3 moves */
286 return __generic_copy_from_user(to, from, n);
292 #define __constant_copy_to_user_asm(res, to, from, tmp, n, s1, s2, s3) \
294 " move."#s1" (%2)+,%3\n" \
295 "11: "MOVES"."#s1" %3,(%1)+\n" \
296 "12: move."#s2" (%2)+,%3\n" \
297 "21: "MOVES"."#s2" %3,(%1)+\n" \
299 " .ifnc \""#s3"\",\"\"\n" \
300 " move."#s3" (%2)+,%3\n" \
301 "31: "MOVES"."#s3" %3,(%1)+\n" \
306 " .section __ex_table,\"a\"\n" \
312 " .ifnc \""#s3"\",\"\"\n" \
318 " .section .fixup,\"ax\"\n" \
320 "5: moveq.l #"#n",%0\n" \
323 : "+d" (res), "+a" (to), "+a" (from), "=&d" (tmp) \
326 static __always_inline unsigned long
327 __constant_copy_to_user(void __user *to, const void *from, unsigned long n)
329 unsigned long res = 0, tmp;
333 __put_user_asm(res, *(u8 *)from, (u8 __user *)to, b, d, 1);
336 __put_user_asm(res, *(u16 *)from, (u16 __user *)to, w, r, 2);
339 __constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,);
342 __put_user_asm(res, *(u32 *)from, (u32 __user *)to, l, r, 4);
345 __constant_copy_to_user_asm(res, to, from, tmp, 5, l, b,);
348 __constant_copy_to_user_asm(res, to, from, tmp, 6, l, w,);
351 __constant_copy_to_user_asm(res, to, from, tmp, 7, l, w, b);
354 __constant_copy_to_user_asm(res, to, from, tmp, 8, l, l,);
357 __constant_copy_to_user_asm(res, to, from, tmp, 9, l, l, b);
360 __constant_copy_to_user_asm(res, to, from, tmp, 10, l, l, w);
363 __constant_copy_to_user_asm(res, to, from, tmp, 12, l, l, l);
366 /* limit the inlined version to 3 moves */
367 return __generic_copy_to_user(to, from, n);
373 static inline unsigned long
374 raw_copy_from_user(void *to, const void __user *from, unsigned long n)
376 if (__builtin_constant_p(n))
377 return __constant_copy_from_user(to, from, n);
378 return __generic_copy_from_user(to, from, n);
381 static inline unsigned long
382 raw_copy_to_user(void __user *to, const void *from, unsigned long n)
384 if (__builtin_constant_p(n))
385 return __constant_copy_to_user(to, from, n);
386 return __generic_copy_to_user(to, from, n);
388 #define INLINE_COPY_FROM_USER
389 #define INLINE_COPY_TO_USER
391 #define user_addr_max() \
392 (uaccess_kernel() ? ~0UL : TASK_SIZE)
394 extern long strncpy_from_user(char *dst, const char __user *src, long count);
395 extern __must_check long strnlen_user(const char __user *str, long n);
397 unsigned long __clear_user(void __user *to, unsigned long n);
399 #define clear_user __clear_user
401 #else /* !CONFIG_MMU */
402 #include <asm-generic/uaccess.h>
405 #endif /* _M68K_UACCESS_H */