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