]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/powerpc/aim/copyinout.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / powerpc / aim / 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
73 int     setfault(faultbuf);     /* defined in locore.S */
74
75 /*
76  * Makes sure that the right segment of userspace is mapped in.
77  */
78
79 #ifdef __powerpc64__
80 static __inline void
81 set_user_sr(pmap_t pm, const void *addr)
82 {
83         struct slb *slb;
84         register_t slbv;
85
86         /* Try lockless look-up first */
87         slb = user_va_to_slb_entry(pm, (vm_offset_t)addr);
88
89         if (slb == NULL) {
90                 /* If it isn't there, we need to pre-fault the VSID */
91                 PMAP_LOCK(pm);
92                 slbv = va_to_vsid(pm, (vm_offset_t)addr) << SLBV_VSID_SHIFT;
93                 PMAP_UNLOCK(pm);
94         } else {
95                 slbv = slb->slbv;
96         }
97
98         /* Mark segment no-execute */
99         slbv |= SLBV_N;
100
101         /* If we have already set this VSID, we can just return */
102         if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv) 
103                 return;
104
105         __asm __volatile("isync");
106         curthread->td_pcb->pcb_cpu.aim.usr_segm =
107             (uintptr_t)addr >> ADDR_SR_SHFT;
108         curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv;
109         __asm __volatile ("slbie %0; slbmte %1, %2; isync" ::
110             "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE));
111 }
112 #else
113 static __inline void
114 set_user_sr(pmap_t pm, const void *addr)
115 {
116         register_t vsid;
117
118         vsid = va_to_vsid(pm, (vm_offset_t)addr);
119
120         /* Mark segment no-execute */
121         vsid |= SR_N;
122
123         /* If we have already set this VSID, we can just return */
124         if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid)
125                 return;
126
127         __asm __volatile("isync");
128         curthread->td_pcb->pcb_cpu.aim.usr_segm =
129             (uintptr_t)addr >> ADDR_SR_SHFT;
130         curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid;
131         __asm __volatile("mtsr %0,%1; isync" :: "n"(USER_SR), "r"(vsid));
132 }
133 #endif
134
135 int
136 copyout(const void *kaddr, void *udaddr, size_t len)
137 {
138         struct          thread *td;
139         pmap_t          pm;
140         faultbuf        env;
141         const char      *kp;
142         char            *up, *p;
143         size_t          l;
144
145         td = curthread;
146         pm = &td->td_proc->p_vmspace->vm_pmap;
147
148         if (setfault(env)) {
149                 td->td_pcb->pcb_onfault = NULL;
150                 return (EFAULT);
151         }
152
153         kp = kaddr;
154         up = udaddr;
155
156         while (len > 0) {
157                 p = (char *)USER_ADDR + ((uintptr_t)up & ~SEGMENT_MASK);
158
159                 l = ((char *)USER_ADDR + SEGMENT_LENGTH) - p;
160                 if (l > len)
161                         l = len;
162
163                 set_user_sr(pm,up);
164
165                 bcopy(kp, p, l);
166
167                 up += l;
168                 kp += l;
169                 len -= l;
170         }
171
172         td->td_pcb->pcb_onfault = NULL;
173         return (0);
174 }
175
176 int
177 copyin(const void *udaddr, void *kaddr, size_t len)
178 {
179         struct          thread *td;
180         pmap_t          pm;
181         faultbuf        env;
182         const char      *up;
183         char            *kp, *p;
184         size_t          l;
185
186         td = curthread;
187         pm = &td->td_proc->p_vmspace->vm_pmap;
188
189         if (setfault(env)) {
190                 td->td_pcb->pcb_onfault = NULL;
191                 return (EFAULT);
192         }
193
194         kp = kaddr;
195         up = udaddr;
196
197         while (len > 0) {
198                 p = (char *)USER_ADDR + ((uintptr_t)up & ~SEGMENT_MASK);
199
200                 l = ((char *)USER_ADDR + SEGMENT_LENGTH) - p;
201                 if (l > len)
202                         l = len;
203
204                 set_user_sr(pm,up);
205
206                 bcopy(p, kp, l);
207
208                 up += l;
209                 kp += l;
210                 len -= l;
211         }
212
213         td->td_pcb->pcb_onfault = NULL;
214         return (0);
215 }
216
217 int
218 copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done)
219 {
220         struct          thread *td;
221         pmap_t          pm;
222         faultbuf        env;
223         const char      *up;
224         char            *kp;
225         size_t          l;
226         int             rv, c;
227
228         td = curthread;
229         pm = &td->td_proc->p_vmspace->vm_pmap;
230
231         if (setfault(env)) {
232                 td->td_pcb->pcb_onfault = NULL;
233                 return (EFAULT);
234         }
235
236         kp = kaddr;
237         up = udaddr;
238
239         rv = ENAMETOOLONG;
240
241         for (l = 0; len-- > 0; l++) {
242                 if ((c = fubyte(up++)) < 0) {
243                         rv = EFAULT;
244                         break;
245                 }
246
247                 if (!(*kp++ = c)) {
248                         l++;
249                         rv = 0;
250                         break;
251                 }
252         }
253
254         if (done != NULL) {
255                 *done = l;
256         }
257
258         td->td_pcb->pcb_onfault = NULL;
259         return (rv);
260 }
261
262 int
263 subyte(void *addr, int byte)
264 {
265         struct          thread *td;
266         pmap_t          pm;
267         faultbuf        env;
268         char            *p;
269
270         td = curthread;
271         pm = &td->td_proc->p_vmspace->vm_pmap;
272         p = (char *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
273
274         if (setfault(env)) {
275                 td->td_pcb->pcb_onfault = NULL;
276                 return (-1);
277         }
278
279         set_user_sr(pm,addr);
280
281         *p = (char)byte;
282
283         td->td_pcb->pcb_onfault = NULL;
284         return (0);
285 }
286
287 #ifdef __powerpc64__
288 int
289 suword32(void *addr, int word)
290 {
291         struct          thread *td;
292         pmap_t          pm;
293         faultbuf        env;
294         int             *p;
295
296         td = curthread;
297         pm = &td->td_proc->p_vmspace->vm_pmap;
298         p = (int *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
299
300         if (setfault(env)) {
301                 td->td_pcb->pcb_onfault = NULL;
302                 return (-1);
303         }
304
305         set_user_sr(pm,addr);
306
307         *p = word;
308
309         td->td_pcb->pcb_onfault = NULL;
310         return (0);
311 }
312 #endif
313
314 int
315 suword(void *addr, long word)
316 {
317         struct          thread *td;
318         pmap_t          pm;
319         faultbuf        env;
320         long            *p;
321
322         td = curthread;
323         pm = &td->td_proc->p_vmspace->vm_pmap;
324         p = (long *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
325
326         if (setfault(env)) {
327                 td->td_pcb->pcb_onfault = NULL;
328                 return (-1);
329         }
330
331         set_user_sr(pm,addr);
332
333         *p = word;
334
335         td->td_pcb->pcb_onfault = NULL;
336         return (0);
337 }
338
339 #ifdef __powerpc64__
340 int
341 suword64(void *addr, int64_t word)
342 {
343         return (suword(addr, (long)word));
344 }
345 #else
346 int
347 suword32(void *addr, int32_t word)
348 {
349         return (suword(addr, (long)word));
350 }
351 #endif
352
353 int
354 fubyte(const void *addr)
355 {
356         struct          thread *td;
357         pmap_t          pm;
358         faultbuf        env;
359         u_char          *p;
360         int             val;
361
362         td = curthread;
363         pm = &td->td_proc->p_vmspace->vm_pmap;
364         p = (u_char *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
365
366         if (setfault(env)) {
367                 td->td_pcb->pcb_onfault = NULL;
368                 return (-1);
369         }
370
371         set_user_sr(pm,addr);
372
373         val = *p;
374
375         td->td_pcb->pcb_onfault = NULL;
376         return (val);
377 }
378
379 #ifdef __powerpc64__
380 int32_t
381 fuword32(const void *addr)
382 {
383         struct          thread *td;
384         pmap_t          pm;
385         faultbuf        env;
386         int32_t         *p, val;
387
388         td = curthread;
389         pm = &td->td_proc->p_vmspace->vm_pmap;
390         p = (int32_t *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
391
392         if (setfault(env)) {
393                 td->td_pcb->pcb_onfault = NULL;
394                 return (-1);
395         }
396
397         set_user_sr(pm,addr);
398
399         val = *p;
400
401         td->td_pcb->pcb_onfault = NULL;
402         return (val);
403 }
404 #endif
405
406 long
407 fuword(const void *addr)
408 {
409         struct          thread *td;
410         pmap_t          pm;
411         faultbuf        env;
412         long            *p, val;
413
414         td = curthread;
415         pm = &td->td_proc->p_vmspace->vm_pmap;
416         p = (long *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
417
418         if (setfault(env)) {
419                 td->td_pcb->pcb_onfault = NULL;
420                 return (-1);
421         }
422
423         set_user_sr(pm,addr);
424
425         val = *p;
426
427         td->td_pcb->pcb_onfault = NULL;
428         return (val);
429 }
430
431 #ifndef __powerpc64__
432 int32_t
433 fuword32(const void *addr)
434 {
435         return ((int32_t)fuword(addr));
436 }
437 #endif
438
439 uint32_t
440 casuword32(volatile uint32_t *addr, uint32_t old, uint32_t new)
441 {
442         struct thread *td;
443         pmap_t pm;
444         faultbuf env;
445         uint32_t *p, val;
446
447         td = curthread;
448         pm = &td->td_proc->p_vmspace->vm_pmap;
449         p = (uint32_t *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
450
451         set_user_sr(pm,(const void *)(vm_offset_t)addr);
452
453         if (setfault(env)) {
454                 td->td_pcb->pcb_onfault = NULL;
455                 return (-1);
456         }
457
458         __asm __volatile (
459                 "1:\tlwarx %0, 0, %2\n\t"       /* load old value */
460                 "cmplw %3, %0\n\t"              /* compare */
461                 "bne 2f\n\t"                    /* exit if not equal */
462                 "stwcx. %4, 0, %2\n\t"          /* attempt to store */
463                 "bne- 1b\n\t"                   /* spin if failed */
464                 "b 3f\n\t"                      /* we've succeeded */
465                 "2:\n\t"
466                 "stwcx. %0, 0, %2\n\t"          /* clear reservation (74xx) */
467                 "3:\n\t"
468                 : "=&r" (val), "=m" (*p)
469                 : "r" (p), "r" (old), "r" (new), "m" (*p)
470                 : "cc", "memory");
471
472         td->td_pcb->pcb_onfault = NULL;
473
474         return (val);
475 }
476
477 #ifndef __powerpc64__
478 u_long
479 casuword(volatile u_long *addr, u_long old, u_long new)
480 {
481         return (casuword32((volatile uint32_t *)addr, old, new));
482 }
483 #else
484 u_long
485 casuword(volatile u_long *addr, u_long old, u_long new)
486 {
487         struct thread *td;
488         pmap_t pm;
489         faultbuf env;
490         u_long *p, val;
491
492         td = curthread;
493         pm = &td->td_proc->p_vmspace->vm_pmap;
494         p = (u_long *)(USER_ADDR + ((uintptr_t)addr & ~SEGMENT_MASK));
495
496         set_user_sr(pm,(const void *)(vm_offset_t)addr);
497
498         if (setfault(env)) {
499                 td->td_pcb->pcb_onfault = NULL;
500                 return (-1);
501         }
502
503         __asm __volatile (
504                 "1:\tldarx %0, 0, %2\n\t"       /* load old value */
505                 "cmpld %3, %0\n\t"              /* compare */
506                 "bne 2f\n\t"                    /* exit if not equal */
507                 "stdcx. %4, 0, %2\n\t"          /* attempt to store */
508                 "bne- 1b\n\t"                   /* spin if failed */
509                 "b 3f\n\t"                      /* we've succeeded */
510                 "2:\n\t"
511                 "stdcx. %0, 0, %2\n\t"          /* clear reservation (74xx) */
512                 "3:\n\t"
513                 : "=&r" (val), "=m" (*p)
514                 : "r" (p), "r" (old), "r" (new), "m" (*p)
515                 : "cc", "memory");
516
517         td->td_pcb->pcb_onfault = NULL;
518
519         return (val);
520 }
521 #endif
522