xref: /freebsd/sys/powerpc/powerpc/copyinout.c (revision 4f52dfbb)
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 
437 	td = curthread;
438 	pm = &td->td_proc->p_vmspace->vm_pmap;
439 
440 	td->td_pcb->pcb_onfault = &env;
441 	if (setjmp(env)) {
442 		td->td_pcb->pcb_onfault = NULL;
443 		return (-1);
444 	}
445 
446 	if (pmap_map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p,
447 	    sizeof(*p), NULL)) {
448 		td->td_pcb->pcb_onfault = NULL;
449 		return (-1);
450 	}
451 
452 	__asm __volatile (
453 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
454 		"cmplw %3, %0\n\t"		/* compare */
455 		"bne 2f\n\t"			/* exit if not equal */
456 		"stwcx. %4, 0, %2\n\t"      	/* attempt to store */
457 		"bne- 1b\n\t"			/* spin if failed */
458 		"b 3f\n\t"			/* we've succeeded */
459 		"2:\n\t"
460 		"stwcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
461 		"3:\n\t"
462 		: "=&r" (val), "=m" (*p)
463 		: "r" (p), "r" (old), "r" (new), "m" (*p)
464 		: "cr0", "memory");
465 
466 	td->td_pcb->pcb_onfault = NULL;
467 
468 	*oldvalp = val;
469 	return (0);
470 }
471 
472 #ifndef __powerpc64__
473 int
474 casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
475 {
476 
477 	return (casueword32((volatile uint32_t *)addr, old,
478 	    (uint32_t *)oldvalp, new));
479 }
480 #else
481 int
482 casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
483 {
484 	struct thread *td;
485 	pmap_t pm;
486 	jmp_buf		env;
487 	u_long *p, val;
488 
489 	td = curthread;
490 	pm = &td->td_proc->p_vmspace->vm_pmap;
491 
492 	td->td_pcb->pcb_onfault = &env;
493 	if (setjmp(env)) {
494 		td->td_pcb->pcb_onfault = NULL;
495 		return (-1);
496 	}
497 
498 	if (pmap_map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p,
499 	    sizeof(*p), NULL)) {
500 		td->td_pcb->pcb_onfault = NULL;
501 		return (-1);
502 	}
503 
504 	__asm __volatile (
505 		"1:\tldarx %0, 0, %2\n\t"	/* load old value */
506 		"cmpld %3, %0\n\t"		/* compare */
507 		"bne 2f\n\t"			/* exit if not equal */
508 		"stdcx. %4, 0, %2\n\t"      	/* attempt to store */
509 		"bne- 1b\n\t"			/* spin if failed */
510 		"b 3f\n\t"			/* we've succeeded */
511 		"2:\n\t"
512 		"stdcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
513 		"3:\n\t"
514 		: "=&r" (val), "=m" (*p)
515 		: "r" (p), "r" (old), "r" (new), "m" (*p)
516 		: "cr0", "memory");
517 
518 	td->td_pcb->pcb_onfault = NULL;
519 
520 	*oldvalp = val;
521 	return (0);
522 }
523 #endif
524