| #ifndef __M68K_UACCESS_H | |
| #define __M68K_UACCESS_H | |
| /* | |
| * User space memory access functions | |
| */ | |
| #include <linux/compiler.h> | |
| #include <linux/errno.h> | |
| #include <linux/types.h> | |
| #include <linux/sched.h> | |
| #include <asm/segment.h> | |
| #define VERIFY_READ 0 | |
| #define VERIFY_WRITE 1 | |
| /* We let the MMU do all checking */ | |
| static inline int access_ok(int type, const void __user *addr, | |
| unsigned long size) | |
| { | |
| return 1; | |
| } | |
| /* | |
| * The exception table consists of pairs of addresses: the first is the | |
| * address of an instruction that is allowed to fault, and the second is | |
| * the address at which the program should continue. No registers are | |
| * modified, so it is entirely up to the continuation code to figure out | |
| * what to do. | |
| * | |
| * All the routines below use bits of fixup code that are out of line | |
| * with the main instruction path. This means when everything is well, | |
| * we don't even have to jump over them. Further, they do not intrude | |
| * on our cache or tlb entries. | |
| */ | |
| struct exception_table_entry | |
| { | |
| unsigned long insn, fixup; | |
| }; | |
| extern int __put_user_bad(void); | |
| extern int __get_user_bad(void); | |
| #define __put_user_asm(res, x, ptr, bwl, reg, err) \ | |
| asm volatile ("\n" \ | |
| "1: moves."#bwl" %2,%1\n" \ | |
| "2:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " .even\n" \ | |
| "10: moveq.l %3,%0\n" \ | |
| " jra 2b\n" \ | |
| " .previous\n" \ | |
| "\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .align 4\n" \ | |
| " .long 1b,10b\n" \ | |
| " .long 2b,10b\n" \ | |
| " .previous" \ | |
| : "+d" (res), "=m" (*(ptr)) \ | |
| : #reg (x), "i" (err)) | |
| /* | |
| * These are the main single-value transfer routines. They automatically | |
| * use the right size if we just have the right pointer type. | |
| */ | |
| #define __put_user(x, ptr) \ | |
| ({ \ | |
| typeof(*(ptr)) __pu_val = (x); \ | |
| int __pu_err = 0; \ | |
| __chk_user_ptr(ptr); \ | |
| switch (sizeof (*(ptr))) { \ | |
| case 1: \ | |
| __put_user_asm(__pu_err, __pu_val, ptr, b, d, -EFAULT); \ | |
| break; \ | |
| case 2: \ | |
| __put_user_asm(__pu_err, __pu_val, ptr, w, d, -EFAULT); \ | |
| break; \ | |
| case 4: \ | |
| __put_user_asm(__pu_err, __pu_val, ptr, l, r, -EFAULT); \ | |
| break; \ | |
| case 8: \ | |
| { \ | |
| const void __user *__pu_ptr = (ptr); \ | |
| asm volatile ("\n" \ | |
| "1: moves.l %2,(%1)+\n" \ | |
| "2: moves.l %R2,(%1)\n" \ | |
| "3:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " .even\n" \ | |
| "10: movel %3,%0\n" \ | |
| " jra 3b\n" \ | |
| " .previous\n" \ | |
| "\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .align 4\n" \ | |
| " .long 1b,10b\n" \ | |
| " .long 2b,10b\n" \ | |
| " .long 3b,10b\n" \ | |
| " .previous" \ | |
| : "+d" (__pu_err), "+a" (__pu_ptr) \ | |
| : "r" (__pu_val), "i" (-EFAULT) \ | |
| : "memory"); \ | |
| break; \ | |
| } \ | |
| default: \ | |
| __pu_err = __put_user_bad(); \ | |
| break; \ | |
| } \ | |
| __pu_err; \ | |
| }) | |
| #define put_user(x, ptr) __put_user(x, ptr) | |
| #define __get_user_asm(res, x, ptr, type, bwl, reg, err) ({ \ | |
| type __gu_val; \ | |
| asm volatile ("\n" \ | |
| "1: moves."#bwl" %2,%1\n" \ | |
| "2:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " .even\n" \ | |
| "10: move.l %3,%0\n" \ | |
| " sub."#bwl" %1,%1\n" \ | |
| " jra 2b\n" \ | |
| " .previous\n" \ | |
| "\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .align 4\n" \ | |
| " .long 1b,10b\n" \ | |
| " .previous" \ | |
| : "+d" (res), "=&" #reg (__gu_val) \ | |
| : "m" (*(ptr)), "i" (err)); \ | |
| (x) = (typeof(*(ptr)))(unsigned long)__gu_val; \ | |
| }) | |
| #define __get_user(x, ptr) \ | |
| ({ \ | |
| int __gu_err = 0; \ | |
| __chk_user_ptr(ptr); \ | |
| switch (sizeof(*(ptr))) { \ | |
| case 1: \ | |
| __get_user_asm(__gu_err, x, ptr, u8, b, d, -EFAULT); \ | |
| break; \ | |
| case 2: \ | |
| __get_user_asm(__gu_err, x, ptr, u16, w, d, -EFAULT); \ | |
| break; \ | |
| case 4: \ | |
| __get_user_asm(__gu_err, x, ptr, u32, l, r, -EFAULT); \ | |
| break; \ | |
| /* case 8: disabled because gcc-4.1 has a broken typeof \ | |
| { \ | |
| const void *__gu_ptr = (ptr); \ | |
| u64 __gu_val; \ | |
| asm volatile ("\n" \ | |
| "1: moves.l (%2)+,%1\n" \ | |
| "2: moves.l (%2),%R1\n" \ | |
| "3:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " .even\n" \ | |
| "10: move.l %3,%0\n" \ | |
| " sub.l %1,%1\n" \ | |
| " sub.l %R1,%R1\n" \ | |
| " jra 3b\n" \ | |
| " .previous\n" \ | |
| "\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .align 4\n" \ | |
| " .long 1b,10b\n" \ | |
| " .long 2b,10b\n" \ | |
| " .previous" \ | |
| : "+d" (__gu_err), "=&r" (__gu_val), \ | |
| "+a" (__gu_ptr) \ | |
| : "i" (-EFAULT) \ | |
| : "memory"); \ | |
| (x) = (typeof(*(ptr)))__gu_val; \ | |
| break; \ | |
| } */ \ | |
| default: \ | |
| __gu_err = __get_user_bad(); \ | |
| break; \ | |
| } \ | |
| __gu_err; \ | |
| }) | |
| #define get_user(x, ptr) __get_user(x, ptr) | |
| unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long n); | |
| unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long n); | |
| #define __constant_copy_from_user_asm(res, to, from, tmp, n, s1, s2, s3)\ | |
| asm volatile ("\n" \ | |
| "1: moves."#s1" (%2)+,%3\n" \ | |
| " move."#s1" %3,(%1)+\n" \ | |
| "2: moves."#s2" (%2)+,%3\n" \ | |
| " move."#s2" %3,(%1)+\n" \ | |
| " .ifnc \""#s3"\",\"\"\n" \ | |
| "3: moves."#s3" (%2)+,%3\n" \ | |
| " move."#s3" %3,(%1)+\n" \ | |
| " .endif\n" \ | |
| "4:\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .align 4\n" \ | |
| " .long 1b,10f\n" \ | |
| " .long 2b,20f\n" \ | |
| " .ifnc \""#s3"\",\"\"\n" \ | |
| " .long 3b,30f\n" \ | |
| " .endif\n" \ | |
| " .previous\n" \ | |
| "\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " .even\n" \ | |
| "10: clr."#s1" (%1)+\n" \ | |
| "20: clr."#s2" (%1)+\n" \ | |
| " .ifnc \""#s3"\",\"\"\n" \ | |
| "30: clr."#s3" (%1)+\n" \ | |
| " .endif\n" \ | |
| " moveq.l #"#n",%0\n" \ | |
| " jra 4b\n" \ | |
| " .previous\n" \ | |
| : "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp) \ | |
| : : "memory") | |
| static __always_inline unsigned long | |
| __constant_copy_from_user(void *to, const void __user *from, unsigned long n) | |
| { | |
| unsigned long res = 0, tmp; | |
| switch (n) { | |
| case 1: | |
| __get_user_asm(res, *(u8 *)to, (u8 __user *)from, u8, b, d, 1); | |
| break; | |
| case 2: | |
| __get_user_asm(res, *(u16 *)to, (u16 __user *)from, u16, w, d, 2); | |
| break; | |
| case 3: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 3, w, b,); | |
| break; | |
| case 4: | |
| __get_user_asm(res, *(u32 *)to, (u32 __user *)from, u32, l, r, 4); | |
| break; | |
| case 5: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 5, l, b,); | |
| break; | |
| case 6: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 6, l, w,); | |
| break; | |
| case 7: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 7, l, w, b); | |
| break; | |
| case 8: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 8, l, l,); | |
| break; | |
| case 9: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 9, l, l, b); | |
| break; | |
| case 10: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 10, l, l, w); | |
| break; | |
| case 12: | |
| __constant_copy_from_user_asm(res, to, from, tmp, 12, l, l, l); | |
| break; | |
| default: | |
| /* we limit the inlined version to 3 moves */ | |
| return __generic_copy_from_user(to, from, n); | |
| } | |
| return res; | |
| } | |
| #define __constant_copy_to_user_asm(res, to, from, tmp, n, s1, s2, s3) \ | |
| asm volatile ("\n" \ | |
| " move."#s1" (%2)+,%3\n" \ | |
| "11: moves."#s1" %3,(%1)+\n" \ | |
| "12: move."#s2" (%2)+,%3\n" \ | |
| "21: moves."#s2" %3,(%1)+\n" \ | |
| "22:\n" \ | |
| " .ifnc \""#s3"\",\"\"\n" \ | |
| " move."#s3" (%2)+,%3\n" \ | |
| "31: moves."#s3" %3,(%1)+\n" \ | |
| "32:\n" \ | |
| " .endif\n" \ | |
| "4:\n" \ | |
| "\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .align 4\n" \ | |
| " .long 11b,5f\n" \ | |
| " .long 12b,5f\n" \ | |
| " .long 21b,5f\n" \ | |
| " .long 22b,5f\n" \ | |
| " .ifnc \""#s3"\",\"\"\n" \ | |
| " .long 31b,5f\n" \ | |
| " .long 32b,5f\n" \ | |
| " .endif\n" \ | |
| " .previous\n" \ | |
| "\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " .even\n" \ | |
| "5: moveq.l #"#n",%0\n" \ | |
| " jra 4b\n" \ | |
| " .previous\n" \ | |
| : "+d" (res), "+a" (to), "+a" (from), "=&d" (tmp) \ | |
| : : "memory") | |
| static __always_inline unsigned long | |
| __constant_copy_to_user(void __user *to, const void *from, unsigned long n) | |
| { | |
| unsigned long res = 0, tmp; | |
| switch (n) { | |
| case 1: | |
| __put_user_asm(res, *(u8 *)from, (u8 __user *)to, b, d, 1); | |
| break; | |
| case 2: | |
| __put_user_asm(res, *(u16 *)from, (u16 __user *)to, w, d, 2); | |
| break; | |
| case 3: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,); | |
| break; | |
| case 4: | |
| __put_user_asm(res, *(u32 *)from, (u32 __user *)to, l, r, 4); | |
| break; | |
| case 5: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 5, l, b,); | |
| break; | |
| case 6: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 6, l, w,); | |
| break; | |
| case 7: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 7, l, w, b); | |
| break; | |
| case 8: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 8, l, l,); | |
| break; | |
| case 9: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 9, l, l, b); | |
| break; | |
| case 10: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 10, l, l, w); | |
| break; | |
| case 12: | |
| __constant_copy_to_user_asm(res, to, from, tmp, 12, l, l, l); | |
| break; | |
| default: | |
| /* limit the inlined version to 3 moves */ | |
| return __generic_copy_to_user(to, from, n); | |
| } | |
| return res; | |
| } | |
| #define __copy_from_user(to, from, n) \ | |
| (__builtin_constant_p(n) ? \ | |
| __constant_copy_from_user(to, from, n) : \ | |
| __generic_copy_from_user(to, from, n)) | |
| #define __copy_to_user(to, from, n) \ | |
| (__builtin_constant_p(n) ? \ | |
| __constant_copy_to_user(to, from, n) : \ | |
| __generic_copy_to_user(to, from, n)) | |
| #define __copy_to_user_inatomic __copy_to_user | |
| #define __copy_from_user_inatomic __copy_from_user | |
| #define copy_from_user(to, from, n) __copy_from_user(to, from, n) | |
| #define copy_to_user(to, from, n) __copy_to_user(to, from, n) | |
| long strncpy_from_user(char *dst, const char __user *src, long count); | |
| long strnlen_user(const char __user *src, long n); | |
| unsigned long __clear_user(void __user *to, unsigned long n); | |
| #define clear_user __clear_user | |
| #define strlen_user(str) strnlen_user(str, 32767) | |
| #endif /* _M68K_UACCESS_H */ |