copyinout.c revision 214574
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: head/sys/powerpc/aim/copyinout.c 214574 2010-10-30 23:07:30Z nwhitehorn $"); 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 73int setfault(faultbuf); /* defined in locore.S */ 74 75/* 76 * Makes sure that the right segment of userspace is mapped in. 77 */ 78 79#ifdef __powerpc64__ 80static __inline void 81set_user_sr(pmap_t pm, const void *addr) 82{ 83 struct slb *slb; 84 register_t slbv; 85 86 /* Try lockless look-up first */ 87 slb = user_va_to_slb_entry(pm, (vm_offset_t)addr); 88 89 if (slb == NULL) { 90 /* If it isn't there, we need to pre-fault the VSID */ 91 PMAP_LOCK(pm); 92 slbv = va_to_vsid(pm, (vm_offset_t)addr) << SLBV_VSID_SHIFT; 93 PMAP_UNLOCK(pm); 94 } else { 95 slbv = slb->slbv; 96 } 97 98 /* If we have already set this VSID, we can just return */ 99 if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv) 100 return; 101 102 __asm __volatile ("isync; slbie %0; slbmte %1, %2; isync" :: 103 "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE)); 104 curthread->td_pcb->pcb_cpu.aim.usr_segm = 105 (uintptr_t)addr >> ADDR_SR_SHFT; 106 curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv; 107} 108#else 109static __inline void 110set_user_sr(pmap_t pm, const void *addr) 111{ 112 register_t vsid; 113 114 vsid = va_to_vsid(pm, (vm_offset_t)addr); 115 116 /* If we have already set this VSID, we can just return */ 117 if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid) 118 return; 119 120 __asm __volatile ("sync; mtsr %0,%1; sync; isync" :: "n"(USER_SR), 121 "r"(vsid)); 122 curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid; 123} 124#endif 125 126int 127copyout(const void *kaddr, void *udaddr, size_t len) 128{ 129 struct thread *td; 130 pmap_t pm; 131 faultbuf env; 132 const char *kp; 133 char *up, *p; 134 size_t l; 135 136 td = PCPU_GET(curthread); 137 pm = &td->td_proc->p_vmspace->vm_pmap; 138 139 if (setfault(env)) { 140 td->td_pcb->pcb_onfault = NULL; 141 return (EFAULT); 142 } 143 144 kp = kaddr; 145 up = udaddr; 146 147 while (len > 0) { 148 p = (char *)USER_ADDR + ((uintptr_t)up & ~SEGMENT_MASK); 149 150 l = ((char *)USER_ADDR + SEGMENT_LENGTH) - p; 151 if (l > len) 152 l = len; 153 154 set_user_sr(pm,up); 155 156 bcopy(kp, p, l); 157 158 up += l; 159 kp += l; 160 len -= l; 161 } 162 163 td->td_pcb->pcb_onfault = NULL; 164 return (0); 165} 166 167int 168copyin(const void *udaddr, void *kaddr, size_t len) 169{ 170 struct thread *td; 171 pmap_t pm; 172 faultbuf env; 173 const char *up; 174 char *kp, *p; 175 size_t l; 176 177 td = PCPU_GET(curthread); 178 pm = &td->td_proc->p_vmspace->vm_pmap; 179 180 if (setfault(env)) { 181 td->td_pcb->pcb_onfault = NULL; 182 return (EFAULT); 183 } 184 185 kp = kaddr; 186 up = udaddr; 187 188 while (len > 0) { 189 p = (char *)USER_ADDR + ((uintptr_t)up & ~SEGMENT_MASK); 190 191 l = ((char *)USER_ADDR + SEGMENT_LENGTH) - p; 192 if (l > len) 193 l = len; 194 195 set_user_sr(pm,up); 196 197 bcopy(p, kp, l); 198 199 up += l; 200 kp += l; 201 len -= l; 202 } 203 204 td->td_pcb->pcb_onfault = NULL; 205 return (0); 206} 207 208int 209copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done) 210{ 211 struct thread *td; 212 pmap_t pm; 213 faultbuf env; 214 const char *up; 215 char *kp; 216 size_t l; 217 int rv, c; 218 219 td = PCPU_GET(curthread); 220 pm = &td->td_proc->p_vmspace->vm_pmap; 221 222 if (setfault(env)) { 223 td->td_pcb->pcb_onfault = NULL; 224 return (EFAULT); 225 } 226 227 kp = kaddr; 228 up = udaddr; 229 230 rv = ENAMETOOLONG; 231 232 for (l = 0; len-- > 0; l++) { 233 if ((c = fubyte(up++)) < 0) { 234 rv = EFAULT; 235 break; 236 } 237 238 if (!(*kp++ = c)) { 239 l++; 240 rv = 0; 241 break; 242 } 243 } 244 245 if (done != NULL) { 246 *done = l; 247 } 248 249 td->td_pcb->pcb_onfault = NULL; 250 return (rv); 251} 252 253int 254subyte(void *addr, int byte) 255{ 256 struct thread *td; 257 pmap_t pm; 258 faultbuf env; 259 char *p; 260 261 td = PCPU_GET(curthread); 262 pm = &td->td_proc->p_vmspace->vm_pmap; 263 p = (char *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 264 265 if (setfault(env)) { 266 td->td_pcb->pcb_onfault = NULL; 267 return (-1); 268 } 269 270 set_user_sr(pm,addr); 271 272 *p = (char)byte; 273 274 td->td_pcb->pcb_onfault = NULL; 275 return (0); 276} 277 278#ifdef __powerpc64__ 279int 280suword32(void *addr, int word) 281{ 282 struct thread *td; 283 pmap_t pm; 284 faultbuf env; 285 int *p; 286 287 td = PCPU_GET(curthread); 288 pm = &td->td_proc->p_vmspace->vm_pmap; 289 p = (int *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 290 291 if (setfault(env)) { 292 td->td_pcb->pcb_onfault = NULL; 293 return (-1); 294 } 295 296 set_user_sr(pm,addr); 297 298 *p = word; 299 300 td->td_pcb->pcb_onfault = NULL; 301 return (0); 302} 303#endif 304 305int 306suword(void *addr, long word) 307{ 308 struct thread *td; 309 pmap_t pm; 310 faultbuf env; 311 long *p; 312 313 td = PCPU_GET(curthread); 314 pm = &td->td_proc->p_vmspace->vm_pmap; 315 p = (long *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 316 317 if (setfault(env)) { 318 td->td_pcb->pcb_onfault = NULL; 319 return (-1); 320 } 321 322 set_user_sr(pm,addr); 323 324 *p = word; 325 326 td->td_pcb->pcb_onfault = NULL; 327 return (0); 328} 329 330#ifdef __powerpc64__ 331int 332suword64(void *addr, int64_t word) 333{ 334 return (suword(addr, (long)word)); 335} 336#else 337int 338suword32(void *addr, int32_t word) 339{ 340 return (suword(addr, (long)word)); 341} 342#endif 343 344int 345fubyte(const void *addr) 346{ 347 struct thread *td; 348 pmap_t pm; 349 faultbuf env; 350 u_char *p; 351 int val; 352 353 td = PCPU_GET(curthread); 354 pm = &td->td_proc->p_vmspace->vm_pmap; 355 p = (u_char *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 356 357 if (setfault(env)) { 358 td->td_pcb->pcb_onfault = NULL; 359 return (-1); 360 } 361 362 set_user_sr(pm,addr); 363 364 val = *p; 365 366 td->td_pcb->pcb_onfault = NULL; 367 return (val); 368} 369 370#ifdef __powerpc64__ 371int32_t 372fuword32(const void *addr) 373{ 374 struct thread *td; 375 pmap_t pm; 376 faultbuf env; 377 int32_t *p, val; 378 379 td = PCPU_GET(curthread); 380 pm = &td->td_proc->p_vmspace->vm_pmap; 381 p = (int32_t *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 382 383 if (setfault(env)) { 384 td->td_pcb->pcb_onfault = NULL; 385 return (-1); 386 } 387 388 set_user_sr(pm,addr); 389 390 val = *p; 391 392 td->td_pcb->pcb_onfault = NULL; 393 return (val); 394} 395#endif 396 397long 398fuword(const void *addr) 399{ 400 struct thread *td; 401 pmap_t pm; 402 faultbuf env; 403 long *p, val; 404 405 td = PCPU_GET(curthread); 406 pm = &td->td_proc->p_vmspace->vm_pmap; 407 p = (long *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 408 409 if (setfault(env)) { 410 td->td_pcb->pcb_onfault = NULL; 411 return (-1); 412 } 413 414 set_user_sr(pm,addr); 415 416 val = *p; 417 418 td->td_pcb->pcb_onfault = NULL; 419 return (val); 420} 421 422#ifndef __powerpc64__ 423int32_t 424fuword32(const void *addr) 425{ 426 return ((int32_t)fuword(addr)); 427} 428#endif 429 430uint32_t 431casuword32(volatile uint32_t *addr, uint32_t old, uint32_t new) 432{ 433 struct thread *td; 434 pmap_t pm; 435 faultbuf env; 436 uint32_t *p, val; 437 438 td = PCPU_GET(curthread); 439 pm = &td->td_proc->p_vmspace->vm_pmap; 440 p = (uint32_t *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 441 442 set_user_sr(pm,(const void *)(vm_offset_t)addr); 443 444 if (setfault(env)) { 445 td->td_pcb->pcb_onfault = NULL; 446 return (-1); 447 } 448 449 __asm __volatile ( 450 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 451 "cmplw %3, %0\n\t" /* compare */ 452 "bne 2f\n\t" /* exit if not equal */ 453 "stwcx. %4, 0, %2\n\t" /* attempt to store */ 454 "bne- 1b\n\t" /* spin if failed */ 455 "b 3f\n\t" /* we've succeeded */ 456 "2:\n\t" 457 "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 458 "3:\n\t" 459 : "=&r" (val), "=m" (*p) 460 : "r" (p), "r" (old), "r" (new), "m" (*p) 461 : "cc", "memory"); 462 463 td->td_pcb->pcb_onfault = NULL; 464 465 return (val); 466} 467 468#ifndef __powerpc64__ 469u_long 470casuword(volatile u_long *addr, u_long old, u_long new) 471{ 472 return (casuword32((volatile uint32_t *)addr, old, new)); 473} 474#else 475u_long 476casuword(volatile u_long *addr, u_long old, u_long new) 477{ 478 struct thread *td; 479 pmap_t pm; 480 faultbuf env; 481 u_long *p, val; 482 483 td = PCPU_GET(curthread); 484 pm = &td->td_proc->p_vmspace->vm_pmap; 485 p = (u_long *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK)); 486 487 set_user_sr(pm,(const void *)(vm_offset_t)addr); 488 489 if (setfault(env)) { 490 td->td_pcb->pcb_onfault = NULL; 491 return (-1); 492 } 493 494 __asm __volatile ( 495 "1:\tldarx %0, 0, %2\n\t" /* load old value */ 496 "cmpld %3, %0\n\t" /* compare */ 497 "bne 2f\n\t" /* exit if not equal */ 498 "stdcx. %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 "stdcx. %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#endif 513 514