NetBSD-5.0.2/sys/arch/m68k/m68k/copy.s

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: copy.s,v 1.41 2008/04/28 20:23:27 martin Exp $	*/

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum and by Jason R. Thorpe.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * This file contains the functions for user-space access:
 * copyin/copyout, fuword/suword, etc.
 */

#include <sys/errno.h>
#include <machine/asm.h>

#include "assym.h"

	.file	"copy.s"
	.text

#ifdef	DIAGNOSTIC
/*
 * The following routines all use the "moves" instruction to access
 * memory with "user" privilege while running in supervisor mode.
 * The "function code" registers actually determine what type of
 * access "moves" does, and the kernel arranges to leave them set
 * for "user data" access when these functions are called.
 *
 * The diagnostics:  CHECK_SFC,  CHECK_DFC
 * will verify that the sfc/dfc register values are correct.
 */
Lbadfc:
	PANIC("copy.s: bad sfc or dfc")
	bra	Lbadfc
#define	CHECK_SFC	movec %sfc,%d0; subql #FC_USERD,%d0; bne Lbadfc
#define	CHECK_DFC	movec %dfc,%d0; subql #FC_USERD,%d0; bne Lbadfc
#else	/* DIAGNOSTIC */
#define	CHECK_SFC
#define	CHECK_DFC
#endif	/* DIAGNOSTIC */

/*
 * copyin(void *from, void *to, size_t len);
 * Copy len bytes from the user's address space.
 *
 * This is probably not the best we can do, but it is still 2-10 times
 * faster than the C version in the portable gen directory.
 *
 * Things that might help:
 *	- unroll the longword copy loop (might not be good for a 68020)
 *	- longword align when possible (only on the 68020)
 */
ENTRY(copyin)
	CHECK_SFC
	movl	%sp@(12),%d0		| check count
	beq	Lciret			| == 0, don't do anything
#ifdef MAPPEDCOPY
	cmpl	_C_LABEL(mappedcopysize),%d0 | size >= mappedcopysize
	bcc	_C_LABEL(mappedcopyin)	| yes, go do it the new way
#endif
	movl	%d2,%sp@-		| save scratch register
	movl	_C_LABEL(curpcb),%a0	| set fault handler
	movl	#Lcifault,%a0@(PCB_ONFAULT)
	movl	%sp@(8),%a0		| src address
	movl	%sp@(12),%a1		| dest address
	movl	%a0,%d1
	btst	#0,%d1			| src address odd?
	beq	Lcieven			| no, skip alignment
	movsb	%a0@+,%d2		| yes, copy a byte
	movb	%d2,%a1@+
	subql	#1,%d0			| adjust count
	beq	Lcidone			| count 0, all done
Lcieven:
	movl	%a1,%d1
	btst	#0,%d1			| dest address odd?
	bne	Lcibytes		| yes, must copy bytes
	movl	%d0,%d1			| OK, both even.  Get count
	lsrl	#2,%d1			|   and convert to longwords
	beq	Lcibytes		| count 0, skip longword loop
	subql	#1,%d1			| predecrement for dbf
Lcilloop:
	movsl	%a0@+,%d2		| copy a longword
	movl	%d2,%a1@+
	dbf	%d1,Lcilloop		| decrement low word of count
	subil	#0x10000,%d1		| decrement high word of count
	bcc	Lcilloop
	andl	#3,%d0			| what remains
	beq	Lcidone			| nothing, all done
Lcibytes:
	subql	#1,%d0			| predecrement for dbf
Lcibloop:
	movsb	%a0@+,%d2		| copy a byte
	movb	%d2,%a1@+
	dbf	%d0,Lcibloop		| decrement low word of count
	subil	#0x10000,%d0		| decrement high word of count
	bcc	Lcibloop
	clrl	%d0			| no error
Lcidone:
	movl	_C_LABEL(curpcb),%a0	| clear fault handler
	clrl	%a0@(PCB_ONFAULT)
	movl	%sp@+,%d2		| restore scratch register
Lciret:
	rts
Lcifault:
	moveq	#EFAULT,%d0		| got a fault
	bra	Lcidone

