root/arch/m68k/include/asm/uaccess.h
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __M68K_UACCESS_H
#define __M68K_UACCESS_H

#ifdef CONFIG_MMU

/*
 * User space memory access functions
 */
#include <linux/compiler.h>
#include <linux/types.h>
#include <asm/extable.h>
#include <asm-generic/access_ok.h>

/*
 * Not all varients of the 68k family support the notion of address spaces.
 * The traditional 680x0 parts do, and they use the sfc/dfc registers and
 * the "moves" instruction to access user space from kernel space. Other
 * family members like ColdFire don't support this, and only have a single
 * address space, and use the usual "move" instruction for user space access.
 *
 * Outside of this difference the user space access functions are the same.
 * So lets keep the code simple and just define in what we need to use.
 */
#ifdef CONFIG_CPU_HAS_ADDRESS_SPACES
#define MOVES   "moves"
#else
#define MOVES   "move"
#endif

#define __put_user_asm(inst, res, x, ptr, bwl, reg, err) \
asm volatile ("\n"                                      \
        "1:     "inst"."#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))

#define __put_user_asm8(inst, res, x, ptr)                      \
do {                                                            \
        const void *__pu_ptr = (const void __force *)(ptr);     \
                                                                \
        asm volatile ("\n"                                      \
                "1:     "inst".l %2,(%1)+\n"                    \
                "2:     "inst".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" (res), "+a" (__pu_ptr)                   \
                : "r" (x), "i" (-EFAULT)                        \
                : "memory");                                    \
} while (0)

/*
 * 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(MOVES, __pu_err, __pu_val, ptr, b, d, -EFAULT); \
                break;                                                  \
        case 2:                                                         \
                __put_user_asm(MOVES, __pu_err, __pu_val, ptr, w, r, -EFAULT); \
                break;                                                  \
        case 4:                                                         \
                __put_user_asm(MOVES, __pu_err, __pu_val, ptr, l, r, -EFAULT); \
                break;                                                  \
        case 8:                                                         \
                __put_user_asm8(MOVES, __pu_err, __pu_val, ptr);        \
                break;                                                  \
        default:                                                        \
                BUILD_BUG();                                            \
        }                                                               \
        __pu_err;                                                       \
})
#define put_user(x, ptr)        __put_user(x, ptr)


#define __get_user_asm(inst, res, x, ptr, type, bwl, reg, err) ({       \
        type __gu_val;                                                  \
        asm volatile ("\n"                                              \
                "1:     "inst"."#bwl"   %2,%1\n"                        \
                "2:\n"                                                  \
                "       .section .fixup,\"ax\"\n"                       \
                "       .even\n"                                        \
                "10:    move.l  %3,%0\n"                                \
                "       sub.l   %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) = (__force typeof(*(ptr)))(__force unsigned long)__gu_val;  \
})

#define __get_user_asm8(inst, res, x, ptr)                              \
do {                                                                    \
        const void *__gu_ptr = (const void __force *)(ptr);             \
        union {                                                         \
                u64 l;                                                  \
                __typeof__(*(ptr)) t;                                   \
        } __gu_val;                                                     \
                                                                        \
        asm volatile ("\n"                                              \
                "1:     "inst".l (%2)+,%1\n"                            \
                "2:     "inst".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" (res), "=&r" (__gu_val.l),                       \
                  "+a" (__gu_ptr)                                       \
                : "i" (-EFAULT)                                         \
                : "memory");                                            \
        (x) = __gu_val.t;                                               \
} while (0)

#define __get_user(x, ptr)                                              \
({                                                                      \
        int __gu_err = 0;                                               \
        __chk_user_ptr(ptr);                                            \
        switch (sizeof(*(ptr))) {                                       \
        case 1:                                                         \
                __get_user_asm(MOVES, __gu_err, x, ptr, u8, b, d, -EFAULT); \
                break;                                                  \
        case 2:                                                         \
                __get_user_asm(MOVES, __gu_err, x, ptr, u16, w, r, -EFAULT); \
                break;                                                  \
        case 4:                                                         \
                __get_user_asm(MOVES, __gu_err, x, ptr, u32, l, r, -EFAULT); \
                break;                                                  \
        case 8:                                                         \
                __get_user_asm8(MOVES, __gu_err, x, ptr);               \
                break;                                                  \
        default:                                                        \
                BUILD_BUG();                                            \
        }                                                               \
        __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 __suffix0
#define __suffix1 b
#define __suffix2 w
#define __suffix4 l

#define ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
        asm volatile ("\n"                                              \
                "1:     "MOVES"."#s1"   (%2)+,%3\n"                     \
                "       move."#s1"      %3,(%1)+\n"                     \
                "       .ifnc   \""#s2"\",\"\"\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"                                       \
                "       .endif\n"                                       \
                "4:\n"                                                  \
                "       .section __ex_table,\"a\"\n"                    \
                "       .align  4\n"                                    \
                "       .long   1b,10f\n"                               \
                "       .ifnc   \""#s2"\",\"\"\n"                       \
                "       .long   2b,20f\n"                               \
                "       .ifnc   \""#s3"\",\"\"\n"                       \
                "       .long   3b,30f\n"                               \
                "       .endif\n"                                       \
                "       .endif\n"                                       \
                "       .previous\n"                                    \
                "\n"                                                    \
                "       .section .fixup,\"ax\"\n"                       \
                "       .even\n"                                        \
                "10:    addq.l #"#n1",%0\n"                             \
                "       .ifnc   \""#s2"\",\"\"\n"                       \
                "20:    addq.l #"#n2",%0\n"                             \
                "       .ifnc   \""#s3"\",\"\"\n"                       \
                "30:    addq.l #"#n3",%0\n"                             \
                "       .endif\n"                                       \
                "       .endif\n"                                       \
                "       jra     4b\n"                                   \
                "       .previous\n"                                    \
                : "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp)      \
                : : "memory")

#define ___constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
        ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)
#define __constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3)   \
        ___constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3,  \
                                        __suffix##n1, __suffix##n2, __suffix##n3)

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:
                __constant_copy_from_user_asm(res, to, from, tmp, 1, 0, 0);
                break;
        case 2:
                __constant_copy_from_user_asm(res, to, from, tmp, 2, 0, 0);
                break;
        case 3:
                __constant_copy_from_user_asm(res, to, from, tmp, 2, 1, 0);
                break;
        case 4:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 0, 0);
                break;
        case 5:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 1, 0);
                break;
        case 6:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 2, 0);
                break;
        case 7:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 2, 1);
                break;
        case 8:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 0);
                break;
        case 9:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 1);
                break;
        case 10:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 2);
                break;
        case 12:
                __constant_copy_from_user_asm(res, to, from, tmp, 4, 4, 4);
                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(MOVES, res, *(u8 *)from, (u8 __user *)to,
                                b, d, 1);
                break;
        case 2:
                __put_user_asm(MOVES, res, *(u16 *)from, (u16 __user *)to,
                                w, r, 2);
                break;
        case 3:
                __constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,);
                break;
        case 4:
                __put_user_asm(MOVES, 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;
}

static inline unsigned long
raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
        if (__builtin_constant_p(n))
                return __constant_copy_from_user(to, from, n);
        return __generic_copy_from_user(to, from, n);
}

static inline unsigned long
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
        if (__builtin_constant_p(n))
                return __constant_copy_to_user(to, from, n);
        return __generic_copy_to_user(to, from, n);
}
#define INLINE_COPY_FROM_USER
#define INLINE_COPY_TO_USER

#define __get_kernel_nofault(dst, src, type, err_label)                 \
do {                                                                    \
        type *__gk_dst = (type *)(dst);                                 \
        type *__gk_src = (type *)(src);                                 \
        int __gk_err = 0;                                               \
                                                                        \
        switch (sizeof(type)) {                                         \
        case 1:                                                         \
                __get_user_asm("move", __gk_err, *__gk_dst, __gk_src,   \
                                u8, b, d, -EFAULT);                     \
                break;                                                  \
        case 2:                                                         \
                __get_user_asm("move", __gk_err, *__gk_dst, __gk_src,   \
                                u16, w, r, -EFAULT);                    \
                break;                                                  \
        case 4:                                                         \
                __get_user_asm("move", __gk_err, *__gk_dst, __gk_src,   \
                                u32, l, r, -EFAULT);                    \
                break;                                                  \
        case 8:                                                         \
                __get_user_asm8("move", __gk_err, *__gk_dst, __gk_src); \
                break;                                                  \
        default:                                                        \
                BUILD_BUG();                                            \
        }                                                               \
        if (unlikely(__gk_err))                                         \
                goto err_label;                                         \
} while (0)

#define __put_kernel_nofault(dst, src, type, err_label)                 \
do {                                                                    \
        type __pk_src = *(type *)(src);                                 \
        type *__pk_dst = (type *)(dst);                                 \
        int __pk_err = 0;                                               \
                                                                        \
        switch (sizeof(type)) {                                         \
        case 1:                                                         \
                __put_user_asm("move", __pk_err, __pk_src, __pk_dst,    \
                                b, d, -EFAULT);                         \
                break;                                                  \
        case 2:                                                         \
                __put_user_asm("move", __pk_err, __pk_src, __pk_dst,    \
                                w, r, -EFAULT);                         \
                break;                                                  \
        case 4:                                                         \
                __put_user_asm("move", __pk_err, __pk_src, __pk_dst,    \
                                l, r, -EFAULT);                         \
                break;                                                  \
        case 8:                                                         \
                __put_user_asm8("move", __pk_err, __pk_src, __pk_dst);  \
                break;                                                  \
        default:                                                        \
                BUILD_BUG();                                            \
        }                                                               \
        if (unlikely(__pk_err))                                         \
                goto err_label;                                         \
} while (0)

extern long strncpy_from_user(char *dst, const char __user *src, long count);
extern __must_check long strnlen_user(const char __user *str, long n);

unsigned long __clear_user(void __user *to, unsigned long n);

#define clear_user      __clear_user

#else /* !CONFIG_MMU */
#include <asm-generic/uaccess.h>
#endif

#endif /* _M68K_UACCESS_H */