One step closer to argv/envp/auxv on the stack
[akaros.git] / kern / src / umem.c
1 /* Copyright (c) 2009, 2010 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * Andrew Waterman <waterman@cs.berkeley.edu>
4  * See LICENSE for details.
5  *
6  * Functions for working with userspace's address space.  The user_mem ones need
7  * to involve some form of pinning (TODO), and that global static needs to go. */
8
9 #include <ros/common.h>
10 #include <umem.h>
11 #include <process.h>
12 #include <error.h>
13 #include <kmalloc.h>
14 #include <assert.h>
15 #include <pmap.h>
16 #include <smp.h>
17
18 /**
19  * @brief Copies data from a user buffer to a kernel buffer.
20  * 
21  * @param p    the process associated with the user program
22  *             from which the buffer is being copied
23  * @param dest the destination address of the kernel buffer
24  * @param va   the address of the userspace buffer from which we are copying
25  * @param len  the length of the userspace buffer
26  *
27  * @return ESUCCESS on success
28  * @return -EFAULT  the page assocaited with 'va' is not present, the user 
29  *                  lacks the proper permissions, or there was an invalid 'va'
30  */
31 int memcpy_from_user(struct proc *p, void *dest, const void *va,
32                      size_t len)
33 {
34         const void *start, *end;
35         size_t num_pages, i;
36         pte_t pte;
37         uintptr_t perm = PTE_USER_RO;
38         size_t bytes_copied = 0;
39
40         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
41
42         start = (void*)ROUNDDOWN((uintptr_t)va, PGSIZE);
43         end = (void*)ROUNDUP((uintptr_t)va + len, PGSIZE);
44
45         if (start >= (void*)ULIM || end > (void*)ULIM)
46                 return -EFAULT;
47
48         num_pages = LA2PPN(end - start);
49         for (i = 0; i < num_pages; i++) {
50                 pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
51                 if (!pte_walk_okay(pte))
52                         return -EFAULT;
53                 if (pte_is_present(pte) && !pte_has_perm_ur(pte))
54                         return -EFAULT;
55                 if (!pte_is_present(pte))
56                         if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_READ))
57                                 return -EFAULT;
58
59                 void *kpage = KADDR(pte_get_paddr(pte));
60                 const void *src_start = i > 0 ? kpage : kpage + (va - start);
61                 void *dst_start = dest + bytes_copied;
62                 size_t copy_len = PGSIZE;
63                 if (i == 0)
64                         copy_len -= va - start;
65                 if (i == num_pages-1)
66                         copy_len -= end - (va + len);
67
68                 memcpy(dst_start, src_start, copy_len);
69                 bytes_copied += copy_len;
70         }
71         assert(bytes_copied == len);
72         return 0;
73 }
74
75 /* Same as above, but sets errno */
76 int memcpy_from_user_errno(struct proc *p, void *dst, const void *src, int len)
77 {
78         if (memcpy_from_user(p, dst, src, len)) {
79                 set_errno(EINVAL);
80                 return -1;
81         }
82         return 0;
83 }
84
85 /**
86  * @brief Copies data to a user buffer from a kernel buffer.
87  * 
88  * @param p    the process associated with the user program
89  *             to which the buffer is being copied
90  * @param dest the destination address of the user buffer
91  * @param va   the address of the kernel buffer from which we are copying
92  * @param len  the length of the user buffer
93  *
94  * @return ESUCCESS on success
95  * @return -EFAULT  the page assocaited with 'va' is not present, the user 
96  *                  lacks the proper permissions, or there was an invalid 'va'
97  */
98 int memcpy_to_user(struct proc *p, void *va, const void *src, size_t len)
99 {
100         const void *start, *end;
101         size_t num_pages, i;
102         pte_t pte;
103         uintptr_t perm = PTE_USER_RW;
104         size_t bytes_copied = 0;
105
106         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
107
108         start = (void*)ROUNDDOWN((uintptr_t)va, PGSIZE);
109         end = (void*)ROUNDUP((uintptr_t)va + len, PGSIZE);
110
111         if (start >= (void*)ULIM || end > (void*)ULIM)
112                 return -EFAULT;
113
114         num_pages = LA2PPN(end - start);
115         for (i = 0; i < num_pages; i++) {
116                 pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
117                 if (!pte_walk_okay(pte))
118                         return -EFAULT;
119                 /* Temporarily change the permissions here to 'ur' instead of 'urw'
120                  * for testing our temporary method of mapping args/envs/aux info
121                  * into user-space.  We still map this into procinfo (which is
122                  * read-only), but will soon map it to the stack (which is
123                  * read-write), so these permissions can change back to normal. */
124                 if (pte_is_present(pte) && !pte_has_perm_ur(pte))
125                         return -EFAULT;
126                 if (!pte_is_present(pte))
127                         if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_WRITE))
128                                 return -EFAULT;
129                 void *kpage = KADDR(pte_get_paddr(pte));
130                 void *dst_start = i > 0 ? kpage : kpage + (va - start);
131                 const void *src_start = src + bytes_copied;
132                 size_t copy_len = PGSIZE;
133                 if (i == 0)
134                         copy_len -= va - start;
135                 if (i == num_pages - 1)
136                         copy_len -= end - (va + len);
137                 memcpy(dst_start, src_start, copy_len);
138                 bytes_copied += copy_len;
139         }
140         assert(bytes_copied == len);
141         return 0;
142 }
143
144 /* Same as above, but sets errno */
145 int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len)
146 {
147         if (memcpy_to_user(p, dst, src, len)) {
148                 set_errno(EFAULT);
149                 return -1;
150         }
151         return 0;
152 }
153
154 /* Creates a buffer (kmalloc) and safely copies into it from va.  Can return an
155  * error code.  Check its response with IS_ERR().  Must be paired with
156  * user_memdup_free() if this succeeded. */
157 void *user_memdup(struct proc *p, const void *va, int len)
158 {
159         void* kva = NULL;
160         if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
161                 return ERR_PTR(-ENOMEM);
162         if (memcpy_from_user(p, kva, va, len)) {
163                 kfree(kva);
164                 return ERR_PTR(-EFAULT);
165         }
166         return kva;
167 }
168
169 void *user_memdup_errno(struct proc *p, const void *va, int len)
170 {
171         void *kva = user_memdup(p, va, len);
172         if (IS_ERR(kva)) {
173                 set_errno(-PTR_ERR(kva));
174                 return NULL;
175         }
176         return kva;
177 }
178
179 void user_memdup_free(struct proc *p, void *va)
180 {
181         kfree(va);
182 }
183
184 /* Same as memdup, but just does strings, and needs to know the actual strlen.
185  * Still needs memdup_free()d.  This will enforce that the string is null
186  * terminated.  The parameter strlen does not include the \0, though it can if
187  * someone else is playing it safe.  Since strlen() doesn't count the \0, we'll
188  * play it safe here. */
189 char *user_strdup(struct proc *p, const char *u_string, size_t strlen)
190 {
191         char *k_string = user_memdup(p, u_string, strlen + 1);
192         if (!IS_ERR(k_string))
193                 k_string[strlen] = '\0';
194         return k_string;
195 }
196
197 /* user_strdup, but this handles the errno.  0 on failure, ptr on success */
198 char *user_strdup_errno(struct proc *p, const char *u_string, size_t strlen)
199 {
200         void *k_string = user_strdup(p, u_string, strlen);
201         if (IS_ERR(k_string)) {
202                 set_errno(-PTR_ERR(k_string));
203                 return NULL;
204         }
205         return k_string;
206 }
207
208 void *kmalloc_errno(int len)
209 {
210         void *kva = NULL;
211         if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
212                 set_errno(ENOMEM);
213         return kva;
214 }
215
216 /* Returns true if uva and kva both resolve to the same phys addr.  If uva is
217  * unmapped, it will return FALSE.  This is probably what you want, since after
218  * all uva isn't kva. */
219 bool uva_is_kva(struct proc *p, void *uva, void *kva)
220 {
221         struct page *u_page;
222         assert(kva);                            /* catch bugs */
223         /* Check offsets first */
224         if (PGOFF(uva) != PGOFF(kva))
225                 return FALSE;
226         /* Check to see if it is the same physical page */
227         u_page = page_lookup(p->env_pgdir, uva, 0);
228         if (!u_page)
229                 return FALSE;
230         return (kva2page(kva) == u_page) ? TRUE : FALSE;
231 }
232
233 /* Given a proc and a user virtual address, gives us the KVA.  Useful for
234  * debugging.  Returns 0 if the page is unmapped (page lookup fails).  If you
235  * give it a kva, it'll give you that same KVA, but this doesn't play nice with
236  * Jumbo pages. */
237 uintptr_t uva2kva(struct proc *p, void *uva)
238 {
239         struct page *u_page;
240         uintptr_t offset = PGOFF(uva);
241         if (!p)
242                 return 0;
243         u_page = page_lookup(p->env_pgdir, uva, 0);
244         if (!u_page)
245                 return 0;
246         return (uintptr_t)page2kva(u_page) + offset;
247 }