]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/copyout.c
Merge clang trunk r338150, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / i386 / i386 / copyout.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
8  * under sponsorship from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/sched.h>
41 #include <sys/sysctl.h>
42 #include <sys/systm.h>
43 #include <vm/vm.h>
44 #include <vm/vm_param.h>
45 #include <vm/vm_extern.h>
46 #include <vm/pmap.h>
47 #include <vm/vm_map.h>
48 #include <vm/vm_page.h>
49
50 #if defined(PAE) || defined(PAE_TABLES)
51 #define KCR3    ((u_int)IdlePDPT)
52 #else
53 #define KCR3    ((u_int)IdlePTD)
54 #endif
55
56 int copyin_fast(const void *udaddr, void *kaddr, size_t len, u_int);
57 static int (*copyin_fast_tramp)(const void *, void *, size_t, u_int);
58 int copyout_fast(const void *kaddr, void *udaddr, size_t len, u_int);
59 static int (*copyout_fast_tramp)(const void *, void *, size_t, u_int);
60 int fubyte_fast(volatile const void *base, u_int kcr3);
61 static int (*fubyte_fast_tramp)(volatile const void *, u_int);
62 int fuword16_fast(volatile const void *base, u_int kcr3);
63 static int (*fuword16_fast_tramp)(volatile const void *, u_int);
64 int fueword_fast(volatile const void *base, long *val, u_int kcr3);
65 static int (*fueword_fast_tramp)(volatile const void *, long *, u_int);
66 int subyte_fast(volatile void *base, int val, u_int kcr3);
67 static int (*subyte_fast_tramp)(volatile void *, int, u_int);
68 int suword16_fast(volatile void *base, int val, u_int kcr3);
69 static int (*suword16_fast_tramp)(volatile void *, int, u_int);
70 int suword_fast(volatile void *base, long val, u_int kcr3);
71 static int (*suword_fast_tramp)(volatile void *, long, u_int);
72
73 static int fast_copyout = 1;
74 SYSCTL_INT(_machdep, OID_AUTO, fast_copyout, CTLFLAG_RWTUN,
75     &fast_copyout, 0,
76     "");
77
78 void
79 copyout_init_tramp(void)
80 {
81
82         copyin_fast_tramp = (int (*)(const void *, void *, size_t, u_int))(
83             (uintptr_t)copyin_fast + setidt_disp);
84         copyout_fast_tramp = (int (*)(const void *, void *, size_t, u_int))(
85             (uintptr_t)copyout_fast + setidt_disp);
86         fubyte_fast_tramp = (int (*)(volatile const void *, u_int))(
87             (uintptr_t)fubyte_fast + setidt_disp);
88         fuword16_fast_tramp = (int (*)(volatile const void *, u_int))(
89             (uintptr_t)fuword16_fast + setidt_disp);
90         fueword_fast_tramp = (int (*)(volatile const void *, long *, u_int))(
91             (uintptr_t)fueword_fast + setidt_disp);
92         subyte_fast_tramp = (int (*)(volatile void *, int, u_int))(
93             (uintptr_t)subyte_fast + setidt_disp);
94         suword16_fast_tramp = (int (*)(volatile void *, int, u_int))(
95             (uintptr_t)suword16_fast + setidt_disp);
96         suword_fast_tramp = (int (*)(volatile void *, long, u_int))(
97             (uintptr_t)suword_fast + setidt_disp);
98 }
99
100 int
101 cp_slow0(vm_offset_t uva, size_t len, bool write,
102     void (*f)(vm_offset_t, void *), void *arg)
103 {
104         struct pcpu *pc;
105         vm_page_t m[2];
106         pt_entry_t *pte;
107         vm_offset_t kaddr;
108         int error, i, plen;
109         bool sleepable;
110
111         plen = howmany(uva - trunc_page(uva) + len, PAGE_SIZE);
112         MPASS(plen <= nitems(m));
113         error = 0;
114         i = vm_fault_quick_hold_pages(&curproc->p_vmspace->vm_map, uva, len,
115             (write ? VM_PROT_WRITE : VM_PROT_READ) | VM_PROT_QUICK_NOFAULT,
116             m, nitems(m));
117         if (i != plen)
118                 return (EFAULT);
119         sched_pin();
120         pc = get_pcpu();
121         if (!THREAD_CAN_SLEEP() || curthread->td_vslock_sz > 0 ||
122             (curthread->td_pflags & TDP_NOFAULTING) != 0) {
123                 sleepable = false;
124                 mtx_lock(&pc->pc_copyout_mlock);
125                 kaddr = pc->pc_copyout_maddr;
126         } else {
127                 sleepable = true;
128                 sx_xlock(&pc->pc_copyout_slock);
129                 kaddr = pc->pc_copyout_saddr;
130         }
131         for (i = 0, pte = vtopte(kaddr); i < plen; i++, pte++) {
132                 *pte = PG_V | PG_RW | PG_A | PG_M | VM_PAGE_TO_PHYS(m[i]) |
133                     pmap_cache_bits(pmap_page_get_memattr(m[i]), FALSE);
134                 invlpg(kaddr + ptoa(i));
135         }
136         kaddr += uva - trunc_page(uva);
137         f(kaddr, arg);
138         sched_unpin();
139         if (sleepable)
140                 sx_xunlock(&pc->pc_copyout_slock);
141         else
142                 mtx_unlock(&pc->pc_copyout_mlock);
143         vm_page_unhold_pages(m, plen);
144         return (error);
145 }
146
147 struct copyinstr_arg0 {
148         vm_offset_t kc;
149         size_t len;
150         size_t alen;
151         bool end;
152 };
153
154 static void
155 copyinstr_slow0(vm_offset_t kva, void *arg)
156 {
157         struct copyinstr_arg0 *ca;
158         char c;
159
160         ca = arg;
161         MPASS(ca->alen == 0 && ca->len > 0 && !ca->end);
162         while (ca->alen < ca->len && !ca->end) {
163                 c = *(char *)(kva + ca->alen);
164                 *(char *)ca->kc = c;
165                 ca->alen++;
166                 ca->kc++;
167                 if (c == '\0')
168                         ca->end = true;
169         }
170 }
171
172 int
173 copyinstr(const void *udaddr, void *kaddr, size_t maxlen, size_t *lencopied)
174 {
175         struct copyinstr_arg0 ca;
176         vm_offset_t uc;
177         size_t plen;
178         int error;
179
180         error = 0;
181         ca.end = false;
182         for (plen = 0, uc = (vm_offset_t)udaddr, ca.kc = (vm_offset_t)kaddr;
183             plen < maxlen && !ca.end; uc += ca.alen, plen += ca.alen) {
184                 ca.len = round_page(uc) - uc;
185                 if (ca.len == 0)
186                         ca.len = PAGE_SIZE;
187                 if (plen + ca.len > maxlen)
188                         ca.len = maxlen - plen;
189                 ca.alen = 0;
190                 if (cp_slow0(uc, ca.len, false, copyinstr_slow0, &ca) != 0) {
191                         error = EFAULT;
192                         break;
193                 }
194         }
195         if (!ca.end && plen == maxlen && error == 0)
196                 error = ENAMETOOLONG;
197         if (lencopied != NULL)
198                 *lencopied = plen;
199         return (error);
200 }
201
202 struct copyin_arg0 {
203         vm_offset_t kc;
204         size_t len;
205 };
206
207 static void
208 copyin_slow0(vm_offset_t kva, void *arg)
209 {
210         struct copyin_arg0 *ca;
211
212         ca = arg;
213         bcopy((void *)kva, (void *)ca->kc, ca->len);
214 }
215
216 int
217 copyin(const void *udaddr, void *kaddr, size_t len)
218 {
219         struct copyin_arg0 ca;
220         vm_offset_t uc;
221         size_t plen;
222
223         if ((uintptr_t)udaddr + len < (uintptr_t)udaddr ||
224             (uintptr_t)udaddr + len > VM_MAXUSER_ADDRESS)
225                 return (EFAULT);
226         if (len == 0 || (fast_copyout && len <= TRAMP_COPYOUT_SZ &&
227             copyin_fast_tramp(udaddr, kaddr, len, KCR3) == 0))
228                 return (0);
229         for (plen = 0, uc = (vm_offset_t)udaddr, ca.kc = (vm_offset_t)kaddr;
230             plen < len; uc += ca.len, ca.kc += ca.len, plen += ca.len) {
231                 ca.len = round_page(uc) - uc;
232                 if (ca.len == 0)
233                         ca.len = PAGE_SIZE;
234                 if (plen + ca.len > len)
235                         ca.len = len - plen;
236                 if (cp_slow0(uc, ca.len, false, copyin_slow0, &ca) != 0)
237                         return (EFAULT);
238         }
239         return (0);
240 }
241
242 static void
243 copyout_slow0(vm_offset_t kva, void *arg)
244 {
245         struct copyin_arg0 *ca;
246
247         ca = arg;
248         bcopy((void *)ca->kc, (void *)kva, ca->len);
249 }
250
251 int
252 copyout(const void *kaddr, void *udaddr, size_t len)
253 {
254         struct copyin_arg0 ca;
255         vm_offset_t uc;
256         size_t plen;
257
258         if ((uintptr_t)udaddr + len < (uintptr_t)udaddr ||
259             (uintptr_t)udaddr + len > VM_MAXUSER_ADDRESS)
260                 return (EFAULT);
261         if (len == 0 || (fast_copyout && len <= TRAMP_COPYOUT_SZ &&
262             copyout_fast_tramp(kaddr, udaddr, len, KCR3) == 0))
263                 return (0);
264         for (plen = 0, uc = (vm_offset_t)udaddr, ca.kc = (vm_offset_t)kaddr;
265             plen < len; uc += ca.len, ca.kc += ca.len, plen += ca.len) {
266                 ca.len = round_page(uc) - uc;
267                 if (ca.len == 0)
268                         ca.len = PAGE_SIZE;
269                 if (plen + ca.len > len)
270                         ca.len = len - plen;
271                 if (cp_slow0(uc, ca.len, true, copyout_slow0, &ca) != 0)
272                         return (EFAULT);
273         }
274         return (0);
275 }
276
277 /*
278  * Fetch (load) a 32-bit word, a 16-bit word, or an 8-bit byte from user
279  * memory.
280  */
281
282 static void
283 fubyte_slow0(vm_offset_t kva, void *arg)
284 {
285
286         *(int *)arg = *(u_char *)kva;
287 }
288
289 int
290 fubyte(volatile const void *base)
291 {
292         int res;
293
294         if ((uintptr_t)base + sizeof(uint8_t) < (uintptr_t)base ||
295             (uintptr_t)base + sizeof(uint8_t) > VM_MAXUSER_ADDRESS)
296                 return (-1);
297         if (fast_copyout) {
298                 res = fubyte_fast_tramp(base, KCR3);
299                 if (res != -1)
300                         return (res);
301         }
302         if (cp_slow0((vm_offset_t)base, sizeof(char), false, fubyte_slow0,
303             &res) != 0)
304                 return (-1);
305         return (res);
306 }
307
308 static void
309 fuword16_slow0(vm_offset_t kva, void *arg)
310 {
311
312         *(int *)arg = *(uint16_t *)kva;
313 }
314
315 int
316 fuword16(volatile const void *base)
317 {
318         int res;
319
320         if ((uintptr_t)base + sizeof(uint16_t) < (uintptr_t)base ||
321             (uintptr_t)base + sizeof(uint16_t) > VM_MAXUSER_ADDRESS)
322                 return (-1);
323         if (fast_copyout) {
324                 res = fuword16_fast_tramp(base, KCR3);
325                 if (res != -1)
326                         return (res);
327         }
328         if (cp_slow0((vm_offset_t)base, sizeof(uint16_t), false,
329             fuword16_slow0, &res) != 0)
330                 return (-1);
331         return (res);
332 }
333
334 static void
335 fueword_slow0(vm_offset_t kva, void *arg)
336 {
337
338         *(uint32_t *)arg = *(uint32_t *)kva;
339 }
340
341 int
342 fueword(volatile const void *base, long *val)
343 {
344         uint32_t res;
345
346         if ((uintptr_t)base + sizeof(*val) < (uintptr_t)base ||
347             (uintptr_t)base + sizeof(*val) > VM_MAXUSER_ADDRESS)
348                 return (-1);
349         if (fast_copyout) {
350                 if (fueword_fast_tramp(base, val, KCR3) == 0)
351                         return (0);
352         }
353         if (cp_slow0((vm_offset_t)base, sizeof(long), false, fueword_slow0,
354             &res) != 0)
355                 return (-1);
356         *val = res;
357         return (0);
358 }
359
360 int
361 fueword32(volatile const void *base, int32_t *val)
362 {
363
364         return (fueword(base, (long *)val));
365 }
366
367 /*
368  * Store a 32-bit word, a 16-bit word, or an 8-bit byte to user memory.
369  */
370
371 static void
372 subyte_slow0(vm_offset_t kva, void *arg)
373 {
374
375         *(u_char *)kva = *(int *)arg;
376 }
377
378 int
379 subyte(volatile void *base, int byte)
380 {
381
382         if ((uintptr_t)base + sizeof(uint8_t) < (uintptr_t)base ||
383             (uintptr_t)base + sizeof(uint8_t) > VM_MAXUSER_ADDRESS)
384                 return (-1);
385         if (fast_copyout && subyte_fast_tramp(base, byte, KCR3) == 0)
386                 return (0);
387         return (cp_slow0((vm_offset_t)base, sizeof(u_char), true, subyte_slow0,
388             &byte) != 0 ? -1 : 0);
389 }
390
391 static void
392 suword16_slow0(vm_offset_t kva, void *arg)
393 {
394
395         *(int *)kva = *(uint16_t *)arg;
396 }
397
398 int
399 suword16(volatile void *base, int word)
400 {
401
402         if ((uintptr_t)base + sizeof(uint16_t) < (uintptr_t)base ||
403             (uintptr_t)base + sizeof(uint16_t) > VM_MAXUSER_ADDRESS)
404                 return (-1);
405         if (fast_copyout && suword16_fast_tramp(base, word, KCR3) == 0)
406                 return (0);
407         return (cp_slow0((vm_offset_t)base, sizeof(int16_t), true,
408             suword16_slow0, &word) != 0 ? -1 : 0);
409 }
410
411 static void
412 suword_slow0(vm_offset_t kva, void *arg)
413 {
414
415         *(int *)kva = *(uint32_t *)arg;
416 }
417
418 int
419 suword(volatile void *base, long word)
420 {
421
422         if ((uintptr_t)base + sizeof(word) < (uintptr_t)base ||
423             (uintptr_t)base + sizeof(word) > VM_MAXUSER_ADDRESS)
424                 return (-1);
425         if (fast_copyout && suword_fast_tramp(base, word, KCR3) == 0)
426                 return (0);
427         return (cp_slow0((vm_offset_t)base, sizeof(long), true,
428             suword_slow0, &word) != 0 ? -1 : 0);
429 }
430
431 int
432 suword32(volatile void *base, int32_t word)
433 {
434
435         return (suword(base, word));
436 }
437
438 struct casueword_arg0 {
439         uint32_t oldval;
440         uint32_t newval;
441 };
442
443 static void
444 casueword_slow0(vm_offset_t kva, void *arg)
445 {
446         struct casueword_arg0 *ca;
447
448         ca = arg;
449         atomic_fcmpset_int((u_int *)kva, &ca->oldval, ca->newval);
450 }
451
452 int
453 casueword32(volatile uint32_t *base, uint32_t oldval, uint32_t *oldvalp,
454     uint32_t newval)
455 {
456         struct casueword_arg0 ca;
457         int res;
458
459         ca.oldval = oldval;
460         ca.newval = newval;
461         res = cp_slow0((vm_offset_t)base, sizeof(int32_t), true,
462             casueword_slow0, &ca);
463         if (res == 0) {
464                 *oldvalp = ca.oldval;
465                 return (0);
466         }
467         return (-1);
468 }
469
470 int
471 casueword(volatile u_long *base, u_long oldval, u_long *oldvalp, u_long newval)
472 {
473         struct casueword_arg0 ca;
474         int res;
475
476         ca.oldval = oldval;
477         ca.newval = newval;
478         res = cp_slow0((vm_offset_t)base, sizeof(int32_t), true,
479             casueword_slow0, &ca);
480         if (res == 0) {
481                 *oldvalp = ca.oldval;
482                 return (0);
483         }
484         return (-1);
485 }