Code-base clean-up and refactoring (#47)
[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 <asm/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 inline int
33 __descend(ptr_t dest_mnt, ptr_t src_mnt, ptr_t va, bool alloc)
34 {
35     pte_t *dest, *src, pte;
36
37     int i = 0;
38     while (!pt_last_level(i))
39     {
40         dest = mklntep_va(i, dest_mnt, va);
41         src  = mklntep_va(i, src_mnt, va);
42         pte  = pte_at(src);
43
44         if (!pte_isloaded(pte) || pte_huge(pte)) {
45             break;
46         }
47
48         if (alloc && pte_isnull(pte_at(dest))) {
49             alloc_kpage_at(dest, pte, 0);
50         }
51
52         i++;
53     }
54
55     return i;
56 }
57
58 static inline void
59 copy_leaf(pte_t* dest, pte_t* src, pte_t pte, int level)
60 {
61     struct leaflet* leaflet;
62
63     set_pte(dest, pte);
64
65     if (!pte_isloaded(pte)) {
66         return;
67     }
68
69     leaflet = pte_leaflet(pte);
70     assert(leaflet_refcount(leaflet));
71     
72     if (leaflet_ppfn(leaflet) == pte_ppfn(pte)) {
73         leaflet_borrow(leaflet);
74     }
75 }
76
77 static inline void
78 copy_root(pte_t* dest, pte_t* src, pte_t pte, int level)
79 {
80     alloc_kpage_at(dest, pte, 0);
81 }
82
83 static void
84 vmrcpy(ptr_t dest_mnt, ptr_t src_mnt, struct mm_region* region)
85 {
86     pte_t *src, *dest;
87     ptr_t loc;
88     int level;
89     struct leaflet* leaflet;
90
91     loc  = region->start;
92     src  = mkptep_va(src_mnt, loc);
93     dest = mkptep_va(dest_mnt, loc);
94
95     level = __descend(dest_mnt, src_mnt, loc, true);
96
97     while (loc < region->end)
98     {
99         pte_t pte = *src;
100
101         if (pte_isnull(pte)) {
102             goto cont;
103         } 
104         
105         if (pt_last_level(level) || pte_huge(pte)) {
106             copy_leaf(dest, src, pte, level);
107             goto cont;
108         }
109         
110         if (!pt_last_level(level)) {
111             copy_root(dest, src, pte, level);
112
113             src = ptep_step_into(src);
114             dest = ptep_step_into(dest);
115             level++;
116
117             continue;
118         }
119         
120     cont:
121         loc += lnt_page_size(level);
122         while (ptep_vfn(src) == MAX_PTEN - 1) {
123             assert(level > 0);
124             src = ptep_step_out(src);
125             dest = ptep_step_out(dest);
126             level--;
127         }
128
129         src++;
130         dest++;
131     }
132 }
133
134 static void
135 vmrfree(ptr_t vm_mnt, struct mm_region* region)
136 {
137     pte_t *src, *end;
138     ptr_t loc;
139     int level;
140     struct leaflet* leaflet;
141
142     loc  = region->start;
143     src  = mkptep_va(vm_mnt, region->start);
144     end  = mkptep_va(vm_mnt, region->end);
145
146     level = __descend(0, vm_mnt, loc, false);
147
148     while (src < end)
149     {
150         pte_t pte = *src;
151         ptr_t pa  = pte_paddr(pte);
152
153         if (pte_isnull(pte)) {
154             goto cont;
155         } 
156
157         if (!pt_last_level(level) && !pte_huge(pte)) {
158             src = ptep_step_into(src);
159             level++;
160
161             continue;
162         }
163
164         if (pte_isloaded(pte)) {
165             leaflet = pte_leaflet_aligned(pte);
166             leaflet_return(leaflet);
167
168             src += __ptep_advancement(leaflet, level);
169         }
170
171     cont:
172         while (ptep_vfn(src) == MAX_PTEN - 1) {
173             src = ptep_step_out(src);
174             leaflet = pte_leaflet_aligned(pte_at(src));
175             
176             assert(leaflet_order(leaflet) == 0);
177             leaflet_return(leaflet);
178             
179             level--;
180         }
181
182         src++;
183     }
184 }
185
186 static void
187 vmscpy(struct proc_mm* dest_mm, struct proc_mm* src_mm)
188 {
189     // Build the self-reference on dest vms
190
191     /* 
192      *        -- What the heck are ptep_ssm and ptep_sms ? --
193      *      
194      *      ptep_dest point to the pagetable itself that is mounted
195      *          at dest_mnt (or simply mnt): 
196      *              mnt -> self -> self -> self -> L0TE@offset
197      * 
198      *      ptep_sms shallowed the recursion chain:
199      *              self -> mnt -> self -> self -> L0TE@self
200      * 
201      *      ptep_ssm shallowed the recursion chain:
202      *              self -> self -> mnt -> self -> L0TE@self
203      *      
204      *      Now, here is the problem, back to x86_32, the translation is 
205      *      a depth-3 recursion:
206      *              L0T -> LFT -> Page
207      *      
208      *      So ptep_ssm will terminate at mnt and give us a leaf
209      *      slot for allocate a fresh page table for mnt:
210      *              self -> self -> L0TE@mnt
211      * 
212      *      but in x86_64 translation has extra two more step:
213      *              L0T -> L1T -> L2T -> LFT -> Page
214      *      
215      *      So we must continue push down.... 
216      *      ptep_sssms shallowed the recursion chain:
217      *              self -> self -> self -> mnt  -> L0TE@self
218      * 
219      *      ptep_ssssm shallowed the recursion chain:
220      *              self -> self -> self -> self -> L0TE@mnt
221      * 
222      *      Note: PML4: 2 extra steps
223      *            PML5: 3 extra steps
224     */
225
226     ptr_t  dest_mnt, src_mnt;
227     
228     dest_mnt = dest_mm->vm_mnt;
229     assert(dest_mnt);
230
231     pte_t* ptep_ssm     = mkl0tep_va(VMS_SELF, dest_mnt);
232     pte_t* ptep_smx     = mkl1tep_va(VMS_SELF, dest_mnt);
233     pte_t  pte_sms      = mkpte_prot(KERNEL_PGTAB);
234
235     pte_sms = alloc_kpage_at(ptep_ssm, pte_sms, 0);
236     set_pte(&ptep_smx[VMS_SELF_L0TI], pte_sms);
237     
238     tlb_flush_kernel((ptr_t)dest_mnt);
239
240     if (!src_mm) {
241         goto done;
242     }
243
244     src_mnt = src_mm->vm_mnt;
245
246     struct mm_region *pos, *n;
247     llist_for_each(pos, n, &src_mm->regions, head)
248     {
249         vmrcpy(dest_mnt, src_mnt, pos);
250     }
251
252 done:;
253     procvm_link_kernel(dest_mnt);
254     
255     dest_mm->vmroot = pte_paddr(pte_sms);
256 }
257
258 static void
259 vmsfree(struct proc_mm* mm)
260 {
261     struct leaflet* leaflet;
262     ptr_t vm_mnt;
263     pte_t* ptep_self;
264     
265     vm_mnt    = mm->vm_mnt;
266     ptep_self = mkl0tep(mkptep_va(vm_mnt, VMS_SELF));
267
268     struct mm_region *pos, *n;
269     llist_for_each(pos, n, &mm->regions, head)
270     {
271         vmrfree(vm_mnt, pos);
272     }
273
274     procvm_unlink_kernel();
275
276     leaflet = pte_leaflet_aligned(pte_at(ptep_self));
277     leaflet_return(leaflet);
278 }
279
280 static inline void
281 __attach_to_current_vms(struct proc_mm* guest_mm)
282 {
283     struct proc_mm* mm_current = vmspace(__current);
284     if (mm_current) {
285         assert(!mm_current->guest_mm);
286         mm_current->guest_mm = guest_mm;
287     }
288 }
289
290 static inline void
291 __detach_from_current_vms(struct proc_mm* guest_mm)
292 {
293     struct proc_mm* mm_current = vmspace(__current);
294     if (mm_current) {
295         assert(mm_current->guest_mm == guest_mm);
296         mm_current->guest_mm = NULL;
297     }
298 }
299
300
301 void
302 procvm_dupvms_mount(struct proc_mm* mm) {
303     assert(__current);
304     assert(!mm->vm_mnt);
305
306     struct proc_mm* mm_current = vmspace(__current);
307     
308     __attach_to_current_vms(mm);
309    
310     mm->heap = mm_current->heap;
311     mm->vm_mnt = VMS_MOUNT_1;
312     
313     vmscpy(mm, mm_current);  
314     region_copy_mm(mm_current, mm);
315 }
316
317 void
318 procvm_mount(struct proc_mm* mm)
319 {
320     // if current mm is already active
321     if (active_vms(mm->vm_mnt)) {
322         return;
323     }
324     
325     // we are double mounting
326     assert(!mm->vm_mnt);
327     assert(mm->vmroot);
328
329     vms_mount(VMS_MOUNT_1, mm->vmroot);
330
331     __attach_to_current_vms(mm);
332
333     mm->vm_mnt = VMS_MOUNT_1;
334 }
335
336 void
337 procvm_unmount(struct proc_mm* mm)
338 {
339     if (active_vms(mm->vm_mnt)) {
340         return;
341     }
342     
343     assert(mm->vm_mnt);
344     vms_unmount(VMS_MOUNT_1);
345     
346     struct proc_mm* mm_current = vmspace(__current);
347     if (mm_current) {
348         mm_current->guest_mm = NULL;
349     }
350
351     mm->vm_mnt = 0;
352 }
353
354 void
355 procvm_initvms_mount(struct proc_mm* mm)
356 {
357     assert(!mm->vm_mnt);
358
359     __attach_to_current_vms(mm);
360
361     mm->vm_mnt = VMS_MOUNT_1;
362     vmscpy(mm, NULL);
363 }
364
365 void
366 procvm_unmount_release(struct proc_mm* mm) {
367     ptr_t vm_mnt = mm->vm_mnt;
368     struct mm_region *pos, *n;
369     llist_for_each(pos, n, &mm->regions, head)
370     {
371         mem_sync_pages(vm_mnt, pos, pos->start, pos->end - pos->start, 0);
372         region_release(pos);
373     }
374
375     vmsfree(mm);
376     vms_unmount(vm_mnt);
377     vfree(mm);
378
379     __detach_from_current_vms(mm);
380 }
381
382 void
383 procvm_mount_self(struct proc_mm* mm) 
384 {
385     assert(!mm->vm_mnt);
386
387     mm->vm_mnt = VMS_SELF;
388 }
389
390 void
391 procvm_unmount_self(struct proc_mm* mm)
392 {
393     assert(active_vms(mm->vm_mnt));
394
395     mm->vm_mnt = 0;
396 }
397
398 ptr_t
399 procvm_enter_remote(struct remote_vmctx* rvmctx, struct proc_mm* mm, 
400                     ptr_t remote_base, size_t size)
401 {
402     ptr_t vm_mnt = mm->vm_mnt;
403     assert(vm_mnt);
404     
405     pfn_t size_pn = pfn(size + PAGE_SIZE);
406     assert(size_pn < REMOTEVM_MAX_PAGES);
407
408     struct mm_region* region = region_get(&mm->regions, remote_base);
409     assert(region && region_contains(region, remote_base + size));
410
411     rvmctx->vms_mnt = vm_mnt;
412     rvmctx->page_cnt = size_pn;
413
414     remote_base = page_aligned(remote_base);
415     rvmctx->remote = remote_base;
416     rvmctx->local_mnt = PG_MOUNT_VAR;
417
418     pte_t* rptep = mkptep_va(vm_mnt, remote_base);
419     pte_t* lptep = mkptep_va(VMS_SELF, rvmctx->local_mnt);
420
421     pte_t pte, rpte = null_pte;
422     rpte = region_tweakpte(region, rpte);
423
424     for (size_t i = 0; i < size_pn; i++)
425     {
426         pte = vmm_tryptep(rptep, PAGE_SIZE);
427         if (pte_isloaded(pte)) {
428             set_pte(lptep, pte);
429             continue;
430         }
431
432         ptr_t pa = ppage_addr(pmm_alloc_normal(0));
433         set_pte(lptep, mkpte(pa, KERNEL_DATA));
434         set_pte(rptep, pte_setpaddr(rpte, pa));
435     }
436
437     return vm_mnt;
438     
439 }
440
441 int
442 procvm_copy_remote_transaction(struct remote_vmctx* rvmctx, 
443                    ptr_t remote_dest, void* local_src, size_t sz)
444 {
445     if (remote_dest < rvmctx->remote) {
446         return -1;
447     }
448
449     ptr_t offset = remote_dest - rvmctx->remote;
450     if (pfn(offset + sz) >= rvmctx->page_cnt) {
451         return -1;
452     }
453
454     memcpy((void*)(rvmctx->local_mnt + offset), local_src, sz);
455
456     return sz;
457 }
458
459 void
460 procvm_exit_remote(struct remote_vmctx* rvmctx)
461 {
462     pte_t* lptep = mkptep_va(VMS_SELF, rvmctx->local_mnt);
463     vmm_unset_ptes(lptep, rvmctx->page_cnt);
464 }