]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/copyinout.c
libucl: vendor import snapshort 20210314
[FreeBSD/FreeBSD.git] / sys / powerpc / powerpc / copyinout.c
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_extern.h>
70 #include <vm/vm_map.h>
71
72 #include <machine/mmuvar.h>
73 #include <machine/pcb.h>
74 #include <machine/vmparam.h>
75 #include <machine/ifunc.h>
76
77 /*
78  * On powerpc64 (AIM only) the copy functions are IFUNCs, selecting the best
79  * option based on the PMAP in use.
80  *
81  * There are two options for copy functions on powerpc64:
82  * - 'remap' copies, which remap userspace segments into kernel space for
83  *   copying.  This is used by the 'oea64' pmap.
84  * - 'direct' copies, which copy directly from userspace.  This does not require
85  *   remapping user segments into kernel.  This is used by the 'radix' pmap for
86  *   performance.
87  *
88  * Book-E does not use the C 'remap' functions, opting instead to use the
89  * 'direct' copies, directly, avoiding the IFUNC overhead.
90  *
91  * On 32-bit AIM these functions bypass the IFUNC machinery for performance.
92  */
93 #ifdef __powerpc64__
94 int subyte_remap(volatile void *addr, int byte);
95 int subyte_direct(volatile void *addr, int byte);
96 int copyinstr_remap(const void *udaddr, void *kaddr, size_t len, size_t *done);
97 int copyinstr_direct(const void *udaddr, void *kaddr, size_t len, size_t *done);
98 int copyout_remap(const void *kaddr, void *udaddr, size_t len);
99 int copyout_direct(const void *kaddr, void *udaddr, size_t len);
100 int copyin_remap(const void *uaddr, void *kaddr, size_t len);
101 int copyin_direct(const void *uaddr, void *kaddr, size_t len);
102 int suword32_remap(volatile void *addr, int word);
103 int suword32_direct(volatile void *addr, int word);
104 int suword_remap(volatile void *addr, long word);
105 int suword_direct(volatile void *addr, long word);
106 int suword64_remap(volatile void *addr, int64_t word);
107 int suword64_direct(volatile void *addr, int64_t word);
108 int fubyte_remap(volatile const void *addr);
109 int fubyte_direct(volatile const void *addr);
110 int fuword16_remap(volatile const void *addr);
111 int fuword16_direct(volatile const void *addr);
112 int fueword32_remap(volatile const void *addr, int32_t *val);
113 int fueword32_direct(volatile const void *addr, int32_t *val);
114 int fueword64_remap(volatile const void *addr, int64_t *val);
115 int fueword64_direct(volatile const void *addr, int64_t *val);
116 int fueword_remap(volatile const void *addr, long *val);
117 int fueword_direct(volatile const void *addr, long *val);
118 int casueword32_remap(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp,
119         uint32_t new);
120 int casueword32_direct(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp,
121         uint32_t new);
122 int casueword_remap(volatile u_long *addr, u_long old, u_long *oldvalp,
123         u_long new);
124 int casueword_direct(volatile u_long *addr, u_long old, u_long *oldvalp,
125         u_long new);
126
127 /*
128  * The IFUNC resolver determines the copy based on whether the PMAP
129  * implementation includes a pmap_map_user_ptr function.
130  */
131 #define DEFINE_COPY_FUNC(ret, func, args)                       \
132         DEFINE_IFUNC(, ret, func, args)                         \
133         {                                                       \
134                 return (PMAP_RESOLVE_FUNC(map_user_ptr) ?       \
135                     func##_remap : func##_direct);              \
136         }
137 DEFINE_COPY_FUNC(int, subyte, (volatile void *, int))
138 DEFINE_COPY_FUNC(int, copyinstr, (const void *, void *, size_t, size_t *))
139 DEFINE_COPY_FUNC(int, copyin, (const void *, void *, size_t))
140 DEFINE_COPY_FUNC(int, copyout, (const void *, void *, size_t))
141 DEFINE_COPY_FUNC(int, suword, (volatile void *, long))
142 DEFINE_COPY_FUNC(int, suword32, (volatile void *, int))
143 DEFINE_COPY_FUNC(int, suword64, (volatile void *, int64_t))
144 DEFINE_COPY_FUNC(int, fubyte, (volatile const void *))
145 DEFINE_COPY_FUNC(int, fuword16, (volatile const void *))
146 DEFINE_COPY_FUNC(int, fueword32, (volatile const void *, int32_t *))
147 DEFINE_COPY_FUNC(int, fueword64, (volatile const void *, int64_t *))
148 DEFINE_COPY_FUNC(int, fueword, (volatile const void *, long *))
149 DEFINE_COPY_FUNC(int, casueword32,
150     (volatile uint32_t *, uint32_t, uint32_t *, uint32_t))
151 DEFINE_COPY_FUNC(int, casueword, (volatile u_long *, u_long, u_long *, u_long))
152
153 #define REMAP(x)        x##_remap
154 #else
155 #define REMAP(x)        x
156 #endif
157
158 int
159 REMAP(copyout)(const void *kaddr, void *udaddr, size_t len)
160 {
161         struct          thread *td;
162         pmap_t          pm;
163         jmp_buf         env;
164         const char      *kp;
165         char            *up, *p;
166         size_t          l;
167
168         td = curthread;
169         pm = &td->td_proc->p_vmspace->vm_pmap;
170
171         td->td_pcb->pcb_onfault = &env;
172         if (setjmp(env)) {
173                 td->td_pcb->pcb_onfault = NULL;
174                 return (EFAULT);
175         }
176
177         kp = kaddr;
178         up = udaddr;
179
180         while (len > 0) {
181                 if (pmap_map_user_ptr(pm, up, (void **)&p, len, &l)) {
182                         td->td_pcb->pcb_onfault = NULL;
183                         return (EFAULT);
184                 }
185
186                 bcopy(kp, p, l);
187
188                 up += l;
189                 kp += l;
190                 len -= l;
191         }
192
193         td->td_pcb->pcb_onfault = NULL;
194         return (0);
195 }
196
197 int
198 REMAP(copyin)(const void *udaddr, void *kaddr, size_t len)
199 {
200         struct          thread *td;
201         pmap_t          pm;
202         jmp_buf         env;
203         const char      *up;
204         char            *kp, *p;
205         size_t          l;
206
207         td = curthread;
208         pm = &td->td_proc->p_vmspace->vm_pmap;
209
210         td->td_pcb->pcb_onfault = &env;
211         if (setjmp(env)) {
212                 td->td_pcb->pcb_onfault = NULL;
213                 return (EFAULT);
214         }
215
216         kp = kaddr;
217         up = udaddr;
218
219         while (len > 0) {
220                 if (pmap_map_user_ptr(pm, up, (void **)&p, len, &l)) {
221                         td->td_pcb->pcb_onfault = NULL;
222                         return (EFAULT);
223                 }
224
225                 bcopy(p, kp, l);
226
227                 up += l;
228                 kp += l;
229                 len -= l;
230         }
231
232         td->td_pcb->pcb_onfault = NULL;
233         return (0);
234 }
235
236 int
237 REMAP(copyinstr)(const void *udaddr, void *kaddr, size_t len, size_t *done)
238 {
239         struct          thread *td;
240         pmap_t          pm;
241         jmp_buf         env;
242         const char      *up;
243         char            *kp, *p;
244         size_t          i, l, t;
245         int             rv;
246
247         td = curthread;
248         pm = &td->td_proc->p_vmspace->vm_pmap;
249
250         t = 0;
251         rv = ENAMETOOLONG;
252
253         td->td_pcb->pcb_onfault = &env;
254         if (setjmp(env)) {
255                 rv = EFAULT;
256                 goto done;
257         }
258
259         kp = kaddr;
260         up = udaddr;
261
262         while (len > 0) {
263                 if (pmap_map_user_ptr(pm, up, (void **)&p, len, &l)) {
264                         rv = EFAULT;
265                         goto done;
266                 }
267
268                 for (i = 0; len > 0 && i < l; i++, t++, len--) {
269                         if ((*kp++ = *p++) == 0) {
270                                 i++, t++;
271                                 rv = 0;
272                                 goto done;
273                         }
274                 }
275
276                 up += l;
277         }
278
279 done:
280         td->td_pcb->pcb_onfault = NULL;
281
282         if (done != NULL) {
283                 *done = t;
284         }
285
286         return (rv);
287 }
288
289 int
290 REMAP(subyte)(volatile void *addr, int byte)
291 {
292         struct          thread *td;
293         pmap_t          pm;
294         jmp_buf         env;
295         char            *p;
296
297         td = curthread;
298         pm = &td->td_proc->p_vmspace->vm_pmap;
299
300         td->td_pcb->pcb_onfault = &env;
301         if (setjmp(env)) {
302                 td->td_pcb->pcb_onfault = NULL;
303                 return (-1);
304         }
305
306         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
307                 td->td_pcb->pcb_onfault = NULL;
308                 return (-1);
309         }
310
311         *p = (char)byte;
312
313         td->td_pcb->pcb_onfault = NULL;
314         return (0);
315 }
316
317 #ifdef __powerpc64__
318 int
319 REMAP(suword32)(volatile void *addr, int word)
320 {
321         struct          thread *td;
322         pmap_t          pm;
323         jmp_buf         env;
324         int             *p;
325
326         td = curthread;
327         pm = &td->td_proc->p_vmspace->vm_pmap;
328
329         td->td_pcb->pcb_onfault = &env;
330         if (setjmp(env)) {
331                 td->td_pcb->pcb_onfault = NULL;
332                 return (-1);
333         }
334
335         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
336                 td->td_pcb->pcb_onfault = NULL;
337                 return (-1);
338         }
339
340         *p = word;
341
342         td->td_pcb->pcb_onfault = NULL;
343         return (0);
344 }
345 #else
346 int
347 REMAP(suword32)(volatile void *addr, int32_t word)
348 {
349 REMAP(  return (suword)(addr, (long)word));
350 }
351 #endif
352
353 int
354 REMAP(suword)(volatile void *addr, long word)
355 {
356         struct          thread *td;
357         pmap_t          pm;
358         jmp_buf         env;
359         long            *p;
360
361         td = curthread;
362         pm = &td->td_proc->p_vmspace->vm_pmap;
363
364         td->td_pcb->pcb_onfault = &env;
365         if (setjmp(env)) {
366                 td->td_pcb->pcb_onfault = NULL;
367                 return (-1);
368         }
369
370         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
371                 td->td_pcb->pcb_onfault = NULL;
372                 return (-1);
373         }
374
375         *p = word;
376
377         td->td_pcb->pcb_onfault = NULL;
378         return (0);
379 }
380
381 #ifdef __powerpc64__
382 int
383 REMAP(suword64)(volatile void *addr, int64_t word)
384 {
385         return (REMAP(suword)(addr, (long)word));
386 }
387 #endif
388
389 int
390 REMAP(fubyte)(volatile const void *addr)
391 {
392         struct          thread *td;
393         pmap_t          pm;
394         jmp_buf         env;
395         u_char          *p;
396         int             val;
397
398         td = curthread;
399         pm = &td->td_proc->p_vmspace->vm_pmap;
400
401         td->td_pcb->pcb_onfault = &env;
402         if (setjmp(env)) {
403                 td->td_pcb->pcb_onfault = NULL;
404                 return (-1);
405         }
406
407         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
408                 td->td_pcb->pcb_onfault = NULL;
409                 return (-1);
410         }
411
412         val = *p;
413
414         td->td_pcb->pcb_onfault = NULL;
415         return (val);
416 }
417
418 int
419 REMAP(fuword16)(volatile const void *addr)
420 {
421         struct          thread *td;
422         pmap_t          pm;
423         jmp_buf         env;
424         uint16_t        *p, val;
425
426         td = curthread;
427         pm = &td->td_proc->p_vmspace->vm_pmap;
428
429         td->td_pcb->pcb_onfault = &env;
430         if (setjmp(env)) {
431                 td->td_pcb->pcb_onfault = NULL;
432                 return (-1);
433         }
434
435         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
436                 td->td_pcb->pcb_onfault = NULL;
437                 return (-1);
438         }
439
440         val = *p;
441
442         td->td_pcb->pcb_onfault = NULL;
443         return (val);
444 }
445
446 int
447 REMAP(fueword32)(volatile const void *addr, int32_t *val)
448 {
449         struct          thread *td;
450         pmap_t          pm;
451         jmp_buf         env;
452         int32_t         *p;
453
454         td = curthread;
455         pm = &td->td_proc->p_vmspace->vm_pmap;
456
457         td->td_pcb->pcb_onfault = &env;
458         if (setjmp(env)) {
459                 td->td_pcb->pcb_onfault = NULL;
460                 return (-1);
461         }
462
463         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
464                 td->td_pcb->pcb_onfault = NULL;
465                 return (-1);
466         }
467
468         *val = *p;
469
470         td->td_pcb->pcb_onfault = NULL;
471         return (0);
472 }
473
474 #ifdef __powerpc64__
475 int
476 REMAP(fueword64)(volatile const void *addr, int64_t *val)
477 {
478         struct          thread *td;
479         pmap_t          pm;
480         jmp_buf         env;
481         int64_t         *p;
482
483         td = curthread;
484         pm = &td->td_proc->p_vmspace->vm_pmap;
485
486         td->td_pcb->pcb_onfault = &env;
487         if (setjmp(env)) {
488                 td->td_pcb->pcb_onfault = NULL;
489                 return (-1);
490         }
491
492         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
493                 td->td_pcb->pcb_onfault = NULL;
494                 return (-1);
495         }
496
497         *val = *p;
498
499         td->td_pcb->pcb_onfault = NULL;
500         return (0);
501 }
502 #endif
503
504 int
505 REMAP(fueword)(volatile const void *addr, long *val)
506 {
507         struct          thread *td;
508         pmap_t          pm;
509         jmp_buf         env;
510         long            *p;
511
512         td = curthread;
513         pm = &td->td_proc->p_vmspace->vm_pmap;
514
515         td->td_pcb->pcb_onfault = &env;
516         if (setjmp(env)) {
517                 td->td_pcb->pcb_onfault = NULL;
518                 return (-1);
519         }
520
521         if (pmap_map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) {
522                 td->td_pcb->pcb_onfault = NULL;
523                 return (-1);
524         }
525
526         *val = *p;
527
528         td->td_pcb->pcb_onfault = NULL;
529         return (0);
530 }
531
532 int
533 REMAP(casueword32)(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp,
534     uint32_t new)
535 {
536         struct thread *td;
537         pmap_t pm;
538         jmp_buf         env;
539         uint32_t *p, val;
540         int res;
541
542         td = curthread;
543         pm = &td->td_proc->p_vmspace->vm_pmap;
544
545         td->td_pcb->pcb_onfault = &env;
546         if (setjmp(env)) {
547                 td->td_pcb->pcb_onfault = NULL;
548                 return (-1);
549         }
550
551         if (pmap_map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p,
552             sizeof(*p), NULL)) {
553                 td->td_pcb->pcb_onfault = NULL;
554                 return (-1);
555         }
556
557         res = 0;
558         __asm __volatile (
559                 "lwarx %0, 0, %3\n\t"           /* load old value */
560                 "cmplw %4, %0\n\t"              /* compare */
561                 "bne 1f\n\t"                    /* exit if not equal */
562                 "stwcx. %5, 0, %3\n\t"          /* attempt to store */
563                 "bne- 2f\n\t"                   /* if failed */
564                 "b 3f\n\t"                      /* we've succeeded */
565                 "1:\n\t"
566                 "stwcx. %0, 0, %3\n\t"          /* clear reservation (74xx) */
567                 "2:li %2, 1\n\t"
568                 "3:\n\t"
569                 : "=&r" (val), "=m" (*p), "+&r" (res)
570                 : "r" (p), "r" (old), "r" (new), "m" (*p)
571                 : "cr0", "memory");
572
573         td->td_pcb->pcb_onfault = NULL;
574
575         *oldvalp = val;
576         return (res);
577 }
578
579 #ifndef __powerpc64__
580 int
581 REMAP(casueword)(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
582 {
583
584         return (casueword32((volatile uint32_t *)addr, old,
585             (uint32_t *)oldvalp, new));
586 }
587 #else
588 int
589 REMAP(casueword)(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new)
590 {
591         struct thread *td;
592         pmap_t pm;
593         jmp_buf         env;
594         u_long *p, val;
595         int res;
596
597         td = curthread;
598         pm = &td->td_proc->p_vmspace->vm_pmap;
599
600         td->td_pcb->pcb_onfault = &env;
601         if (setjmp(env)) {
602                 td->td_pcb->pcb_onfault = NULL;
603                 return (-1);
604         }
605
606         if (pmap_map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p,
607             sizeof(*p), NULL)) {
608                 td->td_pcb->pcb_onfault = NULL;
609                 return (-1);
610         }
611
612         res = 0;
613         __asm __volatile (
614                 "ldarx %0, 0, %3\n\t"           /* load old value */
615                 "cmpld %4, %0\n\t"              /* compare */
616                 "bne 1f\n\t"                    /* exit if not equal */
617                 "stdcx. %5, 0, %3\n\t"          /* attempt to store */
618                 "bne- 2f\n\t"                   /* if failed */
619                 "b 3f\n\t"                      /* we've succeeded */
620                 "1:\n\t"
621                 "stdcx. %0, 0, %3\n\t"          /* clear reservation (74xx) */
622                 "2:li %2, 1\n\t"
623                 "3:\n\t"
624                 : "=&r" (val), "=m" (*p), "+&r" (res)
625                 : "r" (p), "r" (old), "r" (new), "m" (*p)
626                 : "cr0", "memory");
627
628         td->td_pcb->pcb_onfault = NULL;
629
630         *oldvalp = val;
631         return (res);
632 }
633 #endif