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/extable.h>
13 #include <asm-generic/access_ok.h>
16 * Not all varients of the 68k family support the notion of address spaces.
17 * The traditional 680x0 parts do, and they use the sfc/dfc registers and
18 * the "moves" instruction to access user space from kernel space. Other
19 * family members like ColdFire don't support this, and only have a single
20 * address space, and use the usual "move" instruction for user space access.
22 * Outside of this difference the user space access functions are the same.
23 * So lets keep the code simple and just define in what we need to use.
25 #ifdef CONFIG_CPU_HAS_ADDRESS_SPACES
31 #define __put_user_asm(inst, res, x, ptr, bwl, reg, err) \
33 "1: "inst"."#bwl" %2,%1\n" \
35 " .section .fixup,\"ax\"\n" \
37 "10: moveq.l %3,%0\n" \
41 " .section __ex_table,\"a\"\n" \
46 : "+d" (res), "=m" (*(ptr)) \
47 : #reg (x), "i" (err))
49 #define __put_user_asm8(inst, res, x, ptr) \
51 const void *__pu_ptr = (const void __force *)(ptr); \
54 "1: "inst".l %2,(%1)+\n" \
55 "2: "inst".l %R2,(%1)\n" \
57 " .section .fixup,\"ax\"\n" \
63 " .section __ex_table,\"a\"\n" \
69 : "+d" (res), "+a" (__pu_ptr) \
70 : "r" (x), "i" (-EFAULT) \
75 * These are the main single-value transfer routines. They automatically
76 * use the right size if we just have the right pointer type.
79 #define __put_user(x, ptr) \
81 typeof(*(ptr)) __pu_val = (x); \
83 __chk_user_ptr(ptr); \
84 switch (sizeof (*(ptr))) { \
86 __put_user_asm(MOVES, __pu_err, __pu_val, ptr, b, d, -EFAULT); \
89 __put_user_asm(MOVES, __pu_err, __pu_val, ptr, w, r, -EFAULT); \
92 __put_user_asm(MOVES, __pu_err, __pu_val, ptr, l, r, -EFAULT); \
95 __put_user_asm8(MOVES, __pu_err, __pu_val, ptr); \
102 #define put_user(x, ptr) __put_user(x, ptr)
105 #define __get_user_asm(inst, res, x, ptr, type, bwl, reg, err) ({ \
108 "1: "inst"."#bwl" %2,%1\n" \
110 " .section .fixup,\"ax\"\n" \
112 "10: move.l %3,%0\n" \
117 " .section __ex_table,\"a\"\n" \
121 : "+d" (res), "=&" #reg (__gu_val) \
122 : "m" (*(ptr)), "i" (err)); \
123 (x) = (__force typeof(*(ptr)))(__force unsigned long)__gu_val; \
126 #define __get_user_asm8(inst, res, x, ptr) \
128 const void *__gu_ptr = (const void __force *)(ptr); \
131 __typeof__(*(ptr)) t; \
135 "1: "inst".l (%2)+,%1\n" \
136 "2: "inst".l (%2),%R1\n" \
138 " .section .fixup,\"ax\"\n" \
140 "10: move.l %3,%0\n" \
146 " .section __ex_table,\"a\"\n" \
151 : "+d" (res), "=&r" (__gu_val.l), \
158 #define __get_user(x, ptr) \
161 __chk_user_ptr(ptr); \
162 switch (sizeof(*(ptr))) { \
164 __get_user_asm(MOVES, __gu_err, x, ptr, u8, b, d, -EFAULT); \
167 __get_user_asm(MOVES, __gu_err, x, ptr, u16, w, r, -EFAULT); \
170 __get_user_asm(MOVES, __gu_err, x, ptr, u32, l, r, -EFAULT); \
173 __get_user_asm8(MOVES, __gu_err, x, ptr); \
180 #define get_user(x, ptr) __get_user(x, ptr)
182 unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long n);
183 unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long n);
190 #define ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
192 "1: "MOVES"."#s1" (%2)+,%3\n" \
193 " move."#s1" %3,(%1)+\n" \
194 " .ifnc \""#s2"\",\"\"\n" \
195 "2: "MOVES"."#s2" (%2)+,%3\n" \
196 " move."#s2" %3,(%1)+\n" \
197 " .ifnc \""#s3"\",\"\"\n" \
198 "3: "MOVES"."#s3" (%2)+,%3\n" \
199 " move."#s3" %3,(%1)+\n" \
203 " .section __ex_table,\"a\"\n" \
206 " .ifnc \""#s2"\",\"\"\n" \
208 " .ifnc \""#s3"\",\"\"\n" \
214 " .section .fixup,\"ax\"\n" \
216 "10: addq.l #"#n1",%0\n" \
217 " .ifnc \""#s2"\",\"\"\n" \
218 "20: addq.l #"#n2",%0\n" \
219 " .ifnc \""#s3"\",\"\"\n" \
220 "30: addq.l #"#n3",%0\n" \
225 : "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp) \
228 #define ___constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
229 ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)
230 #define __constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3) \
231 ___constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, \
232 __suffix##n1, __suffix##n2, __suffix##n3)
234 static __always_inline unsigned long
235 __constant_copy_from_user(void *to, const void __user *from, unsigned long n)
237 unsigned long res = 0, tmp;
241 __constant_copy_from_user_asm(res, to, from, tmp, 1, 0, 0);
244 __constant_copy_from_user_asm(res, to, from, tmp, 2, 0, 0);
247 __constant_copy_from_user_asm(res, to, from, tmp, 2, 1, 0);
250 __constant_copy_from_user_asm(res, to, from, tmp, 4, 0, 0);
253 __constant_copy_from_user_asm(res, to, from, tmp, 4, 1, 0);
256 __constant_copy_from_user_asm(res, to, from, tmp, 4, 2, 0);
259 __constant_copy_from_user_asm(res, to, from, tmp, 4, 2, 1);
262 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 0);
265 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 1);
268 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 2);
271 __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 4);
274 /* we limit the inlined version to 3 moves */
275 return __generic_copy_from_user(to, from, n);
281 #define __constant_copy_to_user_asm(res, to, from, tmp, n, s1, s2, s3) \
283 " move."#s1" (%2)+,%3\n" \
284 "11: "MOVES"."#s1" %3,(%1)+\n" \
285 "12: move."#s2" (%2)+,%3\n" \
286 "21: "MOVES"."#s2" %3,(%1)+\n" \
288 " .ifnc \""#s3"\",\"\"\n" \
289 " move."#s3" (%2)+,%3\n" \
290 "31: "MOVES"."#s3" %3,(%1)+\n" \
295 " .section __ex_table,\"a\"\n" \
301 " .ifnc \""#s3"\",\"\"\n" \
307 " .section .fixup,\"ax\"\n" \
309 "5: moveq.l #"#n",%0\n" \
312 : "+d" (res), "+a" (to), "+a" (from), "=&d" (tmp) \
315 static __always_inline unsigned long
316 __constant_copy_to_user(void __user *to, const void *from, unsigned long n)
318 unsigned long res = 0, tmp;
322 __put_user_asm(MOVES, res, *(u8 *)from, (u8 __user *)to,
326 __put_user_asm(MOVES, res, *(u16 *)from, (u16 __user *)to,
330 __constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,);
333 __put_user_asm(MOVES, res, *(u32 *)from, (u32 __user *)to,
337 __constant_copy_to_user_asm(res, to, from, tmp, 5, l, b,);
340 __constant_copy_to_user_asm(res, to, from, tmp, 6, l, w,);
343 __constant_copy_to_user_asm(res, to, from, tmp, 7, l, w, b);
346 __constant_copy_to_user_asm(res, to, from, tmp, 8, l, l,);
349 __constant_copy_to_user_asm(res, to, from, tmp, 9, l, l, b);
352 __constant_copy_to_user_asm(res, to, from, tmp, 10, l, l, w);
355 __constant_copy_to_user_asm(res, to, from, tmp, 12, l, l, l);
358 /* limit the inlined version to 3 moves */
359 return __generic_copy_to_user(to, from, n);
365 static inline unsigned long
366 raw_copy_from_user(void *to, const void __user *from, unsigned long n)
368 if (__builtin_constant_p(n))
369 return __constant_copy_from_user(to, from, n);
370 return __generic_copy_from_user(to, from, n);
373 static inline unsigned long
374 raw_copy_to_user(void __user *to, const void *from, unsigned long n)
376 if (__builtin_constant_p(n))
377 return __constant_copy_to_user(to, from, n);
378 return __generic_copy_to_user(to, from, n);
380 #define INLINE_COPY_FROM_USER
381 #define INLINE_COPY_TO_USER
383 #define __get_kernel_nofault(dst, src, type, err_label) \
385 type *__gk_dst = (type *)(dst); \
386 type *__gk_src = (type *)(src); \
389 switch (sizeof(type)) { \
391 __get_user_asm("move", __gk_err, *__gk_dst, __gk_src, \
392 u8, b, d, -EFAULT); \
395 __get_user_asm("move", __gk_err, *__gk_dst, __gk_src, \
396 u16, w, r, -EFAULT); \
399 __get_user_asm("move", __gk_err, *__gk_dst, __gk_src, \
400 u32, l, r, -EFAULT); \
403 __get_user_asm8("move", __gk_err, *__gk_dst, __gk_src); \
408 if (unlikely(__gk_err)) \
412 #define __put_kernel_nofault(dst, src, type, err_label) \
414 type __pk_src = *(type *)(src); \
415 type *__pk_dst = (type *)(dst); \
418 switch (sizeof(type)) { \
420 __put_user_asm("move", __pk_err, __pk_src, __pk_dst, \
424 __put_user_asm("move", __pk_err, __pk_src, __pk_dst, \
428 __put_user_asm("move", __pk_err, __pk_src, __pk_dst, \
432 __put_user_asm8("move", __pk_err, __pk_src, __pk_dst); \
437 if (unlikely(__pk_err)) \
441 extern long strncpy_from_user(char *dst, const char __user *src, long count);
442 extern __must_check long strnlen_user(const char __user *str, long n);
444 unsigned long __clear_user(void __user *to, unsigned long n);
446 #define clear_user __clear_user
448 #else /* !CONFIG_MMU */
449 #include <asm-generic/uaccess.h>
452 #endif /* _M68K_UACCESS_H */