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