2 * linux/arch/arm26/lib/uaccess-user.S
4 * Copyright (C) 1995, 1996,1997,1998 Russell King
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * Routines to block copy data to/from user memory
11 * These are highly optimised both for the 4k page size
12 * and for various alignments.
14 #include <linux/linkage.h>
15 #include <asm/assembler.h>
16 #include <asm/errno.h>
21 //FIXME - surely this can be done in C not asm, removing the problem of keeping C and asm in sync? (this is a struct uaccess_t)
24 .word uaccess_user_put_byte
25 .word uaccess_user_get_byte
26 .word uaccess_user_put_half
27 .word uaccess_user_get_half
28 .word uaccess_user_put_word
29 .word uaccess_user_get_word
30 .word uaccess_user_put_dword
31 .word uaccess_user_copy_from_user
32 .word uaccess_user_copy_to_user
33 .word uaccess_user_clear_user
34 .word uaccess_user_strncpy_from_user
35 .word uaccess_user_strnlen_user
38 @ In : r0 = x, r1 = addr, r2 = error
40 uaccess_user_put_byte:
45 @ In : r0 = x, r1 = addr, r2 = error
47 uaccess_user_put_half:
49 USER( strbt r0, [r1], #1)
54 @ In : r0 = x, r1 = addr, r2 = error
56 uaccess_user_put_word:
61 @ In : r0 = x, r1 = addr, r2 = error
63 uaccess_user_put_dword:
65 USER( strt r0, [r1], #4)
66 USER( strt r0, [r1], #0)
69 9001: mov r2, #-EFAULT
73 @ In : r0 = addr, r1 = error
74 @ Out: r0 = x, r1 = error
75 uaccess_user_get_byte:
80 @ In : r0 = addr, r1 = error
81 @ Out: r0 = x, r1 = error
82 uaccess_user_get_half:
89 @ In : r0 = addr, r1 = error
90 @ Out: r0 = x, r1 = error
91 uaccess_user_get_word:
96 9001: mov r1, #-EFAULT
99 /* Prototype: int uaccess_user_copy_to_user(void *to, const char *from, size_t n)
100 * Purpose : copy a block to user memory from kernel memory
101 * Params : to - user memory
102 * : from - kernel memory
103 * : n - number of bytes to copy
104 * Returns : Number of bytes NOT copied.
107 .c2u_dest_not_aligned:
111 USER( strbt r3, [r0], #1) @ May fault
113 USER( strgebt r3, [r0], #1) @ May fault
115 USER( strgtbt r3, [r0], #1) @ May fault
119 ENTRY(uaccess_user_copy_to_user)
120 stmfd sp!, {r2, r4 - r7, lr}
124 bne .c2u_dest_not_aligned
128 bne .c2u_src_not_aligned
130 * Seeing as there has to be at least 8 bytes to copy, we can
131 * copy one word, and force a user-mode page fault...
134 .c2u_0fupi: subs r2, r2, #4
138 USER( strt r3, [r0], #4) @ May fault
139 mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
141 movs ip, ip, lsr #32 - PAGE_SHIFT
144 * ip = max no. of bytes to copy before needing another "strt" insn
152 .c2u_0cpy8lp: ldmia r1!, {r3 - r6}
153 stmia r0!, {r3 - r6} @ Shouldnt fault
155 stmia r0!, {r3 - r6} @ Shouldnt fault
158 .c2u_0rem8lp: cmn ip, #16
159 ldmgeia r1!, {r3 - r6}
160 stmgeia r0!, {r3 - r6} @ Shouldnt fault
162 ldmneia r1!, {r3 - r4}
163 stmneia r0!, {r3 - r4} @ Shouldnt fault
166 strnet r3, [r0], #4 @ Shouldnt fault
169 .c2u_0nowords: teq ip, #0
171 .c2u_nowords: cmp ip, #2
173 USER( strbt r3, [r0], #1) @ May fault
175 USER( strgebt r3, [r0], #1) @ May fault
177 USER( strgtbt r3, [r0], #1) @ May fault
183 .c2u_finished: mov r0, #0
184 LOADREGS(fd,sp!,{r2, r4 - r7, pc})
186 .c2u_src_not_aligned:
192 .c2u_1fupi: subs r2, r2, #4
197 orr r3, r3, r7, push #24
198 USER( strt r3, [r0], #4) @ May fault
199 mov ip, r0, lsl #32 - PAGE_SHIFT
201 movs ip, ip, lsr #32 - PAGE_SHIFT
209 .c2u_1cpy8lp: mov r3, r7, pull #8
211 orr r3, r3, r4, push #24
213 orr r4, r4, r5, push #24
215 orr r5, r5, r6, push #24
217 orr r6, r6, r7, push #24
218 stmia r0!, {r3 - r6} @ Shouldnt fault
221 .c2u_1rem8lp: tst ip, #8
222 movne r3, r7, pull #8
223 ldmneia r1!, {r4, r7}
224 orrne r3, r3, r4, push #24
225 movne r4, r4, pull #8
226 orrne r4, r4, r7, push #24
227 stmneia r0!, {r3 - r4} @ Shouldnt fault
229 movne r3, r7, pull #8
231 orrne r3, r3, r7, push #24
232 strnet r3, [r0], #4 @ Shouldnt fault
235 .c2u_1nowords: mov r3, r7, lsr #byte(1)
239 USER( strbt r3, [r0], #1) @ May fault
240 movge r3, r7, lsr #byte(2)
241 USER( strgebt r3, [r0], #1) @ May fault
242 movgt r3, r7, lsr #byte(3)
243 USER( strgtbt r3, [r0], #1) @ May fault
246 .c2u_2fupi: subs r2, r2, #4
251 orr r3, r3, r7, push #16
252 USER( strt r3, [r0], #4) @ May fault
253 mov ip, r0, lsl #32 - PAGE_SHIFT
255 movs ip, ip, lsr #32 - PAGE_SHIFT
263 .c2u_2cpy8lp: mov r3, r7, pull #16
265 orr r3, r3, r4, push #16
267 orr r4, r4, r5, push #16
269 orr r5, r5, r6, push #16
271 orr r6, r6, r7, push #16
272 stmia r0!, {r3 - r6} @ Shouldnt fault
275 .c2u_2rem8lp: tst ip, #8
276 movne r3, r7, pull #16
277 ldmneia r1!, {r4, r7}
278 orrne r3, r3, r4, push #16
279 movne r4, r4, pull #16
280 orrne r4, r4, r7, push #16
281 stmneia r0!, {r3 - r4} @ Shouldnt fault
283 movne r3, r7, pull #16
285 orrne r3, r3, r7, push #16
286 strnet r3, [r0], #4 @ Shouldnt fault
289 .c2u_2nowords: mov r3, r7, lsr #byte(2)
293 USER( strbt r3, [r0], #1) @ May fault
294 movge r3, r7, lsr #byte(3)
295 USER( strgebt r3, [r0], #1) @ May fault
297 USER( strgtbt r3, [r0], #1) @ May fault
300 .c2u_3fupi: subs r2, r2, #4
305 orr r3, r3, r7, push #8
306 USER( strt r3, [r0], #4) @ May fault
307 mov ip, r0, lsl #32 - PAGE_SHIFT
309 movs ip, ip, lsr #32 - PAGE_SHIFT
317 .c2u_3cpy8lp: mov r3, r7, pull #24
319 orr r3, r3, r4, push #8
321 orr r4, r4, r5, push #8
323 orr r5, r5, r6, push #8
325 orr r6, r6, r7, push #8
326 stmia r0!, {r3 - r6} @ Shouldnt fault
329 .c2u_3rem8lp: tst ip, #8
330 movne r3, r7, pull #24
331 ldmneia r1!, {r4, r7}
332 orrne r3, r3, r4, push #8
333 movne r4, r4, pull #24
334 orrne r4, r4, r7, push #8
335 stmneia r0!, {r3 - r4} @ Shouldnt fault
337 movne r3, r7, pull #24
339 orrne r3, r3, r7, push #8
340 strnet r3, [r0], #4 @ Shouldnt fault
343 .c2u_3nowords: mov r3, r7, lsr #byte(3)
347 USER( strbt r3, [r0], #1) @ May fault
349 USER( strgebt r3, [r0], #1) @ May fault
351 USER( strgtbt r3, [r0], #1) @ May fault
356 9001: LOADREGS(fd,sp!, {r0, r4 - r7, pc})
359 /* Prototype: unsigned long uaccess_user_copy_from_user(void *to,const void *from,unsigned long n);
360 * Purpose : copy a block from user memory to kernel memory
361 * Params : to - kernel memory
362 * : from - user memory
363 * : n - number of bytes to copy
364 * Returns : Number of bytes NOT copied.
366 .cfu_dest_not_aligned:
369 USER( ldrbt r3, [r1], #1) @ May fault
371 USER( ldrgebt r3, [r1], #1) @ May fault
373 USER( ldrgtbt r3, [r1], #1) @ May fault
378 ENTRY(uaccess_user_copy_from_user)
379 stmfd sp!, {r0, r2, r4 - r7, lr}
383 bne .cfu_dest_not_aligned
386 bne .cfu_src_not_aligned
388 * Seeing as there has to be at least 8 bytes to copy, we can
389 * copy one word, and force a user-mode page fault...
392 .cfu_0fupi: subs r2, r2, #4
395 USER( ldrt r3, [r1], #4)
397 mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
399 movs ip, ip, lsr #32 - PAGE_SHIFT
402 * ip = max no. of bytes to copy before needing another "strt" insn
410 .cfu_0cpy8lp: ldmia r1!, {r3 - r6} @ Shouldnt fault
412 ldmia r1!, {r3 - r6} @ Shouldnt fault
416 .cfu_0rem8lp: cmn ip, #16
417 ldmgeia r1!, {r3 - r6} @ Shouldnt fault
418 stmgeia r0!, {r3 - r6}
420 ldmneia r1!, {r3 - r4} @ Shouldnt fault
421 stmneia r0!, {r3 - r4}
423 ldrnet r3, [r1], #4 @ Shouldnt fault
427 .cfu_0nowords: teq ip, #0
429 .cfu_nowords: cmp ip, #2
430 USER( ldrbt r3, [r1], #1) @ May fault
432 USER( ldrgebt r3, [r1], #1) @ May fault
434 USER( ldrgtbt r3, [r1], #1) @ May fault
441 .cfu_finished: mov r0, #0
443 LOADREGS(fd,sp!,{r4 - r7, pc})
445 .cfu_src_not_aligned:
447 USER( ldrt r7, [r1], #4) @ May fault
451 .cfu_1fupi: subs r2, r2, #4
455 USER( ldrt r7, [r1], #4) @ May fault
456 orr r3, r3, r7, push #24
458 mov ip, r1, lsl #32 - PAGE_SHIFT
460 movs ip, ip, lsr #32 - PAGE_SHIFT
468 .cfu_1cpy8lp: mov r3, r7, pull #8
469 ldmia r1!, {r4 - r7} @ Shouldnt fault
470 orr r3, r3, r4, push #24
472 orr r4, r4, r5, push #24
474 orr r5, r5, r6, push #24
476 orr r6, r6, r7, push #24
480 .cfu_1rem8lp: tst ip, #8
481 movne r3, r7, pull #8
482 ldmneia r1!, {r4, r7} @ Shouldnt fault
483 orrne r3, r3, r4, push #24
484 movne r4, r4, pull #8
485 orrne r4, r4, r7, push #24
486 stmneia r0!, {r3 - r4}
488 movne r3, r7, pull #8
489 USER( ldrnet r7, [r1], #4) @ May fault
490 orrne r3, r3, r7, push #24
494 .cfu_1nowords: mov r3, r7, lsr #byte(1)
499 movge r3, r7, lsr #byte(2)
501 movgt r3, r7, lsr #byte(3)
505 .cfu_2fupi: subs r2, r2, #4
509 USER( ldrt r7, [r1], #4) @ May fault
510 orr r3, r3, r7, push #16
512 mov ip, r1, lsl #32 - PAGE_SHIFT
514 movs ip, ip, lsr #32 - PAGE_SHIFT
522 .cfu_2cpy8lp: mov r3, r7, pull #16
523 ldmia r1!, {r4 - r7} @ Shouldnt fault
524 orr r3, r3, r4, push #16
526 orr r4, r4, r5, push #16
528 orr r5, r5, r6, push #16
530 orr r6, r6, r7, push #16
534 .cfu_2rem8lp: tst ip, #8
535 movne r3, r7, pull #16
536 ldmneia r1!, {r4, r7} @ Shouldnt fault
537 orrne r3, r3, r4, push #16
538 movne r4, r4, pull #16
539 orrne r4, r4, r7, push #16
540 stmneia r0!, {r3 - r4}
542 movne r3, r7, pull #16
543 USER( ldrnet r7, [r1], #4) @ May fault
544 orrne r3, r3, r7, push #16
548 .cfu_2nowords: mov r3, r7, lsr #byte(2)
553 movge r3, r7, lsr #byte(3)
555 USER( ldrgtbt r3, [r1], #0) @ May fault
559 .cfu_3fupi: subs r2, r2, #4
563 USER( ldrt r7, [r1], #4) @ May fault
564 orr r3, r3, r7, push #8
566 mov ip, r1, lsl #32 - PAGE_SHIFT
568 movs ip, ip, lsr #32 - PAGE_SHIFT
576 .cfu_3cpy8lp: mov r3, r7, pull #24
577 ldmia r1!, {r4 - r7} @ Shouldnt fault
578 orr r3, r3, r4, push #8
580 orr r4, r4, r5, push #8
582 orr r5, r5, r6, push #8
584 orr r6, r6, r7, push #8
588 .cfu_3rem8lp: tst ip, #8
589 movne r3, r7, pull #24
590 ldmneia r1!, {r4, r7} @ Shouldnt fault
591 orrne r3, r3, r4, push #8
592 movne r4, r4, pull #24
593 orrne r4, r4, r7, push #8
594 stmneia r0!, {r3 - r4}
596 movne r3, r7, pull #24
597 USER( ldrnet r7, [r1], #4) @ May fault
598 orrne r3, r3, r7, push #8
602 .cfu_3nowords: mov r3, r7, lsr #byte(3)
607 USER( ldrgebt r3, [r1], #1) @ May fault
609 USER( ldrgtbt r3, [r1], #1) @ May fault
616 * We took an exception. r0 contains a pointer to
617 * the byte not copied.
619 9001: ldr r2, [sp], #4 @ void *to
620 sub r2, r0, r2 @ bytes copied
621 ldr r1, [sp], #4 @ unsigned long count
622 subs r4, r1, r2 @ bytes left to copy
626 LOADREGS(fd,sp!, {r4 - r7, pc})
629 /* Prototype: int uaccess_user_clear_user(void *addr, size_t sz)
630 * Purpose : clear some user memory
631 * Params : addr - user memory address to clear
632 * : sz - number of bytes to clear
633 * Returns : number of bytes NOT cleared
635 ENTRY(uaccess_user_clear_user)
643 USER( strbt r2, [r0], #1)
644 USER( strlebt r2, [r0], #1)
645 USER( strltbt r2, [r0], #1)
647 sub r1, r1, ip @ 7 6 5 4 3 2 1
648 1: subs r1, r1, #8 @ -1 -2 -3 -4 -5 -6 -7
649 USER( strplt r2, [r0], #4)
650 USER( strplt r2, [r0], #4)
652 adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3
653 USER( strplt r2, [r0], #4)
654 2: tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x
655 USER( strnebt r2, [r0], #1)
656 USER( strnebt r2, [r0], #1)
657 tst r1, #1 @ x1 x0 x1 x0 x1 x0 x1
658 USER( strnebt r2, [r0], #1)
660 LOADREGS(fd,sp!, {r1, pc})
664 9001: LOADREGS(fd,sp!, {r0, pc})
668 * Copy a string from user space to kernel space.
669 * r0 = dst, r1 = src, r2 = byte length
670 * returns the number of characters copied (strlen of copied string),
671 * -EFAULT on exception, or "len" if we fill the whole buffer
673 ENTRY(uaccess_user_strncpy_from_user)
677 USER( ldrplbt r3, [r1], #1)
682 sub r1, r1, #1 @ take NUL character out of count
689 strb r3, [r0, #0] @ null terminate
694 /* Prototype: unsigned long uaccess_user_strnlen_user(const char *str, long n)
695 * Purpose : get length of a string in user memory
696 * Params : str - address of string in user memory
697 * Returns : length of string *including terminator*
698 * or zero on exception, or n + 1 if too long
700 ENTRY(uaccess_user_strnlen_user)
704 USER( ldrbt r3, [r0], #1)