| /* See COPYRIGHT for copyright information. */ |
| |
| #ifdef __SHARC__ |
| #pragma nosharc |
| #endif |
| |
| #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) |
| WRITES(e->env_pgdir, e->env_cr3, e->procinfo, e->procdata) |
| { |
| int i, r; |
| page_t *pgdir = NULL; |
| static page_t * RO shared_page = 0; |
| |
| /* Get a page for the pgdir. Storing the ref in pgdir/env_pgdir */ |
| r = kpage_alloc(&pgdir); |
| if (r < 0) |
| return r; |
| |
| /* |
| * Next, set up the e->env_pgdir and e->env_cr3 pointers to point |
| * to this newly allocated page and clear its contents |
| */ |
| memset(page2kva(pgdir), 0, PGSIZE); |
| e->env_pgdir = (pde_t *COUNT(NPDENTRIES)) TC(page2kva(pgdir)); |
| e->env_cr3 = (physaddr_t) TC(page2pa(pgdir)); |
| |
| /* |
| * Now start filling in the pgdir with mappings required by all newly |
| * created address spaces |
| */ |
| |
| // Map in the kernel to the top of every address space |
| // should be able to do this so long as boot_pgdir never has |
| // anything put below ULIM |
| // TODO check on this! had a nasty bug because of it |
| // this is a bit wonky, since if it's not PGSIZE, lots of other things are |
| // screwed up... |
| memcpy(e->env_pgdir, boot_pgdir, NPDENTRIES*sizeof(pde_t)); |
| |
| // VPT and UVPT map the env's own page table, with |
| // different permissions. |
| #ifndef NOVPT |
| e->env_pgdir[PDX(VPT)] = PTE(LA2PPN(e->env_cr3), PTE_P | PTE_KERN_RW); |
| e->env_pgdir[PDX(UVPT)] = PTE(LA2PPN(e->env_cr3), PTE_P | PTE_USER_RO); |
| #endif |
| |
| /* 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 = get_cont_pages(LOG2_UP(PROCINFO_NUM_PAGES), 0))) |
| goto env_setup_vm_error_i; |
| if (!(e->procdata = get_cont_pages(LOG2_UP(PROCDATA_NUM_PAGES), 0))) |
| goto env_setup_vm_error_d; |
| /* Normally we'd 0 the pages here. We handle it in proc_init_proc*. Don't |
| * 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*SNT)(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*SNT)(UDATA + i*PGSIZE), PTE_USER_RW) < 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: |
| free_cont_pages(e->procdata, LOG2_UP(PROCDATA_NUM_PAGES)); |
| env_setup_vm_error_d: |
| free_cont_pages(e->procinfo, LOG2_UP(PROCINFO_NUM_PAGES)); |
| env_setup_vm_error_i: |
| page_decref(shared_page); |
| env_user_mem_free(e, 0, UVPT); |
| env_pagetable_free(e); |
| return -ENOMEM; |
| } |
| |
| #define PER_CPU_THING(type,name)\ |
| type SLOCKED(name##_lock) * RWPROTECT name;\ |
| type SLOCKED(name##_lock) *\ |
| (get_per_cpu_##name)()\ |
| {\ |
| { R_PERMITTED(global(name))\ |
| return &name[core_id()];\ |
| }\ |
| } |
| |
| /* 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); //since this keeps fucking happening |
| int user_page_free(env_t* e, pte_t* pte, void* va, void* arg) |
| { |
| if (!PAGE_PRESENT(*pte)) |
| return 0; |
| page_t *page = ppn2page(PTE2PPN(*pte)); |
| *pte = 0; |
| 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(); |
| } |
| |