/*
 * copyout(void *from, void *to, size_t len);
 * Copy len bytes into the user's address space.
 *
 * This is probably not the best we can do, but it is still 2-10 times
 * faster than the C version in the portable gen directory.
 *
 * Things that might help:
 *	- unroll the longword copy loop (might not be good for a 68020)
 *	- longword align when possible (only on the 68020)
 */
ENTRY(copyout)
	CHECK_DFC
	movl	%sp@(12),%d0		| check count
	beq	Lcoret			| == 0, don't do anything
#ifdef MAPPEDCOPY
	cmpl	_C_LABEL(mappedcopysize),%d0 | size >= mappedcopysize
	bcc	_C_LABEL(mappedcopyout)	| yes, go do it the new way
#endif
	movl	%d2,%sp@-		| save scratch register
	movl	_C_LABEL(curpcb),%a0	| set fault handler
	movl	#Lcofault,%a0@(PCB_ONFAULT)
	movl	%sp@(8),%a0		| src address
	movl	%sp@(12),%a1		| dest address
	movl	%a0,%d1
	btst	#0,%d1			| src address odd?
	beq	Lcoeven			| no, skip alignment
	movb	%a0@+,%d2		| yes, copy a byte
	movsb	%d2,%a1@+
	subql	#1,%d0			| adjust count
	beq	Lcodone			| count 0, all done
Lcoeven:
	movl	%a1,%d1
	btst	#0,%d1			| dest address odd?
	bne	Lcobytes		| yes, must copy bytes
	movl	%d0,%d1			| OK, both even.  Get count
	lsrl	#2,%d1			|   and convert to longwords
	beq	Lcobytes		| count 0, skip longword loop
	subql	#1,%d1			| predecrement for dbf
Lcolloop:
	movl	%a0@+,%d2			| copy a longword
	movsl	%d2,%a1@+
	dbf	%d1,Lcolloop		| decrement low word of count
	subil	#0x10000,%d1		| decrement high word of count
	bcc	Lcolloop
	andl	#3,%d0			| what remains
	beq	Lcodone			| nothing, all done
Lcobytes:
	subql	#1,%d0			| predecrement for dbf
Lcobloop:
	movb	%a0@+,%d2		| copy a byte
	movsb	%d2,%a1@+
	dbf	%d0,Lcobloop		| decrement low word of count
	subil	#0x10000,%d0		| decrement high word of count
	bcc	Lcobloop
	clrl	%d0			| no error
Lcodone:
	movl	_C_LABEL(curpcb),%a0	| clear fault handler
	clrl	%a0@(PCB_ONFAULT)
	movl	%sp@+,%d2		| restore scratch register
Lcoret:
	rts
Lcofault:
	moveq	#EFAULT,%d0
	bra	Lcodone

/*
 * copystr(void *from, void *to, size_t maxlen, size_t *lencopied);
 * Copy a NUL-terminated string, at most maxlen characters long.  Return the
 * number of characters copied (including the NUL) in *lencopied.  If the
 * string is too long, return ENAMETOOLONG; else return 0.
 */
ENTRY(copystr)
	movl	%sp@(4),%a0		| a0 = fromaddr
	movl	%sp@(8),%a1		| a1 = toaddr
	clrl	%d0
	movl	%sp@(12),%d1		| count
	beq	Lcstoolong		| nothing to copy
	subql	#1,%d1			| predecrement for dbeq
Lcsloop:
	movb	%a0@+,%a1@+		| copy a byte
	dbeq	%d1,Lcsloop		| decrement low word of count
	beq	Lcsdone			| copied null, exit
	subil	#0x10000,%d1		| decrement high word of count
	bcc	Lcsloop			| more room, keep going
Lcstoolong:
	moveq	#ENAMETOOLONG,%d0	| ran out of space
Lcsdone:
	tstl	%sp@(16)		| length desired?
	beq	Lcsret
	subl	%sp@(4),%a0		| yes, calculate length copied
	movl	%sp@(16),%a1		| store at return location
	movl	%a0,%a1@
Lcsret:
	rts

/*
 * copyinstr(void *from, void *to, size_t maxlen, size_t *lencopied);
 * Copy a NUL-terminated string, at most maxlen characters long, from the
 * user's address space.  Return the number of characters copied (including
 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
 * else return 0 or EFAULT.
 */
