Fixes page reference counting wrt to upage_alloc()
[akaros.git] / kern / src / env.c
1 /* See COPYRIGHT for copyright information. */
2
3 #ifdef __SHARC__
4 #pragma nosharc
5 #endif
6
7 #include <arch/arch.h>
8 #include <arch/mmu.h>
9 #include <arch/bitmask.h>
10 #include <elf.h>
11 #include <smp.h>
12
13 #include <atomic.h>
14 #include <string.h>
15 #include <assert.h>
16 #include <process.h>
17 #include <pmap.h>
18 #include <trap.h>
19 #include <monitor.h>
20 #include <manager.h>
21 #include <stdio.h>
22 #include <schedule.h>
23 #include <kmalloc.h>
24 #include <mm.h>
25
26 #include <ros/syscall.h>
27 #include <error.h>
28
29 atomic_t num_envs;
30
31 // Initialize the kernel virtual memory layout for environment e.
32 // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly,
33 // and initialize the kernel portion of the new environment's address space.
34 // Do NOT (yet) map anything into the user portion
35 // of the environment's virtual address space.
36 //
37 // Returns 0 on success, < 0 on error.  Errors include:
38 //      -ENOMEM if page directory or table could not be allocated.
39 //
40 int env_setup_vm(env_t *e)
41 WRITES(e->env_pgdir, e->env_cr3, e->procinfo, e->procdata)
42 {
43         int i, r;
44         page_t *pgdir = NULL;
45         static page_t * RO shared_page = 0;
46
47         /* Get a page for the pgdir.  Storing the ref in pgdir/env_pgdir */
48         r = kpage_alloc(&pgdir);
49         if (r < 0)
50                 return r;
51
52         /*
53          * Next, set up the e->env_pgdir and e->env_cr3 pointers to point
54          * to this newly allocated page and clear its contents
55          */
56         memset(page2kva(pgdir), 0, PGSIZE);
57         e->env_pgdir = (pde_t *COUNT(NPDENTRIES)) TC(page2kva(pgdir));
58         e->env_cr3 =   (physaddr_t) TC(page2pa(pgdir));
59
60         /*
61          * Now start filling in the pgdir with mappings required by all newly
62          * created address spaces
63          */
64
65         // Map in the kernel to the top of every address space
66         // should be able to do this so long as boot_pgdir never has
67         // anything put below UTOP
68         // TODO check on this!  had a nasty bug because of it
69         // this is a bit wonky, since if it's not PGSIZE, lots of other things are
70         // screwed up...
71         memcpy(e->env_pgdir, boot_pgdir, NPDENTRIES*sizeof(pde_t));
72
73         // VPT and UVPT map the env's own page table, with
74         // 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);
77
78         /* These need to be contiguous, so the kernel can alias them.  Note the
79          * pages return with a refcnt, but it's okay to insert them since we free
80          * them manually when the process is cleaned up. */
81         if (!(e->procinfo = get_cont_pages(LOG2_UP(PROCINFO_NUM_PAGES), 0)))
82                 goto env_setup_vm_error_i;
83         if (!(e->procdata = get_cont_pages(LOG2_UP(PROCDATA_NUM_PAGES), 0)))
84                 goto env_setup_vm_error_d;
85         for (int i = 0; i < PROCINFO_NUM_PAGES; i++) {
86                 if (page_insert(e->env_pgdir, kva2page((void*)e->procinfo + i *
87                                 PGSIZE), (void*SNT)(UINFO + i*PGSIZE), PTE_USER_RO) < 0)
88                         goto env_setup_vm_error;
89         }
90         for (int i = 0; i < PROCDATA_NUM_PAGES; i++) {
91                 if (page_insert(e->env_pgdir, kva2page((void*)e->procdata + i *
92                                 PGSIZE), (void*SNT)(UDATA + i*PGSIZE), PTE_USER_RW) < 0)
93                         goto env_setup_vm_error;
94         }
95         memset(e->procinfo, 0, sizeof(struct procinfo));
96         memset(e->procdata, 0, sizeof(struct procdata));
97
98         /* Finally, set up the Global Shared Data page for all processes.  Can't be
99          * trusted, but still very useful at this stage for us.  Consider removing
100          * when we have real processes (TODO). 
101          *
102          * Note the page is alloced only the first time through, and its ref is
103          * stored in shared_page. */
104         if (!shared_page) {
105                 if (upage_alloc(e, &shared_page, 1) < 0)
106                         goto env_setup_vm_error;
107         }
108         if (page_insert(e->env_pgdir, shared_page, (void*)UGDATA, PTE_USER_RW) < 0)
109                 goto env_setup_vm_error;
110
111         return 0;
112
113 env_setup_vm_error:
114         free_cont_pages(e->procdata, LOG2_UP(PROCDATA_NUM_PAGES));
115 env_setup_vm_error_d:
116         free_cont_pages(e->procinfo, LOG2_UP(PROCINFO_NUM_PAGES));
117 env_setup_vm_error_i:
118         page_decref(shared_page);
119         env_user_mem_free(e, 0, UVPT);
120         env_pagetable_free(e);
121         return -ENOMEM;
122 }
123
124 #define PER_CPU_THING(type,name)\
125 type SLOCKED(name##_lock) * RWPROTECT name;\
126 type SLOCKED(name##_lock) *\
127 (get_per_cpu_##name)()\
128 {\
129         { R_PERMITTED(global(name))\
130                 return &name[core_id()];\
131         }\
132 }
133
134 /* Frees (decrefs) all memory mapped in the given range */
135 void env_user_mem_free(env_t* e, void* start, size_t len)
136 {
137         assert((uintptr_t)start + len <= UVPT); //since this keeps fucking happening
138         int user_page_free(env_t* e, pte_t* pte, void* va, void* arg)
139         {
140                 if(PAGE_PRESENT(*pte))
141                 {
142                         page_t* page = ppn2page(PTE2PPN(*pte));
143                         *pte = 0;
144                         page_decref(page);
145                 } else {
146                         assert(PAGE_PAGED_OUT(*pte));
147                         /* TODO: (SWAP) deal with this */
148                         panic("Swapping not supported!");
149                         *pte = 0;
150                 }
151                 return 0;
152         }
153
154         env_user_mem_walk(e,start,len,&user_page_free,NULL);
155         tlbflush();
156 }
157