0f2fd82e3f9fcba460ddf6a4bb53ac8eba06fdff
[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 Global variable used to store erroneous virtual addresses as the
20  *        result of a failed user_mem_check().
21  *
22  * zra: What if two checks fail at the same time? Maybe this should be per-cpu?
23  *
24  */
25 static void *DANGEROUS RACY user_mem_check_addr;
26
27 /**
28  * @brief Check that an environment is allowed to access the range of memory
29  * [va, va+len) with permissions 'perm | PTE_P'.
30  *
31  * Normally 'perm' will contain PTE_U at least, but this is not required.  The
32  * function get_va_perms only checks for PTE_U, PTE_W, and PTE_P.  It won't
33  * check for things like PTE_PS, PTE_A, etc.
34  * 'va' and 'len' need not be page-aligned;
35  *
36  * A user program can access a virtual address if:
37  *     -# the address is below ULIM
38  *     -# the page table gives it permission.  
39  *
40  * If there is an error, 'user_mem_check_addr' is set to the first
41  * erroneous virtual address.
42  *
43  * @param p    the process associated with the user program trying to access
44  *             the virtual address range
45  * @param va   the first virtual address in the range
46  * @param len  the length of the virtual address range
47  * @param perm the permissions the user is trying to access the virtual address 
48  *             range with
49  *
50  * @return VA a pointer of type COUNT(len) to the address range
51  * @return NULL trying to access this range of virtual addresses is not allowed
52  */
53 void *user_mem_check(struct proc *p, const void *DANGEROUS va, size_t len,
54                      int perm)
55 {
56         if (len == 0) {
57                 warn("Called user_mem_check with a len of 0. Don't do that. Returning NULL");
58                 return NULL;
59         }
60         
61         // TODO - will need to sort this out wrt page faulting / PTE_P
62         // also could be issues with sleeping and waking up to find pages
63         // are unmapped, though i think the lab ignores this since the 
64         // kernel is uninterruptible
65         void *DANGEROUS start, *DANGEROUS end;
66         size_t num_pages, i;
67         int page_perms = 0;
68
69         perm |= PTE_P;
70         start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
71         end = ROUNDUP((void*DANGEROUS)va + len, PGSIZE);
72         if (start >= end) {
73                 warn("Blimey!  Wrap around in VM range calculation!");  
74                 return NULL;
75         }
76         num_pages = LA2PPN(end - start);
77         for (i = 0; i < num_pages; i++, start += PGSIZE) {
78                 page_perms = get_va_perms(p->env_pgdir, start);
79                 // ensures the bits we want on are turned on.  if not, error out
80                 if ((page_perms & perm) != perm) {
81                         if (i == 0)
82                                 user_mem_check_addr = (void*DANGEROUS)va;
83                         else
84                                 user_mem_check_addr = start;
85                         return NULL;
86                 }
87         }
88         // this should never be needed, since the perms should catch it
89         if ((uintptr_t)end > ULIM) {
90                 warn ("I suck - Bug in user permission mappings!");
91                 return NULL;
92         }
93         return (void *COUNT(len))TC(va);
94 }
95
96 /**
97  * @brief Checks that process 'p' is allowed to access the range
98  * of memory [va, va+len) with permissions 'perm | PTE_U'. Destroy 
99  * process 'p' if the assertion fails.
100  *
101  * This function is identical to user_mem_assert() except that it has a side
102  * affect of destroying the process 'p' if the memory check fails.
103  *
104  * @param p    the process associated with the user program trying to access
105  *             the virtual address range
106  * @param va   the first virtual address in the range
107  * @param len  the length of the virtual address range
108  * @param perm the permissions the user is trying to access the virtual address 
109  *             range with
110  *
111  * @return VA a pointer of type COUNT(len) to the address range
112  * @return NULL trying to access this range of virtual addresses is not allowed
113  *              process 'p' is destroyed
114  */
115 void *user_mem_assert(struct proc *p, const void *DANGEROUS va, size_t len,
116                        int perm)
117 {
118         if (len == 0) {
119                 warn("Called user_mem_assert with a len of 0. Don't do that. Returning NULL");
120                 return NULL;
121         }
122         
123         void *COUNT(len) res = user_mem_check(p, va, len, perm | PTE_USER_RO);
124         if (!res) {
125                 cprintf("[%08x] user_mem_check assertion failure for "
126                         "va %08x\n", p->pid, user_mem_check_addr);
127                 proc_destroy(p);        // may not return
128         return NULL;
129         }
130     return res;
131 }
132
133 /**
134  * @brief Copies data from a user buffer to a kernel buffer.
135  * 
136  * @param p    the process associated with the user program
137  *             from which the buffer is being copied
138  * @param dest the destination address of the kernel buffer
139  * @param va   the address of the userspace buffer from which we are copying
140  * @param len  the length of the userspace buffer
141  *
142  * @return ESUCCESS on success
143  * @return -EFAULT  the page assocaited with 'va' is not present, the user 
144  *                  lacks the proper permissions, or there was an invalid 'va'
145  */
146 int memcpy_from_user(struct proc *p, void *dest, const void *DANGEROUS va,
147                      size_t len)
148 {
149         const void *DANGEROUS start, *DANGEROUS end;
150         size_t num_pages, i;
151         pte_t *pte;
152         uintptr_t perm = PTE_P | PTE_USER_RO;
153         size_t bytes_copied = 0;
154
155         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
156
157         start = ROUNDDOWN(va, PGSIZE);
158         end = ROUNDUP(va + len, PGSIZE);
159
160         if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
161                 return -EFAULT;
162
163         num_pages = LA2PPN(end - start);
164         for (i = 0; i < num_pages; i++) {
165                 pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
166                 if (!pte)
167                         return -EFAULT;
168                 if ((*pte & PTE_P) && (*pte & PTE_USER_RO) != PTE_USER_RO)
169                         return -EFAULT;
170                 if (!(*pte & PTE_P))
171                         if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_READ))
172                                 return -EFAULT;
173
174                 void *kpage = KADDR(PTE_ADDR(*pte));
175                 const void *src_start = i > 0 ? kpage : kpage + (va - start);
176                 void *dst_start = dest + bytes_copied;
177                 size_t copy_len = PGSIZE;
178                 if (i == 0)
179                         copy_len -= va - start;
180                 if (i == num_pages-1)
181                         copy_len -= end - (va + len);
182
183                 memcpy(dst_start, src_start, copy_len);
184                 bytes_copied += copy_len;
185         }
186         assert(bytes_copied == len);
187         return 0;
188 }
189
190 /* Same as above, but sets errno */
191 int memcpy_from_user_errno(struct proc *p, void *dst, const void *src, int len)
192 {
193         if (memcpy_from_user(p, dst, src, len)) {
194                 set_errno(EINVAL);
195                 return -1;
196         }
197         return 0;
198 }
199
200 /**
201  * @brief Copies data to a user buffer from a kernel buffer.
202  * 
203  * @param p    the process associated with the user program
204  *             to which the buffer is being copied
205  * @param dest the destination address of the user buffer
206  * @param va   the address of the kernel buffer from which we are copying
207  * @param len  the length of the user buffer
208  *
209  * @return ESUCCESS on success
210  * @return -EFAULT  the page assocaited with 'va' is not present, the user 
211  *                  lacks the proper permissions, or there was an invalid 'va'
212  */
213 int memcpy_to_user(struct proc *p, void *va, const void *src, size_t len)
214 {
215         const void *DANGEROUS start, *DANGEROUS end;
216         size_t num_pages, i;
217         pte_t *pte;
218         uintptr_t perm = PTE_P | PTE_USER_RW;
219         size_t bytes_copied = 0;
220
221         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
222
223         start = ROUNDDOWN(va, PGSIZE);
224         end = ROUNDUP(va + len, PGSIZE);
225
226         if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
227                 return -EFAULT;
228
229         num_pages = LA2PPN(end - start);
230         for (i = 0; i < num_pages; i++) {
231                 pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
232                 if (!pte)
233                         return -EFAULT;
234                 if ((*pte & PTE_P) && (*pte & PTE_USER_RW) != PTE_USER_RW)
235                         return -EFAULT;
236                 if (!(*pte & PTE_P))
237                         if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_WRITE))
238                                 return -EFAULT;
239                 void *kpage = KADDR(PTE_ADDR(*pte));
240                 void *dst_start = i > 0 ? kpage : kpage + (va - start);
241                 const void *src_start = src + bytes_copied;
242                 size_t copy_len = PGSIZE;
243                 if (i == 0)
244                         copy_len -= va - start;
245                 if (i == num_pages - 1)
246                         copy_len -= end - (va + len);
247                 memcpy(dst_start, src_start, copy_len);
248                 bytes_copied += copy_len;
249         }
250         assert(bytes_copied == len);
251         return 0;
252 }
253
254 /* Same as above, but sets errno */
255 int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len)
256 {
257         if (memcpy_to_user(p, dst, src, len)) {
258                 set_errno(EINVAL);
259                 return -1;
260         }
261         return 0;
262 }
263
264 /* Creates a buffer (kmalloc) and safely copies into it from va.  Can return an
265  * error code.  Check its response with IS_ERR().  Must be paired with
266  * user_memdup_free() if this succeeded. */
267 void *user_memdup(struct proc *p, const void *va, int len)
268 {
269         void* kva = NULL;
270         if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
271                 return ERR_PTR(-ENOMEM);
272         if (memcpy_from_user(p, kva, va, len)) {
273                 kfree(kva);
274                 return ERR_PTR(-EINVAL);
275         }
276         return kva;
277 }
278
279 void *user_memdup_errno(struct proc *p, const void *va, int len)
280 {
281         void *kva = user_memdup(p, va, len);
282         if (IS_ERR(kva)) {
283                 set_errno(-PTR_ERR(kva));
284                 return NULL;
285         }
286         return kva;
287 }
288
289 void user_memdup_free(struct proc *p, void *va)
290 {
291         kfree(va);
292 }
293
294 /* Same as memdup, but just does strings, and needs to know the actual strlen.
295  * Still needs memdup_free()d.  This will enforce that the string is null
296  * terminated.  The parameter strlen does not include the \0, though it can if
297  * someone else is playing it safe.  Since strlen() doesn't count the \0, we'll
298  * play it safe here. */
299 char *user_strdup(struct proc *p, const char *u_string, size_t strlen)
300 {
301         char *k_string = user_memdup(p, u_string, strlen + 1);
302         if (!IS_ERR(k_string))
303                 k_string[strlen] = '\0';
304         return k_string;
305 }
306
307 /* user_strdup, but this handles the errno.  0 on failure, ptr on success */
308 char *user_strdup_errno(struct proc *p, const char *u_string, size_t strlen)
309 {
310         void *k_string = user_strdup(p, u_string, strlen);
311         if (IS_ERR(k_string)) {
312                 set_errno(-PTR_ERR(k_string));
313                 return NULL;
314         }
315         return k_string;
316 }
317
318 void *kmalloc_errno(int len)
319 {
320         void *kva = NULL;
321         if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
322                 set_errno(ENOMEM);
323         return kva;
324 }
325
326 /* Returns true if uva and kva both resolve to the same phys addr.  If uva is
327  * unmapped, it will return FALSE.  This is probably what you want, since after
328  * all uva isn't kva. */
329 bool uva_is_kva(struct proc *p, void *uva, void *kva)
330 {
331         struct page *u_page;
332         assert(kva);                            /* catch bugs */
333         /* Check offsets first */
334         if (PGOFF(uva) != PGOFF(kva))
335                 return FALSE;
336         /* Check to see if it is the same physical page */
337         u_page = page_lookup(p->env_pgdir, uva, 0);
338         if (!u_page)
339                 return FALSE;
340         return (kva2page(kva) == u_page) ? TRUE : FALSE;
341 }