ENTRY(copyinstr)
	CHECK_SFC
	movl	_C_LABEL(curpcb),%a0	| set fault handler
	movl	#Lcisfault,%a0@(PCB_ONFAULT)
	movl	%sp@(4),%a0		| a0 = fromaddr
	movl	%sp@(8),%a1		| a1 = toaddr
	clrl	%d0
	movl	%sp@(12),%d1		| count
	beq	Lcistoolong		| nothing to copy
	subql	#1,%d1			| predecrement for dbeq
Lcisloop:
	movsb	%a0@+,%d0		| copy a byte
	movb	%d0,%a1@+
	dbeq	%d1,Lcisloop		| decrement low word of count
	beq	Lcisdone		| copied null, exit
	subil	#0x10000,%d1		| decrement high word of count
	bcc	Lcisloop		| more room, keep going
Lcistoolong:
	moveq	#ENAMETOOLONG,%d0	| ran out of space
Lcisdone:
	tstl	%sp@(16)		| length desired?
	beq	Lcisexit
	subl	%sp@(4),%a0		| yes, calculate length copied
	movl	%sp@(16),%a1		| store at return location
	movl	%a0,%a1@
Lcisexit:
	movl	_C_LABEL(curpcb),%a0	| clear fault handler
	clrl	%a0@(PCB_ONFAULT)
	rts
Lcisfault:
	moveq	#EFAULT,%d0
	bra	Lcisdone

/*
 * copyoutstr(void *from, void *to, size_t maxlen, size_t *lencopied);
 * Copy a NUL-terminated string, at most maxlen characters long, into the
 * user's address space.  Return the number of characters copied (including
 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
 * else return 0 or EFAULT.
 */
ENTRY(copyoutstr)
	CHECK_DFC
	movl	_C_LABEL(curpcb),%a0	| set fault handler
	movl	#Lcosfault,%a0@(PCB_ONFAULT)
	movl	%sp@(4),%a0		| a0 = fromaddr
	movl	%sp@(8),%a1		| a1 = toaddr
	clrl	%d0
	movl	%sp@(12),%d1		| count
	beq	Lcostoolong		| nothing to copy
	subql	#1,%d1			| predecrement for dbeq
Lcosloop:
	movb	%a0@+,%d0		| copy a byte
	movsb	%d0,%a1@+
	dbeq	%d1,Lcosloop		| decrement low word of count
	beq	Lcosdone		| copied null, exit
	subil	#0x10000,%d1		| decrement high word of count
	bcc	Lcosloop		| more room, keep going
Lcostoolong:
	moveq	#ENAMETOOLONG,%d0	| ran out of space
Lcosdone:
	tstl	%sp@(16)		| length desired?
	beq	Lcosexit
	subl	%sp@(4),%a0		| yes, calculate length copied
	movl	%sp@(16),%a1		| store at return location
	movl	%a0,%a1@
Lcosexit:
	movl	_C_LABEL(curpcb),%a0	| clear fault handler
	clrl	%a0@(PCB_ONFAULT)
	rts
Lcosfault:
	moveq	#EFAULT,%d0
	bra	Lcosdone

/*
 * kcopy(const void *src, void *dst, size_t len);
 *
 * Copy len bytes from src to dst, aborting if we encounter a fatal
 * page fault.
 *
 * kcopy() _must_ save and restore the old fault handler since it is
 * called by uiomove(), which may be in the path of servicing a non-fatal
 * page fault.
 */
ENTRY(kcopy)
	link	%a6,#-4
	movl	_C_LABEL(curpcb),%a0	 | set fault handler
	movl	%a0@(PCB_ONFAULT),%a6@(-4) | save old handler first
	movl	#Lkcfault,%a0@(PCB_ONFAULT)
	movl	%a6@(16),%sp@-		| push len
	movl	%a6@(8),%sp@-		| push src
	movl	%a6@(12),%sp@-		| push dst
	jbsr	_C_LABEL(memcpy)	| copy it
	addl	#12,%sp			| pop args
	clrl	%d0			| success!
Lkcdone:
	movl	_C_LABEL(curpcb),%a0	| restore fault handler
	movl	%a6@(-4),%a0@(PCB_ONFAULT)
	unlk	%a6
	rts
Lkcfault:
	addl	#16,%sp			| pop args and return address
	moveq	#EFAULT,%d0		| indicate a fault
	bra	Lkcdone

