copyinout.c revision 274648
1/*- 2 * Copyright (C) 2002 Benno Rice 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24*/ 25/*- 26 * Copyright (C) 1993 Wolfgang Solfrank. 27 * Copyright (C) 1993 TooLs GmbH. 28 * All rights reserved. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in the 37 * documentation and/or other materials provided with the distribution. 38 * 3. All advertising materials mentioning features or use of this software 39 * must display the following acknowledgement: 40 * This product includes software developed by TooLs GmbH. 41 * 4. The name of TooLs GmbH may not be used to endorse or promote products 42 * derived from this software without specific prior written permission. 43 * 44 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 45 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 46 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 47 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 49 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 50 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 51 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 52 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 53 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 */ 55 56#include <sys/cdefs.h> 57__FBSDID("$FreeBSD: stable/10/sys/powerpc/powerpc/copyinout.c 274648 2014-11-18 12:53:32Z kib $"); 58 59#include <sys/param.h> 60#include <sys/lock.h> 61#include <sys/mutex.h> 62#include <sys/systm.h> 63#include <sys/proc.h> 64 65#include <vm/vm.h> 66#include <vm/pmap.h> 67#include <vm/vm_map.h> 68 69#include <machine/pcb.h> 70#include <machine/sr.h> 71#include <machine/slb.h> 72#include <machine/vmparam.h> 73 74int setfault(faultbuf); /* defined in locore.S */ 75 76#ifdef AIM 77/* 78 * Makes sure that the right segment of userspace is mapped in. 79 */ 80 81#ifdef __powerpc64__ 82static __inline void 83set_user_sr(pmap_t pm, volatile const void *addr) 84{ 85 struct slb *slb; 86 register_t slbv; 87 88 /* Try lockless look-up first */ 89 slb = user_va_to_slb_entry(pm, (vm_offset_t)addr); 90 91 if (slb == NULL) { 92 /* If it isn't there, we need to pre-fault the VSID */ 93 PMAP_LOCK(pm); 94 slbv = va_to_vsid(pm, (vm_offset_t)addr) << SLBV_VSID_SHIFT; 95 PMAP_UNLOCK(pm); 96 } else { 97 slbv = slb->slbv; 98 } 99 100 /* Mark segment no-execute */ 101 slbv |= SLBV_N; 102 103 /* If we have already set this VSID, we can just return */ 104 if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv) 105 return; 106 107 __asm __volatile("isync"); 108 curthread->td_pcb->pcb_cpu.aim.usr_segm = 109 (uintptr_t)addr >> ADDR_SR_SHFT; 110 curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv; 111 __asm __volatile ("slbie %0; slbmte %1, %2; isync" :: 112 "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE)); 113} 114#else 115static __inline void 116set_user_sr(pmap_t pm, volatile const void *addr) 117{ 118 register_t vsid; 119 120 vsid = va_to_vsid(pm, (vm_offset_t)addr); 121 122 /* Mark segment no-execute */ 123 vsid |= SR_N; 124 125 /* If we have already set this VSID, we can just return */ 126 if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid) 127 return; 128 129 __asm __volatile("isync"); 130 curthread->td_pcb->pcb_cpu.aim.usr_segm = 131 (uintptr_t)addr >> ADDR_SR_SHFT; 132 curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid; 133 __asm __volatile("mtsr %0,%1; isync" :: "n"(USER_SR), "r"(vsid)); 134} 135#endif 136 137static __inline int 138map_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen, 139 size_t *klen) 140{ 141 size_t l; 142 143 *kaddr = (char *)USER_ADDR + ((uintptr_t)uaddr & ~SEGMENT_MASK); 144 145 l = ((char *)USER_ADDR + SEGMENT_LENGTH) - (char *)(*kaddr); 146 if (l > ulen) 147 l = ulen; 148 if (klen) 149 *klen = l; 150 else if (l != ulen) 151 return (EFAULT); 152 153 set_user_sr(pm, uaddr); 154 155 return (0); 156} 157#else /* Book-E uses a combined kernel/user mapping */ 158static __inline int 159map_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen, 160 size_t *klen) 161{ 162 163 if ((uintptr_t)uaddr + ulen > VM_MAXUSER_ADDRESS + PAGE_SIZE) 164 return (EFAULT); 165 166 *kaddr = (void *)(uintptr_t)uaddr; 167 if (klen) 168 *klen = ulen; 169 170 return (0); 171} 172#endif 173 174int 175copyout(const void *kaddr, void *udaddr, size_t len) 176{ 177 struct thread *td; 178 pmap_t pm; 179 faultbuf env; 180 const char *kp; 181 char *up, *p; 182 size_t l; 183 184 td = curthread; 185 pm = &td->td_proc->p_vmspace->vm_pmap; 186 187 if (setfault(env)) { 188 td->td_pcb->pcb_onfault = NULL; 189 return (EFAULT); 190 } 191 192 kp = kaddr; 193 up = udaddr; 194 195 while (len > 0) { 196 if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) { 197 td->td_pcb->pcb_onfault = NULL; 198 return (EFAULT); 199 } 200 201 bcopy(kp, p, l); 202 203 up += l; 204 kp += l; 205 len -= l; 206 } 207 208 td->td_pcb->pcb_onfault = NULL; 209 return (0); 210} 211 212int 213copyin(const void *udaddr, void *kaddr, size_t len) 214{ 215 struct thread *td; 216 pmap_t pm; 217 faultbuf env; 218 const char *up; 219 char *kp, *p; 220 size_t l; 221 222 td = curthread; 223 pm = &td->td_proc->p_vmspace->vm_pmap; 224 225 if (setfault(env)) { 226 td->td_pcb->pcb_onfault = NULL; 227 return (EFAULT); 228 } 229 230 kp = kaddr; 231 up = udaddr; 232 233 while (len > 0) { 234 if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) { 235 td->td_pcb->pcb_onfault = NULL; 236 return (EFAULT); 237 } 238 239 bcopy(p, kp, l); 240 241 up += l; 242 kp += l; 243 len -= l; 244 } 245 246 td->td_pcb->pcb_onfault = NULL; 247 return (0); 248} 249 250int 251copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done) 252{ 253 const char *up; 254 char *kp; 255 size_t l; 256 int rv, c; 257 258 kp = kaddr; 259 up = udaddr; 260 261 rv = ENAMETOOLONG; 262 263 for (l = 0; len-- > 0; l++) { 264 if ((c = fubyte(up++)) < 0) { 265 rv = EFAULT; 266 break; 267 } 268 269 if (!(*kp++ = c)) { 270 l++; 271 rv = 0; 272 break; 273 } 274 } 275 276 if (done != NULL) { 277 *done = l; 278 } 279 280 return (rv); 281} 282 283int 284subyte(volatile void *addr, int byte) 285{ 286 struct thread *td; 287 pmap_t pm; 288 faultbuf env; 289 char *p; 290 291 td = curthread; 292 pm = &td->td_proc->p_vmspace->vm_pmap; 293 294 if (setfault(env)) { 295 td->td_pcb->pcb_onfault = NULL; 296 return (-1); 297 } 298 299 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 300 td->td_pcb->pcb_onfault = NULL; 301 return (-1); 302 } 303 304 *p = (char)byte; 305 306 td->td_pcb->pcb_onfault = NULL; 307 return (0); 308} 309 310#ifdef __powerpc64__ 311int 312suword32(volatile void *addr, int word) 313{ 314 struct thread *td; 315 pmap_t pm; 316 faultbuf env; 317 int *p; 318 319 td = curthread; 320 pm = &td->td_proc->p_vmspace->vm_pmap; 321 322 if (setfault(env)) { 323 td->td_pcb->pcb_onfault = NULL; 324 return (-1); 325 } 326 327 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 328 td->td_pcb->pcb_onfault = NULL; 329 return (-1); 330 } 331 332 *p = word; 333 334 td->td_pcb->pcb_onfault = NULL; 335 return (0); 336} 337#endif 338 339int 340suword(volatile void *addr, long word) 341{ 342 struct thread *td; 343 pmap_t pm; 344 faultbuf env; 345 long *p; 346 347 td = curthread; 348 pm = &td->td_proc->p_vmspace->vm_pmap; 349 350 if (setfault(env)) { 351 td->td_pcb->pcb_onfault = NULL; 352 return (-1); 353 } 354 355 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 356 td->td_pcb->pcb_onfault = NULL; 357 return (-1); 358 } 359 360 *p = word; 361 362 td->td_pcb->pcb_onfault = NULL; 363 return (0); 364} 365 366#ifdef __powerpc64__ 367int 368suword64(volatile void *addr, int64_t word) 369{ 370 return (suword(addr, (long)word)); 371} 372#else 373int 374suword32(volatile void *addr, int32_t word) 375{ 376 return (suword(addr, (long)word)); 377} 378#endif 379 380int 381fubyte(volatile const void *addr) 382{ 383 struct thread *td; 384 pmap_t pm; 385 faultbuf env; 386 u_char *p; 387 int val; 388 389 td = curthread; 390 pm = &td->td_proc->p_vmspace->vm_pmap; 391 392 if (setfault(env)) { 393 td->td_pcb->pcb_onfault = NULL; 394 return (-1); 395 } 396 397 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 398 td->td_pcb->pcb_onfault = NULL; 399 return (-1); 400 } 401 402 val = *p; 403 404 td->td_pcb->pcb_onfault = NULL; 405 return (val); 406} 407 408int 409fuword16(volatile const void *addr) 410{ 411 struct thread *td; 412 pmap_t pm; 413 faultbuf env; 414 uint16_t *p, val; 415 416 td = curthread; 417 pm = &td->td_proc->p_vmspace->vm_pmap; 418 419 if (setfault(env)) { 420 td->td_pcb->pcb_onfault = NULL; 421 return (-1); 422 } 423 424 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 425 td->td_pcb->pcb_onfault = NULL; 426 return (-1); 427 } 428 429 val = *p; 430 431 td->td_pcb->pcb_onfault = NULL; 432 return (val); 433} 434 435int 436fueword32(volatile const void *addr, int32_t *val) 437{ 438 struct thread *td; 439 pmap_t pm; 440 faultbuf env; 441 int32_t *p; 442 443 td = curthread; 444 pm = &td->td_proc->p_vmspace->vm_pmap; 445 446 if (setfault(env)) { 447 td->td_pcb->pcb_onfault = NULL; 448 return (-1); 449 } 450 451 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 452 td->td_pcb->pcb_onfault = NULL; 453 return (-1); 454 } 455 456 *val = *p; 457 458 td->td_pcb->pcb_onfault = NULL; 459 return (0); 460} 461 462#ifdef __powerpc64__ 463int 464fueword64(volatile const void *addr, int64_t *val) 465{ 466 struct thread *td; 467 pmap_t pm; 468 faultbuf env; 469 int64_t *p; 470 471 td = curthread; 472 pm = &td->td_proc->p_vmspace->vm_pmap; 473 474 if (setfault(env)) { 475 td->td_pcb->pcb_onfault = NULL; 476 return (-1); 477 } 478 479 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 480 td->td_pcb->pcb_onfault = NULL; 481 return (-1); 482 } 483 484 *val = *p; 485 486 td->td_pcb->pcb_onfault = NULL; 487 return (0); 488} 489#endif 490 491int 492fueword(volatile const void *addr, long *val) 493{ 494 struct thread *td; 495 pmap_t pm; 496 faultbuf env; 497 long *p; 498 499 td = curthread; 500 pm = &td->td_proc->p_vmspace->vm_pmap; 501 502 if (setfault(env)) { 503 td->td_pcb->pcb_onfault = NULL; 504 return (-1); 505 } 506 507 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 508 td->td_pcb->pcb_onfault = NULL; 509 return (-1); 510 } 511 512 *val = *p; 513 514 td->td_pcb->pcb_onfault = NULL; 515 return (0); 516} 517 518int 519casueword32(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp, 520 uint32_t new) 521{ 522 struct thread *td; 523 pmap_t pm; 524 faultbuf env; 525 uint32_t *p, val; 526 527 td = curthread; 528 pm = &td->td_proc->p_vmspace->vm_pmap; 529 530 if (setfault(env)) { 531 td->td_pcb->pcb_onfault = NULL; 532 return (-1); 533 } 534 535 if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), 536 NULL)) { 537 td->td_pcb->pcb_onfault = NULL; 538 return (-1); 539 } 540 541 __asm __volatile ( 542 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 543 "cmplw %3, %0\n\t" /* compare */ 544 "bne 2f\n\t" /* exit if not equal */ 545 "stwcx. %4, 0, %2\n\t" /* attempt to store */ 546 "bne- 1b\n\t" /* spin if failed */ 547 "b 3f\n\t" /* we've succeeded */ 548 "2:\n\t" 549 "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 550 "3:\n\t" 551 : "=&r" (val), "=m" (*p) 552 : "r" (p), "r" (old), "r" (new), "m" (*p) 553 : "cc", "memory"); 554 555 td->td_pcb->pcb_onfault = NULL; 556 557 *oldvalp = val; 558 return (0); 559} 560 561#ifndef __powerpc64__ 562int 563casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new) 564{ 565 566 return (casueword32((volatile uint32_t *)addr, old, 567 (uint32_t *)oldvalp, new)); 568} 569#else 570int 571casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new) 572{ 573 struct thread *td; 574 pmap_t pm; 575 faultbuf env; 576 u_long *p, val; 577 578 td = curthread; 579 pm = &td->td_proc->p_vmspace->vm_pmap; 580 581 if (setfault(env)) { 582 td->td_pcb->pcb_onfault = NULL; 583 return (-1); 584 } 585 586 if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), 587 NULL)) { 588 td->td_pcb->pcb_onfault = NULL; 589 return (-1); 590 } 591 592 __asm __volatile ( 593 "1:\tldarx %0, 0, %2\n\t" /* load old value */ 594 "cmpld %3, %0\n\t" /* compare */ 595 "bne 2f\n\t" /* exit if not equal */ 596 "stdcx. %4, 0, %2\n\t" /* attempt to store */ 597 "bne- 1b\n\t" /* spin if failed */ 598 "b 3f\n\t" /* we've succeeded */ 599 "2:\n\t" 600 "stdcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 601 "3:\n\t" 602 : "=&r" (val), "=m" (*p) 603 : "r" (p), "r" (old), "r" (new), "m" (*p) 604 : "cc", "memory"); 605 606 td->td_pcb->pcb_onfault = NULL; 607 608 *oldvalp = val; 609 return (0); 610} 611#endif 612