diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:17:08 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:17:08 -0400 |
commit | 185f3d38900f750a4566f87cde6a178f3595a115 (patch) | |
tree | d463f6da1af452b1bbdf476828ea88427087f255 /arch/x86/lib/usercopy_64.c | |
parent | 51b2833060f26258ea2da091c7b9c6a358ac9dd2 (diff) |
x86_64: move lib
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/lib/usercopy_64.c')
-rw-r--r-- | arch/x86/lib/usercopy_64.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/arch/x86/lib/usercopy_64.c b/arch/x86/lib/usercopy_64.c new file mode 100644 index 000000000000..893d43f838cc --- /dev/null +++ b/arch/x86/lib/usercopy_64.c | |||
@@ -0,0 +1,166 @@ | |||
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 | */ | ||
8 | #include <linux/module.h> | ||
9 | #include <asm/uaccess.h> | ||
10 | |||
11 | /* | ||
12 | * Copy a null terminated string from userspace. | ||
13 | */ | ||
14 | |||
15 | #define __do_strncpy_from_user(dst,src,count,res) \ | ||
16 | do { \ | ||
17 | long __d0, __d1, __d2; \ | ||
18 | might_sleep(); \ | ||
19 | __asm__ __volatile__( \ | ||
20 | " testq %1,%1\n" \ | ||
21 | " jz 2f\n" \ | ||
22 | "0: lodsb\n" \ | ||
23 | " stosb\n" \ | ||
24 | " testb %%al,%%al\n" \ | ||
25 | " jz 1f\n" \ | ||
26 | " decq %1\n" \ | ||
27 | " jnz 0b\n" \ | ||
28 | "1: subq %1,%0\n" \ | ||
29 | "2:\n" \ | ||
30 | ".section .fixup,\"ax\"\n" \ | ||
31 | "3: movq %5,%0\n" \ | ||
32 | " jmp 2b\n" \ | ||
33 | ".previous\n" \ | ||
34 | ".section __ex_table,\"a\"\n" \ | ||
35 | " .align 8\n" \ | ||
36 | " .quad 0b,3b\n" \ | ||
37 | ".previous" \ | ||
38 | : "=r"(res), "=c"(count), "=&a" (__d0), "=&S" (__d1), \ | ||
39 | "=&D" (__d2) \ | ||
40 | : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ | ||
41 | : "memory"); \ | ||
42 | } while (0) | ||
43 | |||
44 | long | ||
45 | __strncpy_from_user(char *dst, const char __user *src, long count) | ||
46 | { | ||
47 | long res; | ||
48 | __do_strncpy_from_user(dst, src, count, res); | ||
49 | return res; | ||
50 | } | ||
51 | EXPORT_SYMBOL(__strncpy_from_user); | ||
52 | |||
53 | long | ||
54 | strncpy_from_user(char *dst, const char __user *src, long count) | ||
55 | { | ||
56 | long res = -EFAULT; | ||
57 | if (access_ok(VERIFY_READ, src, 1)) | ||
58 | return __strncpy_from_user(dst, src, count); | ||
59 | return res; | ||
60 | } | ||
61 | EXPORT_SYMBOL(strncpy_from_user); | ||
62 | |||
63 | /* | ||
64 | * Zero Userspace | ||
65 | */ | ||
66 | |||
67 | unsigned long __clear_user(void __user *addr, unsigned long size) | ||
68 | { | ||
69 | long __d0; | ||
70 | might_sleep(); | ||
71 | /* no memory constraint because it doesn't change any memory gcc knows | ||
72 | about */ | ||
73 | asm volatile( | ||
74 | " testq %[size8],%[size8]\n" | ||
75 | " jz 4f\n" | ||
76 | "0: movq %[zero],(%[dst])\n" | ||
77 | " addq %[eight],%[dst]\n" | ||
78 | " decl %%ecx ; jnz 0b\n" | ||
79 | "4: movq %[size1],%%rcx\n" | ||
80 | " testl %%ecx,%%ecx\n" | ||
81 | " jz 2f\n" | ||
82 | "1: movb %b[zero],(%[dst])\n" | ||
83 | " incq %[dst]\n" | ||
84 | " decl %%ecx ; jnz 1b\n" | ||
85 | "2:\n" | ||
86 | ".section .fixup,\"ax\"\n" | ||
87 | "3: lea 0(%[size1],%[size8],8),%[size8]\n" | ||
88 | " jmp 2b\n" | ||
89 | ".previous\n" | ||
90 | ".section __ex_table,\"a\"\n" | ||
91 | " .align 8\n" | ||
92 | " .quad 0b,3b\n" | ||
93 | " .quad 1b,2b\n" | ||
94 | ".previous" | ||
95 | : [size8] "=c"(size), [dst] "=&D" (__d0) | ||
96 | : [size1] "r"(size & 7), "[size8]" (size / 8), "[dst]"(addr), | ||
97 | [zero] "r" (0UL), [eight] "r" (8UL)); | ||
98 | return size; | ||
99 | } | ||
100 | EXPORT_SYMBOL(__clear_user); | ||
101 | |||
102 | unsigned long clear_user(void __user *to, unsigned long n) | ||
103 | { | ||
104 | if (access_ok(VERIFY_WRITE, to, n)) | ||
105 | return __clear_user(to, n); | ||
106 | return n; | ||
107 | } | ||
108 | EXPORT_SYMBOL(clear_user); | ||
109 | |||
110 | /* | ||
111 | * Return the size of a string (including the ending 0) | ||
112 | * | ||
113 | * Return 0 on exception, a value greater than N if too long | ||
114 | */ | ||
115 | |||
116 | long __strnlen_user(const char __user *s, long n) | ||
117 | { | ||
118 | long res = 0; | ||
119 | char c; | ||
120 | |||
121 | while (1) { | ||
122 | if (res>n) | ||
123 | return n+1; | ||
124 | if (__get_user(c, s)) | ||
125 | return 0; | ||
126 | if (!c) | ||
127 | return res+1; | ||
128 | res++; | ||
129 | s++; | ||
130 | } | ||
131 | } | ||
132 | EXPORT_SYMBOL(__strnlen_user); | ||
133 | |||
134 | long strnlen_user(const char __user *s, long n) | ||
135 | { | ||
136 | if (!access_ok(VERIFY_READ, s, n)) | ||
137 | return 0; | ||
138 | return __strnlen_user(s, n); | ||
139 | } | ||
140 | EXPORT_SYMBOL(strnlen_user); | ||
141 | |||
142 | long strlen_user(const char __user *s) | ||
143 | { | ||
144 | long res = 0; | ||
145 | char c; | ||
146 | |||
147 | for (;;) { | ||
148 | if (get_user(c, s)) | ||
149 | return 0; | ||
150 | if (!c) | ||
151 | return res+1; | ||
152 | res++; | ||
153 | s++; | ||
154 | } | ||
155 | } | ||
156 | EXPORT_SYMBOL(strlen_user); | ||
157 | |||
158 | unsigned long copy_in_user(void __user *to, const void __user *from, unsigned len) | ||
159 | { | ||
160 | if (access_ok(VERIFY_WRITE, to, len) && access_ok(VERIFY_READ, from, len)) { | ||
161 | return copy_user_generic((__force void *)to, (__force void *)from, len); | ||
162 | } | ||
163 | return len; | ||
164 | } | ||
165 | EXPORT_SYMBOL(copy_in_user); | ||
166 | |||