| /* |
| * 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 <linux/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 */ |
| |