1 | /* $NetBSD: subr_copy.c,v 1.7 2016/05/25 17:43:58 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Copyright (c) 1982, 1986, 1991, 1993 |
35 | * The Regents of the University of California. All rights reserved. |
36 | * (c) UNIX System Laboratories, Inc. |
37 | * All or some portions of this file are derived from material licensed |
38 | * to the University of California by American Telephone and Telegraph |
39 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
40 | * the permission of UNIX System Laboratories, Inc. |
41 | * |
42 | * Copyright (c) 1992, 1993 |
43 | * The Regents of the University of California. All rights reserved. |
44 | * |
45 | * This software was developed by the Computer Systems Engineering group |
46 | * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and |
47 | * contributed to Berkeley. |
48 | * |
49 | * All advertising materials mentioning features or use of this software |
50 | * must display the following acknowledgement: |
51 | * This product includes software developed by the University of |
52 | * California, Lawrence Berkeley Laboratory. |
53 | * |
54 | * Redistribution and use in source and binary forms, with or without |
55 | * modification, are permitted provided that the following conditions |
56 | * are met: |
57 | * 1. Redistributions of source code must retain the above copyright |
58 | * notice, this list of conditions and the following disclaimer. |
59 | * 2. Redistributions in binary form must reproduce the above copyright |
60 | * notice, this list of conditions and the following disclaimer in the |
61 | * documentation and/or other materials provided with the distribution. |
62 | * 3. Neither the name of the University nor the names of its contributors |
63 | * may be used to endorse or promote products derived from this software |
64 | * without specific prior written permission. |
65 | * |
66 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
67 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
68 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
69 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
70 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
71 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
72 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
73 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
74 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
75 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
76 | * SUCH DAMAGE. |
77 | * |
78 | * @(#)kern_subr.c 8.4 (Berkeley) 2/14/95 |
79 | */ |
80 | |
81 | #include <sys/cdefs.h> |
82 | __KERNEL_RCSID(0, "$NetBSD: subr_copy.c,v 1.7 2016/05/25 17:43:58 christos Exp $" ); |
83 | |
84 | #include <sys/param.h> |
85 | #include <sys/fcntl.h> |
86 | #include <sys/proc.h> |
87 | #include <sys/systm.h> |
88 | |
89 | #include <uvm/uvm_extern.h> |
90 | |
91 | void |
92 | uio_setup_sysspace(struct uio *uio) |
93 | { |
94 | |
95 | uio->uio_vmspace = vmspace_kernel(); |
96 | } |
97 | |
98 | int |
99 | uiomove(void *buf, size_t n, struct uio *uio) |
100 | { |
101 | struct vmspace *vm = uio->uio_vmspace; |
102 | struct iovec *iov; |
103 | size_t cnt; |
104 | int error = 0; |
105 | char *cp = buf; |
106 | |
107 | ASSERT_SLEEPABLE(); |
108 | |
109 | KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE); |
110 | while (n > 0 && uio->uio_resid) { |
111 | iov = uio->uio_iov; |
112 | cnt = iov->iov_len; |
113 | if (cnt == 0) { |
114 | KASSERT(uio->uio_iovcnt > 0); |
115 | uio->uio_iov++; |
116 | uio->uio_iovcnt--; |
117 | continue; |
118 | } |
119 | if (cnt > n) |
120 | cnt = n; |
121 | if (!VMSPACE_IS_KERNEL_P(vm)) { |
122 | if (curcpu()->ci_schedstate.spc_flags & |
123 | SPCF_SHOULDYIELD) |
124 | preempt(); |
125 | } |
126 | |
127 | if (uio->uio_rw == UIO_READ) { |
128 | error = copyout_vmspace(vm, cp, iov->iov_base, |
129 | cnt); |
130 | } else { |
131 | error = copyin_vmspace(vm, iov->iov_base, cp, |
132 | cnt); |
133 | } |
134 | if (error) { |
135 | break; |
136 | } |
137 | iov->iov_base = (char *)iov->iov_base + cnt; |
138 | iov->iov_len -= cnt; |
139 | uio->uio_resid -= cnt; |
140 | uio->uio_offset += cnt; |
141 | cp += cnt; |
142 | KDASSERT(cnt <= n); |
143 | n -= cnt; |
144 | } |
145 | |
146 | return (error); |
147 | } |
148 | |
149 | /* |
150 | * Wrapper for uiomove() that validates the arguments against a known-good |
151 | * kernel buffer. |
152 | */ |
153 | int |
154 | uiomove_frombuf(void *buf, size_t buflen, struct uio *uio) |
155 | { |
156 | size_t offset; |
157 | |
158 | if (uio->uio_offset < 0 || /* uio->uio_resid < 0 || */ |
159 | (offset = uio->uio_offset) != uio->uio_offset) |
160 | return (EINVAL); |
161 | if (offset >= buflen) |
162 | return (0); |
163 | return (uiomove((char *)buf + offset, buflen - offset, uio)); |
164 | } |
165 | |
166 | /* |
167 | * Give next character to user as result of read. |
168 | */ |
169 | int |
170 | ureadc(int c, struct uio *uio) |
171 | { |
172 | struct iovec *iov; |
173 | |
174 | if (uio->uio_resid <= 0) |
175 | panic("ureadc: non-positive resid" ); |
176 | again: |
177 | if (uio->uio_iovcnt <= 0) |
178 | panic("ureadc: non-positive iovcnt" ); |
179 | iov = uio->uio_iov; |
180 | if (iov->iov_len <= 0) { |
181 | uio->uio_iovcnt--; |
182 | uio->uio_iov++; |
183 | goto again; |
184 | } |
185 | if (!VMSPACE_IS_KERNEL_P(uio->uio_vmspace)) { |
186 | if (subyte(iov->iov_base, c) < 0) |
187 | return (EFAULT); |
188 | } else { |
189 | *(char *)iov->iov_base = c; |
190 | } |
191 | iov->iov_base = (char *)iov->iov_base + 1; |
192 | iov->iov_len--; |
193 | uio->uio_resid--; |
194 | uio->uio_offset++; |
195 | return (0); |
196 | } |
197 | |
198 | /* |
199 | * Like copyin(), but operates on an arbitrary vmspace. |
200 | */ |
201 | int |
202 | copyin_vmspace(struct vmspace *vm, const void *uaddr, void *kaddr, size_t len) |
203 | { |
204 | struct iovec iov; |
205 | struct uio uio; |
206 | int error; |
207 | |
208 | if (len == 0) |
209 | return (0); |
210 | |
211 | if (VMSPACE_IS_KERNEL_P(vm)) { |
212 | return kcopy(uaddr, kaddr, len); |
213 | } |
214 | if (__predict_true(vm == curproc->p_vmspace)) { |
215 | return copyin(uaddr, kaddr, len); |
216 | } |
217 | |
218 | iov.iov_base = kaddr; |
219 | iov.iov_len = len; |
220 | uio.uio_iov = &iov; |
221 | uio.uio_iovcnt = 1; |
222 | uio.uio_offset = (off_t)(uintptr_t)uaddr; |
223 | uio.uio_resid = len; |
224 | uio.uio_rw = UIO_READ; |
225 | UIO_SETUP_SYSSPACE(&uio); |
226 | error = uvm_io(&vm->vm_map, &uio, 0); |
227 | |
228 | return (error); |
229 | } |
230 | |
231 | /* |
232 | * Like copyout(), but operates on an arbitrary vmspace. |
233 | */ |
234 | int |
235 | copyout_vmspace(struct vmspace *vm, const void *kaddr, void *uaddr, size_t len) |
236 | { |
237 | struct iovec iov; |
238 | struct uio uio; |
239 | int error; |
240 | |
241 | if (len == 0) |
242 | return (0); |
243 | |
244 | if (VMSPACE_IS_KERNEL_P(vm)) { |
245 | return kcopy(kaddr, uaddr, len); |
246 | } |
247 | if (__predict_true(vm == curproc->p_vmspace)) { |
248 | return copyout(kaddr, uaddr, len); |
249 | } |
250 | |
251 | iov.iov_base = __UNCONST(kaddr); /* XXXUNCONST cast away const */ |
252 | iov.iov_len = len; |
253 | uio.uio_iov = &iov; |
254 | uio.uio_iovcnt = 1; |
255 | uio.uio_offset = (off_t)(uintptr_t)uaddr; |
256 | uio.uio_resid = len; |
257 | uio.uio_rw = UIO_WRITE; |
258 | UIO_SETUP_SYSSPACE(&uio); |
259 | error = uvm_io(&vm->vm_map, &uio, 0); |
260 | |
261 | return (error); |
262 | } |
263 | |
264 | /* |
265 | * Like copyin(), but operates on an arbitrary process. |
266 | */ |
267 | int |
268 | copyin_proc(struct proc *p, const void *uaddr, void *kaddr, size_t len) |
269 | { |
270 | struct vmspace *vm; |
271 | int error; |
272 | |
273 | error = proc_vmspace_getref(p, &vm); |
274 | if (error) { |
275 | return error; |
276 | } |
277 | error = copyin_vmspace(vm, uaddr, kaddr, len); |
278 | uvmspace_free(vm); |
279 | |
280 | return error; |
281 | } |
282 | |
283 | /* |
284 | * Like copyout(), but operates on an arbitrary process. |
285 | */ |
286 | int |
287 | copyout_proc(struct proc *p, const void *kaddr, void *uaddr, size_t len) |
288 | { |
289 | struct vmspace *vm; |
290 | int error; |
291 | |
292 | error = proc_vmspace_getref(p, &vm); |
293 | if (error) { |
294 | return error; |
295 | } |
296 | error = copyout_vmspace(vm, kaddr, uaddr, len); |
297 | uvmspace_free(vm); |
298 | |
299 | return error; |
300 | } |
301 | |
302 | /* |
303 | * Like copyin(), except it operates on kernel addresses when the FKIOCTL |
304 | * flag is passed in `ioctlflags' from the ioctl call. |
305 | */ |
306 | int |
307 | ioctl_copyin(int ioctlflags, const void *src, void *dst, size_t len) |
308 | { |
309 | if (ioctlflags & FKIOCTL) |
310 | return kcopy(src, dst, len); |
311 | return copyin(src, dst, len); |
312 | } |
313 | |
314 | /* |
315 | * Like copyout(), except it operates on kernel addresses when the FKIOCTL |
316 | * flag is passed in `ioctlflags' from the ioctl call. |
317 | */ |
318 | int |
319 | ioctl_copyout(int ioctlflags, const void *src, void *dst, size_t len) |
320 | { |
321 | if (ioctlflags & FKIOCTL) |
322 | return kcopy(src, dst, len); |
323 | return copyout(src, dst, len); |
324 | } |
325 | |