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