| /* 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]; |
| } |