/* * 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() algorism: * { * 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() algorism: * { * 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 */