xref: /freebsd/sys/powerpc/powerpc/copyinout.c (revision d6b92ffa)
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$");
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 
74 #ifdef AIM
75 /*
76  * Makes sure that the right segment of userspace is mapped in.
77  */
78 
79 #ifdef __powerpc64__
80 static __inline void
81 set_user_sr(pmap_t pm, volatile 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 	/* Mark segment no-execute */
99 	slbv |= SLBV_N;
100 
101 	/* If we have already set this VSID, we can just return */
102 	if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv)
103 		return;
104 
105 	__asm __volatile("isync");
106 	curthread->td_pcb->pcb_cpu.aim.usr_segm =
107 	    (uintptr_t)addr >> ADDR_SR_SHFT;
108 	curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv;
109 	__asm __volatile ("slbie %0; slbmte %1, %2; isync" ::
110 	    "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE));
111 }
112 #else
113 static __inline void
114 set_user_sr(pmap_t pm, volatile const void *addr)
115 {
116 	register_t vsid;
117 
118 	vsid = va_to_vsid(pm, (vm_offset_t)addr);
119 
120 	/* Mark segment no-execute */
121 	vsid |= SR_N;
122 
123 	/* If we have already set this VSID, we can just return */
124 	if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid)
125 		return;
126 
127 	__asm __volatile("isync");
128 	curthread->td_pcb->pcb_cpu.aim.usr_segm =
129 	    (uintptr_t)addr >> ADDR_SR_SHFT;
130 	curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid;
131 	__asm __volatile("mtsr %0,%1; isync" :: "n"(USER_SR), "r"(vsid));
132 }
133 #endif
134 
135 static __inline int
136 map_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen,
137     size_t *klen)
138 {
139 	size_t l;
140 
141 	*kaddr = (char *)USER_ADDR + ((uintptr_t)uaddr & ~SEGMENT_MASK);
142 
143 	l = ((char *)USER_ADDR + SEGMENT_LENGTH) - (char *)(*kaddr);
144 	if (l > ulen)
145 		l = ulen;
146 	if (klen)
147 		*klen = l;
148 	else if (l != ulen)
149 		return (EFAULT);
150 
151 	set_user_sr(pm, uaddr);
152 
153 	return (0);
154 }
155 #else /* Book-E uses a combined kernel/user mapping */
156 static __inline int
157 map_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen,
158     size_t *klen)
159 {
160 
161 	if ((uintptr_t)uaddr + ulen > VM_MAXUSER_ADDRESS + PAGE_SIZE)
162 		return (EFAULT);
163 
164 	*kaddr = (void *)(uintptr_t)uaddr;
165 	if (klen)
166 		*klen = ulen;
167 
168 	return (0);
169 }
170 #endif
171 
172 int
173 copyout(const void *kaddr, void *udaddr, size_t len)
174 {
175 	struct		thread *td;
176 	pmap_t		pm;
177 	jmp_buf		env;
178 	const char	*kp;
179 	char		*up, *p;
180 	size_t		l;
181 
182 	td = curthread;
183 	pm = &td->td_proc->p_vmspace->vm_pmap;
184 
185 	td->td_pcb->pcb_onfault = &env;
186 	if (setjmp(env)) {
187 		td->td_pcb->pcb_onfault = NULL;
188 		return (EFAULT);
189 	}
190 
191 	kp = kaddr;
192 	up = udaddr;
193 
194 	while (len > 0) {
195 		if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) {
196 			td->td_pcb->pcb_onfault = NULL;
197 			return (EFAULT);
198 		}
199 
200 		bcopy(kp, p, l);
201 
202 		up += l;
203 		kp += l;
204 		len -= l;
205 	}
206 
207 	td->td_pcb->pcb_onfault = NULL;
208 	return (0);
209 }
210 
211 int
212 copyin(const void *udaddr, void *kaddr, size_t len)
213 {
214 	struct		thread *td;
215 	pmap_t		pm;
216 	jmp_buf		env;
217 	const char	*up;
218 	char		*kp, *p;
219 	size_t		l;
220 
221 	td = curthread;
222 	pm = &td->td_proc->p_vmspace->vm_pmap;
223 
224 	td->td_pcb->pcb_onfault = &env;
225 	if (setjmp(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 
250 int
251 copyinstr(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 
283 int
284 subyte(volatile void *addr, int byte)
285 {
286 	struct		thread *td;
287 	pmap_t		pm;
288 	jmp_buf		env;
289 	char		*p;
290 
291 	td = curthread;
292 	pm = &td->td_proc->p_vmspace->vm_pmap;
293 
294 	td->td_pcb->pcb_onfault = &env;
295 	if (setjmp(env)) {
296 		td->td_pcb->pcb_onfault = NULL;
297 		return (-1);
298 	}
299 
300 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
301 		td->td_pcb->pcb_onfault = NULL;
302 		return (-1);
303 	}
304 
305 	*p = (char)byte;
306 
307 	td->td_pcb->pcb_onfault = NULL;
308 	return (0);
309 }
310 
311 #ifdef __powerpc64__
312 int
313 suword32(volatile void *addr, int word)
314 {
315 	struct		thread *td;
316 	pmap_t		pm;
317 	jmp_buf		env;
318 	int		*p;
319 
320 	td = curthread;
321 	pm = &td->td_proc->p_vmspace->vm_pmap;
322 
323 	td->td_pcb->pcb_onfault = &env;
324 	if (setjmp(env)) {
325 		td->td_pcb->pcb_onfault = NULL;
326 		return (-1);
327 	}
328 
329 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
330 		td->td_pcb->pcb_onfault = NULL;
331 		return (-1);
332 	}
333 
334 	*p = word;
335 
336 	td->td_pcb->pcb_onfault = NULL;
337 	return (0);
338 }
339 #endif
340 
341 int
342 suword(volatile void *addr, long word)
343 {
344 	struct		thread *td;
345 	pmap_t		pm;
346 	jmp_buf		env;
347 	long		*p;
348 
349 	td = curthread;
350 	pm = &td->td_proc->p_vmspace->vm_pmap;
351 
352 	td->td_pcb->pcb_onfault = &env;
353 	if (setjmp(env)) {
354 		td->td_pcb->pcb_onfault = NULL;
355 		return (-1);
356 	}
357 
358 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
359 		td->td_pcb->pcb_onfault = NULL;
360 		return (-1);
361 	}
362 
363 	*p = word;
364 
365 	td->td_pcb->pcb_onfault = NULL;
366 	return (0);
367 }
368 
369 #ifdef __powerpc64__
370 int
371 suword64(volatile void *addr, int64_t word)
372 {
373 	return (suword(addr, (long)word));
374 }
375 #else
376 int
377 suword32(volatile void *addr, int32_t word)
378 {
379 	return (suword(addr, (long)word));
380 }
381 #endif
382 
383 int
384 fubyte(volatile const void *addr)
385 {
386 	struct		thread *td;
387 	pmap_t		pm;
388 	jmp_buf		env;
389 	u_char		*p;
390 	int		val;
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 (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
402 		td->td_pcb->pcb_onfault = NULL;
403 		return (-1);
404 	}
405 
406 	val = *p;
407 
408 	td->td_pcb->pcb_onfault = NULL;
409 	return (val);
410 }
411 
412 int
413 fuword16(volatile const void *addr)
414 {
415 	struct		thread *td;
416 	pmap_t		pm;
417 	jmp_buf		env;
418 	uint16_t	*p, val;
419 
420 	td = curthread;
421 	pm = &td->td_proc->p_vmspace->vm_pmap;
422 
423 	td->td_pcb->pcb_onfault = &env;
424 	if (setjmp(env)) {
425 		td->td_pcb->pcb_onfault = NULL;
426 		return (-1);
427 	}
428 
429 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
430 		td->td_pcb->pcb_onfault = NULL;
431 		return (-1);
432 	}
433 
434 	val = *p;
435 
436 	td->td_pcb->pcb_onfault = NULL;
437 	return (val);
438 }
439 
440 int
441 fueword32(volatile const void *addr, int32_t *val)
442 {
443 	struct		thread *td;
444 	pmap_t		pm;
445 	jmp_buf		env;
446 	int32_t		*p;
447 
448 	td = curthread;
449 	pm = &td->td_proc->p_vmspace->vm_pmap;
450 
451 	td->td_pcb->pcb_onfault = &env;
452 	if (setjmp(env)) {
453 		td->td_pcb->pcb_onfault = NULL;
454 		return (-1);
455 	}
456 
457 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
458 		td->td_pcb->pcb_onfault = NULL;
459 		return (-1);
460 	}
461 
462 	*val = *p;
463 
464 	td->td_pcb->pcb_onfault = NULL;
465 	return (0);
466 }
467 
468 #ifdef __powerpc64__
469 int
470 fueword64(volatile const void *addr, int64_t *val)
471 {
472 	struct		thread *td;
473 	pmap_t		pm;
474 	jmp_buf		env;
475 	int64_t		*p;
476 
477 	td = curthread;
478 	pm = &td->td_proc->p_vmspace->vm_pmap;
479 
480 	td->td_pcb->pcb_onfault = &env;
481 	if (setjmp(env)) {
482 		td->td_pcb->pcb_onfault = NULL;
483 		return (-1);
484 	}
485 
486 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
487 		td->td_pcb->pcb_onfault = NULL;
488 		return (-1);
489 	}
490 
491 	*val = *p;
492 
493 	td->td_pcb->pcb_onfault = NULL;
494 	return (0);
495 }
496 #endif
497 
498 int
499 fueword(volatile const void *addr, long *val)
500 {
501 	struct		thread *td;
502 	pmap_t		pm;
503 	jmp_buf		env;
504 	long		*p;
505 
506 	td = curthread;
507 	pm = &td->td_proc->p_vmspace->vm_pmap;
508 
509 	td->td_pcb->pcb_onfault = &env;
510 	if (setjmp(env)) {
511 		td->td_pcb->pcb_onfault = NULL;
512 		return (-1);
513 	}
514 
515 	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
516 		td->td_pcb->pcb_onfault = NULL;
517 		return (-1);
518 	}
519 
520 	*val = *p;
521 
522 	td->td_pcb->pcb_onfault = NULL;
523 	return (0);
524 }
525 
526 int
527 casueword32(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp,
528     uint32_t new)
529 {
530 	struct thread *td;
531 	pmap_t pm;
532 	jmp_buf		env;
533 	uint32_t *p, val;
534 
535 	td = curthread;
536 	pm = &td->td_proc->p_vmspace->vm_pmap;
537 
538 	td->td_pcb->pcb_onfault = &env;
539 	if (setjmp(env)) {
540 		td->td_pcb->pcb_onfault = NULL;
541 		return (-1);
542 	}
543 
544 	if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
545 	    NULL)) {
546 		td->td_pcb->pcb_onfault = NULL;
547 		return (-1);
548 	}
549 
550 	__asm __volatile (
551 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
552 		"cmplw %3, %0\n\t"		/* compare */
553 		"bne 2f\n\t"			/* exit if not equal */
554 		"stwcx. %4, 0, %2\n\t"      	/* attempt to store */
555 		"bne- 1b\n\t"			/* spin if failed */
556 		"b 3f\n\t"			/* we've succeeded */
557 		"2:\n\t"
558 		"stwcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
559 		"3:\n\t"
560 		: "=&r" (val), "=m" (*p)
561 		: "r" (p), "r" (old), "r" (new), "m" (*p)
562 		: "cr0", "memory");
563 
564 	td->td_pcb->pcb_onfault = NULL;
565 
566 	*oldvalp = val;
567 	return (0);
568 }
569 
570 #ifndef __powerpc64__
571 int
572 casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
573 {
574 
575 	return (casueword32((volatile uint32_t *)addr, old,
576 	    (uint32_t *)oldvalp, new));
577 }
578 #else
579 int
580 casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
581 {
582 	struct thread *td;
583 	pmap_t pm;
584 	jmp_buf		env;
585 	u_long *p, val;
586 
587 	td = curthread;
588 	pm = &td->td_proc->p_vmspace->vm_pmap;
589 
590 	td->td_pcb->pcb_onfault = &env;
591 	if (setjmp(env)) {
592 		td->td_pcb->pcb_onfault = NULL;
593 		return (-1);
594 	}
595 
596 	if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
597 	    NULL)) {
598 		td->td_pcb->pcb_onfault = NULL;
599 		return (-1);
600 	}
601 
602 	__asm __volatile (
603 		"1:\tldarx %0, 0, %2\n\t"	/* load old value */
604 		"cmpld %3, %0\n\t"		/* compare */
605 		"bne 2f\n\t"			/* exit if not equal */
606 		"stdcx. %4, 0, %2\n\t"      	/* attempt to store */
607 		"bne- 1b\n\t"			/* spin if failed */
608 		"b 3f\n\t"			/* we've succeeded */
609 		"2:\n\t"
610 		"stdcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
611 		"3:\n\t"
612 		: "=&r" (val), "=m" (*p)
613 		: "r" (p), "r" (old), "r" (new), "m" (*p)
614 		: "cr0", "memory");
615 
616 	td->td_pcb->pcb_onfault = NULL;
617 
618 	*oldvalp = val;
619 	return (0);
620 }
621 #endif
622