1129198Scognet/*	$NetBSD: bcopyinout.S,v 1.11 2003/10/13 21:22:40 scw Exp $	*/
2129198Scognet
3139735Simp/*-
4129198Scognet * Copyright (c) 2002 Wasabi Systems, Inc.
5129198Scognet * All rights reserved.
6129198Scognet *
7129198Scognet * Written by Allen Briggs for Wasabi Systems, Inc.
8129198Scognet *
9129198Scognet * Redistribution and use in source and binary forms, with or without
10129198Scognet * modification, are permitted provided that the following conditions
11129198Scognet * are met:
12129198Scognet * 1. Redistributions of source code must retain the above copyright
13129198Scognet *    notice, this list of conditions and the following disclaimer.
14129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
15129198Scognet *    notice, this list of conditions and the following disclaimer in the
16129198Scognet *    documentation and/or other materials provided with the distribution.
17129198Scognet * 3. All advertising materials mentioning features or use of this software
18129198Scognet *    must display the following acknowledgement:
19129198Scognet *      This product includes software developed for the NetBSD Project by
20129198Scognet *      Wasabi Systems, Inc.
21129198Scognet * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22129198Scognet *    or promote products derived from this software without specific prior
23129198Scognet *    written permission.
24129198Scognet *
25129198Scognet * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27129198Scognet * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28129198Scognet * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29129198Scognet * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30129198Scognet * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31129198Scognet * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32129198Scognet * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33129198Scognet * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34129198Scognet * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35129198Scognet * POSSIBILITY OF SUCH DAMAGE.
36129198Scognet */
37129198Scognet
38129198Scognet
39129198Scognet#include "assym.s"
40129198Scognet
41129198Scognet#include <machine/asm.h>
42239033Sandrew#include <sys/errno.h>
43129198Scognet
44150864Scognet.L_arm_memcpy:
45150864Scognet	.word	_C_LABEL(_arm_memcpy)
46150864Scognet.L_min_memcpy_size:
47150864Scognet	.word	_C_LABEL(_min_memcpy_size)
48150864Scognet
49129198Scognet__FBSDID("$FreeBSD$");
50172614Scognet#ifdef _ARM_ARCH_5E
51135643Scognet#include <arm/arm/bcopyinout_xscale.S>
52129198Scognet#else
53129198Scognet
54129198Scognet	.text
55129198Scognet	.align	0
56129198Scognet
57239268Sgonzo#ifdef _ARM_ARCH_6
58239268Sgonzo#define GET_PCB(tmp) \
59239268Sgonzo	mrc p15, 0, tmp, c13, c0, 4; \
60266159Sian	add	tmp, tmp, #(TD_PCB)
61129198Scognet#else
62129198Scognet.Lcurpcb:
63239268Sgonzo	.word	_C_LABEL(__pcpu) + PC_CURPCB
64239268Sgonzo
65239268Sgonzo#define GET_PCB(tmp) \
66239268Sgonzo	ldr	tmp, .Lcurpcb
67129198Scognet#endif
68129198Scognet
69239268Sgonzo
70129198Scognet#define SAVE_REGS	stmfd	sp!, {r4-r11}
71129198Scognet#define RESTORE_REGS	ldmfd	sp!, {r4-r11}
72129198Scognet
73172614Scognet#if defined(_ARM_ARCH_5E)
74129198Scognet#define HELLOCPP #
75129198Scognet#define PREFETCH(rx,o)	pld	[ rx , HELLOCPP (o) ]
76129198Scognet#else
77129198Scognet#define PREFETCH(rx,o)
78129198Scognet#endif
79129198Scognet
80129198Scognet/*
81129198Scognet * r0 = user space address
82129198Scognet * r1 = kernel space address
83129198Scognet * r2 = length
84129198Scognet *
85129198Scognet * Copies bytes from user space to kernel space
86129198Scognet *
87129198Scognet * We save/restore r4-r11:
88129198Scognet * r4-r11 are scratch
89129198Scognet */
90129198ScognetENTRY(copyin)
91129198Scognet	/* Quick exit if length is zero */
92129198Scognet	teq	r2, #0
93129198Scognet	moveq	r0, #0
94138665Scognet	RETeq
95129198Scognet
96150864Scognet	ldr	r3, .L_arm_memcpy
97150864Scognet	ldr	r3, [r3]
98150864Scognet	cmp	r3, #0
99150864Scognet	beq	.Lnormal
100150864Scognet	ldr	r3, .L_min_memcpy_size
101150864Scognet	ldr	r3, [r3]
102150864Scognet	cmp	r2, r3
103150864Scognet	blt	.Lnormal
104150864Scognet	stmfd	sp!, {r0-r2, r4, lr}
105150864Scognet	mov     r3, r0
106150864Scognet	mov     r0, r1
107150864Scognet	mov     r1, r3
108150864Scognet	mov     r3, #2 /* SRC_IS_USER */
109150864Scognet	ldr	r4, .L_arm_memcpy
110150864Scognet	mov	lr, pc
111150864Scognet	ldr	pc, [r4]
112150864Scognet	cmp     r0, #0
113150864Scognet	ldmfd   sp!, {r0-r2, r4, lr}
114150864Scognet	moveq	r0, #0
115150864Scognet	RETeq
116150864Scognet
117151596Scognet.Lnormal:
118129198Scognet	SAVE_REGS
119239268Sgonzo	GET_PCB(r4)
120129198Scognet	ldr	r4, [r4]
121129198Scognet
122239268Sgonzo
123129198Scognet	ldr	r5, [r4, #PCB_ONFAULT]
124129198Scognet	adr	r3, .Lcopyfault
125129198Scognet	str	r3, [r4, #PCB_ONFAULT]
126129198Scognet
127129198Scognet	PREFETCH(r0, 0)
128129198Scognet	PREFETCH(r1, 0)
129129198Scognet
130129198Scognet	/*
131129198Scognet	 * If not too many bytes, take the slow path.
132129198Scognet	 */
133129198Scognet	cmp	r2, #0x08
134129198Scognet	blt	.Licleanup
135129198Scognet
136129198Scognet	/*
137129198Scognet	 * Align destination to word boundary.
138129198Scognet	 */
139129198Scognet	and	r6, r1, #0x3
140129198Scognet	ldr	pc, [pc, r6, lsl #2]
141129198Scognet	b	.Lialend
142129198Scognet	.word	.Lialend
143129198Scognet	.word	.Lial3
144129198Scognet	.word	.Lial2
145129198Scognet	.word	.Lial1
146129198Scognet.Lial3:	ldrbt	r6, [r0], #1
147129198Scognet	sub	r2, r2, #1
148129198Scognet	strb	r6, [r1], #1
149129198Scognet.Lial2:	ldrbt	r7, [r0], #1
150129198Scognet	sub	r2, r2, #1
151129198Scognet	strb	r7, [r1], #1
152129198Scognet.Lial1:	ldrbt	r6, [r0], #1
153129198Scognet	sub	r2, r2, #1
154129198Scognet	strb	r6, [r1], #1
155129198Scognet.Lialend:
156129198Scognet
157129198Scognet	/*
158129198Scognet	 * If few bytes left, finish slow.
159129198Scognet	 */
160129198Scognet	cmp	r2, #0x08
161129198Scognet	blt	.Licleanup
162129198Scognet
163129198Scognet	/*
164129198Scognet	 * If source is not aligned, finish slow.
165129198Scognet	 */
166129198Scognet	ands	r3, r0, #0x03
167129198Scognet	bne	.Licleanup
168129198Scognet
169129198Scognet	cmp	r2, #0x60	/* Must be > 0x5f for unrolled cacheline */
170129198Scognet	blt	.Licleanup8
171129198Scognet
172129198Scognet	/*
173129198Scognet	 * Align destination to cacheline boundary.
174129198Scognet	 * If source and destination are nicely aligned, this can be a big
175129198Scognet	 * win.  If not, it's still cheaper to copy in groups of 32 even if
176129198Scognet	 * we don't get the nice cacheline alignment.
177129198Scognet	 */
178129198Scognet	and	r6, r1, #0x1f
179129198Scognet	ldr	pc, [pc, r6]
180129198Scognet	b	.Licaligned
181129198Scognet	.word	.Licaligned
182129198Scognet	.word	.Lical28
183129198Scognet	.word	.Lical24
184129198Scognet	.word	.Lical20
185129198Scognet	.word	.Lical16
186129198Scognet	.word	.Lical12
187129198Scognet	.word	.Lical8
188129198Scognet	.word	.Lical4
189129198Scognet.Lical28:ldrt	r6, [r0], #4
190129198Scognet	sub	r2, r2, #4
191129198Scognet	str	r6, [r1], #4
192129198Scognet.Lical24:ldrt	r7, [r0], #4
193129198Scognet	sub	r2, r2, #4
194129198Scognet	str	r7, [r1], #4
195129198Scognet.Lical20:ldrt	r6, [r0], #4
196129198Scognet	sub	r2, r2, #4
197129198Scognet	str	r6, [r1], #4
198129198Scognet.Lical16:ldrt	r7, [r0], #4
199129198Scognet	sub	r2, r2, #4
200129198Scognet	str	r7, [r1], #4
201129198Scognet.Lical12:ldrt	r6, [r0], #4
202129198Scognet	sub	r2, r2, #4
203129198Scognet	str	r6, [r1], #4
204129198Scognet.Lical8:ldrt	r7, [r0], #4
205129198Scognet	sub	r2, r2, #4
206129198Scognet	str	r7, [r1], #4
207129198Scognet.Lical4:ldrt	r6, [r0], #4
208129198Scognet	sub	r2, r2, #4
209129198Scognet	str	r6, [r1], #4
210129198Scognet
211129198Scognet	/*
212129198Scognet	 * We start with > 0x40 bytes to copy (>= 0x60 got us into this
213129198Scognet	 * part of the code, and we may have knocked that down by as much
214129198Scognet	 * as 0x1c getting aligned).
215129198Scognet	 *
216129198Scognet	 * This loop basically works out to:
217129198Scognet	 * do {
218129198Scognet	 * 	prefetch-next-cacheline(s)
219129198Scognet	 *	bytes -= 0x20;
220129198Scognet	 *	copy cacheline
221129198Scognet	 * } while (bytes >= 0x40);
222129198Scognet	 * bytes -= 0x20;
223129198Scognet	 * copy cacheline
224129198Scognet	 */
225129198Scognet.Licaligned:
226129198Scognet	PREFETCH(r0, 32)
227129198Scognet	PREFETCH(r1, 32)
228129198Scognet
229129198Scognet	sub	r2, r2, #0x20
230129198Scognet
231129198Scognet	/* Copy a cacheline */
232129198Scognet	ldrt	r10, [r0], #4
233129198Scognet	ldrt	r11, [r0], #4
234129198Scognet	ldrt	r6, [r0], #4
235129198Scognet	ldrt	r7, [r0], #4
236129198Scognet	ldrt	r8, [r0], #4
237129198Scognet	ldrt	r9, [r0], #4
238129198Scognet	stmia	r1!, {r10-r11}
239129198Scognet	ldrt	r10, [r0], #4
240129198Scognet	ldrt	r11, [r0], #4
241129198Scognet	stmia	r1!, {r6-r11}
242129198Scognet
243129198Scognet	cmp	r2, #0x40
244129198Scognet	bge	.Licaligned
245129198Scognet
246129198Scognet	sub	r2, r2, #0x20
247129198Scognet
248129198Scognet	/* Copy a cacheline */
249129198Scognet	ldrt	r10, [r0], #4
250129198Scognet	ldrt	r11, [r0], #4
251129198Scognet	ldrt	r6, [r0], #4
252129198Scognet	ldrt	r7, [r0], #4
253129198Scognet	ldrt	r8, [r0], #4
254129198Scognet	ldrt	r9, [r0], #4
255129198Scognet	stmia	r1!, {r10-r11}
256129198Scognet	ldrt	r10, [r0], #4
257129198Scognet	ldrt	r11, [r0], #4
258129198Scognet	stmia	r1!, {r6-r11}
259129198Scognet
260129198Scognet	cmp	r2, #0x08
261129198Scognet	blt	.Liprecleanup
262129198Scognet
263129198Scognet.Licleanup8:
264129198Scognet	ldrt	r8, [r0], #4
265129198Scognet	ldrt	r9, [r0], #4
266129198Scognet	sub	r2, r2, #8
267129198Scognet	stmia	r1!, {r8, r9}
268129198Scognet	cmp	r2, #8
269129198Scognet	bge	.Licleanup8
270129198Scognet
271129198Scognet.Liprecleanup:
272129198Scognet	/*
273129198Scognet	 * If we're done, bail.
274129198Scognet	 */
275129198Scognet	cmp	r2, #0
276129198Scognet	beq	.Lout
277129198Scognet
278129198Scognet.Licleanup:
279129198Scognet	and	r6, r2, #0x3
280129198Scognet	ldr	pc, [pc, r6, lsl #2]
281129198Scognet	b	.Licend
282129198Scognet	.word	.Lic4
283129198Scognet	.word	.Lic1
284129198Scognet	.word	.Lic2
285129198Scognet	.word	.Lic3
286129198Scognet.Lic4:	ldrbt	r6, [r0], #1
287129198Scognet	sub	r2, r2, #1
288129198Scognet	strb	r6, [r1], #1
289129198Scognet.Lic3:	ldrbt	r7, [r0], #1
290129198Scognet	sub	r2, r2, #1
291129198Scognet	strb	r7, [r1], #1
292129198Scognet.Lic2:	ldrbt	r6, [r0], #1
293129198Scognet	sub	r2, r2, #1
294129198Scognet	strb	r6, [r1], #1
295129198Scognet.Lic1:	ldrbt	r7, [r0], #1
296129198Scognet	subs	r2, r2, #1
297129198Scognet	strb	r7, [r1], #1
298129198Scognet.Licend:
299129198Scognet	bne	.Licleanup
300129198Scognet
301129198Scognet.Liout:
302129198Scognet	mov	r0, #0
303129198Scognet
304129198Scognet	str	r5, [r4, #PCB_ONFAULT]
305129198Scognet	RESTORE_REGS
306129198Scognet
307137463Scognet	RET
308129198Scognet
309129198Scognet.Lcopyfault:
310239033Sandrew	ldr	r0, =EFAULT
311129198Scognet	str	r5, [r4, #PCB_ONFAULT]
312129198Scognet	RESTORE_REGS
313129198Scognet
314137463Scognet	RET
315248361SandrewEND(copyin)
316129198Scognet
317129198Scognet/*
318129198Scognet * r0 = kernel space address
319129198Scognet * r1 = user space address
320129198Scognet * r2 = length
321129198Scognet *
322129198Scognet * Copies bytes from kernel space to user space
323129198Scognet *
324129198Scognet * We save/restore r4-r11:
325129198Scognet * r4-r11 are scratch
326129198Scognet */
327129198Scognet
328129198ScognetENTRY(copyout)
329129198Scognet	/* Quick exit if length is zero */
330129198Scognet	teq	r2, #0
331129198Scognet	moveq	r0, #0
332137463Scognet	RETeq
333129198Scognet
334150864Scognet	ldr	r3, .L_arm_memcpy
335150864Scognet	ldr	r3, [r3]
336150864Scognet	cmp	r3, #0
337150864Scognet	beq	.Lnormale
338150864Scognet	ldr	r3, .L_min_memcpy_size
339150864Scognet	ldr	r3, [r3]
340150864Scognet	cmp	r2, r3
341150864Scognet	blt	.Lnormale
342150864Scognet	stmfd	sp!, {r0-r2, r4, lr}
343150864Scognet	mov     r3, r0
344150864Scognet	mov     r0, r1
345150864Scognet	mov     r1, r3
346150864Scognet	mov     r3, #1 /* DST_IS_USER */
347150864Scognet	ldr	r4, .L_arm_memcpy
348150864Scognet	mov	lr, pc
349150864Scognet	ldr	pc, [r4]
350150864Scognet	cmp     r0, #0
351150864Scognet	ldmfd   sp!, {r0-r2, r4, lr}
352150864Scognet	moveq	r0, #0
353150864Scognet	RETeq
354150864Scognet
355151596Scognet.Lnormale:
356129198Scognet	SAVE_REGS
357239268Sgonzo	GET_PCB(r4)
358129198Scognet	ldr	r4, [r4]
359129198Scognet
360129198Scognet	ldr	r5, [r4, #PCB_ONFAULT]
361129198Scognet	adr	r3, .Lcopyfault
362129198Scognet	str	r3, [r4, #PCB_ONFAULT]
363129198Scognet
364129198Scognet	PREFETCH(r0, 0)
365129198Scognet	PREFETCH(r1, 0)
366129198Scognet
367129198Scognet	/*
368129198Scognet	 * If not too many bytes, take the slow path.
369129198Scognet	 */
370129198Scognet	cmp	r2, #0x08
371129198Scognet	blt	.Lcleanup
372129198Scognet
373129198Scognet	/*
374129198Scognet	 * Align destination to word boundary.
375129198Scognet	 */
376129198Scognet	and	r6, r1, #0x3
377129198Scognet	ldr	pc, [pc, r6, lsl #2]
378129198Scognet	b	.Lalend
379129198Scognet	.word	.Lalend
380129198Scognet	.word	.Lal3
381129198Scognet	.word	.Lal2
382129198Scognet	.word	.Lal1
383129198Scognet.Lal3:	ldrb	r6, [r0], #1
384129198Scognet	sub	r2, r2, #1
385129198Scognet	strbt	r6, [r1], #1
386129198Scognet.Lal2:	ldrb	r7, [r0], #1
387129198Scognet	sub	r2, r2, #1
388129198Scognet	strbt	r7, [r1], #1
389129198Scognet.Lal1:	ldrb	r6, [r0], #1
390129198Scognet	sub	r2, r2, #1
391129198Scognet	strbt	r6, [r1], #1
392129198Scognet.Lalend:
393129198Scognet
394129198Scognet	/*
395129198Scognet	 * If few bytes left, finish slow.
396129198Scognet	 */
397129198Scognet	cmp	r2, #0x08
398129198Scognet	blt	.Lcleanup
399129198Scognet
400129198Scognet	/*
401129198Scognet	 * If source is not aligned, finish slow.
402129198Scognet	 */
403129198Scognet	ands	r3, r0, #0x03
404129198Scognet	bne	.Lcleanup
405129198Scognet
406129198Scognet	cmp	r2, #0x60	/* Must be > 0x5f for unrolled cacheline */
407129198Scognet	blt	.Lcleanup8
408129198Scognet
409129198Scognet	/*
410129198Scognet	 * Align source & destination to cacheline boundary.
411129198Scognet	 */
412129198Scognet	and	r6, r1, #0x1f
413129198Scognet	ldr	pc, [pc, r6]
414129198Scognet	b	.Lcaligned
415129198Scognet	.word	.Lcaligned
416129198Scognet	.word	.Lcal28
417129198Scognet	.word	.Lcal24
418129198Scognet	.word	.Lcal20
419129198Scognet	.word	.Lcal16
420129198Scognet	.word	.Lcal12
421129198Scognet	.word	.Lcal8
422129198Scognet	.word	.Lcal4
423129198Scognet.Lcal28:ldr	r6, [r0], #4
424129198Scognet	sub	r2, r2, #4
425129198Scognet	strt	r6, [r1], #4
426129198Scognet.Lcal24:ldr	r7, [r0], #4
427129198Scognet	sub	r2, r2, #4
428129198Scognet	strt	r7, [r1], #4
429129198Scognet.Lcal20:ldr	r6, [r0], #4
430129198Scognet	sub	r2, r2, #4
431129198Scognet	strt	r6, [r1], #4
432129198Scognet.Lcal16:ldr	r7, [r0], #4
433129198Scognet	sub	r2, r2, #4
434129198Scognet	strt	r7, [r1], #4
435129198Scognet.Lcal12:ldr	r6, [r0], #4
436129198Scognet	sub	r2, r2, #4
437129198Scognet	strt	r6, [r1], #4
438129198Scognet.Lcal8:	ldr	r7, [r0], #4
439129198Scognet	sub	r2, r2, #4
440129198Scognet	strt	r7, [r1], #4
441129198Scognet.Lcal4:	ldr	r6, [r0], #4
442129198Scognet	sub	r2, r2, #4
443129198Scognet	strt	r6, [r1], #4
444129198Scognet
445129198Scognet	/*
446129198Scognet	 * We start with > 0x40 bytes to copy (>= 0x60 got us into this
447129198Scognet	 * part of the code, and we may have knocked that down by as much
448129198Scognet	 * as 0x1c getting aligned).
449129198Scognet	 *
450129198Scognet	 * This loop basically works out to:
451129198Scognet	 * do {
452129198Scognet	 * 	prefetch-next-cacheline(s)
453129198Scognet	 *	bytes -= 0x20;
454129198Scognet	 *	copy cacheline
455129198Scognet	 * } while (bytes >= 0x40);
456129198Scognet	 * bytes -= 0x20;
457129198Scognet	 * copy cacheline
458129198Scognet	 */
459129198Scognet.Lcaligned:
460129198Scognet	PREFETCH(r0, 32)
461129198Scognet	PREFETCH(r1, 32)
462129198Scognet
463129198Scognet	sub	r2, r2, #0x20
464129198Scognet
465129198Scognet	/* Copy a cacheline */
466129198Scognet	ldmia	r0!, {r6-r11}
467129198Scognet	strt	r6, [r1], #4
468129198Scognet	strt	r7, [r1], #4
469129198Scognet	ldmia	r0!, {r6-r7}
470129198Scognet	strt	r8, [r1], #4
471129198Scognet	strt	r9, [r1], #4
472129198Scognet	strt	r10, [r1], #4
473129198Scognet	strt	r11, [r1], #4
474129198Scognet	strt	r6, [r1], #4
475129198Scognet	strt	r7, [r1], #4
476129198Scognet
477129198Scognet	cmp	r2, #0x40
478129198Scognet	bge	.Lcaligned
479129198Scognet
480129198Scognet	sub	r2, r2, #0x20
481129198Scognet
482129198Scognet	/* Copy a cacheline */
483129198Scognet	ldmia	r0!, {r6-r11}
484129198Scognet	strt	r6, [r1], #4
485129198Scognet	strt	r7, [r1], #4
486129198Scognet	ldmia	r0!, {r6-r7}
487129198Scognet	strt	r8, [r1], #4
488129198Scognet	strt	r9, [r1], #4
489129198Scognet	strt	r10, [r1], #4
490129198Scognet	strt	r11, [r1], #4
491129198Scognet	strt	r6, [r1], #4
492129198Scognet	strt	r7, [r1], #4
493129198Scognet
494129198Scognet	cmp	r2, #0x08
495129198Scognet	blt	.Lprecleanup
496129198Scognet
497129198Scognet.Lcleanup8:
498129198Scognet	ldmia	r0!, {r8-r9}
499129198Scognet	sub	r2, r2, #8
500129198Scognet	strt	r8, [r1], #4
501129198Scognet	strt	r9, [r1], #4
502129198Scognet	cmp	r2, #8
503129198Scognet	bge	.Lcleanup8
504129198Scognet
505129198Scognet.Lprecleanup:
506129198Scognet	/*
507129198Scognet	 * If we're done, bail.
508129198Scognet	 */
509129198Scognet	cmp	r2, #0
510129198Scognet	beq	.Lout
511129198Scognet
512129198Scognet.Lcleanup:
513129198Scognet	and	r6, r2, #0x3
514129198Scognet	ldr	pc, [pc, r6, lsl #2]
515129198Scognet	b	.Lcend
516129198Scognet	.word	.Lc4
517129198Scognet	.word	.Lc1
518129198Scognet	.word	.Lc2
519129198Scognet	.word	.Lc3
520129198Scognet.Lc4:	ldrb	r6, [r0], #1
521129198Scognet	sub	r2, r2, #1
522129198Scognet	strbt	r6, [r1], #1
523129198Scognet.Lc3:	ldrb	r7, [r0], #1
524129198Scognet	sub	r2, r2, #1
525129198Scognet	strbt	r7, [r1], #1
526129198Scognet.Lc2:	ldrb	r6, [r0], #1
527129198Scognet	sub	r2, r2, #1
528129198Scognet	strbt	r6, [r1], #1
529129198Scognet.Lc1:	ldrb	r7, [r0], #1
530129198Scognet	subs	r2, r2, #1
531129198Scognet	strbt	r7, [r1], #1
532129198Scognet.Lcend:
533129198Scognet	bne	.Lcleanup
534129198Scognet
535129198Scognet.Lout:
536129198Scognet	mov	r0, #0
537129198Scognet
538129198Scognet	str	r5, [r4, #PCB_ONFAULT]
539129198Scognet	RESTORE_REGS
540129198Scognet
541137463Scognet	RET
542248361SandrewEND(copyout)
543129198Scognet#endif
544129198Scognet
545129198Scognet/*
546129198Scognet * int badaddr_read_1(const uint8_t *src, uint8_t *dest)
547129198Scognet *
548129198Scognet * Copies a single 8-bit value from src to dest, returning 0 on success,
549129198Scognet * else EFAULT if a page fault occurred.
550129198Scognet */
551129198ScognetENTRY(badaddr_read_1)
552239268Sgonzo	GET_PCB(r2)
553129198Scognet	ldr	r2, [r2]
554239268Sgonzo
555129198Scognet	ldr	ip, [r2, #PCB_ONFAULT]
556129198Scognet	adr	r3, 1f
557129198Scognet	str	r3, [r2, #PCB_ONFAULT]
558129198Scognet	nop
559129198Scognet	nop
560129198Scognet	nop
561129198Scognet	ldrb	r3, [r0]
562129198Scognet	nop
563129198Scognet	nop
564129198Scognet	nop
565129198Scognet	strb	r3, [r1]
566129198Scognet	mov	r0, #0		/* No fault */
567129198Scognet1:	str	ip, [r2, #PCB_ONFAULT]
568137463Scognet	RET
569248361SandrewEND(badaddr_read_1)
570129198Scognet
571129198Scognet/*
572129198Scognet * int badaddr_read_2(const uint16_t *src, uint16_t *dest)
573129198Scognet *
574129198Scognet * Copies a single 16-bit value from src to dest, returning 0 on success,
575129198Scognet * else EFAULT if a page fault occurred.
576129198Scognet */
577129198ScognetENTRY(badaddr_read_2)
578239268Sgonzo	GET_PCB(r2)
579129198Scognet	ldr	r2, [r2]
580239268Sgonzo
581129198Scognet	ldr	ip, [r2, #PCB_ONFAULT]
582129198Scognet	adr	r3, 1f
583129198Scognet	str	r3, [r2, #PCB_ONFAULT]
584129198Scognet	nop
585129198Scognet	nop
586129198Scognet	nop
587129198Scognet	ldrh	r3, [r0]
588129198Scognet	nop
589129198Scognet	nop
590129198Scognet	nop
591129198Scognet	strh	r3, [r1]
592129198Scognet	mov	r0, #0		/* No fault */
593129198Scognet1:	str	ip, [r2, #PCB_ONFAULT]
594137463Scognet	RET
595248361SandrewEND(badaddr_read_2)
596129198Scognet
597129198Scognet/*
598129198Scognet * int badaddr_read_4(const uint32_t *src, uint32_t *dest)
599129198Scognet *
600129198Scognet * Copies a single 32-bit value from src to dest, returning 0 on success,
601129198Scognet * else EFAULT if a page fault occurred.
602129198Scognet */
603129198ScognetENTRY(badaddr_read_4)
604239268Sgonzo	GET_PCB(r2)
605129198Scognet	ldr	r2, [r2]
606239268Sgonzo
607129198Scognet	ldr	ip, [r2, #PCB_ONFAULT]
608129198Scognet	adr	r3, 1f
609129198Scognet	str	r3, [r2, #PCB_ONFAULT]
610129198Scognet	nop
611129198Scognet	nop
612129198Scognet	nop
613129198Scognet	ldr	r3, [r0]
614129198Scognet	nop
615129198Scognet	nop
616129198Scognet	nop
617129198Scognet	str	r3, [r1]
618129198Scognet	mov	r0, #0		/* No fault */
619129198Scognet1:	str	ip, [r2, #PCB_ONFAULT]
620137463Scognet	RET
621248361SandrewEND(badaddr_read_4)
622129198Scognet
623