blob: bca490ec1a8b36f95816559469d1327c7849fc4d [file] [log] [blame]
/* 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];
}