| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*  | 
 | 2 |  * User address space access functions. | 
 | 3 |  * | 
 | 4 |  * Copyright 1997 Andi Kleen <ak@muc.de> | 
 | 5 |  * Copyright 1997 Linus Torvalds | 
 | 6 |  * Copyright 2002 Andi Kleen <ak@suse.de> | 
 | 7 |  */ | 
| Andi Kleen | 2ee60e17 | 2006-06-26 13:59:44 +0200 | [diff] [blame] | 8 | #include <linux/module.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 9 | #include <asm/uaccess.h> | 
 | 10 |  | 
 | 11 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 |  * Zero Userspace | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | unsigned long __clear_user(void __user *addr, unsigned long size) | 
 | 16 | { | 
 | 17 | 	long __d0; | 
| Nick Piggin | 3ee1afa | 2008-09-10 13:37:17 +0200 | [diff] [blame] | 18 | 	might_fault(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | 	/* no memory constraint because it doesn't change any memory gcc knows | 
 | 20 | 	   about */ | 
 | 21 | 	asm volatile( | 
 | 22 | 		"	testq  %[size8],%[size8]\n" | 
 | 23 | 		"	jz     4f\n" | 
 | 24 | 		"0:	movq %[zero],(%[dst])\n" | 
 | 25 | 		"	addq   %[eight],%[dst]\n" | 
 | 26 | 		"	decl %%ecx ; jnz   0b\n" | 
 | 27 | 		"4:	movq  %[size1],%%rcx\n" | 
 | 28 | 		"	testl %%ecx,%%ecx\n" | 
 | 29 | 		"	jz     2f\n" | 
 | 30 | 		"1:	movb   %b[zero],(%[dst])\n" | 
 | 31 | 		"	incq   %[dst]\n" | 
 | 32 | 		"	decl %%ecx ; jnz  1b\n" | 
 | 33 | 		"2:\n" | 
 | 34 | 		".section .fixup,\"ax\"\n" | 
 | 35 | 		"3:	lea 0(%[size1],%[size8],8),%[size8]\n" | 
 | 36 | 		"	jmp 2b\n" | 
 | 37 | 		".previous\n" | 
| H. Peter Anvin | 8da804f | 2008-02-04 16:47:57 +0100 | [diff] [blame] | 38 | 		_ASM_EXTABLE(0b,3b) | 
 | 39 | 		_ASM_EXTABLE(1b,2b) | 
| Andi Kleen | e0a9612 | 2009-01-16 15:22:11 +0100 | [diff] [blame] | 40 | 		: [size8] "=&c"(size), [dst] "=&D" (__d0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | 		: [size1] "r"(size & 7), "[size8]" (size / 8), "[dst]"(addr), | 
 | 42 | 		  [zero] "r" (0UL), [eight] "r" (8UL)); | 
 | 43 | 	return size; | 
 | 44 | } | 
| Andi Kleen | 2ee60e17 | 2006-06-26 13:59:44 +0200 | [diff] [blame] | 45 | EXPORT_SYMBOL(__clear_user); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 |  | 
 | 47 | unsigned long clear_user(void __user *to, unsigned long n) | 
 | 48 | { | 
 | 49 | 	if (access_ok(VERIFY_WRITE, to, n)) | 
 | 50 | 		return __clear_user(to, n); | 
 | 51 | 	return n; | 
 | 52 | } | 
| Andi Kleen | 2ee60e17 | 2006-06-26 13:59:44 +0200 | [diff] [blame] | 53 | EXPORT_SYMBOL(clear_user); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | unsigned long copy_in_user(void __user *to, const void __user *from, unsigned len) | 
 | 56 | { | 
 | 57 | 	if (access_ok(VERIFY_WRITE, to, len) && access_ok(VERIFY_READ, from, len)) {  | 
 | 58 | 		return copy_user_generic((__force void *)to, (__force void *)from, len); | 
 | 59 | 	}  | 
 | 60 | 	return len;		 | 
 | 61 | } | 
| Andi Kleen | 2ee60e17 | 2006-06-26 13:59:44 +0200 | [diff] [blame] | 62 | EXPORT_SYMBOL(copy_in_user); | 
 | 63 |  | 
| Vitaly Mayatskikh | 1129585 | 2008-07-02 15:48:21 +0200 | [diff] [blame] | 64 | /* | 
 | 65 |  * Try to copy last bytes and clear the rest if needed. | 
 | 66 |  * Since protection fault in copy_from/to_user is not a normal situation, | 
 | 67 |  * it is not necessary to optimize tail handling. | 
 | 68 |  */ | 
 | 69 | unsigned long | 
 | 70 | copy_user_handle_tail(char *to, char *from, unsigned len, unsigned zerorest) | 
 | 71 | { | 
 | 72 | 	char c; | 
 | 73 | 	unsigned zero_len; | 
 | 74 |  | 
 | 75 | 	for (; len; --len) { | 
 | 76 | 		if (__get_user_nocheck(c, from++, sizeof(char))) | 
 | 77 | 			break; | 
 | 78 | 		if (__put_user_nocheck(c, to++, sizeof(char))) | 
 | 79 | 			break; | 
 | 80 | 	} | 
 | 81 |  | 
 | 82 | 	for (c = 0, zero_len = len; zerorest && zero_len; --zero_len) | 
 | 83 | 		if (__put_user_nocheck(c, to++, sizeof(char))) | 
 | 84 | 			break; | 
 | 85 | 	return len; | 
 | 86 | } |