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