]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * User address space access functions. | |
3 | * The non inlined parts of asm-m32r/uaccess.h are here. | |
4 | * | |
5 | * Copyright 1997 Andi Kleen <ak@muc.de> | |
6 | * Copyright 1997 Linus Torvalds | |
7 | * Copyright 2001, 2002, 2004 Hirokazu Takata | |
8 | */ | |
9 | #include <linux/config.h> | |
10 | #include <linux/prefetch.h> | |
11 | #include <linux/string.h> | |
12 | #include <linux/thread_info.h> | |
13 | #include <asm/uaccess.h> | |
14 | ||
15 | unsigned long | |
a880948b | 16 | __generic_copy_to_user(void __user *to, const void *from, unsigned long n) |
1da177e4 LT |
17 | { |
18 | prefetch(from); | |
19 | if (access_ok(VERIFY_WRITE, to, n)) | |
20 | __copy_user(to,from,n); | |
21 | return n; | |
22 | } | |
23 | ||
24 | unsigned long | |
a880948b | 25 | __generic_copy_from_user(void *to, const void __user *from, unsigned long n) |
1da177e4 LT |
26 | { |
27 | prefetchw(to); | |
28 | if (access_ok(VERIFY_READ, from, n)) | |
29 | __copy_user_zeroing(to,from,n); | |
30 | else | |
31 | memset(to, 0, n); | |
32 | return n; | |
33 | } | |
34 | ||
35 | ||
36 | /* | |
37 | * Copy a null terminated string from userspace. | |
38 | */ | |
39 | ||
40 | #ifdef CONFIG_ISA_DUAL_ISSUE | |
41 | ||
42 | #define __do_strncpy_from_user(dst,src,count,res) \ | |
43 | do { \ | |
44 | int __d0, __d1, __d2; \ | |
45 | __asm__ __volatile__( \ | |
46 | " beqz %1, 2f\n" \ | |
47 | " .fillinsn\n" \ | |
48 | "0: ldb r14, @%3 || addi %3, #1\n" \ | |
49 | " stb r14, @%4 || addi %4, #1\n" \ | |
50 | " beqz r14, 1f\n" \ | |
51 | " addi %1, #-1\n" \ | |
52 | " bnez %1, 0b\n" \ | |
53 | " .fillinsn\n" \ | |
54 | "1: sub %0, %1\n" \ | |
55 | " .fillinsn\n" \ | |
56 | "2:\n" \ | |
57 | ".section .fixup,\"ax\"\n" \ | |
58 | " .balign 4\n" \ | |
59 | "3: seth r14, #high(2b)\n" \ | |
60 | " or3 r14, r14, #low(2b)\n" \ | |
61 | " jmp r14 || ldi %0, #%5\n" \ | |
62 | ".previous\n" \ | |
63 | ".section __ex_table,\"a\"\n" \ | |
64 | " .balign 4\n" \ | |
65 | " .long 0b,3b\n" \ | |
66 | ".previous" \ | |
6ced13cd | 67 | : "=&r"(res), "=&r"(count), "=&r" (__d0), "=&r" (__d1), \ |
1da177e4 LT |
68 | "=&r" (__d2) \ |
69 | : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \ | |
70 | "4"(dst) \ | |
71 | : "r14", "cbit", "memory"); \ | |
72 | } while (0) | |
73 | ||
74 | #else /* not CONFIG_ISA_DUAL_ISSUE */ | |
75 | ||
76 | #define __do_strncpy_from_user(dst,src,count,res) \ | |
77 | do { \ | |
78 | int __d0, __d1, __d2; \ | |
79 | __asm__ __volatile__( \ | |
80 | " beqz %1, 2f\n" \ | |
81 | " .fillinsn\n" \ | |
82 | "0: ldb r14, @%3\n" \ | |
83 | " stb r14, @%4\n" \ | |
84 | " addi %3, #1\n" \ | |
85 | " addi %4, #1\n" \ | |
86 | " beqz r14, 1f\n" \ | |
87 | " addi %1, #-1\n" \ | |
88 | " bnez %1, 0b\n" \ | |
89 | " .fillinsn\n" \ | |
90 | "1: sub %0, %1\n" \ | |
91 | " .fillinsn\n" \ | |
92 | "2:\n" \ | |
93 | ".section .fixup,\"ax\"\n" \ | |
94 | " .balign 4\n" \ | |
95 | "3: ldi %0, #%5\n" \ | |
96 | " seth r14, #high(2b)\n" \ | |
97 | " or3 r14, r14, #low(2b)\n" \ | |
98 | " jmp r14\n" \ | |
99 | ".previous\n" \ | |
100 | ".section __ex_table,\"a\"\n" \ | |
101 | " .balign 4\n" \ | |
102 | " .long 0b,3b\n" \ | |
103 | ".previous" \ | |
6ced13cd | 104 | : "=&r"(res), "=&r"(count), "=&r" (__d0), "=&r" (__d1), \ |
1da177e4 LT |
105 | "=&r" (__d2) \ |
106 | : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \ | |
107 | "4"(dst) \ | |
108 | : "r14", "cbit", "memory"); \ | |
109 | } while (0) | |
110 | ||
111 | #endif /* CONFIG_ISA_DUAL_ISSUE */ | |
112 | ||
113 | long | |
a880948b | 114 | __strncpy_from_user(char *dst, const char __user *src, long count) |
1da177e4 LT |
115 | { |
116 | long res; | |
117 | __do_strncpy_from_user(dst, src, count, res); | |
118 | return res; | |
119 | } | |
120 | ||
121 | long | |
a880948b | 122 | strncpy_from_user(char *dst, const char __user *src, long count) |
1da177e4 LT |
123 | { |
124 | long res = -EFAULT; | |
125 | if (access_ok(VERIFY_READ, src, 1)) | |
126 | __do_strncpy_from_user(dst, src, count, res); | |
127 | return res; | |
128 | } | |
129 | ||
130 | ||
131 | /* | |
132 | * Zero Userspace | |
133 | */ | |
134 | ||
135 | #ifdef CONFIG_ISA_DUAL_ISSUE | |
136 | ||
137 | #define __do_clear_user(addr,size) \ | |
138 | do { \ | |
139 | int __dst, __c; \ | |
140 | __asm__ __volatile__( \ | |
141 | " beqz %1, 9f\n" \ | |
142 | " and3 r14, %0, #3\n" \ | |
143 | " bnez r14, 2f\n" \ | |
144 | " and3 r14, %1, #3\n" \ | |
145 | " bnez r14, 2f\n" \ | |
146 | " and3 %1, %1, #3\n" \ | |
147 | " beqz %2, 2f\n" \ | |
148 | " addi %0, #-4\n" \ | |
149 | " .fillinsn\n" \ | |
150 | "0: ; word clear \n" \ | |
151 | " st %6, @+%0 || addi %2, #-1\n" \ | |
152 | " bnez %2, 0b\n" \ | |
153 | " beqz %1, 9f\n" \ | |
154 | " .fillinsn\n" \ | |
155 | "2: ; byte clear \n" \ | |
156 | " stb %6, @%0 || addi %1, #-1\n" \ | |
157 | " addi %0, #1\n" \ | |
158 | " bnez %1, 2b\n" \ | |
159 | " .fillinsn\n" \ | |
160 | "9:\n" \ | |
161 | ".section .fixup,\"ax\"\n" \ | |
162 | " .balign 4\n" \ | |
163 | "4: slli %2, #2\n" \ | |
164 | " seth r14, #high(9b)\n" \ | |
165 | " or3 r14, r14, #low(9b)\n" \ | |
166 | " jmp r14 || add %1, %2\n" \ | |
167 | ".previous\n" \ | |
168 | ".section __ex_table,\"a\"\n" \ | |
169 | " .balign 4\n" \ | |
170 | " .long 0b,4b\n" \ | |
171 | " .long 2b,9b\n" \ | |
172 | ".previous\n" \ | |
173 | : "=&r"(__dst), "=&r"(size), "=&r"(__c) \ | |
174 | : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \ | |
175 | : "r14", "cbit", "memory"); \ | |
176 | } while (0) | |
177 | ||
178 | #else /* not CONFIG_ISA_DUAL_ISSUE */ | |
179 | ||
180 | #define __do_clear_user(addr,size) \ | |
181 | do { \ | |
182 | int __dst, __c; \ | |
183 | __asm__ __volatile__( \ | |
184 | " beqz %1, 9f\n" \ | |
185 | " and3 r14, %0, #3\n" \ | |
186 | " bnez r14, 2f\n" \ | |
187 | " and3 r14, %1, #3\n" \ | |
188 | " bnez r14, 2f\n" \ | |
189 | " and3 %1, %1, #3\n" \ | |
190 | " beqz %2, 2f\n" \ | |
191 | " addi %0, #-4\n" \ | |
192 | " .fillinsn\n" \ | |
193 | "0: st %6, @+%0 ; word clear \n" \ | |
194 | " addi %2, #-1\n" \ | |
195 | " bnez %2, 0b\n" \ | |
196 | " beqz %1, 9f\n" \ | |
197 | " .fillinsn\n" \ | |
198 | "2: stb %6, @%0 ; byte clear \n" \ | |
199 | " addi %1, #-1\n" \ | |
200 | " addi %0, #1\n" \ | |
201 | " bnez %1, 2b\n" \ | |
202 | " .fillinsn\n" \ | |
203 | "9:\n" \ | |
204 | ".section .fixup,\"ax\"\n" \ | |
205 | " .balign 4\n" \ | |
206 | "4: slli %2, #2\n" \ | |
207 | " add %1, %2\n" \ | |
208 | " seth r14, #high(9b)\n" \ | |
209 | " or3 r14, r14, #low(9b)\n" \ | |
210 | " jmp r14\n" \ | |
211 | ".previous\n" \ | |
212 | ".section __ex_table,\"a\"\n" \ | |
213 | " .balign 4\n" \ | |
214 | " .long 0b,4b\n" \ | |
215 | " .long 2b,9b\n" \ | |
216 | ".previous\n" \ | |
217 | : "=&r"(__dst), "=&r"(size), "=&r"(__c) \ | |
218 | : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \ | |
219 | : "r14", "cbit", "memory"); \ | |
220 | } while (0) | |
221 | ||
222 | #endif /* not CONFIG_ISA_DUAL_ISSUE */ | |
223 | ||
224 | unsigned long | |
a880948b | 225 | clear_user(void __user *to, unsigned long n) |
1da177e4 LT |
226 | { |
227 | if (access_ok(VERIFY_WRITE, to, n)) | |
228 | __do_clear_user(to, n); | |
229 | return n; | |
230 | } | |
231 | ||
232 | unsigned long | |
a880948b | 233 | __clear_user(void __user *to, unsigned long n) |
1da177e4 LT |
234 | { |
235 | __do_clear_user(to, n); | |
236 | return n; | |
237 | } | |
238 | ||
239 | /* | |
240 | * Return the size of a string (including the ending 0) | |
241 | * | |
242 | * Return 0 on exception, a value greater than N if too long | |
243 | */ | |
244 | ||
245 | #ifdef CONFIG_ISA_DUAL_ISSUE | |
246 | ||
a880948b | 247 | long strnlen_user(const char __user *s, long n) |
1da177e4 LT |
248 | { |
249 | unsigned long mask = -__addr_ok(s); | |
250 | unsigned long res; | |
251 | ||
252 | __asm__ __volatile__( | |
253 | " and %0, %5 || mv r1, %1\n" | |
254 | " beqz %0, strnlen_exit\n" | |
255 | " and3 r0, %1, #3\n" | |
256 | " bnez r0, strnlen_byte_loop\n" | |
257 | " cmpui %0, #4\n" | |
258 | " bc strnlen_byte_loop\n" | |
259 | "strnlen_word_loop:\n" | |
260 | "0: ld r0, @%1+\n" | |
261 | " pcmpbz r0\n" | |
262 | " bc strnlen_last_bytes_fixup\n" | |
263 | " addi %0, #-4\n" | |
264 | " beqz %0, strnlen_exit\n" | |
265 | " bgtz %0, strnlen_word_loop\n" | |
266 | "strnlen_last_bytes:\n" | |
267 | " mv %0, %4\n" | |
268 | "strnlen_last_bytes_fixup:\n" | |
269 | " addi %1, #-4\n" | |
270 | "strnlen_byte_loop:\n" | |
271 | "1: ldb r0, @%1 || addi %0, #-1\n" | |
272 | " beqz r0, strnlen_exit\n" | |
273 | " addi %1, #1\n" | |
274 | " bnez %0, strnlen_byte_loop\n" | |
275 | "strnlen_exit:\n" | |
276 | " sub %1, r1\n" | |
277 | " add3 %0, %1, #1\n" | |
278 | " .fillinsn\n" | |
279 | "9:\n" | |
280 | ".section .fixup,\"ax\"\n" | |
281 | " .balign 4\n" | |
282 | "4: addi %1, #-4\n" | |
283 | " .fillinsn\n" | |
284 | "5: seth r1, #high(9b)\n" | |
285 | " or3 r1, r1, #low(9b)\n" | |
286 | " jmp r1 || ldi %0, #0\n" | |
287 | ".previous\n" | |
288 | ".section __ex_table,\"a\"\n" | |
289 | " .balign 4\n" | |
290 | " .long 0b,4b\n" | |
291 | " .long 1b,5b\n" | |
292 | ".previous" | |
293 | : "=&r" (res), "=r" (s) | |
294 | : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) | |
295 | : "r0", "r1", "cbit"); | |
296 | ||
297 | /* NOTE: strnlen_user() algorism: | |
298 | * { | |
299 | * char *p; | |
300 | * for (p = s; n-- && *p != '\0'; ++p) | |
301 | * ; | |
302 | * return p - s + 1; | |
303 | * } | |
304 | */ | |
305 | ||
306 | /* NOTE: If a null char. exists, return 0. | |
307 | * if ((x - 0x01010101) & ~x & 0x80808080)\n" | |
308 | * return 0;\n" | |
309 | */ | |
310 | ||
311 | return res & mask; | |
312 | } | |
313 | ||
314 | #else /* not CONFIG_ISA_DUAL_ISSUE */ | |
315 | ||
a880948b | 316 | long strnlen_user(const char __user *s, long n) |
1da177e4 LT |
317 | { |
318 | unsigned long mask = -__addr_ok(s); | |
319 | unsigned long res; | |
320 | ||
321 | __asm__ __volatile__( | |
322 | " and %0, %5\n" | |
323 | " mv r1, %1\n" | |
324 | " beqz %0, strnlen_exit\n" | |
325 | " and3 r0, %1, #3\n" | |
326 | " bnez r0, strnlen_byte_loop\n" | |
327 | " cmpui %0, #4\n" | |
328 | " bc strnlen_byte_loop\n" | |
329 | " sll3 r3, %6, #7\n" | |
330 | "strnlen_word_loop:\n" | |
331 | "0: ld r0, @%1+\n" | |
332 | " not r2, r0\n" | |
333 | " sub r0, %6\n" | |
334 | " and r2, r3\n" | |
335 | " and r2, r0\n" | |
336 | " bnez r2, strnlen_last_bytes_fixup\n" | |
337 | " addi %0, #-4\n" | |
338 | " beqz %0, strnlen_exit\n" | |
339 | " bgtz %0, strnlen_word_loop\n" | |
340 | "strnlen_last_bytes:\n" | |
341 | " mv %0, %4\n" | |
342 | "strnlen_last_bytes_fixup:\n" | |
343 | " addi %1, #-4\n" | |
344 | "strnlen_byte_loop:\n" | |
345 | "1: ldb r0, @%1\n" | |
346 | " addi %0, #-1\n" | |
347 | " beqz r0, strnlen_exit\n" | |
348 | " addi %1, #1\n" | |
349 | " bnez %0, strnlen_byte_loop\n" | |
350 | "strnlen_exit:\n" | |
351 | " sub %1, r1\n" | |
352 | " add3 %0, %1, #1\n" | |
353 | " .fillinsn\n" | |
354 | "9:\n" | |
355 | ".section .fixup,\"ax\"\n" | |
356 | " .balign 4\n" | |
357 | "4: addi %1, #-4\n" | |
358 | " .fillinsn\n" | |
359 | "5: ldi %0, #0\n" | |
360 | " seth r1, #high(9b)\n" | |
361 | " or3 r1, r1, #low(9b)\n" | |
362 | " jmp r1\n" | |
363 | ".previous\n" | |
364 | ".section __ex_table,\"a\"\n" | |
365 | " .balign 4\n" | |
366 | " .long 0b,4b\n" | |
367 | " .long 1b,5b\n" | |
368 | ".previous" | |
369 | : "=&r" (res), "=r" (s) | |
370 | : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) | |
371 | : "r0", "r1", "r2", "r3", "cbit"); | |
372 | ||
373 | /* NOTE: strnlen_user() algorism: | |
374 | * { | |
375 | * char *p; | |
376 | * for (p = s; n-- && *p != '\0'; ++p) | |
377 | * ; | |
378 | * return p - s + 1; | |
379 | * } | |
380 | */ | |
381 | ||
382 | /* NOTE: If a null char. exists, return 0. | |
383 | * if ((x - 0x01010101) & ~x & 0x80808080)\n" | |
384 | * return 0;\n" | |
385 | */ | |
386 | ||
387 | return res & mask; | |
388 | } | |
389 | ||
390 | #endif /* CONFIG_ISA_DUAL_ISSUE */ | |
391 |