]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/copyinout.c
Merge forgotten .h files from vendor branch.
[FreeBSD/FreeBSD.git] / sys / powerpc / powerpc / copyinout.c
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 int     setfault(faultbuf);     /* defined in locore.S */
75
76 #ifdef AIM
77 /*
78  * Makes sure that the right segment of userspace is mapped in.
79  */
80
81 #ifdef __powerpc64__
82 static __inline void
83 set_user_sr(pmap_t pm, const void *addr)
84 {
85         struct slb *slb;
86         register_t slbv;
87
88         /* Try lockless look-up first */
89         slb = user_va_to_slb_entry(pm, (vm_offset_t)addr);
90
91         if (slb == NULL) {
92                 /* If it isn't there, we need to pre-fault the VSID */
93                 PMAP_LOCK(pm);
94                 slbv = va_to_vsid(pm, (vm_offset_t)addr) << SLBV_VSID_SHIFT;
95                 PMAP_UNLOCK(pm);
96         } else {
97                 slbv = slb->slbv;
98         }
99
100         /* Mark segment no-execute */
101         slbv |= SLBV_N;
102
103         /* If we have already set this VSID, we can just return */
104         if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv) 
105                 return;
106
107         __asm __volatile("isync");
108         curthread->td_pcb->pcb_cpu.aim.usr_segm =
109             (uintptr_t)addr >> ADDR_SR_SHFT;
110         curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv;
111         __asm __volatile ("slbie %0; slbmte %1, %2; isync" ::
112             "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE));
113 }
114 #else
115 static __inline void
116 set_user_sr(pmap_t pm, const void *addr)
117 {
118         register_t vsid;
119
120         vsid = va_to_vsid(pm, (vm_offset_t)addr);
121
122         /* Mark segment no-execute */
123         vsid |= SR_N;
124
125         /* If we have already set this VSID, we can just return */
126         if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid)
127                 return;
128
129         __asm __volatile("isync");
130         curthread->td_pcb->pcb_cpu.aim.usr_segm =
131             (uintptr_t)addr >> ADDR_SR_SHFT;
132         curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid;
133         __asm __volatile("mtsr %0,%1; isync" :: "n"(USER_SR), "r"(vsid));
134 }
135 #endif
136
137 static __inline int
138 map_user_ptr(pmap_t pm, const void *uaddr, void **kaddr, size_t ulen,
139     size_t *klen)
140 {
141         size_t l;
142
143         *kaddr = (char *)USER_ADDR + ((uintptr_t)uaddr & ~SEGMENT_MASK);
144
145         l = ((char *)USER_ADDR + SEGMENT_LENGTH) - (char *)(*kaddr);
146         if (l > ulen)
147                 l = ulen;
148         if (klen)
149                 *klen = l;
150         else if (l != ulen)
151                 return (EFAULT);
152
153         set_user_sr(pm, uaddr);
154
155         return (0);
156 }
157 #else /* Book-E uses a combined kernel/user mapping */
158 static __inline int
159 map_user_ptr(pmap_t pm, const void *uaddr, void **kaddr, size_t ulen,
160     size_t *klen)
161 {
162
163         if ((uintptr_t)uaddr + ulen > VM_MAXUSER_ADDRESS + PAGE_SIZE)
164                 return (EFAULT);
165
166         *kaddr = (void *)(uintptr_t)uaddr;
167         if (klen)
168                 *klen = ulen;
169
170         return (0);
171 }
172 #endif
173
174 int
175 copyout(const void *kaddr, void *udaddr, size_t len)
176 {
177         struct          thread *td;
178         pmap_t          pm;
179         faultbuf        env;
180         const char      *kp;
181         char            *up, *p;
182         size_t          l;
183
184         td = curthread;
185         pm = &td->td_proc->p_vmspace->vm_pmap;
186
187         if (setfault(env)) {
188                 td->td_pcb->pcb_onfault = NULL;
189                 return (EFAULT);
190         }
191
192         kp = kaddr;
193         up = udaddr;
194
195         while (len > 0) {
196                 if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) {
197                         td->td_pcb->pcb_onfault = NULL;
198                         return (EFAULT);
199                 }
200
201                 bcopy(kp, p, l);
202
203                 up += l;
204                 kp += l;
205                 len -= l;
206         }
207
208         td->td_pcb->pcb_onfault = NULL;
209         return (0);
210 }
211
212 int
213 copyin(const void *udaddr, void *kaddr, size_t len)
214 {
215         struct          thread *td;
216         pmap_t          pm;
217         faultbuf        env;
218         const char      *up;
219         char            *kp, *p;
220         size_t          l;
221
222         td = curthread;
223         pm = &td->td_proc->p_vmspace->vm_pmap;
224
225         if (setfault(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(void *addr, int byte)
285 {
286         struct          thread *td;
287         pmap_t          pm;
288         faultbuf        env;
289         char            *p;
290
291         td = curthread;
292         pm = &td->td_proc->p_vmspace->vm_pmap;
293
294         if (setfault(env)) {
295                 td->td_pcb->pcb_onfault = NULL;
296                 return (-1);
297         }
298
299         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
300                 td->td_pcb->pcb_onfault = NULL;
301                 return (-1);
302         }
303
304         *p = (char)byte;
305
306         td->td_pcb->pcb_onfault = NULL;
307         return (0);
308 }
309
310 #ifdef __powerpc64__
311 int
312 suword32(void *addr, int word)
313 {
314         struct          thread *td;
315         pmap_t          pm;
316         faultbuf        env;
317         int             *p;
318
319         td = curthread;
320         pm = &td->td_proc->p_vmspace->vm_pmap;
321
322         if (setfault(env)) {
323                 td->td_pcb->pcb_onfault = NULL;
324                 return (-1);
325         }
326
327         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
328                 td->td_pcb->pcb_onfault = NULL;
329                 return (-1);
330         }
331
332         *p = word;
333
334         td->td_pcb->pcb_onfault = NULL;
335         return (0);
336 }
337 #endif
338
339 int
340 suword(void *addr, long word)
341 {
342         struct          thread *td;
343         pmap_t          pm;
344         faultbuf        env;
345         long            *p;
346
347         td = curthread;
348         pm = &td->td_proc->p_vmspace->vm_pmap;
349
350         if (setfault(env)) {
351                 td->td_pcb->pcb_onfault = NULL;
352                 return (-1);
353         }
354
355         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
356                 td->td_pcb->pcb_onfault = NULL;
357                 return (-1);
358         }
359
360         *p = word;
361
362         td->td_pcb->pcb_onfault = NULL;
363         return (0);
364 }
365
366 #ifdef __powerpc64__
367 int
368 suword64(void *addr, int64_t word)
369 {
370         return (suword(addr, (long)word));
371 }
372 #else
373 int
374 suword32(void *addr, int32_t word)
375 {
376         return (suword(addr, (long)word));
377 }
378 #endif
379
380 int
381 fubyte(const void *addr)
382 {
383         struct          thread *td;
384         pmap_t          pm;
385         faultbuf        env;
386         u_char          *p;
387         int             val;
388
389         td = curthread;
390         pm = &td->td_proc->p_vmspace->vm_pmap;
391
392         if (setfault(env)) {
393                 td->td_pcb->pcb_onfault = NULL;
394                 return (-1);
395         }
396
397         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
398                 td->td_pcb->pcb_onfault = NULL;
399                 return (-1);
400         }
401
402         val = *p;
403
404         td->td_pcb->pcb_onfault = NULL;
405         return (val);
406 }
407
408 #ifdef __powerpc64__
409 int32_t
410 fuword32(const void *addr)
411 {
412         struct          thread *td;
413         pmap_t          pm;
414         faultbuf        env;
415         int32_t         *p, val;
416
417         td = curthread;
418         pm = &td->td_proc->p_vmspace->vm_pmap;
419
420         if (setfault(env)) {
421                 td->td_pcb->pcb_onfault = NULL;
422                 return (-1);
423         }
424
425         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
426                 td->td_pcb->pcb_onfault = NULL;
427                 return (-1);
428         }
429
430         val = *p;
431
432         td->td_pcb->pcb_onfault = NULL;
433         return (val);
434 }
435 #endif
436
437 long
438 fuword(const void *addr)
439 {
440         struct          thread *td;
441         pmap_t          pm;
442         faultbuf        env;
443         long            *p, val;
444
445         td = curthread;
446         pm = &td->td_proc->p_vmspace->vm_pmap;
447
448         if (setfault(env)) {
449                 td->td_pcb->pcb_onfault = NULL;
450                 return (-1);
451         }
452
453         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
454                 td->td_pcb->pcb_onfault = NULL;
455                 return (-1);
456         }
457
458         val = *p;
459
460         td->td_pcb->pcb_onfault = NULL;
461         return (val);
462 }
463
464 #ifndef __powerpc64__
465 int32_t
466 fuword32(const void *addr)
467 {
468         return ((int32_t)fuword(addr));
469 }
470 #endif
471
472 uint32_t
473 casuword32(volatile uint32_t *addr, uint32_t old, uint32_t new)
474 {
475         struct thread *td;
476         pmap_t pm;
477         faultbuf env;
478         uint32_t *p, val;
479
480         td = curthread;
481         pm = &td->td_proc->p_vmspace->vm_pmap;
482
483         if (setfault(env)) {
484                 td->td_pcb->pcb_onfault = NULL;
485                 return (-1);
486         }
487
488         if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
489             NULL)) {
490                 td->td_pcb->pcb_onfault = NULL;
491                 return (-1);
492         }
493
494         __asm __volatile (
495                 "1:\tlwarx %0, 0, %2\n\t"       /* load old value */
496                 "cmplw %3, %0\n\t"              /* compare */
497                 "bne 2f\n\t"                    /* exit if not equal */
498                 "stwcx. %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                 "stwcx. %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                 : "cr0", "memory");
507
508         td->td_pcb->pcb_onfault = NULL;
509
510         return (val);
511 }
512
513 #ifndef __powerpc64__
514 u_long
515 casuword(volatile u_long *addr, u_long old, u_long new)
516 {
517         return (casuword32((volatile uint32_t *)addr, old, new));
518 }
519 #else
520 u_long
521 casuword(volatile u_long *addr, u_long old, u_long new)
522 {
523         struct thread *td;
524         pmap_t pm;
525         faultbuf env;
526         u_long *p, val;
527
528         td = curthread;
529         pm = &td->td_proc->p_vmspace->vm_pmap;
530
531         if (setfault(env)) {
532                 td->td_pcb->pcb_onfault = NULL;
533                 return (-1);
534         }
535
536         if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
537             NULL)) {
538                 td->td_pcb->pcb_onfault = NULL;
539                 return (-1);
540         }
541
542         __asm __volatile (
543                 "1:\tldarx %0, 0, %2\n\t"       /* load old value */
544                 "cmpld %3, %0\n\t"              /* compare */
545                 "bne 2f\n\t"                    /* exit if not equal */
546                 "stdcx. %4, 0, %2\n\t"          /* attempt to store */
547                 "bne- 1b\n\t"                   /* spin if failed */
548                 "b 3f\n\t"                      /* we've succeeded */
549                 "2:\n\t"
550                 "stdcx. %0, 0, %2\n\t"          /* clear reservation (74xx) */
551                 "3:\n\t"
552                 : "=&r" (val), "=m" (*p)
553                 : "r" (p), "r" (old), "r" (new), "m" (*p)
554                 : "cr0", "memory");
555
556         td->td_pcb->pcb_onfault = NULL;
557
558         return (val);
559 }
560 #endif
561