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