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