]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/powerpc/powerpc/copyinout.c
MFC r257702, r257745, r257746, r257747, r257751, r257791, r257793,
[FreeBSD/stable/10.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         struct          thread *td;
254         pmap_t          pm;
255         faultbuf        env;
256         const char      *up;
257         char            *kp;
258         size_t          l;
259         int             rv, c;
260
261         td = curthread;
262         pm = &td->td_proc->p_vmspace->vm_pmap;
263
264         if (setfault(env)) {
265                 td->td_pcb->pcb_onfault = NULL;
266                 return (EFAULT);
267         }
268
269         kp = kaddr;
270         up = udaddr;
271
272         rv = ENAMETOOLONG;
273
274         for (l = 0; len-- > 0; l++) {
275                 if ((c = fubyte(up++)) < 0) {
276                         rv = EFAULT;
277                         break;
278                 }
279
280                 if (!(*kp++ = c)) {
281                         l++;
282                         rv = 0;
283                         break;
284                 }
285         }
286
287         if (done != NULL) {
288                 *done = l;
289         }
290
291         td->td_pcb->pcb_onfault = NULL;
292         return (rv);
293 }
294
295 int
296 subyte(void *addr, int byte)
297 {
298         struct          thread *td;
299         pmap_t          pm;
300         faultbuf        env;
301         char            *p;
302
303         td = curthread;
304         pm = &td->td_proc->p_vmspace->vm_pmap;
305
306         if (setfault(env)) {
307                 td->td_pcb->pcb_onfault = NULL;
308                 return (-1);
309         }
310
311         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
312                 td->td_pcb->pcb_onfault = NULL;
313                 return (-1);
314         }
315
316         *p = (char)byte;
317
318         td->td_pcb->pcb_onfault = NULL;
319         return (0);
320 }
321
322 #ifdef __powerpc64__
323 int
324 suword32(void *addr, int word)
325 {
326         struct          thread *td;
327         pmap_t          pm;
328         faultbuf        env;
329         int             *p;
330
331         td = curthread;
332         pm = &td->td_proc->p_vmspace->vm_pmap;
333
334         if (setfault(env)) {
335                 td->td_pcb->pcb_onfault = NULL;
336                 return (-1);
337         }
338
339         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
340                 td->td_pcb->pcb_onfault = NULL;
341                 return (-1);
342         }
343
344         *p = word;
345
346         td->td_pcb->pcb_onfault = NULL;
347         return (0);
348 }
349 #endif
350
351 int
352 suword(void *addr, long word)
353 {
354         struct          thread *td;
355         pmap_t          pm;
356         faultbuf        env;
357         long            *p;
358
359         td = curthread;
360         pm = &td->td_proc->p_vmspace->vm_pmap;
361
362         if (setfault(env)) {
363                 td->td_pcb->pcb_onfault = NULL;
364                 return (-1);
365         }
366
367         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
368                 td->td_pcb->pcb_onfault = NULL;
369                 return (-1);
370         }
371
372         *p = word;
373
374         td->td_pcb->pcb_onfault = NULL;
375         return (0);
376 }
377
378 #ifdef __powerpc64__
379 int
380 suword64(void *addr, int64_t word)
381 {
382         return (suword(addr, (long)word));
383 }
384 #else
385 int
386 suword32(void *addr, int32_t word)
387 {
388         return (suword(addr, (long)word));
389 }
390 #endif
391
392 int
393 fubyte(const void *addr)
394 {
395         struct          thread *td;
396         pmap_t          pm;
397         faultbuf        env;
398         u_char          *p;
399         int             val;
400
401         td = curthread;
402         pm = &td->td_proc->p_vmspace->vm_pmap;
403
404         if (setfault(env)) {
405                 td->td_pcb->pcb_onfault = NULL;
406                 return (-1);
407         }
408
409         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
410                 td->td_pcb->pcb_onfault = NULL;
411                 return (-1);
412         }
413
414         val = *p;
415
416         td->td_pcb->pcb_onfault = NULL;
417         return (val);
418 }
419
420 #ifdef __powerpc64__
421 int32_t
422 fuword32(const void *addr)
423 {
424         struct          thread *td;
425         pmap_t          pm;
426         faultbuf        env;
427         int32_t         *p, val;
428
429         td = curthread;
430         pm = &td->td_proc->p_vmspace->vm_pmap;
431
432         if (setfault(env)) {
433                 td->td_pcb->pcb_onfault = NULL;
434                 return (-1);
435         }
436
437         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
438                 td->td_pcb->pcb_onfault = NULL;
439                 return (-1);
440         }
441
442         val = *p;
443
444         td->td_pcb->pcb_onfault = NULL;
445         return (val);
446 }
447 #endif
448
449 long
450 fuword(const void *addr)
451 {
452         struct          thread *td;
453         pmap_t          pm;
454         faultbuf        env;
455         long            *p, val;
456
457         td = curthread;
458         pm = &td->td_proc->p_vmspace->vm_pmap;
459
460         if (setfault(env)) {
461                 td->td_pcb->pcb_onfault = NULL;
462                 return (-1);
463         }
464
465         if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
466                 td->td_pcb->pcb_onfault = NULL;
467                 return (-1);
468         }
469
470         val = *p;
471
472         td->td_pcb->pcb_onfault = NULL;
473         return (val);
474 }
475
476 #ifndef __powerpc64__
477 int32_t
478 fuword32(const void *addr)
479 {
480         return ((int32_t)fuword(addr));
481 }
482 #endif
483
484 uint32_t
485 casuword32(volatile uint32_t *addr, uint32_t old, uint32_t new)
486 {
487         struct thread *td;
488         pmap_t pm;
489         faultbuf env;
490         uint32_t *p, val;
491
492         td = curthread;
493         pm = &td->td_proc->p_vmspace->vm_pmap;
494
495         if (setfault(env)) {
496                 td->td_pcb->pcb_onfault = NULL;
497                 return (-1);
498         }
499
500         if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
501             NULL)) {
502                 td->td_pcb->pcb_onfault = NULL;
503                 return (-1);
504         }
505
506         __asm __volatile (
507                 "1:\tlwarx %0, 0, %2\n\t"       /* load old value */
508                 "cmplw %3, %0\n\t"              /* compare */
509                 "bne 2f\n\t"                    /* exit if not equal */
510                 "stwcx. %4, 0, %2\n\t"          /* attempt to store */
511                 "bne- 1b\n\t"                   /* spin if failed */
512                 "b 3f\n\t"                      /* we've succeeded */
513                 "2:\n\t"
514                 "stwcx. %0, 0, %2\n\t"          /* clear reservation (74xx) */
515                 "3:\n\t"
516                 : "=&r" (val), "=m" (*p)
517                 : "r" (p), "r" (old), "r" (new), "m" (*p)
518                 : "cc", "memory");
519
520         td->td_pcb->pcb_onfault = NULL;
521
522         return (val);
523 }
524
525 #ifndef __powerpc64__
526 u_long
527 casuword(volatile u_long *addr, u_long old, u_long new)
528 {
529         return (casuword32((volatile uint32_t *)addr, old, new));
530 }
531 #else
532 u_long
533 casuword(volatile u_long *addr, u_long old, u_long new)
534 {
535         struct thread *td;
536         pmap_t pm;
537         faultbuf env;
538         u_long *p, val;
539
540         td = curthread;
541         pm = &td->td_proc->p_vmspace->vm_pmap;
542
543         if (setfault(env)) {
544                 td->td_pcb->pcb_onfault = NULL;
545                 return (-1);
546         }
547
548         if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p),
549             NULL)) {
550                 td->td_pcb->pcb_onfault = NULL;
551                 return (-1);
552         }
553
554         __asm __volatile (
555                 "1:\tldarx %0, 0, %2\n\t"       /* load old value */
556                 "cmpld %3, %0\n\t"              /* compare */
557                 "bne 2f\n\t"                    /* exit if not equal */
558                 "stdcx. %4, 0, %2\n\t"          /* attempt to store */
559                 "bne- 1b\n\t"                   /* spin if failed */
560                 "b 3f\n\t"                      /* we've succeeded */
561                 "2:\n\t"
562                 "stdcx. %0, 0, %2\n\t"          /* clear reservation (74xx) */
563                 "3:\n\t"
564                 : "=&r" (val), "=m" (*p)
565                 : "r" (p), "r" (old), "r" (new), "m" (*p)
566                 : "cc", "memory");
567
568         td->td_pcb->pcb_onfault = NULL;
569
570         return (val);
571 }
572 #endif
573