copyinout.c revision 266020
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 266020 2014-05-14 14:17:51Z 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 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(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(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(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(void *addr, int64_t word) 369{ 370 return (suword(addr, (long)word)); 371} 372#else 373int 374suword32(void *addr, int32_t word) 375{ 376 return (suword(addr, (long)word)); 377} 378#endif 379 380int 381fubyte(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 408#ifdef __powerpc64__ 409int32_t 410fuword32(const void *addr) 411{ 412 struct thread *td; 413 pmap_t pm; 414 faultbuf env; 415 int32_t *p, val; 416 417 td = curthread; 418 pm = &td->td_proc->p_vmspace->vm_pmap; 419 420 if (setfault(env)) { 421 td->td_pcb->pcb_onfault = NULL; 422 return (-1); 423 } 424 425 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 426 td->td_pcb->pcb_onfault = NULL; 427 return (-1); 428 } 429 430 val = *p; 431 432 td->td_pcb->pcb_onfault = NULL; 433 return (val); 434} 435#endif 436 437long 438fuword(const void *addr) 439{ 440 struct thread *td; 441 pmap_t pm; 442 faultbuf env; 443 long *p, val; 444 445 td = curthread; 446 pm = &td->td_proc->p_vmspace->vm_pmap; 447 448 if (setfault(env)) { 449 td->td_pcb->pcb_onfault = NULL; 450 return (-1); 451 } 452 453 if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { 454 td->td_pcb->pcb_onfault = NULL; 455 return (-1); 456 } 457 458 val = *p; 459 460 td->td_pcb->pcb_onfault = NULL; 461 return (val); 462} 463 464#ifndef __powerpc64__ 465int32_t 466fuword32(const void *addr) 467{ 468 return ((int32_t)fuword(addr)); 469} 470#endif 471 472uint32_t 473casuword32(volatile uint32_t *addr, uint32_t old, uint32_t new) 474{ 475 struct thread *td; 476 pmap_t pm; 477 faultbuf env; 478 uint32_t *p, val; 479 480 td = curthread; 481 pm = &td->td_proc->p_vmspace->vm_pmap; 482 483 if (setfault(env)) { 484 td->td_pcb->pcb_onfault = NULL; 485 return (-1); 486 } 487 488 if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), 489 NULL)) { 490 td->td_pcb->pcb_onfault = NULL; 491 return (-1); 492 } 493 494 __asm __volatile ( 495 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 496 "cmplw %3, %0\n\t" /* compare */ 497 "bne 2f\n\t" /* exit if not equal */ 498 "stwcx. %4, 0, %2\n\t" /* attempt to store */ 499 "bne- 1b\n\t" /* spin if failed */ 500 "b 3f\n\t" /* we've succeeded */ 501 "2:\n\t" 502 "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 503 "3:\n\t" 504 : "=&r" (val), "=m" (*p) 505 : "r" (p), "r" (old), "r" (new), "m" (*p) 506 : "cc", "memory"); 507 508 td->td_pcb->pcb_onfault = NULL; 509 510 return (val); 511} 512 513#ifndef __powerpc64__ 514u_long 515casuword(volatile u_long *addr, u_long old, u_long new) 516{ 517 return (casuword32((volatile uint32_t *)addr, old, new)); 518} 519#else 520u_long 521casuword(volatile u_long *addr, u_long old, u_long new) 522{ 523 struct thread *td; 524 pmap_t pm; 525 faultbuf env; 526 u_long *p, val; 527 528 td = curthread; 529 pm = &td->td_proc->p_vmspace->vm_pmap; 530 531 if (setfault(env)) { 532 td->td_pcb->pcb_onfault = NULL; 533 return (-1); 534 } 535 536 if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), 537 NULL)) { 538 td->td_pcb->pcb_onfault = NULL; 539 return (-1); 540 } 541 542 __asm __volatile ( 543 "1:\tldarx %0, 0, %2\n\t" /* load old value */ 544 "cmpld %3, %0\n\t" /* compare */ 545 "bne 2f\n\t" /* exit if not equal */ 546 "stdcx. %4, 0, %2\n\t" /* attempt to store */ 547 "bne- 1b\n\t" /* spin if failed */ 548 "b 3f\n\t" /* we've succeeded */ 549 "2:\n\t" 550 "stdcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 551 "3:\n\t" 552 : "=&r" (val), "=m" (*p) 553 : "r" (p), "r" (old), "r" (new), "m" (*p) 554 : "cc", "memory"); 555 556 td->td_pcb->pcb_onfault = NULL; 557 558 return (val); 559} 560#endif 561 562