|  | /* Copyright (c) 2009, 2010 The Regents of the University of California | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * Andrew Waterman <waterman@cs.berkeley.edu> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * Functions for working with userspace's address space.  The user_mem ones need | 
|  | * to involve some form of pinning (TODO), and that global static needs to go. */ | 
|  |  | 
|  | #include <ros/common.h> | 
|  | #include <umem.h> | 
|  | #include <process.h> | 
|  | #include <error.h> | 
|  | #include <kmalloc.h> | 
|  | #include <assert.h> | 
|  | #include <pmap.h> | 
|  | #include <smp.h> | 
|  |  | 
|  | /** | 
|  | * @brief Copies data from a user buffer to a kernel buffer. | 
|  | * | 
|  | * @param p    the process associated with the user program | 
|  | *             from which the buffer is being copied | 
|  | * @param dest the destination address of the kernel buffer | 
|  | * @param va   the address of the userspace buffer from which we are copying | 
|  | * @param len  the length of the userspace buffer | 
|  | * | 
|  | * @return ESUCCESS on success | 
|  | * @return -EFAULT  the page assocaited with 'va' is not present, the user | 
|  | *                  lacks the proper permissions, or there was an invalid 'va' | 
|  | */ | 
|  | int memcpy_from_user(struct proc *p, void *dest, const void *va, | 
|  | size_t len) | 
|  | { | 
|  | const void *start, *end; | 
|  | size_t num_pages, i; | 
|  | pte_t pte; | 
|  | uintptr_t perm = PTE_USER_RO; | 
|  | size_t bytes_copied = 0; | 
|  |  | 
|  | static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around | 
|  |  | 
|  | start = (void*)ROUNDDOWN((uintptr_t)va, PGSIZE); | 
|  | end = (void*)ROUNDUP((uintptr_t)va + len, PGSIZE); | 
|  |  | 
|  | if (start >= (void*)ULIM || end > (void*)ULIM) | 
|  | return -EFAULT; | 
|  |  | 
|  | num_pages = LA2PPN(end - start); | 
|  | for (i = 0; i < num_pages; i++) { | 
|  | pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0); | 
|  | if (!pte_walk_okay(pte)) | 
|  | return -EFAULT; | 
|  | if (pte_is_present(pte) && !pte_has_perm_ur(pte)) | 
|  | return -EFAULT; | 
|  | if (!pte_is_present(pte)) | 
|  | if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_READ)) | 
|  | return -EFAULT; | 
|  |  | 
|  | void *kpage = KADDR(pte_get_paddr(pte)); | 
|  | const void *src_start = i > 0 ? kpage : kpage + (va - start); | 
|  | void *dst_start = dest + bytes_copied; | 
|  | size_t copy_len = PGSIZE; | 
|  | if (i == 0) | 
|  | copy_len -= va - start; | 
|  | if (i == num_pages-1) | 
|  | copy_len -= end - (va + len); | 
|  |  | 
|  | memcpy(dst_start, src_start, copy_len); | 
|  | bytes_copied += copy_len; | 
|  | } | 
|  | assert(bytes_copied == len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Same as above, but sets errno */ | 
|  | int memcpy_from_user_errno(struct proc *p, void *dst, const void *src, int len) | 
|  | { | 
|  | if (memcpy_from_user(p, dst, src, len)) { | 
|  | set_errno(EINVAL); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief Copies data to a user buffer from a kernel buffer. | 
|  | * | 
|  | * @param p    the process associated with the user program | 
|  | *             to which the buffer is being copied | 
|  | * @param dest the destination address of the user buffer | 
|  | * @param va   the address of the kernel buffer from which we are copying | 
|  | * @param len  the length of the user buffer | 
|  | * | 
|  | * @return ESUCCESS on success | 
|  | * @return -EFAULT  the page assocaited with 'va' is not present, the user | 
|  | *                  lacks the proper permissions, or there was an invalid 'va' | 
|  | */ | 
|  | int memcpy_to_user(struct proc *p, void *va, const void *src, size_t len) | 
|  | { | 
|  | const void *start, *end; | 
|  | size_t num_pages, i; | 
|  | pte_t pte; | 
|  | uintptr_t perm = PTE_USER_RW; | 
|  | size_t bytes_copied = 0; | 
|  |  | 
|  | static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around | 
|  |  | 
|  | start = (void*)ROUNDDOWN((uintptr_t)va, PGSIZE); | 
|  | end = (void*)ROUNDUP((uintptr_t)va + len, PGSIZE); | 
|  |  | 
|  | if (start >= (void*)ULIM || end > (void*)ULIM) | 
|  | return -EFAULT; | 
|  |  | 
|  | num_pages = LA2PPN(end - start); | 
|  | for (i = 0; i < num_pages; i++) { | 
|  | pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0); | 
|  | if (!pte_walk_okay(pte)) | 
|  | return -EFAULT; | 
|  | if (pte_is_present(pte) && !pte_has_perm_urw(pte)) | 
|  | return -EFAULT; | 
|  | if (!pte_is_present(pte)) | 
|  | if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_WRITE)) | 
|  | return -EFAULT; | 
|  | void *kpage = KADDR(pte_get_paddr(pte)); | 
|  | void *dst_start = i > 0 ? kpage : kpage + (va - start); | 
|  | const void *src_start = src + bytes_copied; | 
|  | size_t copy_len = PGSIZE; | 
|  | if (i == 0) | 
|  | copy_len -= va - start; | 
|  | if (i == num_pages - 1) | 
|  | copy_len -= end - (va + len); | 
|  | memcpy(dst_start, src_start, copy_len); | 
|  | bytes_copied += copy_len; | 
|  | } | 
|  | assert(bytes_copied == len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Same as above, but sets errno */ | 
|  | int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len) | 
|  | { | 
|  | if (memcpy_to_user(p, dst, src, len)) { | 
|  | set_errno(EFAULT); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Creates a buffer (kmalloc) and safely copies into it from va.  Can return an | 
|  | * error code.  Check its response with IS_ERR().  Must be paired with | 
|  | * user_memdup_free() if this succeeded. */ | 
|  | void *user_memdup(struct proc *p, const void *va, int len) | 
|  | { | 
|  | void* kva = NULL; | 
|  | if (len < 0 || (kva = kmalloc(len, 0)) == NULL) | 
|  | return ERR_PTR(-ENOMEM); | 
|  | if (memcpy_from_user(p, kva, va, len)) { | 
|  | kfree(kva); | 
|  | return ERR_PTR(-EFAULT); | 
|  | } | 
|  | return kva; | 
|  | } | 
|  |  | 
|  | void *user_memdup_errno(struct proc *p, const void *va, int len) | 
|  | { | 
|  | void *kva = user_memdup(p, va, len); | 
|  | if (IS_ERR(kva)) { | 
|  | set_errno(-PTR_ERR(kva)); | 
|  | return NULL; | 
|  | } | 
|  | return kva; | 
|  | } | 
|  |  | 
|  | void user_memdup_free(struct proc *p, void *va) | 
|  | { | 
|  | kfree(va); | 
|  | } | 
|  |  | 
|  | /* Same as memdup, but just does strings, and needs to know the actual strlen. | 
|  | * Still needs memdup_free()d.  This will enforce that the string is null | 
|  | * terminated.  The parameter strlen does not include the \0, though it can if | 
|  | * someone else is playing it safe.  Since strlen() doesn't count the \0, we'll | 
|  | * play it safe here. */ | 
|  | char *user_strdup(struct proc *p, const char *u_string, size_t strlen) | 
|  | { | 
|  | char *k_string = user_memdup(p, u_string, strlen + 1); | 
|  | if (!IS_ERR(k_string)) | 
|  | k_string[strlen] = '\0'; | 
|  | return k_string; | 
|  | } | 
|  |  | 
|  | /* user_strdup, but this handles the errno.  0 on failure, ptr on success */ | 
|  | char *user_strdup_errno(struct proc *p, const char *u_string, size_t strlen) | 
|  | { | 
|  | void *k_string = user_strdup(p, u_string, strlen); | 
|  | if (IS_ERR(k_string)) { | 
|  | set_errno(-PTR_ERR(k_string)); | 
|  | return NULL; | 
|  | } | 
|  | return k_string; | 
|  | } | 
|  |  | 
|  | void *kmalloc_errno(int len) | 
|  | { | 
|  | void *kva = NULL; | 
|  | if (len < 0 || (kva = kmalloc(len, 0)) == NULL) | 
|  | set_errno(ENOMEM); | 
|  | return kva; | 
|  | } | 
|  |  | 
|  | /* Returns true if uva and kva both resolve to the same phys addr.  If uva is | 
|  | * unmapped, it will return FALSE.  This is probably what you want, since after | 
|  | * all uva isn't kva. */ | 
|  | bool uva_is_kva(struct proc *p, void *uva, void *kva) | 
|  | { | 
|  | struct page *u_page; | 
|  | assert(kva);				/* catch bugs */ | 
|  | /* Check offsets first */ | 
|  | if (PGOFF(uva) != PGOFF(kva)) | 
|  | return FALSE; | 
|  | /* Check to see if it is the same physical page */ | 
|  | u_page = page_lookup(p->env_pgdir, uva, 0); | 
|  | if (!u_page) | 
|  | return FALSE; | 
|  | return (kva2page(kva) == u_page) ? TRUE : FALSE; | 
|  | } | 
|  |  | 
|  | /* Given a proc and a user virtual address, gives us the KVA.  Useful for | 
|  | * debugging.  Returns 0 if the page is unmapped (page lookup fails).  If you | 
|  | * give it a kva, it'll give you that same KVA, but this doesn't play nice with | 
|  | * Jumbo pages. */ | 
|  | uintptr_t uva2kva(struct proc *p, void *uva) | 
|  | { | 
|  | struct page *u_page; | 
|  | uintptr_t offset = PGOFF(uva); | 
|  | if (!p) | 
|  | return 0; | 
|  | u_page = page_lookup(p->env_pgdir, uva, 0); | 
|  | if (!u_page) | 
|  | return 0; | 
|  | return (uintptr_t)page2kva(u_page) + offset; | 
|  | } |