d9e357a92dbce6ea601d3b2454022d791ff51105
[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/mempart.h>
10
11 #include <klibc/string.h>
12
13 struct proc_mm*
14 procvm_create(struct proc_info* proc) {
15     struct proc_mm* mm = valloc(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
27 static ptr_t
28 __dup_vmspace(ptr_t mount_point, bool only_kernel)
29 {
30     ptr_t ptd_pp = pmm_alloc_page(PP_FGPERSIST);
31     vmm_set_mapping(VMS_SELF, PG_MOUNT_1, ptd_pp, PG_PREM_RW, VMAP_NULL);
32
33     x86_page_table* ptd = (x86_page_table*)PG_MOUNT_1;
34     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
35
36     size_t kspace_l1inx = L1_INDEX(KERNEL_EXEC);
37     size_t i = 1;   // skip first 4MiB, to avoid bring other thread's stack
38
39     ptd->entry[0] = 0;
40     if (only_kernel) {
41         i = kspace_l1inx;
42         memset(ptd, 0, PG_SIZE);
43     }
44
45     for (; i < PG_MAX_ENTRIES - 1; i++) {
46
47         x86_pte_t ptde = pptd->entry[i];
48         // 空或者是未在内存中的L1页表项直接照搬过去。
49         // 内核地址空间直接共享过去。
50         if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
51             ptd->entry[i] = ptde;
52             continue;
53         }
54
55         // 复制L2页表
56         ptr_t pt_pp = pmm_alloc_page(PP_FGPERSIST);
57         vmm_set_mapping(VMS_SELF, PG_MOUNT_2, pt_pp, PG_PREM_RW, VMAP_NULL);
58
59         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
60         x86_page_table* pt = (x86_page_table*)PG_MOUNT_2;
61
62         for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
63             x86_pte_t pte = ppt->entry[j];
64             pmm_ref_page(PG_ENTRY_ADDR(pte));
65             pt->entry[j] = pte;
66         }
67
68         ptd->entry[i] = (ptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
69     }
70
71     ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
72
73     return ptd_pp;
74 }
75
76 void
77 procvm_dup(struct proc_info* proc) {
78    struct proc_mm* mm = vmspace(proc);
79    struct proc_mm* mm_current = vmspace(__current);
80    
81    mm->heap = mm_current->heap;
82    mm->vmroot = __dup_vmspace(VMS_SELF, false);
83    
84    region_copy_mm(mm_current, mm);
85 }
86
87 void
88 procvm_init_clean(struct proc_info* proc)
89 {
90     struct proc_mm* mm = vmspace(proc);
91     mm->vmroot = __dup_vmspace(VMS_SELF, true);
92 }
93
94
95 static void
96 __delete_vmspace(ptr_t vm_mnt)
97 {
98     x86_page_table* pptd = (x86_page_table*)(vm_mnt | (0x3FF << 12));
99
100     // only remove user address space
101     for (size_t i = 0; i < L1_INDEX(KERNEL_EXEC); i++) {
102         x86_pte_t ptde = pptd->entry[i];
103         if (!ptde || !(ptde & PG_PRESENT)) {
104             continue;
105         }
106
107         x86_page_table* ppt = (x86_page_table*)(vm_mnt | (i << 12));
108
109         for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
110             x86_pte_t pte = ppt->entry[j];
111             // free the 4KB data page
112             if ((pte & PG_PRESENT)) {
113                 pmm_free_page(PG_ENTRY_ADDR(pte));
114             }
115         }
116         // free the L2 page table
117         pmm_free_page(PG_ENTRY_ADDR(ptde));
118     }
119     // free the L1 directory
120     pmm_free_page(PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
121 }
122
123 void
124 procvm_cleanup(ptr_t vm_mnt, struct proc_info* proc) {
125     struct mm_region *pos, *n;
126     llist_for_each(pos, n, vmregions(proc), head)
127     {
128         mem_sync_pages(vm_mnt, pos, pos->start, pos->end - pos->start, 0);
129         region_release(pos);
130     }
131
132     vfree(proc->mm);
133
134     __delete_vmspace(vm_mnt);
135 }
136
137 ptr_t
138 procvm_enter_remote(struct remote_vmctx* rvmctx, struct proc_mm* mm, 
139                     ptr_t vm_mnt, ptr_t remote_base, size_t size)
140 {
141     ptr_t size_pn = PN(size + MEM_PAGE);
142     assert(size_pn < REMOTEVM_MAX_PAGES);
143
144     struct mm_region* region = region_get(&mm->regions, remote_base);
145     assert(region && region_contains(region, remote_base + size));
146
147     rvmctx->vms_mnt = vm_mnt;
148     rvmctx->page_cnt = size_pn;
149
150     remote_base = PG_ALIGN(remote_base);
151     rvmctx->remote = remote_base;
152     rvmctx->local_mnt = PG_MOUNT_4_END + 1;
153
154     v_mapping m;
155     unsigned int pattr = region_ptattr(region);
156     ptr_t raddr = remote_base, lmnt = rvmctx->local_mnt;
157     for (size_t i = 0; i < size_pn; i++, lmnt += MEM_PAGE, raddr += MEM_PAGE)
158     {
159         if (vmm_lookupat(vm_mnt, raddr, &m) && PG_IS_PRESENT(m.flags)) {
160             vmm_set_mapping(VMS_SELF, lmnt, m.pa, PG_PREM_RW, 0);
161             continue;
162         }
163
164         ptr_t pa = pmm_alloc_page(0);
165         vmm_set_mapping(VMS_SELF, lmnt, pa, PG_PREM_RW, 0);
166         vmm_set_mapping(vm_mnt, raddr, pa, pattr, 0);
167     }
168
169     return vm_mnt;
170     
171 }
172
173 int
174 procvm_copy_remote_transaction(struct remote_vmctx* rvmctx, 
175                    ptr_t remote_dest, void* local_src, size_t sz)
176 {
177     if (remote_dest < rvmctx->remote) {
178         return -1;
179     }
180
181     ptr_t offset = remote_dest - rvmctx->remote;
182     if (PN(offset + sz) >= rvmctx->page_cnt) {
183         return -1;
184     }
185
186     memcpy((void*)(rvmctx->local_mnt + offset), local_src, sz);
187
188     return sz;
189 }
190
191 void
192 procvm_exit_remote_transaction(struct remote_vmctx* rvmctx)
193 {
194     ptr_t lmnt = rvmctx->local_mnt;
195     for (size_t i = 0; i < rvmctx->page_cnt; i++, lmnt += MEM_PAGE)
196     {
197         vmm_del_mapping(VMS_SELF, lmnt);
198     }
199 }