1 /* See COPYRIGHT for copyright information. */
25 #include <ros/syscall.h>
30 // Initialize the kernel virtual memory layout for environment e.
31 // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly,
32 // and initialize the kernel portion of the new environment's address space.
33 // Do NOT (yet) map anything into the user portion
34 // of the environment's virtual address space.
36 // Returns 0 on success, < 0 on error. Errors include:
37 // -ENOMEM if page directory or table could not be allocated.
39 int env_setup_vm(env_t *e)
40 WRITES(e->env_pgdir, e->env_cr3, e->procinfo, e->procdata)
44 static page_t * RO shared_page = 0;
46 /* Get a page for the pgdir. Storing the ref in pgdir/env_pgdir */
47 r = kpage_alloc(&pgdir);
52 * Next, set up the e->env_pgdir and e->env_cr3 pointers to point
53 * to this newly allocated page and clear its contents
55 memset(page2kva(pgdir), 0, PGSIZE);
56 e->env_pgdir = (pde_t *COUNT(NPDENTRIES)) TC(page2kva(pgdir));
57 e->env_cr3 = (physaddr_t) TC(page2pa(pgdir));
60 * Now start filling in the pgdir with mappings required by all newly
61 * created address spaces
64 // Map in the kernel to the top of every address space
65 // should be able to do this so long as boot_pgdir never has
66 // anything put below UTOP
67 // TODO check on this! had a nasty bug because of it
68 // this is a bit wonky, since if it's not PGSIZE, lots of other things are
70 memcpy(e->env_pgdir, boot_pgdir, NPDENTRIES*sizeof(pde_t));
72 // VPT and UVPT map the env's own page table, with
73 // different permissions.
75 e->env_pgdir[PDX(VPT)] = PTE(LA2PPN(e->env_cr3), PTE_P | PTE_KERN_RW);
76 e->env_pgdir[PDX(UVPT)] = PTE(LA2PPN(e->env_cr3), PTE_P | PTE_USER_RO);
79 /* These need to be contiguous, so the kernel can alias them. Note the
80 * pages return with a refcnt, but it's okay to insert them since we free
81 * them manually when the process is cleaned up. */
82 if (!(e->procinfo = get_cont_pages(LOG2_UP(PROCINFO_NUM_PAGES), 0)))
83 goto env_setup_vm_error_i;
84 if (!(e->procdata = get_cont_pages(LOG2_UP(PROCDATA_NUM_PAGES), 0)))
85 goto env_setup_vm_error_d;
86 for (int i = 0; i < PROCINFO_NUM_PAGES; i++) {
87 if (page_insert(e->env_pgdir, kva2page((void*)e->procinfo + i *
88 PGSIZE), (void*SNT)(UINFO + i*PGSIZE), PTE_USER_RO) < 0)
89 goto env_setup_vm_error;
91 for (int i = 0; i < PROCDATA_NUM_PAGES; i++) {
92 if (page_insert(e->env_pgdir, kva2page((void*)e->procdata + i *
93 PGSIZE), (void*SNT)(UDATA + i*PGSIZE), PTE_USER_RW) < 0)
94 goto env_setup_vm_error;
96 memset(e->procinfo, 0, sizeof(struct procinfo));
97 memset(e->procdata, 0, sizeof(struct procdata));
99 /* Finally, set up the Global Shared Data page for all processes. Can't be
100 * trusted, but still very useful at this stage for us. Consider removing
101 * when we have real processes (TODO).
103 * Note the page is alloced only the first time through, and its ref is
104 * stored in shared_page. */
106 if (upage_alloc(e, &shared_page, 1) < 0)
107 goto env_setup_vm_error;
109 if (page_insert(e->env_pgdir, shared_page, (void*)UGDATA, PTE_USER_RW) < 0)
110 goto env_setup_vm_error;
115 free_cont_pages(e->procdata, LOG2_UP(PROCDATA_NUM_PAGES));
116 env_setup_vm_error_d:
117 free_cont_pages(e->procinfo, LOG2_UP(PROCINFO_NUM_PAGES));
118 env_setup_vm_error_i:
119 page_decref(shared_page);
120 env_user_mem_free(e, 0, UVPT);
121 env_pagetable_free(e);
125 #define PER_CPU_THING(type,name)\
126 type SLOCKED(name##_lock) * RWPROTECT name;\
127 type SLOCKED(name##_lock) *\
128 (get_per_cpu_##name)()\
130 { R_PERMITTED(global(name))\
131 return &name[core_id()];\
135 /* Frees (decrefs) all memory mapped in the given range */
136 void env_user_mem_free(env_t* e, void* start, size_t len)
138 assert((uintptr_t)start + len <= UVPT); //since this keeps fucking happening
139 int user_page_free(env_t* e, pte_t* pte, void* va, void* arg)
141 if(PAGE_PRESENT(*pte))
143 page_t* page = ppn2page(PTE2PPN(*pte));
147 assert(PAGE_PAGED_OUT(*pte));
148 /* TODO: (SWAP) deal with this */
149 panic("Swapping not supported!");
155 env_user_mem_walk(e,start,len,&user_page_free,NULL);