| /* See COPYRIGHT for copyright information. */ | 
 |  | 
 | #include <arch/arch.h> | 
 | #include <arch/mmu.h> | 
 | #include <bitmask.h> | 
 | #include <elf.h> | 
 | #include <smp.h> | 
 | #include <atomic.h> | 
 | #include <string.h> | 
 | #include <assert.h> | 
 | #include <process.h> | 
 | #include <pmap.h> | 
 | #include <trap.h> | 
 | #include <monitor.h> | 
 | #include <manager.h> | 
 | #include <stdio.h> | 
 | #include <schedule.h> | 
 | #include <kmalloc.h> | 
 | #include <mm.h> | 
 |  | 
 | #include <ros/syscall.h> | 
 | #include <error.h> | 
 |  | 
 | atomic_t num_envs; | 
 |  | 
 | // Initialize the kernel virtual memory layout for environment e. | 
 | // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly, | 
 | // and initialize the kernel portion of the new environment's address space. | 
 | // Do NOT (yet) map anything into the user portion | 
 | // of the environment's virtual address space. | 
 | // | 
 | // Returns 0 on success, < 0 on error.  Errors include: | 
 | //	-ENOMEM if page directory or table could not be allocated. | 
 | // | 
 | int env_setup_vm(env_t *e) | 
 | { | 
 | 	int i, ret; | 
 | 	static page_t *shared_page = 0; | 
 |  | 
 | 	if ((ret = arch_pgdir_setup(boot_pgdir, &e->env_pgdir))) | 
 | 		return ret; | 
 | 	e->env_cr3 = arch_pgdir_get_cr3(e->env_pgdir); | 
 |  | 
 | 	/* These need to be contiguous, so the kernel can alias them.  Note the | 
 | 	 * pages return with a refcnt, but it's okay to insert them since we | 
 | 	 * free them manually when the process is cleaned up. */ | 
 | 	if (!(e->procinfo = kpages_alloc(PROCINFO_NUM_PAGES * PGSIZE, | 
 | 					 MEM_WAIT))) | 
 | 		goto env_setup_vm_error_i; | 
 | 	if (!(e->procdata = kpages_alloc(PROCDATA_NUM_PAGES * PGSIZE, | 
 | 					 MEM_WAIT))) | 
 | 		goto env_setup_vm_error_d; | 
 | 	/* Normally we would 0 the pages here.  We handle it in proc_init_proc*. | 
 | 	 * Do not start the process without calling those. */ | 
 | 	for (int i = 0; i < PROCINFO_NUM_PAGES; i++) { | 
 | 		if (page_insert(e->env_pgdir, | 
 | 				kva2page((void*)e->procinfo + i * PGSIZE), | 
 | 				(void*)(UINFO + i * PGSIZE), PTE_USER_RO) < 0) | 
 | 			goto env_setup_vm_error; | 
 | 	} | 
 | 	for (int i = 0; i < PROCDATA_NUM_PAGES; i++) { | 
 | 		if (page_insert(e->env_pgdir, | 
 | 				kva2page((void*)e->procdata + i * PGSIZE), | 
 | 				(void*)(UDATA + i * PGSIZE), PTE_USER_RW) < 0) | 
 | 			goto env_setup_vm_error; | 
 | 	} | 
 | 	for (int i = 0; i < PROCGINFO_NUM_PAGES; i++) { | 
 | 		if (page_insert(e->env_pgdir, | 
 | 		                kva2page((void*)&__proc_global_info | 
 | 					 + i * PGSIZE), | 
 | 		                (void*)(UGINFO + i * PGSIZE), PTE_USER_RO) < 0) | 
 | 			goto env_setup_vm_error; | 
 | 	} | 
 | 	/* Finally, set up the Global Shared Data page for all processes.  Can't | 
 | 	 * be trusted, but still very useful at this stage for us.  Consider | 
 | 	 * removing when we have real processes (TODO). | 
 | 	 * | 
 | 	 * Note the page is alloced only the first time through, and its ref is | 
 | 	 * stored in shared_page. */ | 
 | 	if (!shared_page) { | 
 | 		if (upage_alloc(e, &shared_page, 1) < 0) | 
 | 			goto env_setup_vm_error; | 
 | 	} | 
 | 	if (page_insert(e->env_pgdir, shared_page, (void*)UGDATA, PTE_USER_RW) | 
 | 	    < 0) | 
 | 		goto env_setup_vm_error; | 
 |  | 
 | 	return 0; | 
 |  | 
 | env_setup_vm_error: | 
 | 	kpages_free(e->procdata, PROCDATA_NUM_PAGES * PGSIZE); | 
 | env_setup_vm_error_d: | 
 | 	kpages_free(e->procinfo, PROCINFO_NUM_PAGES * PGSIZE); | 
 | env_setup_vm_error_i: | 
 | 	env_user_mem_free(e, 0, UVPT); | 
 | 	env_pagetable_free(e); | 
 | 	return -ENOMEM; | 
 | } | 
 |  | 
 | /* Frees (decrefs) all memory mapped in the given range */ | 
 | void env_user_mem_free(env_t* e, void* start, size_t len) | 
 | { | 
 | 	assert((uintptr_t)start + len <= UVPT); | 
 | 	int user_page_free(env_t* e, pte_t pte, void* va, void* arg) | 
 | 	{ | 
 | 		if (!pte_is_mapped(pte)) | 
 | 			return 0; | 
 | 		page_t *page = pa2page(pte_get_paddr(pte)); | 
 | 		pte_clear(pte); | 
 | 		page_decref(page); | 
 | 		/* TODO: consider other states here (like !P, yet still tracking | 
 | 		 * a page, for VM tricks, page map stuff, etc.  Should be okay: | 
 | 		 * once we're freeing, everything else about this proc is dead. | 
 | 		 * */ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	env_user_mem_walk(e,start,len,&user_page_free,NULL); | 
 | 	tlbflush(); | 
 | } | 
 |  | 
 | void set_username(struct username *u, char *name) | 
 | { | 
 | 	ERRSTACK(1); | 
 |  | 
 | 	spin_lock(&u->name_lock); | 
 |  | 
 | 	if (waserror()) { | 
 | 		spin_unlock(&u->name_lock); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	__set_username(u, name); | 
 |  | 
 | 	poperror(); | 
 | 	spin_unlock(&u->name_lock); | 
 | } | 
 |  | 
 | /* | 
 |  * This function exists so that you can do your own locking - do not use it | 
 |  * without locking the username's spinlock yourself. | 
 |  */ | 
 | void __set_username(struct username *u, char *name) | 
 | { | 
 | 	if (!name) | 
 | 		error(EINVAL, "New username is NULL"); | 
 |  | 
 | 	if (strlen(name) > sizeof(u->name) - 1) | 
 | 		error(EINVAL, | 
 | 		      "New username for process more than %d chars long", | 
 | 		      sizeof(u->name) - 1); | 
 |  | 
 | 	// 'backward' copy since reads aren't protected | 
 | 	u->name[0] = 0; | 
 | 	wmb(); // ensure user.name="" before writing the rest of the new name | 
 | 	strlcpy(&u->name[1], &name[1], sizeof(u->name)); | 
 | 	wmb(); // ensure new name is written before writing first byte | 
 | 	u->name[0] = name[0]; | 
 | } |