1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-4-Clause 3 * 4 * Copyright (C) 2002 Benno Rice 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 /*- 28 * Copyright (C) 1993 Wolfgang Solfrank. 29 * Copyright (C) 1993 TooLs GmbH. 30 * All rights reserved. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by TooLs GmbH. 43 * 4. The name of TooLs GmbH may not be used to endorse or promote products 44 * derived from this software without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 49 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 52 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 54 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 55 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57 58 #include <sys/cdefs.h> 59 __FBSDID("$FreeBSD$"); 60 61 #include <sys/param.h> 62 #include <sys/lock.h> 63 #include <sys/mutex.h> 64 #include <sys/systm.h> 65 #include <sys/proc.h> 66 67 #include <vm/vm.h> 68 #include <vm/pmap.h> 69 #include <vm/vm_extern.h> 70 #include <vm/vm_map.h> 71 72 #include <machine/mmuvar.h> 73 #include <machine/pcb.h> 74 #include <machine/vmparam.h> 75 #include <machine/ifunc.h> 76 77 /* 78 * On powerpc64 (AIM only) the copy functions are IFUNCs, selecting the best 79 * option based on the PMAP in use. 80 * 81 * There are two options for copy functions on powerpc64: 82 * - 'remap' copies, which remap userspace segments into kernel space for 83 * copying. This is used by the 'oea64' pmap. 84 * - 'direct' copies, which copy directly from userspace. This does not require 85 * remapping user segments into kernel. This is used by the 'radix' pmap for 86 * performance. 87 * 88 * Book-E does not use the C 'remap' functions, opting instead to use the 89 * 'direct' copies, directly, avoiding the IFUNC overhead. 90 * 91 * On 32-bit AIM these functions bypass the IFUNC machinery for performance. 92 */ 93 #ifdef __powerpc64__ 94 int subyte_remap(volatile void *addr, int byte); 95 int subyte_direct(volatile void *addr, int byte); 96 int copyinstr_remap(const void *udaddr, void *kaddr, size_t len, size_t *done); 97 int copyinstr_direct(const void *udaddr, void *kaddr, size_t len, size_t *done); 98 int copyout_remap(const void *kaddr, void *udaddr, size_t len); 99 int copyout_direct(const void *kaddr, void *udaddr, size_t len); 100 int copyin_remap(const void *uaddr, void *kaddr, size_t len); 101 int copyin_direct(const void *uaddr, void *kaddr, size_t len); 102 int suword16_remap(volatile void *addr, int word); 103 int suword16_direct(volatile void *addr, int word); 104 int suword32_remap(volatile void *addr, int word); 105 int suword32_direct(volatile void *addr, int word); 106 int suword_remap(volatile void *addr, long word); 107 int suword_direct(volatile void *addr, long word); 108 int suword64_remap(volatile void *addr, int64_t word); 109 int suword64_direct(volatile void *addr, int64_t word); 110 int fubyte_remap(volatile const void *addr); 111 int fubyte_direct(volatile const void *addr); 112 int fuword16_remap(volatile const void *addr); 113 int fuword16_direct(volatile const void *addr); 114 int fueword32_remap(volatile const void *addr, int32_t *val); 115 int fueword32_direct(volatile const void *addr, int32_t *val); 116 int fueword64_remap(volatile const void *addr, int64_t *val); 117 int fueword64_direct(volatile const void *addr, int64_t *val); 118 int fueword_remap(volatile const void *addr, long *val); 119 int fueword_direct(volatile const void *addr, long *val); 120 int casueword32_remap(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp, 121 uint32_t new); 122 int casueword32_direct(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp, 123 uint32_t new); 124 int casueword_remap(volatile u_long *addr, u_long old, u_long *oldvalp, 125 u_long new); 126 int casueword_direct(volatile u_long *addr, u_long old, u_long *oldvalp, 127 u_long new); 128 129 /* 130 * The IFUNC resolver determines the copy based on whether the PMAP 131 * implementation includes a pmap_map_user_ptr function. 132 */ 133 #define DEFINE_COPY_FUNC(ret, func, args) \ 134 DEFINE_IFUNC(, ret, func, args) \ 135 { \ 136 return (PMAP_RESOLVE_FUNC(map_user_ptr) ? \ 137 func##_remap : func##_direct); \ 138 } 139 DEFINE_COPY_FUNC(int, subyte, (volatile void *, int)) 140 DEFINE_COPY_FUNC(int, copyinstr, (const void *, void *, size_t, size_t *)) 141 DEFINE_COPY_FUNC(int, copyin, (const void *, void *, size_t)) 142 DEFINE_COPY_FUNC(int, copyout, (const void *, void *, size_t)) 143 DEFINE_COPY_FUNC(int, suword, (volatile void *, long)) 144 DEFINE_COPY_FUNC(int, suword16, (volatile void *, int)) 145 DEFINE_COPY_FUNC(int, suword32, (volatile void *, int)) 146 DEFINE_COPY_FUNC(int, suword64, (volatile void *, int64_t)) 147 DEFINE_COPY_FUNC(int, fubyte, (volatile const void *)) 148 DEFINE_COPY_FUNC(int, fuword16, (volatile const void *)) 149 DEFINE_COPY_FUNC(int, fueword32, (volatile const void *, int32_t *)) 150 DEFINE_COPY_FUNC(int, fueword64, (volatile const void *, int64_t *)) 151 DEFINE_COPY_FUNC(int, fueword, (volatile const void *, long *)) 152 DEFINE_COPY_FUNC(int, casueword32, 153 (volatile uint32_t *, uint32_t, uint32_t *, uint32_t)) 154 DEFINE_COPY_FUNC(int, casueword, (volatile u_long *, u_long, u_long *, u_long)) 155 156 #define REMAP(x) x##_remap 157 #else 158 #define REMAP(x) x 159 #endif 160 161 int 162 REMAP(copyout)(const void *kaddr, void *udaddr, size_t len) 163 { 164 struct thread *td; 165 pmap_t pm; 166 jmp_buf env; 167 const char *kp; 168 char *up, *p; 169 size_t l; 170 171 td = curthread; 172 pm = &td->td_proc->p_vmspace->vm_pmap; 173 174 td->td_pcb->pcb_onfault = &env; 175 if (setjmp(env)) { 176 td->td_pcb->pcb_onfault = NULL; 177 return (EFAULT); 178 } 179 180 kp = kaddr; 181 up = udaddr; 182 183 while (len > 0) { 184 if (pmap_map_user_ptr(pm, up, (void **)&p, len, &l)) { 185 td->td_pcb->pcb_onfault = NULL; 186 return (EFAULT); 187 } 188 189 bcopy(kp, p, l); 190 191 up += l; 192 kp += l; 193 len -= l; 194 } 195 196 td->td_pcb->pcb_onfault = NULL; 197 return (0); 198 } 199 200 int 201 REMAP(copyin)(const void *udaddr, void *kaddr, size_t len) 202 { 203 struct thread *td; 204 pmap_t pm; 205 jmp_buf env; 206 const char *up; 207 char *kp, *p; 208 size_t l; 209 210 td = curthread; 211 pm = &td->td_proc->p_vmspace->vm_pmap; 212 213 td->td_pcb->pcb_onfault = &env; 214 if (setjmp(env)) { 215 td->td_pcb->pcb_onfault = NULL; 216 return (EFAULT); 217 } 218 219 kp = kaddr; 220 up = udaddr; 221 222 while (len > 0) { 223 if (pmap_map_user_ptr(pm, up, (void **)&p, len, &l)) { 224 td->td_pcb->pcb_onfault = NULL; 225 return (EFAULT); 226 } 227 228 bcopy(p, kp, l); 229 230 up += l; 231 kp += l; 232 len -= l; 233 } 234 235 td->td_pcb->pcb_onfault = NULL; 236 return (0); 237 } 238 239 int 240 REMAP(copyinstr)(const void *udaddr, void *kaddr, size_t len, size_t *done) 241 { 242 struct thread *td; 243 pmap_t pm; 244 jmp_buf env; 245 const char *up; 246 char *kp, *p; 247 size_t i, l, t; 248 int rv; 249 250 td = curthread; 251 pm = &td->td_proc->p_vmspace->vm_pmap; 252 253 t = 0; 254 rv = ENAMETOOLONG; 255 256 td->td_pcb->pcb_onfault = &env; 257 if (setjmp(env)) { 258 rv = EFAULT; 259 goto done; 260 } 261 262 kp = kaddr; 263 up = udaddr; 264 265 while (len > 0) { 266 if (pmap_map_user_ptr(pm, up, (void **)&p, len, &l)) { 267 rv = EFAULT; 268 goto done; 269 } 270 271 for (i = 0; len > 0 && i < l; i++, t++, len--) { 272 if ((*kp++ = *p++) == 0) { 273 i++, t++; 274 rv = 0; 275 goto done; 276 } 277 } 278 279 up += l; 280 } 281 282 done: 283 td->td_pcb->pcb_onfault = NULL; 284 285 if (done != NULL) { 286 *done = t; 287 } 288 289 return (rv); 290 } 291 292 int 293 REMAP(subyte)(volatile void *addr, int byte) 294 { 295 struct thread *td; 296 pmap_t pm; 297 jmp_buf env; 298 char *p; 299 300 td = curthread; 301 pm = &td->td_proc->p_vmspace->vm_pmap; 302 303 td->td_pcb->pcb_onfault = &env; 304 if (setjmp(env)) { 305 td->td_pcb->pcb_onfault = NULL; 306 return (-1); 307 } 308 309 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 310 td->td_pcb->pcb_onfault = NULL; 311 return (-1); 312 } 313 314 *p = (char)byte; 315 316 td->td_pcb->pcb_onfault = NULL; 317 return (0); 318 } 319 320 int 321 REMAP(suword16)(volatile void *addr, int word) 322 { 323 struct thread *td; 324 pmap_t pm; 325 jmp_buf env; 326 int16_t *p; 327 328 td = curthread; 329 pm = &td->td_proc->p_vmspace->vm_pmap; 330 331 td->td_pcb->pcb_onfault = &env; 332 if (setjmp(env)) { 333 td->td_pcb->pcb_onfault = NULL; 334 return (-1); 335 } 336 337 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 338 td->td_pcb->pcb_onfault = NULL; 339 return (-1); 340 } 341 342 *p = (int16_t)word; 343 344 td->td_pcb->pcb_onfault = NULL; 345 return (0); 346 } 347 348 #ifdef __powerpc64__ 349 int 350 REMAP(suword32)(volatile void *addr, int word) 351 { 352 struct thread *td; 353 pmap_t pm; 354 jmp_buf env; 355 int *p; 356 357 td = curthread; 358 pm = &td->td_proc->p_vmspace->vm_pmap; 359 360 td->td_pcb->pcb_onfault = &env; 361 if (setjmp(env)) { 362 td->td_pcb->pcb_onfault = NULL; 363 return (-1); 364 } 365 366 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 367 td->td_pcb->pcb_onfault = NULL; 368 return (-1); 369 } 370 371 *p = word; 372 373 td->td_pcb->pcb_onfault = NULL; 374 return (0); 375 } 376 #else 377 int 378 REMAP(suword32)(volatile void *addr, int32_t word) 379 { 380 REMAP( return (suword)(addr, (long)word)); 381 } 382 #endif 383 384 int 385 REMAP(suword)(volatile void *addr, long word) 386 { 387 struct thread *td; 388 pmap_t pm; 389 jmp_buf env; 390 long *p; 391 392 td = curthread; 393 pm = &td->td_proc->p_vmspace->vm_pmap; 394 395 td->td_pcb->pcb_onfault = &env; 396 if (setjmp(env)) { 397 td->td_pcb->pcb_onfault = NULL; 398 return (-1); 399 } 400 401 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 402 td->td_pcb->pcb_onfault = NULL; 403 return (-1); 404 } 405 406 *p = word; 407 408 td->td_pcb->pcb_onfault = NULL; 409 return (0); 410 } 411 412 #ifdef __powerpc64__ 413 int 414 REMAP(suword64)(volatile void *addr, int64_t word) 415 { 416 return (REMAP(suword)(addr, (long)word)); 417 } 418 #endif 419 420 int 421 REMAP(fubyte)(volatile const void *addr) 422 { 423 struct thread *td; 424 pmap_t pm; 425 jmp_buf env; 426 u_char *p; 427 int val; 428 429 td = curthread; 430 pm = &td->td_proc->p_vmspace->vm_pmap; 431 432 td->td_pcb->pcb_onfault = &env; 433 if (setjmp(env)) { 434 td->td_pcb->pcb_onfault = NULL; 435 return (-1); 436 } 437 438 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 439 td->td_pcb->pcb_onfault = NULL; 440 return (-1); 441 } 442 443 val = *p; 444 445 td->td_pcb->pcb_onfault = NULL; 446 return (val); 447 } 448 449 int 450 REMAP(fuword16)(volatile const void *addr) 451 { 452 struct thread *td; 453 pmap_t pm; 454 jmp_buf env; 455 uint16_t *p, val; 456 457 td = curthread; 458 pm = &td->td_proc->p_vmspace->vm_pmap; 459 460 td->td_pcb->pcb_onfault = &env; 461 if (setjmp(env)) { 462 td->td_pcb->pcb_onfault = NULL; 463 return (-1); 464 } 465 466 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 467 td->td_pcb->pcb_onfault = NULL; 468 return (-1); 469 } 470 471 val = *p; 472 473 td->td_pcb->pcb_onfault = NULL; 474 return (val); 475 } 476 477 int 478 REMAP(fueword32)(volatile const void *addr, int32_t *val) 479 { 480 struct thread *td; 481 pmap_t pm; 482 jmp_buf env; 483 int32_t *p; 484 485 td = curthread; 486 pm = &td->td_proc->p_vmspace->vm_pmap; 487 488 td->td_pcb->pcb_onfault = &env; 489 if (setjmp(env)) { 490 td->td_pcb->pcb_onfault = NULL; 491 return (-1); 492 } 493 494 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 495 td->td_pcb->pcb_onfault = NULL; 496 return (-1); 497 } 498 499 *val = *p; 500 501 td->td_pcb->pcb_onfault = NULL; 502 return (0); 503 } 504 505 #ifdef __powerpc64__ 506 int 507 REMAP(fueword64)(volatile const void *addr, int64_t *val) 508 { 509 struct thread *td; 510 pmap_t pm; 511 jmp_buf env; 512 int64_t *p; 513 514 td = curthread; 515 pm = &td->td_proc->p_vmspace->vm_pmap; 516 517 td->td_pcb->pcb_onfault = &env; 518 if (setjmp(env)) { 519 td->td_pcb->pcb_onfault = NULL; 520 return (-1); 521 } 522 523 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 524 td->td_pcb->pcb_onfault = NULL; 525 return (-1); 526 } 527 528 *val = *p; 529 530 td->td_pcb->pcb_onfault = NULL; 531 return (0); 532 } 533 #endif 534 535 int 536 REMAP(fueword)(volatile const void *addr, long *val) 537 { 538 struct thread *td; 539 pmap_t pm; 540 jmp_buf env; 541 long *p; 542 543 td = curthread; 544 pm = &td->td_proc->p_vmspace->vm_pmap; 545 546 td->td_pcb->pcb_onfault = &env; 547 if (setjmp(env)) { 548 td->td_pcb->pcb_onfault = NULL; 549 return (-1); 550 } 551 552 if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 553 td->td_pcb->pcb_onfault = NULL; 554 return (-1); 555 } 556 557 *val = *p; 558 559 td->td_pcb->pcb_onfault = NULL; 560 return (0); 561 } 562 563 int 564 REMAP(casueword32)(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp, 565 uint32_t new) 566 { 567 struct thread *td; 568 pmap_t pm; 569 jmp_buf env; 570 uint32_t *p, val; 571 int res; 572 573 td = curthread; 574 pm = &td->td_proc->p_vmspace->vm_pmap; 575 576 td->td_pcb->pcb_onfault = &env; 577 if (setjmp(env)) { 578 td->td_pcb->pcb_onfault = NULL; 579 return (-1); 580 } 581 582 if (pmap_map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, 583 sizeof(*p), NULL)) { 584 td->td_pcb->pcb_onfault = NULL; 585 return (-1); 586 } 587 588 res = 0; 589 __asm __volatile ( 590 "lwarx %0, 0, %3\n\t" /* load old value */ 591 "cmplw %4, %0\n\t" /* compare */ 592 "bne 1f\n\t" /* exit if not equal */ 593 "stwcx. %5, 0, %3\n\t" /* attempt to store */ 594 "bne- 2f\n\t" /* if failed */ 595 "b 3f\n\t" /* we've succeeded */ 596 "1:\n\t" 597 "stwcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ 598 "2:li %2, 1\n\t" 599 "3:\n\t" 600 : "=&r" (val), "=m" (*p), "+&r" (res) 601 : "r" (p), "r" (old), "r" (new), "m" (*p) 602 : "cr0", "memory"); 603 604 td->td_pcb->pcb_onfault = NULL; 605 606 *oldvalp = val; 607 return (res); 608 } 609 610 #ifndef __powerpc64__ 611 int 612 REMAP(casueword)(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new) 613 { 614 615 return (casueword32((volatile uint32_t *)addr, old, 616 (uint32_t *)oldvalp, new)); 617 } 618 #else 619 int 620 REMAP(casueword)(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new) 621 { 622 struct thread *td; 623 pmap_t pm; 624 jmp_buf env; 625 u_long *p, val; 626 int res; 627 628 td = curthread; 629 pm = &td->td_proc->p_vmspace->vm_pmap; 630 631 td->td_pcb->pcb_onfault = &env; 632 if (setjmp(env)) { 633 td->td_pcb->pcb_onfault = NULL; 634 return (-1); 635 } 636 637 if (pmap_map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, 638 sizeof(*p), NULL)) { 639 td->td_pcb->pcb_onfault = NULL; 640 return (-1); 641 } 642 643 res = 0; 644 __asm __volatile ( 645 "ldarx %0, 0, %3\n\t" /* load old value */ 646 "cmpld %4, %0\n\t" /* compare */ 647 "bne 1f\n\t" /* exit if not equal */ 648 "stdcx. %5, 0, %3\n\t" /* attempt to store */ 649 "bne- 2f\n\t" /* if failed */ 650 "b 3f\n\t" /* we've succeeded */ 651 "1:\n\t" 652 "stdcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ 653 "2:li %2, 1\n\t" 654 "3:\n\t" 655 : "=&r" (val), "=m" (*p), "+&r" (res) 656 : "r" (p), "r" (old), "r" (new), "m" (*p) 657 : "cr0", "memory"); 658 659 td->td_pcb->pcb_onfault = NULL; 660 661 *oldvalp = val; 662 return (res); 663 } 664 #endif 665