| /* 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 <arch/uaccess.h> | 
 | #include <umem.h> | 
 | #include <process.h> | 
 | #include <error.h> | 
 | #include <kmalloc.h> | 
 | #include <assert.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 |  | 
 | static int string_copy_from_user(char *dst, const char *src) | 
 | { | 
 | 	int error; | 
 | 	const char *top = src + valid_user_rbytes_from(src); | 
 |  | 
 | 	for (;; dst++, src++) { | 
 | 		if (unlikely(src >= top)) | 
 | 			return -EFAULT; | 
 | 		error = __get_user(dst, src, 1); | 
 | 		if (unlikely(error)) | 
 | 			return error; | 
 | 		if (unlikely(!*dst)) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int string_copy_to_user(char *dst, const char *src) | 
 | { | 
 | 	int error; | 
 | 	char *top = dst + valid_user_rwbytes_from(dst); | 
 |  | 
 | 	for (;; dst++, src++) { | 
 | 		if (unlikely(dst >= top)) | 
 | 			return -EFAULT; | 
 | 		error = __put_user(dst, src, 1); | 
 | 		if (unlikely(error)) | 
 | 			return error; | 
 | 		if (unlikely(!*src)) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int strcpy_from_user(struct proc *p, char *dst, const char *src) | 
 | { | 
 | 	uintptr_t prev = switch_to(p); | 
 | 	int error = string_copy_from_user(dst, src); | 
 |  | 
 | 	switch_back(p, prev); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | int strcpy_to_user(struct proc *p, char *dst, const char *src) | 
 | { | 
 | 	uintptr_t prev = switch_to(p); | 
 | 	int error = string_copy_to_user(dst, src); | 
 |  | 
 | 	switch_back(p, prev); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | int memcpy_from_user(struct proc *p, void *dest, const void *va, size_t len) | 
 | { | 
 | 	uintptr_t prev = switch_to(p); | 
 | 	int error = copy_from_user(dest, va, len); | 
 |  | 
 | 	switch_back(p, prev); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | int memcpy_to_user(struct proc *p, void *dest, const void *src, size_t len) | 
 | { | 
 | 	uintptr_t prev = switch_to(p); | 
 | 	int error = copy_to_user(dest, src, len); | 
 |  | 
 | 	switch_back(p, prev); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | /* Same as above, but sets errno */ | 
 | int memcpy_from_user_errno(struct proc *p, void *dst, const void *src, int len) | 
 | { | 
 | 	int error = memcpy_from_user(p, dst, src, len); | 
 |  | 
 | 	if (unlikely(error < 0)) | 
 | 		set_errno(-error); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | /* Same as above, but sets errno */ | 
 | int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len) | 
 | { | 
 | 	int error = memcpy_to_user(p, dst, src, len); | 
 |  | 
 | 	if (unlikely(error < 0)) | 
 | 		set_errno(-error); | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | /* 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).  This | 
 |  * doesn't play nice with Jumbo pages. */ | 
 | uintptr_t uva2kva(struct proc *p, void *uva, size_t len, int prot) | 
 | { | 
 | 	struct page *u_page; | 
 | 	uintptr_t offset = PGOFF(uva); | 
 | 	if (!p) | 
 | 		return 0; | 
 | 	if (prot & PROT_WRITE) { | 
 | 		if (!is_user_rwaddr(uva, len)) | 
 | 			return 0; | 
 | 	} else { | 
 | 		if (!is_user_raddr(uva, len)) | 
 | 			return 0; | 
 | 	} | 
 | 	u_page = page_lookup(p->env_pgdir, uva, 0); | 
 | 	if (!u_page) | 
 | 		return 0; | 
 | 	return (uintptr_t)page2kva(u_page) + offset; | 
 | } | 
 |  | 
 | /* Helper, copies a pathname from the process into the kernel.  Returns a string | 
 |  * on success, which you must free with free_path.  Returns 0 on failure and | 
 |  * sets errno. */ | 
 | char *copy_in_path(struct proc *p, const char *path, size_t path_l) | 
 | { | 
 | 	struct per_cpu_info *pcpui = &per_cpu_info[core_id()]; | 
 | 	char *t_path; | 
 |  | 
 | 	/* PATH_MAX includes the \0 */ | 
 | 	if (path_l > PATH_MAX) { | 
 | 		set_errno(ENAMETOOLONG); | 
 | 		return 0; | 
 | 	} | 
 | 	t_path = user_strdup_errno(p, path, path_l); | 
 | 	if (!t_path) | 
 | 		return 0; | 
 | 	return t_path; | 
 | } | 
 |  | 
 | /* Helper, frees a path that was allocated with copy_in_path. */ | 
 | void free_path(struct proc *p, char *t_path) | 
 | { | 
 | 	user_memdup_free(p, t_path); | 
 | } |