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