copyinout.c revision 266001
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 266001 2014-05-14 03:09:37Z ian $"); 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, 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, 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, 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, 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 struct thread *td; 254 pmap_t pm; 255 faultbuf env; 256 const char *up; 257 char *kp; 258 size_t l; 259 int rv, c; 260 261 td = curthread; 262 pm = &td->td_proc->p_vmspace->vm_pmap; 263 264 if (setfault(env)) { 265 td->td_pcb->pcb_onfault = NULL; 266 return (EFAULT); 267 } 268 269 kp = kaddr; 270 up = udaddr; 271 272 rv = ENAMETOOLONG; 273 274 for (l = 0; len-- > 0; l++) { 275 if ((c = fubyte(up++)) < 0) { 276 rv = EFAULT; 277 break; 278 } 279 280 if (!(*kp++ = c)) { 281 l++; 282 rv = 0; 283 break; 284 } 285 } 286 287 if (done != NULL) { 288 *done = l; 289 } 290 291 td->td_pcb->pcb_onfault = NULL; 292 return (rv); 293} 294 295int 296subyte(void *addr, int byte) 297{ 298 struct thread *td; 299 pmap_t pm; 300 faultbuf env; 301 char *p; 302 303 td = curthread; 304 pm = &td->td_proc->p_vmspace->vm_pmap; 305 306 if (setfault(env)) { 307 td->td_pcb->pcb_onfault = NULL; 308 return (-1); 309 } 310 311 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 312 td->td_pcb->pcb_onfault = NULL; 313 return (-1); 314 } 315 316 *p = (char)byte; 317 318 td->td_pcb->pcb_onfault = NULL; 319 return (0); 320} 321 322#ifdef __powerpc64__ 323int 324suword32(void *addr, int word) 325{ 326 struct thread *td; 327 pmap_t pm; 328 faultbuf env; 329 int *p; 330 331 td = curthread; 332 pm = &td->td_proc->p_vmspace->vm_pmap; 333 334 if (setfault(env)) { 335 td->td_pcb->pcb_onfault = NULL; 336 return (-1); 337 } 338 339 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 340 td->td_pcb->pcb_onfault = NULL; 341 return (-1); 342 } 343 344 *p = word; 345 346 td->td_pcb->pcb_onfault = NULL; 347 return (0); 348} 349#endif 350 351int 352suword(void *addr, long word) 353{ 354 struct thread *td; 355 pmap_t pm; 356 faultbuf env; 357 long *p; 358 359 td = curthread; 360 pm = &td->td_proc->p_vmspace->vm_pmap; 361 362 if (setfault(env)) { 363 td->td_pcb->pcb_onfault = NULL; 364 return (-1); 365 } 366 367 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 368 td->td_pcb->pcb_onfault = NULL; 369 return (-1); 370 } 371 372 *p = word; 373 374 td->td_pcb->pcb_onfault = NULL; 375 return (0); 376} 377 378#ifdef __powerpc64__ 379int 380suword64(void *addr, int64_t word) 381{ 382 return (suword(addr, (long)word)); 383} 384#else 385int 386suword32(void *addr, int32_t word) 387{ 388 return (suword(addr, (long)word)); 389} 390#endif 391 392int 393fubyte(const void *addr) 394{ 395 struct thread *td; 396 pmap_t pm; 397 faultbuf env; 398 u_char *p; 399 int val; 400 401 td = curthread; 402 pm = &td->td_proc->p_vmspace->vm_pmap; 403 404 if (setfault(env)) { 405 td->td_pcb->pcb_onfault = NULL; 406 return (-1); 407 } 408 409 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 410 td->td_pcb->pcb_onfault = NULL; 411 return (-1); 412 } 413 414 val = *p; 415 416 td->td_pcb->pcb_onfault = NULL; 417 return (val); 418} 419 420#ifdef __powerpc64__ 421int32_t 422fuword32(const void *addr) 423{ 424 struct thread *td; 425 pmap_t pm; 426 faultbuf env; 427 int32_t *p, val; 428 429 td = curthread; 430 pm = &td->td_proc->p_vmspace->vm_pmap; 431 432 if (setfault(env)) { 433 td->td_pcb->pcb_onfault = NULL; 434 return (-1); 435 } 436 437 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 438 td->td_pcb->pcb_onfault = NULL; 439 return (-1); 440 } 441 442 val = *p; 443 444 td->td_pcb->pcb_onfault = NULL; 445 return (val); 446} 447#endif 448 449long 450fuword(const void *addr) 451{ 452 struct thread *td; 453 pmap_t pm; 454 faultbuf env; 455 long *p, val; 456 457 td = curthread; 458 pm = &td->td_proc->p_vmspace->vm_pmap; 459 460 if (setfault(env)) { 461 td->td_pcb->pcb_onfault = NULL; 462 return (-1); 463 } 464 465 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 466 td->td_pcb->pcb_onfault = NULL; 467 return (-1); 468 } 469 470 val = *p; 471 472 td->td_pcb->pcb_onfault = NULL; 473 return (val); 474} 475 476#ifndef __powerpc64__ 477int32_t 478fuword32(const void *addr) 479{ 480 return ((int32_t)fuword(addr)); 481} 482#endif 483 484uint32_t 485casuword32(volatile uint32_t *addr, uint32_t old, uint32_t new) 486{ 487 struct thread *td; 488 pmap_t pm; 489 faultbuf env; 490 uint32_t *p, val; 491 492 td = curthread; 493 pm = &td->td_proc->p_vmspace->vm_pmap; 494 495 if (setfault(env)) { 496 td->td_pcb->pcb_onfault = NULL; 497 return (-1); 498 } 499 500 if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), 501 NULL)) { 502 td->td_pcb->pcb_onfault = NULL; 503 return (-1); 504 } 505 506 __asm __volatile ( 507 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 508 "cmplw %3, %0\n\t" /* compare */ 509 "bne 2f\n\t" /* exit if not equal */ 510 "stwcx. %4, 0, %2\n\t" /* attempt to store */ 511 "bne- 1b\n\t" /* spin if failed */ 512 "b 3f\n\t" /* we've succeeded */ 513 "2:\n\t" 514 "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 515 "3:\n\t" 516 : "=&r" (val), "=m" (*p) 517 : "r" (p), "r" (old), "r" (new), "m" (*p) 518 : "cc", "memory"); 519 520 td->td_pcb->pcb_onfault = NULL; 521 522 return (val); 523} 524 525#ifndef __powerpc64__ 526u_long 527casuword(volatile u_long *addr, u_long old, u_long new) 528{ 529 return (casuword32((volatile uint32_t *)addr, old, new)); 530} 531#else 532u_long 533casuword(volatile u_long *addr, u_long old, u_long new) 534{ 535 struct thread *td; 536 pmap_t pm; 537 faultbuf env; 538 u_long *p, val; 539 540 td = curthread; 541 pm = &td->td_proc->p_vmspace->vm_pmap; 542 543 if (setfault(env)) { 544 td->td_pcb->pcb_onfault = NULL; 545 return (-1); 546 } 547 548 if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), 549 NULL)) { 550 td->td_pcb->pcb_onfault = NULL; 551 return (-1); 552 } 553 554 __asm __volatile ( 555 "1:\tldarx %0, 0, %2\n\t" /* load old value */ 556 "cmpld %3, %0\n\t" /* compare */ 557 "bne 2f\n\t" /* exit if not equal */ 558 "stdcx. %4, 0, %2\n\t" /* attempt to store */ 559 "bne- 1b\n\t" /* spin if failed */ 560 "b 3f\n\t" /* we've succeeded */ 561 "2:\n\t" 562 "stdcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 563 "3:\n\t" 564 : "=&r" (val), "=m" (*p) 565 : "r" (p), "r" (old), "r" (new), "m" (*p) 566 : "cc", "memory"); 567 568 td->td_pcb->pcb_onfault = NULL; 569 570 return (val); 571} 572#endif 573 574