xref: /netbsd/sys/arch/m68k/m68k/copy.s (revision 6550d01e)
1/*	$NetBSD: copy.s,v 1.43 2010/07/07 01:16:25 chs Exp $	*/
2
3/*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum and by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*-
33 * Copyright (c) 1990 The Regents of the University of California.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to Berkeley by
37 * the Systems Programming Group of the University of Utah Computer
38 * Science Department.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 *    notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 *    notice, this list of conditions and the following disclaimer in the
47 *    documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 *    may be used to endorse or promote products derived from this software
50 *    without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 */
64
65/*
66 * This file contains the functions for user-space access:
67 * copyin/copyout, fuword/suword, etc.
68 */
69
70#include "opt_multiprocessor.h"
71#ifdef MULTIPROCESSOR
72#error need to write MP support for ucas_* functions
73#endif
74
75#include <sys/errno.h>
76#include <machine/asm.h>
77
78#include "assym.h"
79
80	.file	"copy.s"
81	.text
82
83#ifdef	DIAGNOSTIC
84/*
85 * The following routines all use the "moves" instruction to access
86 * memory with "user" privilege while running in supervisor mode.
87 * The "function code" registers actually determine what type of
88 * access "moves" does, and the kernel arranges to leave them set
89 * for "user data" access when these functions are called.
90 *
91 * The diagnostics:  CHECK_SFC,  CHECK_DFC
92 * will verify that the sfc/dfc register values are correct.
93 */
94Lbadfc:
95	PANIC("copy.s: bad sfc or dfc")
96	bra	Lbadfc
97#define	CHECK_SFC	movec %sfc,%d0; subql #FC_USERD,%d0; bne Lbadfc
98#define	CHECK_DFC	movec %dfc,%d0; subql #FC_USERD,%d0; bne Lbadfc
99#else	/* DIAGNOSTIC */
100#define	CHECK_SFC
101#define	CHECK_DFC
102#endif	/* DIAGNOSTIC */
103
104/*
105 * copyin(void *from, void *to, size_t len);
106 * Copy len bytes from the user's address space.
107 *
108 * This is probably not the best we can do, but it is still 2-10 times
109 * faster than the C version in the portable gen directory.
110 *
111 * Things that might help:
112 *	- unroll the longword copy loop (might not be good for a 68020)
113 *	- longword align when possible (only on the 68020)
114 */
115ENTRY(copyin)
116	CHECK_SFC
117	movl	%sp@(12),%d0		| check count
118	beq	Lciret			| == 0, don't do anything
119#ifdef MAPPEDCOPY
120	cmpl	_C_LABEL(mappedcopysize),%d0 | size >= mappedcopysize
121	bcc	_C_LABEL(mappedcopyin)	| yes, go do it the new way
122#endif
123	movl	%d2,%sp@-		| save scratch register
124	movl	_C_LABEL(curpcb),%a0	| set fault handler
125	movl	#Lcifault,%a0@(PCB_ONFAULT)
126	movl	%sp@(8),%a0		| src address
127	movl	%sp@(12),%a1		| dest address
128	movl	%a0,%d1
129	btst	#0,%d1			| src address odd?
130	beq	Lcieven			| no, skip alignment
131	movsb	%a0@+,%d2		| yes, copy a byte
132	movb	%d2,%a1@+
133	subql	#1,%d0			| adjust count
134	beq	Lcidone			| count 0, all done
135Lcieven:
136	movl	%a1,%d1
137	btst	#0,%d1			| dest address odd?
138	bne	Lcibytes		| yes, must copy bytes
139	movl	%d0,%d1			| OK, both even.  Get count
140	lsrl	#2,%d1			|   and convert to longwords
141	beq	Lcibytes		| count 0, skip longword loop
142	subql	#1,%d1			| predecrement for dbf
143Lcilloop:
144	movsl	%a0@+,%d2		| copy a longword
145	movl	%d2,%a1@+
146	dbf	%d1,Lcilloop		| decrement low word of count
147	subil	#0x10000,%d1		| decrement high word of count
148	bcc	Lcilloop
149	andl	#3,%d0			| what remains
150	beq	Lcidone			| nothing, all done
151Lcibytes:
152	subql	#1,%d0			| predecrement for dbf
153Lcibloop:
154	movsb	%a0@+,%d2		| copy a byte
155	movb	%d2,%a1@+
156	dbf	%d0,Lcibloop		| decrement low word of count
157	subil	#0x10000,%d0		| decrement high word of count
158	bcc	Lcibloop
159	clrl	%d0			| no error
160Lcidone:
161	movl	_C_LABEL(curpcb),%a0	| clear fault handler
162	clrl	%a0@(PCB_ONFAULT)
163	movl	%sp@+,%d2		| restore scratch register
164Lciret:
165	rts
166Lcifault:
167	bra	Lcidone
168
169/*
170 * copyout(void *from, void *to, size_t len);
171 * Copy len bytes into the user's address space.
172 *
173 * This is probably not the best we can do, but it is still 2-10 times
174 * faster than the C version in the portable gen directory.
175 *
176 * Things that might help:
177 *	- unroll the longword copy loop (might not be good for a 68020)
178 *	- longword align when possible (only on the 68020)
179 */
180ENTRY(copyout)
181	CHECK_DFC
182	movl	%sp@(12),%d0		| check count
183	beq	Lcoret			| == 0, don't do anything
184#ifdef MAPPEDCOPY
185	cmpl	_C_LABEL(mappedcopysize),%d0 | size >= mappedcopysize
186	bcc	_C_LABEL(mappedcopyout)	| yes, go do it the new way
187#endif
188	movl	%d2,%sp@-		| save scratch register
189	movl	_C_LABEL(curpcb),%a0	| set fault handler
190	movl	#Lcofault,%a0@(PCB_ONFAULT)
191	movl	%sp@(8),%a0		| src address
192	movl	%sp@(12),%a1		| dest address
193	movl	%a0,%d1
194	btst	#0,%d1			| src address odd?
195	beq	Lcoeven			| no, skip alignment
196	movb	%a0@+,%d2		| yes, copy a byte
197	movsb	%d2,%a1@+
198	subql	#1,%d0			| adjust count
199	beq	Lcodone			| count 0, all done
200Lcoeven:
201	movl	%a1,%d1
202	btst	#0,%d1			| dest address odd?
203	bne	Lcobytes		| yes, must copy bytes
204	movl	%d0,%d1			| OK, both even.  Get count
205	lsrl	#2,%d1			|   and convert to longwords
206	beq	Lcobytes		| count 0, skip longword loop
207	subql	#1,%d1			| predecrement for dbf
208Lcolloop:
209	movl	%a0@+,%d2			| copy a longword
210	movsl	%d2,%a1@+
211	dbf	%d1,Lcolloop		| decrement low word of count
212	subil	#0x10000,%d1		| decrement high word of count
213	bcc	Lcolloop
214	andl	#3,%d0			| what remains
215	beq	Lcodone			| nothing, all done
216Lcobytes:
217	subql	#1,%d0			| predecrement for dbf
218Lcobloop:
219	movb	%a0@+,%d2		| copy a byte
220	movsb	%d2,%a1@+
221	dbf	%d0,Lcobloop		| decrement low word of count
222	subil	#0x10000,%d0		| decrement high word of count
223	bcc	Lcobloop
224	clrl	%d0			| no error
225Lcodone:
226	movl	_C_LABEL(curpcb),%a0	| clear fault handler
227	clrl	%a0@(PCB_ONFAULT)
228	movl	%sp@+,%d2		| restore scratch register
229Lcoret:
230	rts
231Lcofault:
232	bra	Lcodone
233
234/*
235 * copystr(void *from, void *to, size_t maxlen, size_t *lencopied);
236 * Copy a NUL-terminated string, at most maxlen characters long.  Return the
237 * number of characters copied (including the NUL) in *lencopied.  If the
238 * string is too long, return ENAMETOOLONG; else return 0.
239 */
240ENTRY(copystr)
241	movl	%sp@(4),%a0		| a0 = fromaddr
242	movl	%sp@(8),%a1		| a1 = toaddr
243	clrl	%d0
244	movl	%sp@(12),%d1		| count
245	beq	Lcstoolong		| nothing to copy
246	subql	#1,%d1			| predecrement for dbeq
247Lcsloop:
248	movb	%a0@+,%a1@+		| copy a byte
249	dbeq	%d1,Lcsloop		| decrement low word of count
250	beq	Lcsdone			| copied null, exit
251	subil	#0x10000,%d1		| decrement high word of count
252	bcc	Lcsloop			| more room, keep going
253Lcstoolong:
254	moveq	#ENAMETOOLONG,%d0	| ran out of space
255Lcsdone:
256	tstl	%sp@(16)		| length desired?
257	beq	Lcsret
258	subl	%sp@(4),%a0		| yes, calculate length copied
259	movl	%sp@(16),%a1		| store at return location
260	movl	%a0,%a1@
261Lcsret:
262	rts
263
264/*
265 * copyinstr(void *from, void *to, size_t maxlen, size_t *lencopied);
266 * Copy a NUL-terminated string, at most maxlen characters long, from the
267 * user's address space.  Return the number of characters copied (including
268 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
269 * else return 0 or EFAULT.
270 */
271ENTRY(copyinstr)
272	CHECK_SFC
273	movl	_C_LABEL(curpcb),%a0	| set fault handler
274	movl	#Lcisfault,%a0@(PCB_ONFAULT)
275	movl	%sp@(4),%a0		| a0 = fromaddr
276	movl	%sp@(8),%a1		| a1 = toaddr
277	clrl	%d0
278	movl	%sp@(12),%d1		| count
279	beq	Lcistoolong		| nothing to copy
280	subql	#1,%d1			| predecrement for dbeq
281Lcisloop:
282	movsb	%a0@+,%d0		| copy a byte
283	movb	%d0,%a1@+
284	dbeq	%d1,Lcisloop		| decrement low word of count
285	beq	Lcisdone		| copied null, exit
286	subil	#0x10000,%d1		| decrement high word of count
287	bcc	Lcisloop		| more room, keep going
288Lcistoolong:
289	moveq	#ENAMETOOLONG,%d0	| ran out of space
290Lcisdone:
291	tstl	%sp@(16)		| length desired?
292	beq	Lcisexit
293	subl	%sp@(4),%a0		| yes, calculate length copied
294	movl	%sp@(16),%a1		| store at return location
295	movl	%a0,%a1@
296Lcisexit:
297	movl	_C_LABEL(curpcb),%a0	| clear fault handler
298	clrl	%a0@(PCB_ONFAULT)
299	rts
300Lcisfault:
301	bra	Lcisdone
302
303/*
304 * copyoutstr(void *from, void *to, size_t maxlen, size_t *lencopied);
305 * Copy a NUL-terminated string, at most maxlen characters long, into the
306 * user's address space.  Return the number of characters copied (including
307 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
308 * else return 0 or EFAULT.
309 */
310ENTRY(copyoutstr)
311	CHECK_DFC
312	movl	_C_LABEL(curpcb),%a0	| set fault handler
313	movl	#Lcosfault,%a0@(PCB_ONFAULT)
314	movl	%sp@(4),%a0		| a0 = fromaddr
315	movl	%sp@(8),%a1		| a1 = toaddr
316	clrl	%d0
317	movl	%sp@(12),%d1		| count
318	beq	Lcostoolong		| nothing to copy
319	subql	#1,%d1			| predecrement for dbeq
320Lcosloop:
321	movb	%a0@+,%d0		| copy a byte
322	movsb	%d0,%a1@+
323	dbeq	%d1,Lcosloop		| decrement low word of count
324	beq	Lcosdone		| copied null, exit
325	subil	#0x10000,%d1		| decrement high word of count
326	bcc	Lcosloop		| more room, keep going
327Lcostoolong:
328	moveq	#ENAMETOOLONG,%d0	| ran out of space
329Lcosdone:
330	tstl	%sp@(16)		| length desired?
331	beq	Lcosexit
332	subl	%sp@(4),%a0		| yes, calculate length copied
333	movl	%sp@(16),%a1		| store at return location
334	movl	%a0,%a1@
335Lcosexit:
336	movl	_C_LABEL(curpcb),%a0	| clear fault handler
337	clrl	%a0@(PCB_ONFAULT)
338	rts
339Lcosfault:
340	bra	Lcosdone
341
342/*
343 * kcopy(const void *src, void *dst, size_t len);
344 *
345 * Copy len bytes from src to dst, aborting if we encounter a fatal
346 * page fault.
347 *
348 * kcopy() _must_ save and restore the old fault handler since it is
349 * called by uiomove(), which may be in the path of servicing a non-fatal
350 * page fault.
351 */
352ENTRY(kcopy)
353	link	%a6,#-4
354	movl	_C_LABEL(curpcb),%a0	 | set fault handler
355	movl	%a0@(PCB_ONFAULT),%a6@(-4) | save old handler first
356	movl	#Lkcfault,%a0@(PCB_ONFAULT)
357	movl	%a6@(16),%sp@-		| push len
358	movl	%a6@(8),%sp@-		| push src
359	movl	%a6@(12),%sp@-		| push dst
360	jbsr	_C_LABEL(memcpy)	| copy it
361	addl	#12,%sp			| pop args
362	clrl	%d0			| success!
363Lkcdone:
364	movl	_C_LABEL(curpcb),%a0	| restore fault handler
365	movl	%a6@(-4),%a0@(PCB_ONFAULT)
366	unlk	%a6
367	rts
368Lkcfault:
369	addl	#16,%sp			| pop args and return address
370	bra	Lkcdone
371
372/*
373 * fuword(void *uaddr);
374 * Fetch an int from the user's address space.
375 */
376ENTRY(fuword)
377	CHECK_SFC
378	movl	%sp@(4),%a0		| address to read
379	movl	_C_LABEL(curpcb),%a1	| set fault handler
380	movl	#Lferr,%a1@(PCB_ONFAULT)
381	movsl	%a0@,%d0		| do read from user space
382	bra	Lfdone
383
384/*
385 * fusword(void *uaddr);
386 * Fetch a short from the user's address space.
387 */
388ENTRY(fusword)
389	CHECK_SFC
390	movl	%sp@(4),%a0		| address to read
391	movl	_C_LABEL(curpcb),%a1	| set fault handler
392	movl	#Lferr,%a1@(PCB_ONFAULT)
393	moveq	#0,%d0
394	movsw	%a0@,%d0		| do read from user space
395	bra	Lfdone
396
397/*
398 * fuswintr(void *uaddr);
399 * Fetch a short from the user's address space.
400 * Can be called during an interrupt.
401 */
402ENTRY(fuswintr)
403	CHECK_SFC
404	movl	%sp@(4),%a0		| address to read
405	movl	_C_LABEL(curpcb),%a1	| set fault handler
406	movl	#_C_LABEL(fubail),%a1@(PCB_ONFAULT)
407	moveq	#0,%d0
408	movsw	%a0@,%d0		| do read from user space
409	bra	Lfdone
410
411/*
412 * fubyte(void *uaddr);
413 * Fetch a byte from the user's address space.
414 */
415ENTRY(fubyte)
416	CHECK_SFC
417	movl	%sp@(4),%a0		| address to read
418	movl	_C_LABEL(curpcb),%a1	| set fault handler
419	movl	#Lferr,%a1@(PCB_ONFAULT)
420	moveq	#0,%d0
421	movsb	%a0@,%d0		| do read from user space
422	bra	Lfdone
423
424/*
425 * Error routine for fuswintr.  The fault handler in trap.c
426 * checks for pcb_onfault set to this fault handler and
427 * "bails out" before calling the VM fault handler.
428 * (We can not call VM code from interrupt level.)
429 * Same code as Lferr but must have a different address.
430 */
431ENTRY(fubail)
432	nop
433Lferr:
434	moveq	#-1,%d0			| error indicator
435Lfdone:
436	clrl	%a1@(PCB_ONFAULT) 	| clear fault handler
437	rts
438
439/*
440 * suword(void *uaddr, int x);
441 * Store an int in the user's address space.
442 */
443ENTRY(suword)
444	CHECK_DFC
445	movl	%sp@(4),%a0		| address to write
446	movl	%sp@(8),%d0		| value to put there
447	movl	_C_LABEL(curpcb),%a1	| set fault handler
448	movl	#Lserr,%a1@(PCB_ONFAULT)
449	movsl	%d0,%a0@		| do write to user space
450	moveq	#0,%d0			| indicate no fault
451	bra	Lsdone
452
453/*
454 * susword(void *uaddr, short x);
455 * Store a short in the user's address space.
456 */
457ENTRY(susword)
458	CHECK_DFC
459	movl	%sp@(4),%a0		| address to write
460	movw	%sp@(10),%d0		| value to put there
461	movl	_C_LABEL(curpcb),%a1	| set fault handler
462	movl	#Lserr,%a1@(PCB_ONFAULT)
463	movsw	%d0,%a0@		| do write to user space
464	moveq	#0,%d0			| indicate no fault
465	bra	Lsdone
466
467/*
468 * suswintr(void *uaddr, short x);
469 * Store a short in the user's address space.
470 * Can be called during an interrupt.
471 */
472ENTRY(suswintr)
473	CHECK_DFC
474	movl	%sp@(4),%a0		| address to write
475	movw	%sp@(10),%d0		| value to put there
476	movl	_C_LABEL(curpcb),%a1	| set fault handler
477	movl	#_C_LABEL(subail),%a1@(PCB_ONFAULT)
478	movsw	%d0,%a0@		| do write to user space
479	moveq	#0,%d0			| indicate no fault
480	bra	Lsdone
481
482/*
483 * subyte(void *uaddr, char x);
484 * Store a byte in the user's address space.
485 */
486ENTRY(subyte)
487	CHECK_DFC
488	movl	%sp@(4),%a0		| address to write
489	movb	%sp@(11),%d0		| value to put there
490	movl	_C_LABEL(curpcb),%a1	| set fault handler
491	movl	#Lserr,%a1@(PCB_ONFAULT)
492	movsb	%d0,%a0@		| do write to user space
493	moveq	#0,%d0			| indicate no fault
494	bra	Lsdone
495
496/*
497 * Error routine for suswintr.  The fault handler in trap.c
498 * checks for pcb_onfault set to this fault handler and
499 * "bails out" before calling the VM fault handler.
500 * (We can not call VM code from interrupt level.)
501 * Same code as Lserr but must have a different address.
502 */
503ENTRY(subail)
504	nop
505Lserr:
506	moveq	#-1,%d0			| error indicator
507Lsdone:
508	clrl	%a1@(PCB_ONFAULT) 	| clear fault handler
509	rts
510
511/*
512 * int ucas_32(volatile int32_t *uptr, int32_t old, int32_t new, int32_t *ret);
513 * Atomically compare-and-swap an int32_t in user space.
514 */
515	.globl		_C_LABEL(ucas_32_ras_start)
516	.globl		_C_LABEL(ucas_32_ras_end)
517ENTRY(ucas_32)
518	CHECK_SFC
519	CHECK_DFC
520	movl	_C_LABEL(curpcb),%a1
521	movl	#Lucasfault,%a1@(PCB_ONFAULT)	| set fault handler
522	movl	%sp@(4),%a0		| a0 = uptr
523_C_LABEL(ucas_32_ras_start):
524	movl	%sp@(8),%d0		| d0 = old
525	movsl	%a0@,%d1		| d1 = *uptr
526	cmpl	%d0,%d1			| does *uptr == old?
527	bne	Lucasdiff		| if not, don't change it
528	movl	%sp@(12),%d0		| d0 = new
529	movsl	%d0,%a0@		| *uptr = new
530	nop				| pipeline sync
531_C_LABEL(ucas_32_ras_end):
532Lucasdiff:
533	movl	%sp@(16),%a0		| a0 = ret
534	movl	%d1,%a0@		| *ret = d1 (old *uptr)
535	clrl	%d0			| return 0
536
537Lucasfault:
538	clrl	%a1@(PCB_ONFAULT)	| clear fault handler
539	rts
540
541STRONG_ALIAS(ucas_int,ucas_32)
542STRONG_ALIAS(ucas_ptr,ucas_32)
543