1241675Suqs/*-
2241675Suqs * Copyright (C) 2002 Benno Rice
3241675Suqs * All rights reserved.
4241675Suqs *
5241675Suqs * Redistribution and use in source and binary forms, with or without
6241675Suqs * modification, are permitted provided that the following conditions
7241675Suqs * are met:
8241675Suqs * 1. Redistributions of source code must retain the above copyright
9241675Suqs *    notice, this list of conditions and the following disclaimer.
10241675Suqs * 2. Redistributions in binary form must reproduce the above copyright
11241675Suqs *    notice, this list of conditions and the following disclaimer in the
12241675Suqs *    documentation and/or other materials provided with the distribution.
13241675Suqs *
14241675Suqs * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15241675Suqs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16241675Suqs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17241675Suqs * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18241675Suqs * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19241675Suqs * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20241675Suqs * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21241675Suqs * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22241675Suqs * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23241675Suqs * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24241675Suqs*/
25241675Suqs/*-
26241675Suqs * Copyright (C) 1993 Wolfgang Solfrank.
27241675Suqs * Copyright (C) 1993 TooLs GmbH.
28241675Suqs * All rights reserved.
29241675Suqs *
30241675Suqs * Redistribution and use in source and binary forms, with or without
31241675Suqs * modification, are permitted provided that the following conditions
32241675Suqs * are met:
33241675Suqs * 1. Redistributions of source code must retain the above copyright
34241675Suqs *    notice, this list of conditions and the following disclaimer.
35241675Suqs * 2. Redistributions in binary form must reproduce the above copyright
36241675Suqs *    notice, this list of conditions and the following disclaimer in the
37241675Suqs *    documentation and/or other materials provided with the distribution.
38241675Suqs * 3. All advertising materials mentioning features or use of this software
39241675Suqs *    must display the following acknowledgement:
40241675Suqs *	This product includes software developed by TooLs GmbH.
41241675Suqs * 4. The name of TooLs GmbH may not be used to endorse or promote products
42241675Suqs *    derived from this software without specific prior written permission.
43241675Suqs *
44241675Suqs * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
45241675Suqs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
46241675Suqs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
47241675Suqs * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
48241675Suqs * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
49241675Suqs * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
50241675Suqs * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
51241675Suqs * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
52241675Suqs * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
53241675Suqs * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54241675Suqs */
55241675Suqs
56241675Suqs#include <sys/cdefs.h>
57241675Suqs__FBSDID("$FreeBSD: releng/10.3/sys/powerpc/powerpc/copyinout.c 274648 2014-11-18 12:53:32Z kib $");
58241675Suqs
59241675Suqs#include <sys/param.h>
60241675Suqs#include <sys/lock.h>
61241675Suqs#include <sys/mutex.h>
62241675Suqs#include <sys/systm.h>
63241675Suqs#include <sys/proc.h>
64241675Suqs
65241675Suqs#include <vm/vm.h>
66241675Suqs#include <vm/pmap.h>
67241675Suqs#include <vm/vm_map.h>
68241675Suqs
69241675Suqs#include <machine/pcb.h>
70241675Suqs#include <machine/sr.h>
71241675Suqs#include <machine/slb.h>
72241675Suqs#include <machine/vmparam.h>
73241675Suqs
74241675Suqsint	setfault(faultbuf);	/* defined in locore.S */
75241675Suqs
76241675Suqs#ifdef AIM
77241675Suqs/*
78241675Suqs * Makes sure that the right segment of userspace is mapped in.
79241675Suqs */
80241675Suqs
81241675Suqs#ifdef __powerpc64__
82241675Suqsstatic __inline void
83241675Suqsset_user_sr(pmap_t pm, volatile const void *addr)
84241675Suqs{
85241675Suqs	struct slb *slb;
86241675Suqs	register_t slbv;
87241675Suqs
88241675Suqs	/* Try lockless look-up first */
89241675Suqs	slb = user_va_to_slb_entry(pm, (vm_offset_t)addr);
90241675Suqs
91241675Suqs	if (slb == NULL) {
92241675Suqs		/* If it isn't there, we need to pre-fault the VSID */
93241675Suqs		PMAP_LOCK(pm);
94241675Suqs		slbv = va_to_vsid(pm, (vm_offset_t)addr) << SLBV_VSID_SHIFT;
95241675Suqs		PMAP_UNLOCK(pm);
96241675Suqs	} else {
97241675Suqs		slbv = slb->slbv;
98241675Suqs	}
99241675Suqs
100241675Suqs	/* Mark segment no-execute */
101241675Suqs	slbv |= SLBV_N;
102241675Suqs
103241675Suqs	/* If we have already set this VSID, we can just return */
104241675Suqs	if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv)
105241675Suqs		return;
106241675Suqs
107241675Suqs	__asm __volatile("isync");
108241675Suqs	curthread->td_pcb->pcb_cpu.aim.usr_segm =
109241675Suqs	    (uintptr_t)addr >> ADDR_SR_SHFT;
110241675Suqs	curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv;
111241675Suqs	__asm __volatile ("slbie %0; slbmte %1, %2; isync" ::
112241675Suqs	    "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE));
113241675Suqs}
114241675Suqs#else
115241675Suqsstatic __inline void
116241675Suqsset_user_sr(pmap_t pm, volatile const void *addr)
117241675Suqs{
118241675Suqs	register_t vsid;
119241675Suqs
120241675Suqs	vsid = va_to_vsid(pm, (vm_offset_t)addr);
121241675Suqs
122241675Suqs	/* Mark segment no-execute */
123241675Suqs	vsid |= SR_N;
124241675Suqs
125241675Suqs	/* If we have already set this VSID, we can just return */
126241675Suqs	if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid)
127241675Suqs		return;
128241675Suqs
129241675Suqs	__asm __volatile("isync");
130241675Suqs	curthread->td_pcb->pcb_cpu.aim.usr_segm =
131241675Suqs	    (uintptr_t)addr >> ADDR_SR_SHFT;
132241675Suqs	curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid;
133241675Suqs	__asm __volatile("mtsr %0,%1; isync" :: "n"(USER_SR), "r"(vsid));
134241675Suqs}
135241675Suqs#endif
136241675Suqs
137241675Suqsstatic __inline int
138241675Suqsmap_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen,
139241675Suqs    size_t *klen)
140241675Suqs{
141241675Suqs	size_t l;
142241675Suqs
143241675Suqs	*kaddr = (char *)USER_ADDR + ((uintptr_t)uaddr & ~SEGMENT_MASK);
144241675Suqs
145241675Suqs	l = ((char *)USER_ADDR + SEGMENT_LENGTH) - (char *)(*kaddr);
146241675Suqs	if (l > ulen)
147241675Suqs		l = ulen;
148241675Suqs	if (klen)
149241675Suqs		*klen = l;
150241675Suqs	else if (l != ulen)
151241675Suqs		return (EFAULT);
152241675Suqs
153241675Suqs	set_user_sr(pm, uaddr);
154241675Suqs
155241675Suqs	return (0);
156241675Suqs}
157241675Suqs#else /* Book-E uses a combined kernel/user mapping */
158241675Suqsstatic __inline int
159241675Suqsmap_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen,
160241675Suqs    size_t *klen)
161241675Suqs{
162241675Suqs
163241675Suqs	if ((uintptr_t)uaddr + ulen > VM_MAXUSER_ADDRESS + PAGE_SIZE)
164241675Suqs		return (EFAULT);
165241675Suqs
166241675Suqs	*kaddr = (void *)(uintptr_t)uaddr;
167241675Suqs	if (klen)
168241675Suqs		*klen = ulen;
169241675Suqs
170241675Suqs	return (0);
171241675Suqs}
172241675Suqs#endif
173241675Suqs
174241675Suqsint
175241675Suqscopyout(const void *kaddr, void *udaddr, size_t len)
176241675Suqs{
177241675Suqs	struct		thread *td;
178241675Suqs	pmap_t		pm;
179241675Suqs	faultbuf	env;
180241675Suqs	const char	*kp;
181241675Suqs	char		*up, *p;
182241675Suqs	size_t		l;
183241675Suqs
184241675Suqs	td = curthread;
185241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
186241675Suqs
187241675Suqs	if (setfault(env)) {
188241675Suqs		td->td_pcb->pcb_onfault = NULL;
189241675Suqs		return (EFAULT);
190241675Suqs	}
191241675Suqs
192241675Suqs	kp = kaddr;
193241675Suqs	up = udaddr;
194241675Suqs
195241675Suqs	while (len > 0) {
196241675Suqs		if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) {
197241675Suqs			td->td_pcb->pcb_onfault = NULL;
198241675Suqs			return (EFAULT);
199241675Suqs		}
200241675Suqs
201241675Suqs		bcopy(kp, p, l);
202241675Suqs
203241675Suqs		up += l;
204241675Suqs		kp += l;
205241675Suqs		len -= l;
206241675Suqs	}
207241675Suqs
208241675Suqs	td->td_pcb->pcb_onfault = NULL;
209241675Suqs	return (0);
210241675Suqs}
211241675Suqs
212241675Suqsint
213241675Suqscopyin(const void *udaddr, void *kaddr, size_t len)
214241675Suqs{
215241675Suqs	struct		thread *td;
216241675Suqs	pmap_t		pm;
217241675Suqs	faultbuf	env;
218241675Suqs	const char	*up;
219241675Suqs	char		*kp, *p;
220241675Suqs	size_t		l;
221241675Suqs
222241675Suqs	td = curthread;
223241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
224241675Suqs
225241675Suqs	if (setfault(env)) {
226241675Suqs		td->td_pcb->pcb_onfault = NULL;
227241675Suqs		return (EFAULT);
228241675Suqs	}
229241675Suqs
230241675Suqs	kp = kaddr;
231241675Suqs	up = udaddr;
232241675Suqs
233241675Suqs	while (len > 0) {
234241675Suqs		if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) {
235241675Suqs			td->td_pcb->pcb_onfault = NULL;
236241675Suqs			return (EFAULT);
237241675Suqs		}
238241675Suqs
239241675Suqs		bcopy(p, kp, l);
240241675Suqs
241241675Suqs		up += l;
242241675Suqs		kp += l;
243241675Suqs		len -= l;
244241675Suqs	}
245241675Suqs
246241675Suqs	td->td_pcb->pcb_onfault = NULL;
247241675Suqs	return (0);
248241675Suqs}
249241675Suqs
250241675Suqsint
251241675Suqscopyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done)
252241675Suqs{
253241675Suqs	const char	*up;
254241675Suqs	char		*kp;
255241675Suqs	size_t		l;
256241675Suqs	int		rv, c;
257241675Suqs
258241675Suqs	kp = kaddr;
259241675Suqs	up = udaddr;
260241675Suqs
261241675Suqs	rv = ENAMETOOLONG;
262241675Suqs
263241675Suqs	for (l = 0; len-- > 0; l++) {
264241675Suqs		if ((c = fubyte(up++)) < 0) {
265241675Suqs			rv = EFAULT;
266241675Suqs			break;
267241675Suqs		}
268241675Suqs
269241675Suqs		if (!(*kp++ = c)) {
270241675Suqs			l++;
271241675Suqs			rv = 0;
272241675Suqs			break;
273241675Suqs		}
274241675Suqs	}
275241675Suqs
276241675Suqs	if (done != NULL) {
277241675Suqs		*done = l;
278241675Suqs	}
279241675Suqs
280241675Suqs	return (rv);
281241675Suqs}
282241675Suqs
283241675Suqsint
284241675Suqssubyte(volatile void *addr, int byte)
285241675Suqs{
286241675Suqs	struct		thread *td;
287241675Suqs	pmap_t		pm;
288241675Suqs	faultbuf	env;
289241675Suqs	char		*p;
290241675Suqs
291241675Suqs	td = curthread;
292241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
293241675Suqs
294241675Suqs	if (setfault(env)) {
295241675Suqs		td->td_pcb->pcb_onfault = NULL;
296241675Suqs		return (-1);
297241675Suqs	}
298241675Suqs
299241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
300241675Suqs		td->td_pcb->pcb_onfault = NULL;
301241675Suqs		return (-1);
302241675Suqs	}
303241675Suqs
304241675Suqs	*p = (char)byte;
305241675Suqs
306241675Suqs	td->td_pcb->pcb_onfault = NULL;
307241675Suqs	return (0);
308241675Suqs}
309241675Suqs
310241675Suqs#ifdef __powerpc64__
311241675Suqsint
312241675Suqssuword32(volatile void *addr, int word)
313241675Suqs{
314241675Suqs	struct		thread *td;
315241675Suqs	pmap_t		pm;
316241675Suqs	faultbuf	env;
317241675Suqs	int		*p;
318241675Suqs
319241675Suqs	td = curthread;
320241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
321241675Suqs
322241675Suqs	if (setfault(env)) {
323241675Suqs		td->td_pcb->pcb_onfault = NULL;
324241675Suqs		return (-1);
325241675Suqs	}
326241675Suqs
327241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
328241675Suqs		td->td_pcb->pcb_onfault = NULL;
329241675Suqs		return (-1);
330241675Suqs	}
331241675Suqs
332241675Suqs	*p = word;
333241675Suqs
334241675Suqs	td->td_pcb->pcb_onfault = NULL;
335241675Suqs	return (0);
336241675Suqs}
337241675Suqs#endif
338241675Suqs
339241675Suqsint
340241675Suqssuword(volatile void *addr, long word)
341241675Suqs{
342241675Suqs	struct		thread *td;
343241675Suqs	pmap_t		pm;
344241675Suqs	faultbuf	env;
345241675Suqs	long		*p;
346241675Suqs
347241675Suqs	td = curthread;
348241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
349241675Suqs
350241675Suqs	if (setfault(env)) {
351241675Suqs		td->td_pcb->pcb_onfault = NULL;
352241675Suqs		return (-1);
353241675Suqs	}
354241675Suqs
355241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
356241675Suqs		td->td_pcb->pcb_onfault = NULL;
357241675Suqs		return (-1);
358241675Suqs	}
359241675Suqs
360241675Suqs	*p = word;
361241675Suqs
362241675Suqs	td->td_pcb->pcb_onfault = NULL;
363241675Suqs	return (0);
364241675Suqs}
365241675Suqs
366241675Suqs#ifdef __powerpc64__
367241675Suqsint
368241675Suqssuword64(volatile void *addr, int64_t word)
369241675Suqs{
370241675Suqs	return (suword(addr, (long)word));
371241675Suqs}
372241675Suqs#else
373241675Suqsint
374241675Suqssuword32(volatile void *addr, int32_t word)
375241675Suqs{
376241675Suqs	return (suword(addr, (long)word));
377241675Suqs}
378241675Suqs#endif
379241675Suqs
380241675Suqsint
381241675Suqsfubyte(volatile const void *addr)
382241675Suqs{
383241675Suqs	struct		thread *td;
384241675Suqs	pmap_t		pm;
385241675Suqs	faultbuf	env;
386241675Suqs	u_char		*p;
387241675Suqs	int		val;
388241675Suqs
389241675Suqs	td = curthread;
390241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
391241675Suqs
392241675Suqs	if (setfault(env)) {
393241675Suqs		td->td_pcb->pcb_onfault = NULL;
394241675Suqs		return (-1);
395241675Suqs	}
396241675Suqs
397241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
398241675Suqs		td->td_pcb->pcb_onfault = NULL;
399241675Suqs		return (-1);
400241675Suqs	}
401241675Suqs
402241675Suqs	val = *p;
403241675Suqs
404241675Suqs	td->td_pcb->pcb_onfault = NULL;
405241675Suqs	return (val);
406241675Suqs}
407241675Suqs
408241675Suqsint
409241675Suqsfuword16(volatile const void *addr)
410241675Suqs{
411241675Suqs	struct		thread *td;
412241675Suqs	pmap_t		pm;
413241675Suqs	faultbuf	env;
414241675Suqs	uint16_t	*p, val;
415241675Suqs
416241675Suqs	td = curthread;
417241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
418241675Suqs
419241675Suqs	if (setfault(env)) {
420241675Suqs		td->td_pcb->pcb_onfault = NULL;
421241675Suqs		return (-1);
422241675Suqs	}
423241675Suqs
424241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
425241675Suqs		td->td_pcb->pcb_onfault = NULL;
426241675Suqs		return (-1);
427241675Suqs	}
428241675Suqs
429241675Suqs	val = *p;
430241675Suqs
431241675Suqs	td->td_pcb->pcb_onfault = NULL;
432241675Suqs	return (val);
433241675Suqs}
434241675Suqs
435241675Suqsint
436241675Suqsfueword32(volatile const void *addr, int32_t *val)
437241675Suqs{
438241675Suqs	struct		thread *td;
439241675Suqs	pmap_t		pm;
440241675Suqs	faultbuf	env;
441241675Suqs	int32_t		*p;
442241675Suqs
443241675Suqs	td = curthread;
444241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
445241675Suqs
446241675Suqs	if (setfault(env)) {
447241675Suqs		td->td_pcb->pcb_onfault = NULL;
448241675Suqs		return (-1);
449241675Suqs	}
450241675Suqs
451241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
452241675Suqs		td->td_pcb->pcb_onfault = NULL;
453241675Suqs		return (-1);
454241675Suqs	}
455241675Suqs
456241675Suqs	*val = *p;
457241675Suqs
458241675Suqs	td->td_pcb->pcb_onfault = NULL;
459241675Suqs	return (0);
460241675Suqs}
461241675Suqs
462241675Suqs#ifdef __powerpc64__
463241675Suqsint
464241675Suqsfueword64(volatile const void *addr, int64_t *val)
465241675Suqs{
466241675Suqs	struct		thread *td;
467241675Suqs	pmap_t		pm;
468241675Suqs	faultbuf	env;
469241675Suqs	int64_t		*p;
470241675Suqs
471241675Suqs	td = curthread;
472241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
473241675Suqs
474241675Suqs	if (setfault(env)) {
475241675Suqs		td->td_pcb->pcb_onfault = NULL;
476241675Suqs		return (-1);
477241675Suqs	}
478241675Suqs
479241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
480241675Suqs		td->td_pcb->pcb_onfault = NULL;
481241675Suqs		return (-1);
482241675Suqs	}
483241675Suqs
484241675Suqs	*val = *p;
485241675Suqs
486241675Suqs	td->td_pcb->pcb_onfault = NULL;
487241675Suqs	return (0);
488241675Suqs}
489241675Suqs#endif
490241675Suqs
491241675Suqsint
492241675Suqsfueword(volatile const void *addr, long *val)
493241675Suqs{
494241675Suqs	struct		thread *td;
495241675Suqs	pmap_t		pm;
496241675Suqs	faultbuf	env;
497241675Suqs	long		*p;
498241675Suqs
499241675Suqs	td = curthread;
500241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
501241675Suqs
502241675Suqs	if (setfault(env)) {
503241675Suqs		td->td_pcb->pcb_onfault = NULL;
504241675Suqs		return (-1);
505241675Suqs	}
506241675Suqs
507241675Suqs	if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
508241675Suqs		td->td_pcb->pcb_onfault = NULL;
509241675Suqs		return (-1);
510241675Suqs	}
511241675Suqs
512241675Suqs	*val = *p;
513241675Suqs
514241675Suqs	td->td_pcb->pcb_onfault = NULL;
515241675Suqs	return (0);
516241675Suqs}
517241675Suqs
518241675Suqsint
519241675Suqscasueword32(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp,
520241675Suqs    uint32_t new)
521241675Suqs{
522241675Suqs	struct thread *td;
523241675Suqs	pmap_t pm;
524241675Suqs	faultbuf env;
525241675Suqs	uint32_t *p, val;
526241675Suqs
527241675Suqs	td = curthread;
528241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
529241675Suqs
530241675Suqs	if (setfault(env)) {
531241675Suqs		td->td_pcb->pcb_onfault = NULL;
532241675Suqs		return (-1);
533241675Suqs	}
534241675Suqs
535241675Suqs	if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
536241675Suqs	    NULL)) {
537241675Suqs		td->td_pcb->pcb_onfault = NULL;
538241675Suqs		return (-1);
539241675Suqs	}
540241675Suqs
541241675Suqs	__asm __volatile (
542241675Suqs		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
543241675Suqs		"cmplw %3, %0\n\t"		/* compare */
544241675Suqs		"bne 2f\n\t"			/* exit if not equal */
545241675Suqs		"stwcx. %4, 0, %2\n\t"      	/* attempt to store */
546241675Suqs		"bne- 1b\n\t"			/* spin if failed */
547241675Suqs		"b 3f\n\t"			/* we've succeeded */
548241675Suqs		"2:\n\t"
549241675Suqs		"stwcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
550241675Suqs		"3:\n\t"
551241675Suqs		: "=&r" (val), "=m" (*p)
552241675Suqs		: "r" (p), "r" (old), "r" (new), "m" (*p)
553241675Suqs		: "cc", "memory");
554241675Suqs
555241675Suqs	td->td_pcb->pcb_onfault = NULL;
556241675Suqs
557241675Suqs	*oldvalp = val;
558241675Suqs	return (0);
559241675Suqs}
560241675Suqs
561241675Suqs#ifndef __powerpc64__
562241675Suqsint
563241675Suqscasueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
564241675Suqs{
565241675Suqs
566241675Suqs	return (casueword32((volatile uint32_t *)addr, old,
567241675Suqs	    (uint32_t *)oldvalp, new));
568241675Suqs}
569241675Suqs#else
570241675Suqsint
571241675Suqscasueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
572241675Suqs{
573241675Suqs	struct thread *td;
574241675Suqs	pmap_t pm;
575241675Suqs	faultbuf env;
576241675Suqs	u_long *p, val;
577241675Suqs
578241675Suqs	td = curthread;
579241675Suqs	pm = &td->td_proc->p_vmspace->vm_pmap;
580241675Suqs
581241675Suqs	if (setfault(env)) {
582241675Suqs		td->td_pcb->pcb_onfault = NULL;
583241675Suqs		return (-1);
584241675Suqs	}
585241675Suqs
586241675Suqs	if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
587241675Suqs	    NULL)) {
588241675Suqs		td->td_pcb->pcb_onfault = NULL;
589241675Suqs		return (-1);
590241675Suqs	}
591241675Suqs
592241675Suqs	__asm __volatile (
593241675Suqs		"1:\tldarx %0, 0, %2\n\t"	/* load old value */
594241675Suqs		"cmpld %3, %0\n\t"		/* compare */
595241675Suqs		"bne 2f\n\t"			/* exit if not equal */
596241675Suqs		"stdcx. %4, 0, %2\n\t"      	/* attempt to store */
597241675Suqs		"bne- 1b\n\t"			/* spin if failed */
598241675Suqs		"b 3f\n\t"			/* we've succeeded */
599241675Suqs		"2:\n\t"
600241675Suqs		"stdcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
601241675Suqs		"3:\n\t"
602241675Suqs		: "=&r" (val), "=m" (*p)
603241675Suqs		: "r" (p), "r" (old), "r" (new), "m" (*p)
604241675Suqs		: "cc", "memory");
605241675Suqs
606241675Suqs	td->td_pcb->pcb_onfault = NULL;
607241675Suqs
608241675Suqs	*oldvalp = val;
609241675Suqs	return (0);
610241675Suqs}
611241675Suqs#endif
612241675Suqs