/*
 * fuword(void *uaddr);
 * Fetch an int from the user's address space.
 */
ENTRY(fuword)
	CHECK_SFC
	movl	%sp@(4),%a0		| address to read
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#Lferr,%a1@(PCB_ONFAULT)
	movsl	%a0@,%d0		| do read from user space
	bra	Lfdone

/*
 * fusword(void *uaddr);
 * Fetch a short from the user's address space.
 */
ENTRY(fusword)
	CHECK_SFC
	movl	%sp@(4),%a0		| address to read
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#Lferr,%a1@(PCB_ONFAULT)
	moveq	#0,%d0
	movsw	%a0@,%d0		| do read from user space
	bra	Lfdone

/*
 * fuswintr(void *uaddr);
 * Fetch a short from the user's address space.
 * Can be called during an interrupt.
 */
ENTRY(fuswintr)
	CHECK_SFC
	movl	%sp@(4),%a0		| address to read
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#_C_LABEL(fubail),%a1@(PCB_ONFAULT)
	moveq	#0,%d0
	movsw	%a0@,%d0		| do read from user space
	bra	Lfdone

/*
 * fubyte(void *uaddr);
 * Fetch a byte from the user's address space.
 */
ENTRY(fubyte)
	CHECK_SFC
	movl	%sp@(4),%a0		| address to read
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#Lferr,%a1@(PCB_ONFAULT)
	moveq	#0,%d0
	movsb	%a0@,%d0		| do read from user space
	bra	Lfdone

/*
 * Error routine for fuswintr.  The fault handler in trap.c
 * checks for pcb_onfault set to this fault handler and
 * "bails out" before calling the VM fault handler.
 * (We can not call VM code from interrupt level.)
 * Same code as Lferr but must have a different address.
 */
ENTRY(fubail)
	nop
Lferr:
	moveq	#-1,%d0			| error indicator
Lfdone:
	clrl	%a1@(PCB_ONFAULT) 	| clear fault handler
	rts

/*
 * suword(void *uaddr, int x);
 * Store an int in the user's address space.
 */
ENTRY(suword)
	CHECK_DFC
	movl	%sp@(4),%a0		| address to write
	movl	%sp@(8),%d0		| value to put there
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#Lserr,%a1@(PCB_ONFAULT)
	movsl	%d0,%a0@		| do write to user space
	moveq	#0,%d0			| indicate no fault
	bra	Lsdone

/*
 * susword(void *uaddr, short x);
 * Store a short in the user's address space.
 */
ENTRY(susword)
	CHECK_DFC
	movl	%sp@(4),%a0		| address to write
	movw	%sp@(10),%d0		| value to put there
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#Lserr,%a1@(PCB_ONFAULT)
	movsw	%d0,%a0@		| do write to user space
	moveq	#0,%d0			| indicate no fault
	bra	Lsdone

/*
 * suswintr(void *uaddr, short x);
 * Store a short in the user's address space.
 * Can be called during an interrupt.
 */
ENTRY(suswintr)
	CHECK_DFC
	movl	%sp@(4),%a0		| address to write
	movw	%sp@(10),%d0		| value to put there
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#_C_LABEL(subail),%a1@(PCB_ONFAULT)
	movsw	%d0,%a0@		| do write to user space
	moveq	#0,%d0			| indicate no fault
	bra	Lsdone

/*
 * subyte(void *uaddr, char x);
 * Store a byte in the user's address space.
 */
ENTRY(subyte)
	CHECK_DFC
	movl	%sp@(4),%a0		| address to write
	movb	%sp@(11),%d0		| value to put there
	movl	_C_LABEL(curpcb),%a1	| set fault handler
	movl	#Lserr,%a1@(PCB_ONFAULT)
	movsb	%d0,%a0@		| do write to user space
	moveq	#0,%d0			| indicate no fault
	bra	Lsdone

/*
 * Error routine for suswintr.  The fault handler in trap.c
 * checks for pcb_onfault set to this fault handler and
 * "bails out" before calling the VM fault handler.
 * (We can not call VM code from interrupt level.)
 * Same code as Lserr but must have a different address.
 */
ENTRY(subail)
	nop
Lserr:
	moveq	#-1,%d0			| error indicator
Lsdone:
	clrl	%a1@(PCB_ONFAULT) 	| clear fault handler
	rts