1/*
2 *  linux/arch/arm26/lib/uaccess-user.S
3 *
4 *  Copyright (C) 1995, 1996,1997,1998 Russell King
5 *
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.
9 *
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.
13 */
14#include <linux/linkage.h>
15#include <asm/assembler.h>
16#include <asm/errno.h>
17#include <asm/page.h>
18
19		.text
20
21                .globl  uaccess_user
22uaccess_user:
23                .word   uaccess_user_put_byte
24                .word   uaccess_user_get_byte
25                .word   uaccess_user_put_half
26                .word   uaccess_user_get_half
27                .word   uaccess_user_put_word
28                .word   uaccess_user_get_word
29		.word   uaccess_user_put_dword
30                .word   uaccess_user_copy_from_user
31                .word   uaccess_user_copy_to_user
32                .word   uaccess_user_clear_user
33                .word   uaccess_user_strncpy_from_user
34                .word   uaccess_user_strnlen_user
35
36
37@ In : r0 = x, r1 = addr, r2 = error
38@ Out: r2 = error
39uaccess_user_put_byte:
40                stmfd   sp!, {lr}
41USER(           strbt   r0, [r1])
42                ldmfd   sp!, {pc}^
43
44@ In : r0 = x, r1 = addr, r2 = error
45@ Out: r2 = error
46uaccess_user_put_half:
47                stmfd   sp!, {lr}
48USER(           strbt   r0, [r1], #1)
49                mov     r0, r0, lsr #8
50USER(           strbt   r0, [r1])
51                ldmfd   sp!, {pc}^
52
53@ In : r0 = x, r1 = addr, r2 = error
54@ Out: r2 = error
55uaccess_user_put_word:
56                stmfd   sp!, {lr}
57USER(           strt    r0, [r1])
58                ldmfd   sp!, {pc}^
59
60@ In : r0 = x, r1 = addr, r2 = error
61@ Out: r2 = error
62uaccess_user_put_dword:
63                stmfd   sp!, {lr}
64USER(           strt    r0, [r1], #4)
65USER(           strt    r0, [r1], #0)
66                ldmfd   sp!, {pc}^
67
689001:           mov     r2, #-EFAULT
69                ldmfd   sp!, {pc}^
70
71
72@ In : r0 = addr, r1 = error
73@ Out: r0 = x, r1 = error
74uaccess_user_get_byte:
75                stmfd   sp!, {lr}
76USER(           ldrbt   r0, [r0])
77                ldmfd   sp!, {pc}^
78
79@ In : r0 = addr, r1 = error
80@ Out: r0 = x, r1 = error
81uaccess_user_get_half:
82                stmfd   sp!, {lr}
83USER(           ldrt    r0, [r0])
84                mov     r0, r0, lsl #16
85                mov     r0, r0, lsr #16
86                ldmfd   sp!, {pc}^
87
88@ In : r0 = addr, r1 = error
89@ Out: r0 = x, r1 = error
90uaccess_user_get_word:
91                stmfd   sp!, {lr}
92USER(           ldrt    r0, [r0])
93                ldmfd   sp!, {pc}^
94
959001:           mov     r1, #-EFAULT
96                ldmfd   sp!, {pc}^
97
98/* Prototype: int uaccess_user_copy_to_user(void *to, const char *from, size_t n)
99 * Purpose  : copy a block to user memory from kernel memory
100 * Params   : to   - user memory
101 *          : from - kernel memory
102 *          : n    - number of bytes to copy
103 * Returns  : Number of bytes NOT copied.
104 */
105
106.c2u_dest_not_aligned:
107		rsb	ip, ip, #4
108		cmp	ip, #2
109		ldrb	r3, [r1], #1
110USER(		strbt	r3, [r0], #1)			@ May fault
111		ldrgeb	r3, [r1], #1
112USER(		strgebt	r3, [r0], #1)			@ May fault
113		ldrgtb	r3, [r1], #1
114USER(		strgtbt	r3, [r0], #1)			@ May fault
115		sub	r2, r2, ip
116		b	.c2u_dest_aligned
117
118ENTRY(uaccess_user_copy_to_user)
119		stmfd	sp!, {r2, r4 - r7, lr}
120		cmp	r2, #4
121		blt	.c2u_not_enough
122		ands	ip, r0, #3
123		bne	.c2u_dest_not_aligned
124.c2u_dest_aligned:
125
126		ands	ip, r1, #3
127		bne	.c2u_src_not_aligned
128/*
129 * Seeing as there has to be at least 8 bytes to copy, we can
130 * copy one word, and force a user-mode page fault...
131 */
132
133.c2u_0fupi:	subs	r2, r2, #4
134		addmi	ip, r2, #4
135		bmi	.c2u_0nowords
136		ldr	r3, [r1], #4
137USER(		strt	r3, [r0], #4)			@ May fault
138		mov	ip, r0, lsl #32 - PAGE_SHIFT	@ On each page, use a ld/st??t instruction
139		rsb	ip, ip, #0
140		movs	ip, ip, lsr #32 - PAGE_SHIFT
141		beq	.c2u_0fupi
142/*
143 * ip = max no. of bytes to copy before needing another "strt" insn
144 */
145		cmp	r2, ip
146		movlt	ip, r2
147		sub	r2, r2, ip
148		subs	ip, ip, #32
149		blt	.c2u_0rem8lp
150
151.c2u_0cpy8lp:	ldmia	r1!, {r3 - r6}
152		stmia	r0!, {r3 - r6}			@ Shouldnt fault
153		ldmia	r1!, {r3 - r6}
154		stmia	r0!, {r3 - r6}			@ Shouldnt fault
155		subs	ip, ip, #32
156		bpl	.c2u_0cpy8lp
157.c2u_0rem8lp:	cmn	ip, #16
158		ldmgeia	r1!, {r3 - r6}
159		stmgeia	r0!, {r3 - r6}			@ Shouldnt fault
160		tst	ip, #8
161		ldmneia	r1!, {r3 - r4}
162		stmneia	r0!, {r3 - r4}			@ Shouldnt fault
163		tst	ip, #4
164		ldrne	r3, [r1], #4
165		strnet	r3, [r0], #4			@ Shouldnt fault
166		ands	ip, ip, #3
167		beq	.c2u_0fupi
168.c2u_0nowords:	teq	ip, #0
169		beq	.c2u_finished
170.c2u_nowords:	cmp	ip, #2
171		ldrb	r3, [r1], #1
172USER(		strbt	r3, [r0], #1)			@ May fault
173		ldrgeb	r3, [r1], #1
174USER(		strgebt	r3, [r0], #1)			@ May fault
175		ldrgtb	r3, [r1], #1
176USER(		strgtbt	r3, [r0], #1)			@ May fault
177		b	.c2u_finished
178
179.c2u_not_enough:
180		movs	ip, r2
181		bne	.c2u_nowords
182.c2u_finished:	mov	r0, #0
183		LOADREGS(fd,sp!,{r2, r4 - r7, pc})
184
185.c2u_src_not_aligned:
186		bic	r1, r1, #3
187		ldr	r7, [r1], #4
188		cmp	ip, #2
189		bgt	.c2u_3fupi
190		beq	.c2u_2fupi
191.c2u_1fupi:	subs	r2, r2, #4
192		addmi	ip, r2, #4
193		bmi	.c2u_1nowords
194		mov	r3, r7, pull #8
195		ldr	r7, [r1], #4
196		orr	r3, r3, r7, push #24
197USER(		strt	r3, [r0], #4)			@ May fault
198		mov	ip, r0, lsl #32 - PAGE_SHIFT
199		rsb	ip, ip, #0
200		movs	ip, ip, lsr #32 - PAGE_SHIFT
201		beq	.c2u_1fupi
202		cmp	r2, ip
203		movlt	ip, r2
204		sub	r2, r2, ip
205		subs	ip, ip, #16
206		blt	.c2u_1rem8lp
207
208.c2u_1cpy8lp:	mov	r3, r7, pull #8
209		ldmia	r1!, {r4 - r7}
210		orr	r3, r3, r4, push #24
211		mov	r4, r4, pull #8
212		orr	r4, r4, r5, push #24
213		mov	r5, r5, pull #8
214		orr	r5, r5, r6, push #24
215		mov	r6, r6, pull #8
216		orr	r6, r6, r7, push #24
217		stmia	r0!, {r3 - r6}			@ Shouldnt fault
218		subs	ip, ip, #16
219		bpl	.c2u_1cpy8lp
220.c2u_1rem8lp:	tst	ip, #8
221		movne	r3, r7, pull #8
222		ldmneia	r1!, {r4, r7}
223		orrne	r3, r3, r4, push #24
224		movne	r4, r4, pull #8
225		orrne	r4, r4, r7, push #24
226		stmneia	r0!, {r3 - r4}			@ Shouldnt fault
227		tst	ip, #4
228		movne	r3, r7, pull #8
229		ldrne	r7, [r1], #4
230		orrne	r3, r3, r7, push #24
231		strnet	r3, [r0], #4			@ Shouldnt fault
232		ands	ip, ip, #3
233		beq	.c2u_1fupi
234.c2u_1nowords:	mov	r3, r7, lsr #byte(1)
235		teq	ip, #0
236		beq	.c2u_finished
237		cmp	ip, #2
238USER(		strbt	r3, [r0], #1)			@ May fault
239		movge	r3, r7, lsr #byte(2)
240USER(		strgebt	r3, [r0], #1)			@ May fault
241		movgt	r3, r7, lsr #byte(3)
242USER(		strgtbt	r3, [r0], #1)			@ May fault
243		b	.c2u_finished
244
245.c2u_2fupi:	subs	r2, r2, #4
246		addmi	ip, r2, #4
247		bmi	.c2u_2nowords
248		mov	r3, r7, pull #16
249		ldr	r7, [r1], #4
250		orr	r3, r3, r7, push #16
251USER(		strt	r3, [r0], #4)			@ May fault
252		mov	ip, r0, lsl #32 - PAGE_SHIFT
253		rsb	ip, ip, #0
254		movs	ip, ip, lsr #32 - PAGE_SHIFT
255		beq	.c2u_2fupi
256		cmp	r2, ip
257		movlt	ip, r2
258		sub	r2, r2, ip
259		subs	ip, ip, #16
260		blt	.c2u_2rem8lp
261
262.c2u_2cpy8lp:	mov	r3, r7, pull #16
263		ldmia	r1!, {r4 - r7}
264		orr	r3, r3, r4, push #16
265		mov	r4, r4, pull #16
266		orr	r4, r4, r5, push #16
267		mov	r5, r5, pull #16
268		orr	r5, r5, r6, push #16
269		mov	r6, r6, pull #16
270		orr	r6, r6, r7, push #16
271		stmia	r0!, {r3 - r6}			@ Shouldnt fault
272		subs	ip, ip, #16
273		bpl	.c2u_2cpy8lp
274.c2u_2rem8lp:	tst	ip, #8
275		movne	r3, r7, pull #16
276		ldmneia	r1!, {r4, r7}
277		orrne	r3, r3, r4, push #16
278		movne	r4, r4, pull #16
279		orrne	r4, r4, r7, push #16
280		stmneia	r0!, {r3 - r4}			@ Shouldnt fault
281		tst	ip, #4
282		movne	r3, r7, pull #16
283		ldrne	r7, [r1], #4
284		orrne	r3, r3, r7, push #16
285		strnet	r3, [r0], #4			@ Shouldnt fault
286		ands	ip, ip, #3
287		beq	.c2u_2fupi
288.c2u_2nowords:	mov	r3, r7, lsr #byte(2)
289		teq	ip, #0
290		beq	.c2u_finished
291		cmp	ip, #2
292USER(		strbt	r3, [r0], #1)			@ May fault
293		movge	r3, r7, lsr #byte(3)
294USER(		strgebt	r3, [r0], #1)			@ May fault
295		ldrgtb	r3, [r1], #0
296USER(		strgtbt	r3, [r0], #1)			@ May fault
297		b	.c2u_finished
298
299.c2u_3fupi:	subs	r2, r2, #4
300		addmi	ip, r2, #4
301		bmi	.c2u_3nowords
302		mov	r3, r7, pull #24
303		ldr	r7, [r1], #4
304		orr	r3, r3, r7, push #8
305USER(		strt	r3, [r0], #4)			@ May fault
306		mov	ip, r0, lsl #32 - PAGE_SHIFT
307		rsb	ip, ip, #0
308		movs	ip, ip, lsr #32 - PAGE_SHIFT
309		beq	.c2u_3fupi
310		cmp	r2, ip
311		movlt	ip, r2
312		sub	r2, r2, ip
313		subs	ip, ip, #16
314		blt	.c2u_3rem8lp
315
316.c2u_3cpy8lp:	mov	r3, r7, pull #24
317		ldmia	r1!, {r4 - r7}
318		orr	r3, r3, r4, push #8
319		mov	r4, r4, pull #24
320		orr	r4, r4, r5, push #8
321		mov	r5, r5, pull #24
322		orr	r5, r5, r6, push #8
323		mov	r6, r6, pull #24
324		orr	r6, r6, r7, push #8
325		stmia	r0!, {r3 - r6}			@ Shouldnt fault
326		subs	ip, ip, #16
327		bpl	.c2u_3cpy8lp
328.c2u_3rem8lp:	tst	ip, #8
329		movne	r3, r7, pull #24
330		ldmneia	r1!, {r4, r7}
331		orrne	r3, r3, r4, push #8
332		movne	r4, r4, pull #24
333		orrne	r4, r4, r7, push #8
334		stmneia	r0!, {r3 - r4}			@ Shouldnt fault
335		tst	ip, #4
336		movne	r3, r7, pull #24
337		ldrne	r7, [r1], #4
338		orrne	r3, r3, r7, push #8
339		strnet	r3, [r0], #4			@ Shouldnt fault
340		ands	ip, ip, #3
341		beq	.c2u_3fupi
342.c2u_3nowords:	mov	r3, r7, lsr #byte(3)
343		teq	ip, #0
344		beq	.c2u_finished
345		cmp	ip, #2
346USER(		strbt	r3, [r0], #1)			@ May fault
347		ldrgeb	r3, [r1], #1
348USER(		strgebt	r3, [r0], #1)			@ May fault
349		ldrgtb	r3, [r1], #0
350USER(		strgtbt	r3, [r0], #1)			@ May fault
351		b	.c2u_finished
352
353		.section .fixup,"ax"
354		.align	0
3559001:		LOADREGS(fd,sp!, {r0, r4 - r7, pc})
356		.previous
357
358/* Prototype: unsigned long uaccess_user_copy_from_user(void *to,const void *from,unsigned long n);
359 * Purpose  : copy a block from user memory to kernel memory
360 * Params   : to   - kernel memory
361 *          : from - user memory
362 *          : n    - number of bytes to copy
363 * Returns  : Number of bytes NOT copied.
364 */
365.cfu_dest_not_aligned:
366		rsb	ip, ip, #4
367		cmp	ip, #2
368USER(		ldrbt	r3, [r1], #1)			@ May fault
369		strb	r3, [r0], #1
370USER(		ldrgebt	r3, [r1], #1)			@ May fault
371		strgeb	r3, [r0], #1
372USER(		ldrgtbt	r3, [r1], #1)			@ May fault
373		strgtb	r3, [r0], #1
374		sub	r2, r2, ip
375		b	.cfu_dest_aligned
376
377ENTRY(uaccess_user_copy_from_user)
378		stmfd	sp!, {r0, r2, r4 - r7, lr}
379		cmp	r2, #4
380		blt	.cfu_not_enough
381		ands	ip, r0, #3
382		bne	.cfu_dest_not_aligned
383.cfu_dest_aligned:
384		ands	ip, r1, #3
385		bne	.cfu_src_not_aligned
386/*
387 * Seeing as there has to be at least 8 bytes to copy, we can
388 * copy one word, and force a user-mode page fault...
389 */
390
391.cfu_0fupi:	subs	r2, r2, #4
392		addmi	ip, r2, #4
393		bmi	.cfu_0nowords
394USER(		ldrt	r3, [r1], #4)
395		str	r3, [r0], #4
396		mov	ip, r1, lsl #32 - PAGE_SHIFT	@ On each page, use a ld/st??t instruction
397		rsb	ip, ip, #0
398		movs	ip, ip, lsr #32 - PAGE_SHIFT
399		beq	.cfu_0fupi
400/*
401 * ip = max no. of bytes to copy before needing another "strt" insn
402 */
403		cmp	r2, ip
404		movlt	ip, r2
405		sub	r2, r2, ip
406		subs	ip, ip, #32
407		blt	.cfu_0rem8lp
408
409.cfu_0cpy8lp:	ldmia	r1!, {r3 - r6}			@ Shouldnt fault
410		stmia	r0!, {r3 - r6}
411		ldmia	r1!, {r3 - r6}			@ Shouldnt fault
412		stmia	r0!, {r3 - r6}
413		subs	ip, ip, #32
414		bpl	.cfu_0cpy8lp
415.cfu_0rem8lp:	cmn	ip, #16
416		ldmgeia	r1!, {r3 - r6}			@ Shouldnt fault
417		stmgeia	r0!, {r3 - r6}
418		tst	ip, #8
419		ldmneia	r1!, {r3 - r4}			@ Shouldnt fault
420		stmneia	r0!, {r3 - r4}
421		tst	ip, #4
422		ldrnet	r3, [r1], #4			@ Shouldnt fault
423		strne	r3, [r0], #4
424		ands	ip, ip, #3
425		beq	.cfu_0fupi
426.cfu_0nowords:	teq	ip, #0
427		beq	.cfu_finished
428.cfu_nowords:	cmp	ip, #2
429USER(		ldrbt	r3, [r1], #1)			@ May fault
430		strb	r3, [r0], #1
431USER(		ldrgebt	r3, [r1], #1)			@ May fault
432		strgeb	r3, [r0], #1
433USER(		ldrgtbt	r3, [r1], #1)			@ May fault
434		strgtb	r3, [r0], #1
435		b	.cfu_finished
436
437.cfu_not_enough:
438		movs	ip, r2
439		bne	.cfu_nowords
440.cfu_finished:	mov	r0, #0
441		add	sp, sp, #8
442		LOADREGS(fd,sp!,{r4 - r7, pc})
443
444.cfu_src_not_aligned:
445		bic	r1, r1, #3
446USER(		ldrt	r7, [r1], #4)			@ May fault
447		cmp	ip, #2
448		bgt	.cfu_3fupi
449		beq	.cfu_2fupi
450.cfu_1fupi:	subs	r2, r2, #4
451		addmi	ip, r2, #4
452		bmi	.cfu_1nowords
453		mov	r3, r7, pull #8
454USER(		ldrt	r7, [r1], #4)			@ May fault
455		orr	r3, r3, r7, push #24
456		str	r3, [r0], #4
457		mov	ip, r1, lsl #32 - PAGE_SHIFT
458		rsb	ip, ip, #0
459		movs	ip, ip, lsr #32 - PAGE_SHIFT
460		beq	.cfu_1fupi
461		cmp	r2, ip
462		movlt	ip, r2
463		sub	r2, r2, ip
464		subs	ip, ip, #16
465		blt	.cfu_1rem8lp
466
467.cfu_1cpy8lp:	mov	r3, r7, pull #8
468		ldmia	r1!, {r4 - r7}			@ Shouldnt fault
469		orr	r3, r3, r4, push #24
470		mov	r4, r4, pull #8
471		orr	r4, r4, r5, push #24
472		mov	r5, r5, pull #8
473		orr	r5, r5, r6, push #24
474		mov	r6, r6, pull #8
475		orr	r6, r6, r7, push #24
476		stmia	r0!, {r3 - r6}
477		subs	ip, ip, #16
478		bpl	.cfu_1cpy8lp
479.cfu_1rem8lp:	tst	ip, #8
480		movne	r3, r7, pull #8
481		ldmneia	r1!, {r4, r7}			@ Shouldnt fault
482		orrne	r3, r3, r4, push #24
483		movne	r4, r4, pull #8
484		orrne	r4, r4, r7, push #24
485		stmneia	r0!, {r3 - r4}
486		tst	ip, #4
487		movne	r3, r7, pull #8
488USER(		ldrnet	r7, [r1], #4)			@ May fault
489		orrne	r3, r3, r7, push #24
490		strne	r3, [r0], #4
491		ands	ip, ip, #3
492		beq	.cfu_1fupi
493.cfu_1nowords:	mov	r3, r7, lsr #byte(1)
494		teq	ip, #0
495		beq	.cfu_finished
496		cmp	ip, #2
497		strb	r3, [r0], #1
498		movge	r3, r7, lsr #byte(2)
499		strgeb	r3, [r0], #1
500		movgt	r3, r7, lsr #byte(3)
501		strgtb	r3, [r0], #1
502		b	.cfu_finished
503
504.cfu_2fupi:	subs	r2, r2, #4
505		addmi	ip, r2, #4
506		bmi	.cfu_2nowords
507		mov	r3, r7, pull #16
508USER(		ldrt	r7, [r1], #4)			@ May fault
509		orr	r3, r3, r7, push #16
510		str	r3, [r0], #4
511		mov	ip, r1, lsl #32 - PAGE_SHIFT
512		rsb	ip, ip, #0
513		movs	ip, ip, lsr #32 - PAGE_SHIFT
514		beq	.cfu_2fupi
515		cmp	r2, ip
516		movlt	ip, r2
517		sub	r2, r2, ip
518		subs	ip, ip, #16
519		blt	.cfu_2rem8lp
520
521.cfu_2cpy8lp:	mov	r3, r7, pull #16
522		ldmia	r1!, {r4 - r7}			@ Shouldnt fault
523		orr	r3, r3, r4, push #16
524		mov	r4, r4, pull #16
525		orr	r4, r4, r5, push #16
526		mov	r5, r5, pull #16
527		orr	r5, r5, r6, push #16
528		mov	r6, r6, pull #16
529		orr	r6, r6, r7, push #16
530		stmia	r0!, {r3 - r6}
531		subs	ip, ip, #16
532		bpl	.cfu_2cpy8lp
533.cfu_2rem8lp:	tst	ip, #8
534		movne	r3, r7, pull #16
535		ldmneia	r1!, {r4, r7}			@ Shouldnt fault
536		orrne	r3, r3, r4, push #16
537		movne	r4, r4, pull #16
538		orrne	r4, r4, r7, push #16
539		stmneia	r0!, {r3 - r4}
540		tst	ip, #4
541		movne	r3, r7, pull #16
542USER(		ldrnet	r7, [r1], #4)			@ May fault
543		orrne	r3, r3, r7, push #16
544		strne	r3, [r0], #4
545		ands	ip, ip, #3
546		beq	.cfu_2fupi
547.cfu_2nowords:	mov	r3, r7, lsr #byte(2)
548		teq	ip, #0
549		beq	.cfu_finished
550		cmp	ip, #2
551		strb	r3, [r0], #1
552		movge	r3, r7, lsr #byte(3)
553		strgeb	r3, [r0], #1
554USER(		ldrgtbt	r3, [r1], #0)			@ May fault
555		strgtb	r3, [r0], #1
556		b	.cfu_finished
557
558.cfu_3fupi:	subs	r2, r2, #4
559		addmi	ip, r2, #4
560		bmi	.cfu_3nowords
561		mov	r3, r7, pull #24
562USER(		ldrt	r7, [r1], #4)			@ May fault
563		orr	r3, r3, r7, push #8
564		str	r3, [r0], #4
565		mov	ip, r1, lsl #32 - PAGE_SHIFT
566		rsb	ip, ip, #0
567		movs	ip, ip, lsr #32 - PAGE_SHIFT
568		beq	.cfu_3fupi
569		cmp	r2, ip
570		movlt	ip, r2
571		sub	r2, r2, ip
572		subs	ip, ip, #16
573		blt	.cfu_3rem8lp
574
575.cfu_3cpy8lp:	mov	r3, r7, pull #24
576		ldmia	r1!, {r4 - r7}			@ Shouldnt fault
577		orr	r3, r3, r4, push #8
578		mov	r4, r4, pull #24
579		orr	r4, r4, r5, push #8
580		mov	r5, r5, pull #24
581		orr	r5, r5, r6, push #8
582		mov	r6, r6, pull #24
583		orr	r6, r6, r7, push #8
584		stmia	r0!, {r3 - r6}
585		subs	ip, ip, #16
586		bpl	.cfu_3cpy8lp
587.cfu_3rem8lp:	tst	ip, #8
588		movne	r3, r7, pull #24
589		ldmneia	r1!, {r4, r7}			@ Shouldnt fault
590		orrne	r3, r3, r4, push #8
591		movne	r4, r4, pull #24
592		orrne	r4, r4, r7, push #8
593		stmneia	r0!, {r3 - r4}
594		tst	ip, #4
595		movne	r3, r7, pull #24
596USER(		ldrnet	r7, [r1], #4)			@ May fault
597		orrne	r3, r3, r7, push #8
598		strne	r3, [r0], #4
599		ands	ip, ip, #3
600		beq	.cfu_3fupi
601.cfu_3nowords:	mov	r3, r7, lsr #byte(3)
602		teq	ip, #0
603		beq	.cfu_finished
604		cmp	ip, #2
605		strb	r3, [r0], #1
606USER(		ldrgebt	r3, [r1], #1)			@ May fault
607		strgeb	r3, [r0], #1
608USER(		ldrgtbt	r3, [r1], #1)			@ May fault
609		strgtb	r3, [r0], #1
610		b	.cfu_finished
611
612		.section .fixup,"ax"
613		.align	0
614		/*
615		 * We took an exception.  r0 contains a pointer to
616		 * the byte not copied.
617		 */
6189001:		ldr	r2, [sp], #4			@ void *to
619		sub	r2, r0, r2			@ bytes copied
620		ldr	r1, [sp], #4			@ unsigned long count
621		subs	r4, r1, r2			@ bytes left to copy
622		movne	r1, r4
623		blne	__memzero
624		mov	r0, r4
625		LOADREGS(fd,sp!, {r4 - r7, pc})
626		.previous
627
628/* Prototype: int uaccess_user_clear_user(void *addr, size_t sz)
629 * Purpose  : clear some user memory
630 * Params   : addr - user memory address to clear
631 *          : sz   - number of bytes to clear
632 * Returns  : number of bytes NOT cleared
633 */
634ENTRY(uaccess_user_clear_user)
635		stmfd	sp!, {r1, lr}
636		mov	r2, #0
637		cmp	r1, #4
638		blt	2f
639		ands	ip, r0, #3
640		beq	1f
641		cmp	ip, #2
642USER(		strbt	r2, [r0], #1)
643USER(		strlebt	r2, [r0], #1)
644USER(		strltbt	r2, [r0], #1)
645		rsb	ip, ip, #4
646		sub	r1, r1, ip		@  7  6  5  4  3  2  1
6471:		subs	r1, r1, #8		@ -1 -2 -3 -4 -5 -6 -7
648USER(		strplt	r2, [r0], #4)
649USER(		strplt	r2, [r0], #4)
650		bpl	1b
651		adds	r1, r1, #4		@  3  2  1  0 -1 -2 -3
652USER(		strplt	r2, [r0], #4)
6532:		tst	r1, #2			@ 1x 1x 0x 0x 1x 1x 0x
654USER(		strnebt	r2, [r0], #1)
655USER(		strnebt	r2, [r0], #1)
656		tst	r1, #1			@ x1 x0 x1 x0 x1 x0 x1
657USER(		strnebt	r2, [r0], #1)
658		mov	r0, #0
659		LOADREGS(fd,sp!, {r1, pc})
660
661		.section .fixup,"ax"
662		.align	0
6639001:		LOADREGS(fd,sp!, {r0, pc})
664		.previous
665
666/*
667 * Copy a string from user space to kernel space.
668 *  r0 = dst, r1 = src, r2 = byte length
669 * returns the number of characters copied (strlen of copied string),
670 *  -EFAULT on exception, or "len" if we fill the whole buffer
671 */
672ENTRY(uaccess_user_strncpy_from_user)
673        save_lr
674        mov     ip, r1
6751:      subs    r2, r2, #1
676USER(   ldrplbt r3, [r1], #1)
677        bmi     2f
678        strb    r3, [r0], #1
679        teq     r3, #0
680        bne     1b
681        sub     r1, r1, #1      @ take NUL character out of count
6822:      sub     r0, r1, ip
683        restore_pc
684
685        .section .fixup,"ax"
686        .align  0
6879001:   mov     r3, #0
688        strb    r3, [r0, #0]    @ null terminate
689        mov     r0, #-EFAULT
690        restore_pc
691        .previous
692
693/* Prototype: unsigned long uaccess_user_strnlen_user(const char *str, long n)
694 * Purpose  : get length of a string in user memory
695 * Params   : str - address of string in user memory
696 * Returns  : length of string *including terminator*
697 *            or zero on exception, or n + 1 if too long
698 */
699ENTRY(uaccess_user_strnlen_user)
700        save_lr
701        mov     r2, r0
7021:
703USER(   ldrbt   r3, [r0], #1)
704        teq     r3, #0
705        beq     2f
706        subs    r1, r1, #1
707        bne     1b
708        add     r0, r0, #1
7092:      sub     r0, r0, r2
710        restore_pc
711
712        .section .fixup,"ax"
713        .align  0
7149001:   mov     r0, #0
715        restore_pc
716        .previous
717