/* $OpenBSD: copy.s,v 1.18 2006/11/29 13:22:07 miod Exp $ */
/* $NetBSD: copy.s,v 1.30 1998/03/04 06:39:14 thorpej Exp $ */
/*-
* Copyright (c) 1998 Jason R. Thorpe. All rights reserved.
* Copyright (c) 1994, 1995 Charles Hannum.
* 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, etc.
*/
#include <machine/asm.h>
#include "assym.h"
.file "copy.s"
.text
#ifdef DEBUG
/*
* 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 /* DEBUG */
#define CHECK_SFC
#define CHECK_DFC
#endif /* DEBUG */
/*
* copyin(caddr_t from, caddr_t 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
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(caddr_t from, caddr_t 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
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(caddr_t from, caddr_t 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 Lcsdone | 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
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(caddr_t from, caddr_t 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
Lcisnull:
cmpl sp@(8),a1 | do not attempt to clear last byte
beq Lcisdone | if we faulted on first write
subql #1, a1
clrb a1@+ | clear last byte
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 Lcisnull
/*
* copyoutstr(caddr_t from, caddr_t 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@(12),sp@- | push dst
movl a6@(8),sp@- | push src
jbsr _C_LABEL(bcopy) | 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