/* $NetBSD: copy.s,v 1.50 2023/09/26 14:33:55 tsutsui Exp $ */ /*- * Copyright (c) 1998, 2019 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, ufetch/ustore, etc. */ #include #include #include "assym.h" .file "copy.s" .text #ifdef CI_CURPCB #define GETCURPCB(r) movl _C_LABEL(cpu_info_store)+CI_CURPCB,r #else #define GETCURPCB(r) movl _C_LABEL(curpcb),r #endif #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") jra .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 12(%sp),%d0 | check count jeq .Lciret | == 0, don't do anything movl %d2,-(%sp) | save scratch register GETCURPCB(%a0) | set fault handler movl #.Lcifault,PCB_ONFAULT(%a0) movl 8(%sp),%a0 | src address movl 12(%sp),%a1 | dest address movl %a0,%d1 btst #0,%d1 | src address odd? jeq .Lcieven | no, skip alignment movsb (%a0)+,%d2 | yes, copy a byte movb %d2,(%a1)+ subql #1,%d0 | adjust count jeq .Lcidone | count 0, all done .Lcieven: movl %a1,%d1 btst #0,%d1 | dest address odd? jne .Lcibytes | yes, must copy bytes movl %d0,%d1 | OK, both even. Get count lsrl #2,%d1 | and convert to longwords jeq .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 jcc .Lcilloop andl #3,%d0 | what remains jeq .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 jcc .Lcibloop clrl %d0 | no error .Lcidone: GETCURPCB(%a0) | clear fault handler clrl PCB_ONFAULT(%a0) movl (%sp)+,%d2 | restore scratch register .Lciret: rts .Lcifault: jra .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 12(%sp),%d0 | check count jeq .Lcoret | == 0, don't do anything movl %d2,-(%sp) | save scratch register GETCURPCB(%a0) | set fault handler movl #.Lcofault,PCB_ONFAULT(%a0) movl 8(%sp),%a0 | src address movl 12(%sp),%a1 | dest address movl %a0,%d1 btst #0,%d1 | src address odd? jeq .Lcoeven | no, skip alignment movb (%a0)+,%d2 | yes, copy a byte movsb %d2,(%a1)+ subql #1,%d0 | adjust count jeq .Lcodone | count 0, all done .Lcoeven: movl %a1,%d1 btst #0,%d1 | dest address odd? jne .Lcobytes | yes, must copy bytes movl %d0,%d1 | OK, both even. Get count lsrl #2,%d1 | and convert to longwords jeq .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 jcc .Lcolloop andl #3,%d0 | what remains jeq .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 jcc .Lcobloop clrl %d0 | no error .Lcodone: GETCURPCB(%a0) | clear fault handler clrl PCB_ONFAULT(%a0) movl (%sp)+,%d2 | restore scratch register .Lcoret: rts .Lcofault: jra .Lcodone /* * 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 GETCURPCB(%a0) | set fault handler movl #.Lcisfault,PCB_ONFAULT(%a0) movl 4(%sp),%a0 | a0 = fromaddr movl 8(%sp),%a1 | a1 = toaddr clrl %d0 movl 12(%sp),%d1 | count jeq .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 jeq .Lcisdone | copied null, exit subil #0x10000,%d1 | decrement high word of count jcc .Lcisloop | more room, keep going .Lcistoolong: moveq #ENAMETOOLONG,%d0 | ran out of space .Lcisdone: tstl 16(%sp) | length desired? jeq .Lcisexit subl 4(%sp),%a0 | yes, calculate length copied movl 16(%sp),%a1 | store at return location movl %a0,(%a1) .Lcisexit: GETCURPCB(%a0) | clear fault handler clrl PCB_ONFAULT(%a0) rts .Lcisfault: jra .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 GETCURPCB(%a0) | set fault handler movl #.Lcosfault,PCB_ONFAULT(%a0) movl 4(%sp),%a0 | a0 = fromaddr movl 8(%sp),%a1 | a1 = toaddr clrl %d0 movl 12(%sp),%d1 | count jeq .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 jeq .Lcosdone | copied null, exit subil #0x10000,%d1 | decrement high word of count jcc .Lcosloop | more room, keep going .Lcostoolong: moveq #ENAMETOOLONG,%d0 | ran out of space .Lcosdone: tstl 16(%sp) | length desired? jeq .Lcosexit subl 4(%sp),%a0 | yes, calculate length copied movl 16(%sp),%a1 | store at return location movl %a0,(%a1) .Lcosexit: GETCURPCB(%a0) | clear fault handler clrl PCB_ONFAULT(%a0) rts .Lcosfault: jra .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 GETCURPCB(%a0) | set fault handler movl PCB_ONFAULT(%a0),-4(%a6) | save old handler first movl #.Lkcfault,PCB_ONFAULT(%a0) movl 16(%a6),-(%sp) | push len movl 8(%a6),-(%sp) | push src movl 12(%a6),-(%sp) | push dst jbsr _C_LABEL(memcpy) | copy it addl #12,%sp | pop args clrl %d0 | success! .Lkcdone: GETCURPCB(%a0) | restore fault handler movl -4(%a6),PCB_ONFAULT(%a0) unlk %a6 rts .Lkcfault: addl #16,%sp | pop args and return address jra .Lkcdone #define UFETCH_PROLOGUE \ CHECK_SFC ; \ movl 4(%sp),%a0 /* address to read */ ; \ GETCURPCB(%a1) /* a1 = curpcb */ ; \ movl #.Lufetchstore_fault,PCB_ONFAULT(%a1) /* LINTSTUB: _ufetch_8(const uint8_t *uaddr, uint8_t *valp); */ ENTRY(_ufetch_8) UFETCH_PROLOGUE movsb (%a0),%d0 | do read from user space movl 8(%sp),%a0 | destination address movb %d0,(%a0) jra .Lufetchstore_success /* LINTSTUB: _ufetch_16(const uint16_t *uaddr, uint16_t *valp); */ ENTRY(_ufetch_16) UFETCH_PROLOGUE movsw (%a0),%d0 | do read from user space movl 8(%sp),%a0 | destination address movw %d0,(%a0) jra .Lufetchstore_success /* LINTSTUB: _ufetch_32(const uint32_t *uaddr, uint32_t *valp); */ ENTRY(_ufetch_32) UFETCH_PROLOGUE movsl (%a0),%d0 | do read from user space movl 8(%sp),%a0 | destination address movl %d0,(%a0) jra .Lufetchstore_success #define USTORE_PROLOGUE \ CHECK_DFC ; \ movl 4(%sp),%a0 /* address to write */ ; \ GETCURPCB(%a1) /* a1 = curpcb */ ; \ movl #.Lufetchstore_fault,PCB_ONFAULT(%a1) /* LINTSTUB: _ustore_8(uint8_t *uaddr, uint8_t val); */ ENTRY(_ustore_8) USTORE_PROLOGUE movb 11(%sp),%d0 | value to store movsb %d0,(%a0) | do write to user space jra .Lufetchstore_success /* LINTSTUB: _ustore_16(uint16_t *uaddr, uint16_t val); */ ENTRY(_ustore_16) USTORE_PROLOGUE movw 10(%sp),%d0 | value to store movsw %d0,(%a0) | do write to user space jra .Lufetchstore_success /* LINTSTUB: _ustore_32(uint32_t *uaddr, uint32_t val); */ ENTRY(_ustore_32) USTORE_PROLOGUE movl 8(%sp),%d0 | value to store movsl %d0,(%a0) | do write to user space jra .Lufetchstore_success .Lufetchstore_success: clrl %d0 | return 0 .Lufetchstore_fault: clrl PCB_ONFAULT(%a1) | clear fault handler rts