7b07560efec366512b52e98bae663ef9df1e2073
[lunaix-os.git] / lunaix-os / kernel / mm / procvm.c
1 #include <lunaix/mm/procvm.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/mm/region.h>
4 #include <lunaix/mm/page.h>
5 #include <lunaix/mm/mmap.h>
6 #include <lunaix/process.h>
7
8 #include <sys/mm/mm_defs.h>
9
10 #include <klibc/string.h>
11
12 struct proc_mm*
13 procvm_create(struct proc_info* proc) {
14     struct proc_mm* mm = vzalloc(sizeof(struct proc_mm));
15
16     assert(mm);
17
18     mm->heap = 0;
19     mm->proc = proc;
20
21     llist_init_head(&mm->regions);
22     return mm;
23 }
24
25 static inline unsigned int
26 __ptep_advancement(struct leaflet* leaflet, int level)
27 {
28     size_t shifts = MAX(MAX_LEVEL - level - 1, 1) * LEVEL_SHIFT;
29     return (1 << (leaflet_order(leaflet) % shifts)) - 1;
30 }
31
32 static ptr_t
33 vmscpy(ptr_t dest_mnt, ptr_t src_mnt, bool only_kernel)
34 {
35     pte_t* ptep_dest    = mkl0tep(mkptep_va(dest_mnt, 0));
36     pte_t* ptep         = mkl0tep(mkptep_va(src_mnt, 0));
37     pte_t* ptepd_kernel = mkl0tep(mkptep_va(dest_mnt, KERNEL_RESIDENT));
38     pte_t* ptep_kernel  = mkl0tep(mkptep_va(src_mnt, KERNEL_RESIDENT));
39
40     // Build the self-reference on dest vms
41     pte_t* ptep_sms     = mkptep_va(VMS_SELF, (ptr_t)ptep_dest);
42     pte_t* ptep_ssm     = mkptep_va(VMS_SELF, (ptr_t)ptep_sms);
43     pte_t  pte_sms      = mkpte_prot(KERNEL_DATA);
44
45     pte_sms = alloc_kpage_at(ptep_ssm, pte_sms, 0);
46     set_pte(ptep_sms, pte_sms);    
47     
48     tlb_flush_kernel((ptr_t)dest_mnt);
49
50     if (only_kernel) {
51         ptep = ptep_kernel;
52         ptep_dest += ptep_vfn(ptep_kernel);
53     } else {
54         ptep++;
55         ptep_dest++;
56     }
57
58     int level = 0;
59     struct leaflet* leaflet;
60
61     while (ptep < ptep_kernel)
62     {
63         pte_t pte = *ptep;
64
65         if (pte_isnull(pte)) {
66             goto cont;
67         } 
68         
69         if (pt_last_level(level) || pte_huge(pte)) {
70             set_pte(ptep_dest, pte);
71
72             if (pte_isloaded(pte)) {
73                 leaflet = pte_leaflet(pte);
74                 assert(leaflet_refcount(leaflet));
75                 
76                 if (leaflet_ppfn(leaflet) == pte_ppfn(pte)) {
77                     leaflet_borrow(leaflet);
78                 }
79             }
80         }
81         else if (!pt_last_level(level)) {
82             alloc_kpage_at(ptep_dest, pte, 0);
83
84             ptep = ptep_step_into(ptep);
85             ptep_dest = ptep_step_into(ptep_dest);
86             level++;
87
88             continue;
89         }
90         
91     cont:
92         if (ptep_vfn(ptep) == MAX_PTEN - 1) {
93             assert(level > 0);
94             ptep = ptep_step_out(ptep);
95             ptep_dest = ptep_step_out(ptep_dest);
96             level--;
97         }
98
99         ptep++;
100         ptep_dest++;
101     }
102
103     // Ensure we step back to L0T
104     assert(!level);
105     assert(ptep_dest == ptepd_kernel);
106     
107     // Carry over the kernel (exclude last two entry)
108     while (ptep_vfn(ptep) < MAX_PTEN - 2) {
109         pte_t pte = *ptep;
110         assert(!pte_isnull(pte));
111
112         // Ensure it is a next level pagetable,
113         //  we MAY relax this later allow kernel
114         //  to have huge leaflet mapped at L0T
115         leaflet = pte_leaflet_aligned(pte);
116         assert(leaflet_order(leaflet) == 0);
117
118         set_pte(ptep_dest, pte);
119         leaflet_borrow(leaflet);
120         
121         ptep++;
122         ptep_dest++;
123     }
124
125     return pte_paddr(*(ptep_dest + 1));
126 }
127
128 static void
129 vmsfree(ptr_t vm_mnt)
130 {
131     struct leaflet* leaflet;
132     pte_t* ptep_head    = mkl0tep(mkptep_va(vm_mnt, 0));
133     pte_t* ptep_kernel  = mkl0tep(mkptep_va(vm_mnt, KERNEL_RESIDENT));
134
135     int level = 0;
136     pte_t* ptep = ptep_head;
137     while (ptep < ptep_kernel)
138     {
139         pte_t pte = *ptep;
140         ptr_t pa  = pte_paddr(pte);
141
142         if (pte_isnull(pte)) {
143             goto cont;
144         } 
145
146         if (!pt_last_level(level) && !pte_huge(pte)) {
147             ptep = ptep_step_into(ptep);
148             level++;
149
150             continue;
151         }
152
153         if (pte_isloaded(pte)) {
154             leaflet = pte_leaflet_aligned(pte);
155             leaflet_return(leaflet);
156
157             ptep += __ptep_advancement(leaflet, level);
158         }
159
160     cont:
161         if (ptep_vfn(ptep) == MAX_PTEN - 1) {
162             ptep = ptep_step_out(ptep);
163             leaflet = pte_leaflet_aligned(pte_at(ptep));
164             
165             assert(leaflet_order(leaflet) == 0);
166             leaflet_return(leaflet);
167             
168             level--;
169         }
170
171         ptep++;
172     }
173
174     leaflet = pte_leaflet_aligned(ptep_head[MAX_PTEN - 1]);
175     leaflet_return(leaflet);
176 }
177
178 static inline void
179 __attach_to_current_vms(struct proc_mm* guest_mm)
180 {
181     struct proc_mm* mm_current = vmspace(__current);
182     if (mm_current) {
183         assert(!mm_current->guest_mm);
184         mm_current->guest_mm = guest_mm;
185     }
186 }
187
188 static inline void
189 __detach_from_current_vms(struct proc_mm* guest_mm)
190 {
191     struct proc_mm* mm_current = vmspace(__current);
192     if (mm_current) {
193         assert(mm_current->guest_mm == guest_mm);
194         mm_current->guest_mm = NULL;
195     }
196 }
197
198
199 void
200 procvm_dupvms_mount(struct proc_mm* mm) {
201     assert(__current);
202     assert(!mm->vm_mnt);
203
204     struct proc_mm* mm_current = vmspace(__current);
205     
206     __attach_to_current_vms(mm);
207    
208     mm->heap = mm_current->heap;
209     mm->vm_mnt = VMS_MOUNT_1;
210     mm->vmroot = vmscpy(VMS_MOUNT_1, VMS_SELF, false);
211     
212     region_copy_mm(mm_current, mm);
213 }
214
215 void
216 procvm_mount(struct proc_mm* mm)
217 {
218     assert(!mm->vm_mnt);
219     assert(mm->vmroot);
220
221     vms_mount(VMS_MOUNT_1, mm->vmroot);
222
223     __attach_to_current_vms(mm);
224
225     mm->vm_mnt = VMS_MOUNT_1;
226 }
227
228 void
229 procvm_unmount(struct proc_mm* mm)
230 {
231     assert(mm->vm_mnt);
232
233     vms_unmount(VMS_MOUNT_1);
234     struct proc_mm* mm_current = vmspace(__current);
235     if (mm_current) {
236         mm_current->guest_mm = NULL;
237     }
238
239     mm->vm_mnt = 0;
240 }
241
242 void
243 procvm_initvms_mount(struct proc_mm* mm)
244 {
245     assert(!mm->vm_mnt);
246
247     __attach_to_current_vms(mm);
248
249     mm->vm_mnt = VMS_MOUNT_1;
250     mm->vmroot = vmscpy(VMS_MOUNT_1, VMS_SELF, true);
251 }
252
253 void
254 procvm_unmount_release(struct proc_mm* mm) {
255     ptr_t vm_mnt = mm->vm_mnt;
256     struct mm_region *pos, *n;
257     llist_for_each(pos, n, &mm->regions, head)
258     {
259         mem_sync_pages(vm_mnt, pos, pos->start, pos->end - pos->start, 0);
260         region_release(pos);
261     }
262
263     vfree(mm);
264     vmsfree(vm_mnt);
265     vms_unmount(vm_mnt);
266
267     __detach_from_current_vms(mm);
268 }
269
270 void
271 procvm_mount_self(struct proc_mm* mm) 
272 {
273     assert(!mm->vm_mnt);
274     assert(!mm->guest_mm);
275
276     mm->vm_mnt = VMS_SELF;
277 }
278
279 void
280 procvm_unmount_self(struct proc_mm* mm)
281 {
282     assert(mm->vm_mnt == VMS_SELF);
283
284     mm->vm_mnt = 0;
285 }
286
287 ptr_t
288 procvm_enter_remote(struct remote_vmctx* rvmctx, struct proc_mm* mm, 
289                     ptr_t remote_base, size_t size)
290 {
291     ptr_t vm_mnt = mm->vm_mnt;
292     assert(vm_mnt);
293     
294     pfn_t size_pn = pfn(size + MEM_PAGE);
295     assert(size_pn < REMOTEVM_MAX_PAGES);
296
297     struct mm_region* region = region_get(&mm->regions, remote_base);
298     assert(region && region_contains(region, remote_base + size));
299
300     rvmctx->vms_mnt = vm_mnt;
301     rvmctx->page_cnt = size_pn;
302
303     remote_base = page_aligned(remote_base);
304     rvmctx->remote = remote_base;
305     rvmctx->local_mnt = PG_MOUNT_VAR;
306
307     pte_t* rptep = mkptep_va(vm_mnt, remote_base);
308     pte_t* lptep = mkptep_va(VMS_SELF, rvmctx->local_mnt);
309     unsigned int pattr = region_pteprot(region);
310
311     for (size_t i = 0; i < size_pn; i++)
312     {
313         pte_t pte = vmm_tryptep(rptep, PAGE_SIZE);
314         if (pte_isloaded(pte)) {
315             set_pte(lptep, pte);
316             continue;
317         }
318
319         ptr_t pa = ppage_addr(pmm_alloc_normal(0));
320         set_pte(lptep, mkpte(pa, KERNEL_DATA));
321         set_pte(rptep, mkpte(pa, pattr));
322     }
323
324     return vm_mnt;
325     
326 }
327
328 int
329 procvm_copy_remote_transaction(struct remote_vmctx* rvmctx, 
330                    ptr_t remote_dest, void* local_src, size_t sz)
331 {
332     if (remote_dest < rvmctx->remote) {
333         return -1;
334     }
335
336     ptr_t offset = remote_dest - rvmctx->remote;
337     if (pfn(offset + sz) >= rvmctx->page_cnt) {
338         return -1;
339     }
340
341     memcpy((void*)(rvmctx->local_mnt + offset), local_src, sz);
342
343     return sz;
344 }
345
346 void
347 procvm_exit_remote(struct remote_vmctx* rvmctx)
348 {
349     pte_t* lptep = mkptep_va(VMS_SELF, rvmctx->local_mnt);
350     vmm_unset_ptes(lptep, rvmctx->page_cnt);
351 }