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