summaryrefslogtreecommitdiffstats
path: root/kernel/arch/m32r/lib/usercopy.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/arch/m32r/lib/usercopy.c')
-rw-r--r--kernel/arch/m32r/lib/usercopy.c390
1 files changed, 390 insertions, 0 deletions
diff --git a/kernel/arch/m32r/lib/usercopy.c b/kernel/arch/m32r/lib/usercopy.c
new file mode 100644
index 000000000..82abd159d
--- /dev/null
+++ b/kernel/arch/m32r/lib/usercopy.c
@@ -0,0 +1,390 @@
+/*
+ * User address space access functions.
+ * The non inlined parts of asm-m32r/uaccess.h are here.
+ *
+ * Copyright 1997 Andi Kleen <ak@muc.de>
+ * Copyright 1997 Linus Torvalds
+ * Copyright 2001, 2002, 2004 Hirokazu Takata
+ */
+#include <linux/prefetch.h>
+#include <linux/string.h>
+#include <linux/thread_info.h>
+#include <asm/uaccess.h>
+
+unsigned long
+__generic_copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ prefetch(from);
+ if (access_ok(VERIFY_WRITE, to, n))
+ __copy_user(to,from,n);
+ return n;
+}
+
+unsigned long
+__generic_copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ prefetchw(to);
+ if (access_ok(VERIFY_READ, from, n))
+ __copy_user_zeroing(to,from,n);
+ else
+ memset(to, 0, n);
+ return n;
+}
+
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+#define __do_strncpy_from_user(dst,src,count,res) \
+do { \
+ int __d0, __d1, __d2; \
+ __asm__ __volatile__( \
+ " beqz %1, 2f\n" \
+ " .fillinsn\n" \
+ "0: ldb r14, @%3 || addi %3, #1\n" \
+ " stb r14, @%4 || addi %4, #1\n" \
+ " beqz r14, 1f\n" \
+ " addi %1, #-1\n" \
+ " bnez %1, 0b\n" \
+ " .fillinsn\n" \
+ "1: sub %0, %1\n" \
+ " .fillinsn\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "3: seth r14, #high(2b)\n" \
+ " or3 r14, r14, #low(2b)\n" \
+ " jmp r14 || ldi %0, #%5\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,3b\n" \
+ ".previous" \
+ : "=&r"(res), "=&r"(count), "=&r" (__d0), "=&r" (__d1), \
+ "=&r" (__d2) \
+ : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \
+ "4"(dst) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+#define __do_strncpy_from_user(dst,src,count,res) \
+do { \
+ int __d0, __d1, __d2; \
+ __asm__ __volatile__( \
+ " beqz %1, 2f\n" \
+ " .fillinsn\n" \
+ "0: ldb r14, @%3\n" \
+ " stb r14, @%4\n" \
+ " addi %3, #1\n" \
+ " addi %4, #1\n" \
+ " beqz r14, 1f\n" \
+ " addi %1, #-1\n" \
+ " bnez %1, 0b\n" \
+ " .fillinsn\n" \
+ "1: sub %0, %1\n" \
+ " .fillinsn\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "3: ldi %0, #%5\n" \
+ " seth r14, #high(2b)\n" \
+ " or3 r14, r14, #low(2b)\n" \
+ " jmp r14\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,3b\n" \
+ ".previous" \
+ : "=&r"(res), "=&r"(count), "=&r" (__d0), "=&r" (__d1), \
+ "=&r" (__d2) \
+ : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \
+ "4"(dst) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#endif /* CONFIG_ISA_DUAL_ISSUE */
+
+long
+__strncpy_from_user(char *dst, const char __user *src, long count)
+{
+ long res;
+ __do_strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+long
+strncpy_from_user(char *dst, const char __user *src, long count)
+{
+ long res = -EFAULT;
+ if (access_ok(VERIFY_READ, src, 1))
+ __do_strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+
+/*
+ * Zero Userspace
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+#define __do_clear_user(addr,size) \
+do { \
+ int __dst, __c; \
+ __asm__ __volatile__( \
+ " beqz %1, 9f\n" \
+ " and3 r14, %0, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 r14, %1, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 %1, %1, #3\n" \
+ " beqz %2, 2f\n" \
+ " addi %0, #-4\n" \
+ " .fillinsn\n" \
+ "0: ; word clear \n" \
+ " st %6, @+%0 || addi %2, #-1\n" \
+ " bnez %2, 0b\n" \
+ " beqz %1, 9f\n" \
+ " .fillinsn\n" \
+ "2: ; byte clear \n" \
+ " stb %6, @%0 || addi %1, #-1\n" \
+ " addi %0, #1\n" \
+ " bnez %1, 2b\n" \
+ " .fillinsn\n" \
+ "9:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "4: slli %2, #2\n" \
+ " seth r14, #high(9b)\n" \
+ " or3 r14, r14, #low(9b)\n" \
+ " jmp r14 || add %1, %2\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,4b\n" \
+ " .long 2b,9b\n" \
+ ".previous\n" \
+ : "=&r"(__dst), "=&r"(size), "=&r"(__c) \
+ : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+#define __do_clear_user(addr,size) \
+do { \
+ int __dst, __c; \
+ __asm__ __volatile__( \
+ " beqz %1, 9f\n" \
+ " and3 r14, %0, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 r14, %1, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 %1, %1, #3\n" \
+ " beqz %2, 2f\n" \
+ " addi %0, #-4\n" \
+ " .fillinsn\n" \
+ "0: st %6, @+%0 ; word clear \n" \
+ " addi %2, #-1\n" \
+ " bnez %2, 0b\n" \
+ " beqz %1, 9f\n" \
+ " .fillinsn\n" \
+ "2: stb %6, @%0 ; byte clear \n" \
+ " addi %1, #-1\n" \
+ " addi %0, #1\n" \
+ " bnez %1, 2b\n" \
+ " .fillinsn\n" \
+ "9:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "4: slli %2, #2\n" \
+ " add %1, %2\n" \
+ " seth r14, #high(9b)\n" \
+ " or3 r14, r14, #low(9b)\n" \
+ " jmp r14\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,4b\n" \
+ " .long 2b,9b\n" \
+ ".previous\n" \
+ : "=&r"(__dst), "=&r"(size), "=&r"(__c) \
+ : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+unsigned long
+clear_user(void __user *to, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ __do_clear_user(to, n);
+ return n;
+}
+
+unsigned long
+__clear_user(void __user *to, unsigned long n)
+{
+ __do_clear_user(to, n);
+ return n;
+}
+
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 on exception, a value greater than N if too long
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+long strnlen_user(const char __user *s, long n)
+{
+ unsigned long mask = -__addr_ok(s);
+ unsigned long res;
+
+ __asm__ __volatile__(
+ " and %0, %5 || mv r1, %1\n"
+ " beqz %0, strnlen_exit\n"
+ " and3 r0, %1, #3\n"
+ " bnez r0, strnlen_byte_loop\n"
+ " cmpui %0, #4\n"
+ " bc strnlen_byte_loop\n"
+ "strnlen_word_loop:\n"
+ "0: ld r0, @%1+\n"
+ " pcmpbz r0\n"
+ " bc strnlen_last_bytes_fixup\n"
+ " addi %0, #-4\n"
+ " beqz %0, strnlen_exit\n"
+ " bgtz %0, strnlen_word_loop\n"
+ "strnlen_last_bytes:\n"
+ " mv %0, %4\n"
+ "strnlen_last_bytes_fixup:\n"
+ " addi %1, #-4\n"
+ "strnlen_byte_loop:\n"
+ "1: ldb r0, @%1 || addi %0, #-1\n"
+ " beqz r0, strnlen_exit\n"
+ " addi %1, #1\n"
+ " bnez %0, strnlen_byte_loop\n"
+ "strnlen_exit:\n"
+ " sub %1, r1\n"
+ " add3 %0, %1, #1\n"
+ " .fillinsn\n"
+ "9:\n"
+ ".section .fixup,\"ax\"\n"
+ " .balign 4\n"
+ "4: addi %1, #-4\n"
+ " .fillinsn\n"
+ "5: seth r1, #high(9b)\n"
+ " or3 r1, r1, #low(9b)\n"
+ " jmp r1 || ldi %0, #0\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .balign 4\n"
+ " .long 0b,4b\n"
+ " .long 1b,5b\n"
+ ".previous"
+ : "=&r" (res), "=r" (s)
+ : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101)
+ : "r0", "r1", "cbit");
+
+ /* NOTE: strnlen_user() algorithm:
+ * {
+ * char *p;
+ * for (p = s; n-- && *p != '\0'; ++p)
+ * ;
+ * return p - s + 1;
+ * }
+ */
+
+ /* NOTE: If a null char. exists, return 0.
+ * if ((x - 0x01010101) & ~x & 0x80808080)\n"
+ * return 0;\n"
+ */
+
+ return res & mask;
+}
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+long strnlen_user(const char __user *s, long n)
+{
+ unsigned long mask = -__addr_ok(s);
+ unsigned long res;
+
+ __asm__ __volatile__(
+ " and %0, %5\n"
+ " mv r1, %1\n"
+ " beqz %0, strnlen_exit\n"
+ " and3 r0, %1, #3\n"
+ " bnez r0, strnlen_byte_loop\n"
+ " cmpui %0, #4\n"
+ " bc strnlen_byte_loop\n"
+ " sll3 r3, %6, #7\n"
+ "strnlen_word_loop:\n"
+ "0: ld r0, @%1+\n"
+ " not r2, r0\n"
+ " sub r0, %6\n"
+ " and r2, r3\n"
+ " and r2, r0\n"
+ " bnez r2, strnlen_last_bytes_fixup\n"
+ " addi %0, #-4\n"
+ " beqz %0, strnlen_exit\n"
+ " bgtz %0, strnlen_word_loop\n"
+ "strnlen_last_bytes:\n"
+ " mv %0, %4\n"
+ "strnlen_last_bytes_fixup:\n"
+ " addi %1, #-4\n"
+ "strnlen_byte_loop:\n"
+ "1: ldb r0, @%1\n"
+ " addi %0, #-1\n"
+ " beqz r0, strnlen_exit\n"
+ " addi %1, #1\n"
+ " bnez %0, strnlen_byte_loop\n"
+ "strnlen_exit:\n"
+ " sub %1, r1\n"
+ " add3 %0, %1, #1\n"
+ " .fillinsn\n"
+ "9:\n"
+ ".section .fixup,\"ax\"\n"
+ " .balign 4\n"
+ "4: addi %1, #-4\n"
+ " .fillinsn\n"
+ "5: ldi %0, #0\n"
+ " seth r1, #high(9b)\n"
+ " or3 r1, r1, #low(9b)\n"
+ " jmp r1\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .balign 4\n"
+ " .long 0b,4b\n"
+ " .long 1b,5b\n"
+ ".previous"
+ : "=&r" (res), "=r" (s)
+ : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101)
+ : "r0", "r1", "r2", "r3", "cbit");
+
+ /* NOTE: strnlen_user() algorithm:
+ * {
+ * char *p;
+ * for (p = s; n-- && *p != '\0'; ++p)
+ * ;
+ * return p - s + 1;
+ * }
+ */
+
+ /* NOTE: If a null char. exists, return 0.
+ * if ((x - 0x01010101) & ~x & 0x80808080)\n"
+ * return 0;\n"
+ */
+
+ return res & mask;
+}
+
+#endif /* CONFIG_ISA_DUAL_ISSUE */
+