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.
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. */
9 #include <ros/common.h>
19 * @brief Copies data from a user buffer to a kernel buffer.
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
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'
31 int memcpy_from_user(struct proc *p, void *dest, const void *DANGEROUS va,
34 const void *DANGEROUS start, *DANGEROUS end;
37 uintptr_t perm = PTE_P | PTE_USER_RO;
38 size_t bytes_copied = 0;
40 static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
42 start = (void*)ROUNDDOWN((uintptr_t)va, PGSIZE);
43 end = (void*)ROUNDUP((uintptr_t)va + len, PGSIZE);
45 if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
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))
53 if (pte_is_present(pte) && !pte_has_perm_ur(pte))
55 if (!pte_is_present(pte))
56 if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_READ))
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;
64 copy_len -= va - start;
66 copy_len -= end - (va + len);
68 memcpy(dst_start, src_start, copy_len);
69 bytes_copied += copy_len;
71 assert(bytes_copied == len);
75 /* Same as above, but sets errno */
76 int memcpy_from_user_errno(struct proc *p, void *dst, const void *src, int len)
78 if (memcpy_from_user(p, dst, src, len)) {
86 * @brief Copies data to a user buffer from a kernel buffer.
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
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'
98 int memcpy_to_user(struct proc *p, void *va, const void *src, size_t len)
100 const void *DANGEROUS start, *DANGEROUS end;
103 uintptr_t perm = PTE_P | PTE_USER_RW;
104 size_t bytes_copied = 0;
106 static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
108 start = (void*)ROUNDDOWN((uintptr_t)va, PGSIZE);
109 end = (void*)ROUNDUP((uintptr_t)va + len, PGSIZE);
111 if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
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))
119 if (pte_is_present(pte) && !pte_has_perm_urw(pte))
121 if (!pte_is_present(pte))
122 if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_WRITE))
124 void *kpage = KADDR(pte_get_paddr(pte));
125 void *dst_start = i > 0 ? kpage : kpage + (va - start);
126 const void *src_start = src + bytes_copied;
127 size_t copy_len = PGSIZE;
129 copy_len -= va - start;
130 if (i == num_pages - 1)
131 copy_len -= end - (va + len);
132 memcpy(dst_start, src_start, copy_len);
133 bytes_copied += copy_len;
135 assert(bytes_copied == len);
139 /* Same as above, but sets errno */
140 int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len)
142 if (memcpy_to_user(p, dst, src, len)) {
149 /* Creates a buffer (kmalloc) and safely copies into it from va. Can return an
150 * error code. Check its response with IS_ERR(). Must be paired with
151 * user_memdup_free() if this succeeded. */
152 void *user_memdup(struct proc *p, const void *va, int len)
155 if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
156 return ERR_PTR(-ENOMEM);
157 if (memcpy_from_user(p, kva, va, len)) {
159 return ERR_PTR(-EFAULT);
164 void *user_memdup_errno(struct proc *p, const void *va, int len)
166 void *kva = user_memdup(p, va, len);
168 set_errno(-PTR_ERR(kva));
174 void user_memdup_free(struct proc *p, void *va)
179 /* Same as memdup, but just does strings, and needs to know the actual strlen.
180 * Still needs memdup_free()d. This will enforce that the string is null
181 * terminated. The parameter strlen does not include the \0, though it can if
182 * someone else is playing it safe. Since strlen() doesn't count the \0, we'll
183 * play it safe here. */
184 char *user_strdup(struct proc *p, const char *u_string, size_t strlen)
186 char *k_string = user_memdup(p, u_string, strlen + 1);
187 if (!IS_ERR(k_string))
188 k_string[strlen] = '\0';
192 /* user_strdup, but this handles the errno. 0 on failure, ptr on success */
193 char *user_strdup_errno(struct proc *p, const char *u_string, size_t strlen)
195 void *k_string = user_strdup(p, u_string, strlen);
196 if (IS_ERR(k_string)) {
197 set_errno(-PTR_ERR(k_string));
203 void *kmalloc_errno(int len)
206 if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
211 /* Returns true if uva and kva both resolve to the same phys addr. If uva is
212 * unmapped, it will return FALSE. This is probably what you want, since after
213 * all uva isn't kva. */
214 bool uva_is_kva(struct proc *p, void *uva, void *kva)
217 assert(kva); /* catch bugs */
218 /* Check offsets first */
219 if (PGOFF(uva) != PGOFF(kva))
221 /* Check to see if it is the same physical page */
222 u_page = page_lookup(p->env_pgdir, uva, 0);
225 return (kva2page(kva) == u_page) ? TRUE : FALSE;
228 /* Given a proc and a user virtual address, gives us the KVA. Useful for
229 * debugging. Returns 0 if the page is unmapped (page lookup fails). If you
230 * give it a kva, it'll give you that same KVA, but this doesn't play nice with
232 uintptr_t uva2kva(struct proc *p, void *uva)
235 uintptr_t offset = PGOFF(uva);
238 u_page = page_lookup(p->env_pgdir, uva, 0);
241 return (uintptr_t)page2kva(u_page) + offset;