vkernel - Fix copyin/copyout to return the correct error code
[dragonfly.git] / sys / platform / vkernel64 / platform / copyio.c
1 /*
2  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/types.h>
36 #include <sys/systm.h>
37 #include <cpu/lwbuf.h>
38 #include <vm/vm_page.h>
39 #include <vm/vm_extern.h>
40 #include <assert.h>
41
42 #include <sys/stat.h>
43 #include <sys/mman.h>
44
45 /*
46  * A bcopy that works dring low level boot, before FP is working
47  */
48 void
49 ovbcopy(const void *src, void *dst, size_t len)
50 {
51         bcopy(src, dst, len);
52 }
53
54 void
55 bcopyi(const void *src, void *dst, size_t len)
56 {
57         bcopy(src, dst, len);
58 }
59
60 int
61 copystr(const void *kfaddr, void *kdaddr, size_t len, size_t *lencopied)
62 {
63         size_t i;
64
65         for (i = 0; i < len; ++i) {
66                 if ((((char *)kdaddr)[i] = ((const char *)kfaddr)[i]) == 0) {
67                         if (lencopied)
68                                 *lencopied = i + 1;
69                         return(0);
70                 }
71         }
72         return (ENAMETOOLONG);
73 }
74
75 /*
76  * Copies a NUL-terminated string from user space to kernel space.
77  * The number of bytes copied, including the terminator, is returned in
78  * (*res).
79  *
80  * Returns 0 on success, EFAULT or ENAMETOOLONG on failure.
81  */
82 int
83 copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *res)
84 {
85         int error;
86         size_t n;
87         const char *uptr = udaddr;
88         char *kptr = kaddr;
89
90         if (res)
91                 *res = 0;
92         while (len) {
93                 n = PAGE_SIZE - ((vm_offset_t)uptr & PAGE_MASK);
94                 if (n > 32)
95                         n = 32;
96                 if (n > len)
97                         n = len;
98                 if ((error = copyin(uptr, kptr, n)) != 0)
99                         return(error);
100                 while (n) {
101                         if (res)
102                                 ++*res;
103                         if (*kptr == 0)
104                                 return(0);
105                         ++kptr;
106                         ++uptr;
107                         --n;
108                         --len;
109                 }
110
111         }
112         return(ENAMETOOLONG);
113 }
114
115 /*
116  * Copy a binary buffer from user space to kernel space.
117  *
118  * NOTE: on a real system copyin/copyout are MP safe, but the current
119  * implementation on a vkernel is not so we get the mp lock.
120  *
121  * Returns 0 on success, EFAULT on failure.
122  */
123 int
124 copyin(const void *udaddr, void *kaddr, size_t len)
125 {
126         struct vmspace *vm = curproc->p_vmspace;
127         struct lwbuf *lwb;
128         struct lwbuf lwb_cache;
129         vm_page_t m;
130         int error;
131         size_t n;
132
133         error = 0;
134         while (len) {
135                 m = vm_fault_page(&vm->vm_map, trunc_page((vm_offset_t)udaddr),
136                                   VM_PROT_READ,
137                                   VM_FAULT_NORMAL, &error);
138                 if (error)
139                         break;
140                 n = PAGE_SIZE - ((vm_offset_t)udaddr & PAGE_MASK);
141                 if (n > len)
142                         n = len;
143                 lwb = lwbuf_alloc(m, &lwb_cache);
144                 bcopy((char *)lwbuf_kva(lwb)+((vm_offset_t)udaddr & PAGE_MASK),
145                       kaddr, n);
146                 len -= n;
147                 udaddr = (const char *)udaddr + n;
148                 kaddr = (char *)kaddr + n;
149                 lwbuf_free(lwb);
150                 vm_page_unhold(m);
151         }
152         if (error)
153                 error = EFAULT;
154         return (error);
155 }
156
157 /*
158  * Copy a binary buffer from kernel space to user space.
159  *
160  * Returns 0 on success, EFAULT on failure.
161  */
162 int
163 copyout(const void *kaddr, void *udaddr, size_t len)
164 {
165         struct vmspace *vm = curproc->p_vmspace;
166         struct lwbuf *lwb;
167         struct lwbuf lwb_cache;
168         vm_page_t m;
169         int error;
170         size_t n;
171
172         error = 0;
173         while (len) {
174                 m = vm_fault_page(&vm->vm_map, trunc_page((vm_offset_t)udaddr),
175                                   VM_PROT_READ|VM_PROT_WRITE,
176                                   VM_FAULT_NORMAL, &error);
177                 if (error)
178                         break;
179                 n = PAGE_SIZE - ((vm_offset_t)udaddr & PAGE_MASK);
180                 if (n > len)
181                         n = len;
182                 lwb = lwbuf_alloc(m, &lwb_cache);
183                 bcopy(kaddr, (char *)lwbuf_kva(lwb) +
184                              ((vm_offset_t)udaddr & PAGE_MASK), n);
185                 len -= n;
186                 udaddr = (char *)udaddr + n;
187                 kaddr = (const char *)kaddr + n;
188                 vm_page_dirty(m);
189                 lwbuf_free(lwb);
190                 vm_page_unhold(m);
191         }
192         if (error)
193                 error = EFAULT;
194         return (error);
195 }
196
197 /*
198  * Fetch the byte at the specified user address.  Returns -1 on failure.
199  */
200 int
201 fubyte(const void *base)
202 {
203         unsigned char c;
204
205         if (copyin(base, &c, 1) == 0)
206                 return((int)c);
207         return(-1);
208 }
209
210 /*
211  * Store a byte at the specified user address.  Returns -1 on failure.
212  */
213 int
214 subyte (void *base, int byte)
215 {
216         unsigned char c = byte;
217
218         if (copyout(&c, base, 1) == 0)
219                 return(0);
220         return(-1);
221 }
222
223 /*
224  * Fetch a word (integer, 32 bits) from user space
225  */
226 long
227 fuword(const void *base)
228 {
229         long v;
230
231         if (copyin(base, &v, sizeof(v)) == 0)
232                 return(v);
233         return(-1);
234 }
235
236 /*
237  * Store a word (integer, 32 bits) to user space
238  */
239 int
240 suword(void *base, long word)
241 {
242         if (copyout(&word, base, sizeof(word)) == 0)
243                 return(0);
244         return(-1);
245 }
246
247 int
248 suword32(void *base, int word)
249 {
250         if (copyout(&word, base, sizeof(word)) == 0)
251                 return(0);
252         return(-1);
253 }
254
255 /*
256  * Fetch an short word (16 bits) from user space
257  */
258 int
259 fusword(void *base)
260 {
261         unsigned short sword;
262
263         if (copyin(base, &sword, sizeof(sword)) == 0)
264                 return((int)sword);
265         return(-1);
266 }
267
268 /*
269  * Store a short word (16 bits) to user space
270  */
271 int
272 susword (void *base, int word)
273 {
274         unsigned short sword = word;
275
276         if (copyout(&sword, base, sizeof(sword)) == 0)
277                 return(0);
278         return(-1);
279 }