// 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; /* TODO: verify there is nothing below ULIM */ 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 = 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*)(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: 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; }
int get_user_page(struct proc *p, unsigned long uvastart, int write, int force, struct page **plist) { pte_t pte; int ret = -1; struct page *pp; spin_lock(&p->pte_lock); pte = pgdir_walk(p->env_pgdir, (void*)uvastart, TRUE); if (!pte_walk_okay(pte)) goto err1; if (!pte_is_present(pte)) { unsigned long prot = PTE_P | PTE_U | PTE_A | PTE_W | PTE_D; #if 0 printk("[akaros]: get_user_page() uva=0x%llx pte absent\n", uvastart); #endif /* * TODO: ok to allocate with pte_lock? "prot" needs to be * based on VMR writability, refer to pgprot_noncached(). */ if (upage_alloc(p, &pp, 0)) goto err1; pte_write(pte, page2pa(pp), prot); } else { pp = pa2page(pte_get_paddr(pte)); /* __vmr_free_pgs() refcnt's pagemap pages differently */ if (atomic_read(&pp->pg_flags) & PG_PAGEMAP) { printk("[akaros]: get_user_page(): uva=0x%llx\n", uvastart); goto err1; } } if (write && (!pte_has_perm_urw(pte))) { /* TODO: How is Linux using the "force" parameter */ printk("[akaros]: get_user_page() uva=0x%llx pte ro\n", uvastart); goto err1; } /* TODO (GUP): change the interface such that devices provide the memory and * the user mmaps it, instead of trying to pin arbitrary user memory. */ warn_once("Extremely unsafe, unpinned memory mapped! If your process dies, you might scribble on RAM!"); plist[0] = pp; ret = 1; err1: spin_unlock(&p->pte_lock); return ret; }