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