refactor: one more step towards arch-agnostic design
[lunaix-os.git] / lunaix-os / kernel / process / process.c
1 #include <klibc/string.h>
2 #include <lunaix/clock.h>
3 #include <lunaix/common.h>
4 #include <lunaix/mm/mmap.h>
5 #include <lunaix/mm/pmm.h>
6 #include <lunaix/mm/region.h>
7 #include <lunaix/mm/valloc.h>
8 #include <lunaix/mm/vmm.h>
9 #include <lunaix/process.h>
10 #include <lunaix/spike.h>
11 #include <lunaix/status.h>
12 #include <lunaix/syscall.h>
13 #include <lunaix/syslog.h>
14
15 #include <sys/abi.h>
16 #include <sys/mm/mempart.h>
17
18 LOG_MODULE("PROC")
19
20 ptr_t
21 __dup_pagetable(pid_t pid, ptr_t mount_point)
22 {
23     ptr_t ptd_pp = pmm_alloc_page(pid, PP_FGPERSIST);
24     vmm_set_mapping(VMS_SELF, PG_MOUNT_1, ptd_pp, PG_PREM_RW, VMAP_NULL);
25
26     x86_page_table* ptd = (x86_page_table*)PG_MOUNT_1;
27     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
28
29     size_t kspace_l1inx = L1_INDEX(KERNEL_EXEC);
30
31     for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
32
33         x86_pte_t ptde = pptd->entry[i];
34         // 空或者是未在内存中的L1页表项直接照搬过去。
35         // 内核地址空间直接共享过去。
36         if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
37             ptd->entry[i] = ptde;
38             continue;
39         }
40
41         // 复制L2页表
42         ptr_t pt_pp = pmm_alloc_page(pid, PP_FGPERSIST);
43         vmm_set_mapping(VMS_SELF, PG_MOUNT_2, pt_pp, PG_PREM_RW, VMAP_NULL);
44
45         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
46         x86_page_table* pt = (x86_page_table*)PG_MOUNT_2;
47
48         for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
49             x86_pte_t pte = ppt->entry[j];
50             pmm_ref_page(pid, PG_ENTRY_ADDR(pte));
51             pt->entry[j] = pte;
52         }
53
54         ptd->entry[i] = (ptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
55     }
56
57     ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
58
59     return ptd_pp;
60 }
61
62 void
63 __del_pagetable(pid_t pid, ptr_t mount_point)
64 {
65     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
66
67     // only remove user address space
68     for (size_t i = 0; i < L1_INDEX(KERNEL_EXEC); i++) {
69         x86_pte_t ptde = pptd->entry[i];
70         if (!ptde || !(ptde & PG_PRESENT)) {
71             continue;
72         }
73
74         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
75
76         for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
77             x86_pte_t pte = ppt->entry[j];
78             // free the 4KB data page
79             if ((pte & PG_PRESENT)) {
80                 pmm_free_page(pid, PG_ENTRY_ADDR(pte));
81             }
82         }
83         // free the L2 page table
84         pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
85     }
86     // free the L1 directory
87     pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
88 }
89
90 ptr_t
91 vmm_dup_vmspace(pid_t pid)
92 {
93     return __dup_pagetable(pid, VMS_SELF);
94 }
95
96 __DEFINE_LXSYSCALL(pid_t, fork)
97 {
98     return dup_proc();
99 }
100
101 __DEFINE_LXSYSCALL(pid_t, getpid)
102 {
103     return __current->pid;
104 }
105
106 __DEFINE_LXSYSCALL(pid_t, getppid)
107 {
108     return __current->parent->pid;
109 }
110
111 __DEFINE_LXSYSCALL(pid_t, getpgid)
112 {
113     return __current->pgid;
114 }
115
116 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
117 {
118     struct proc_info* proc = pid ? get_process(pid) : __current;
119
120     if (!proc) {
121         __current->k_status = EINVAL;
122         return -1;
123     }
124
125     pgid = pgid ? pgid : proc->pid;
126
127     struct proc_info* gruppenfuhrer = get_process(pgid);
128
129     if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
130         __current->k_status = EINVAL;
131         return -1;
132     }
133
134     llist_delete(&proc->grp_member);
135     llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
136
137     proc->pgid = pgid;
138     return 0;
139 }
140
141 void
142 __stack_copied(struct mm_region* region)
143 {
144     mm_index((void**)&region->proc_vms->stack, region);
145 }
146
147 void
148 init_proc_user_space(struct proc_info* pcb)
149 {
150     vmm_mount_pd(VMS_MOUNT_1, pcb->page_table);
151
152     /*---  分配用户栈  ---*/
153
154     struct mm_region* mapped;
155     struct mmap_param param = { .vms_mnt = VMS_MOUNT_1,
156                                 .pvms = &pcb->mm,
157                                 .mlen = USTACK_SIZE,
158                                 .proct = PROT_READ | PROT_WRITE,
159                                 .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
160                                 .type = REGION_TYPE_STACK };
161
162     int status = 0;
163     if ((status = mem_map(NULL, &mapped, USTACK_END, NULL, &param))) {
164         kprint_panic("fail to alloc user stack: %d", status);
165     }
166
167     mapped->region_copied = __stack_copied;
168     mm_index((void**)&pcb->mm.stack, mapped);
169
170     // TODO other uspace initialization stuff
171
172     vmm_unmount_pd(VMS_MOUNT_1);
173 }
174
175 void
176 __mark_region(ptr_t start_vpn, ptr_t end_vpn, int attr)
177 {
178     for (size_t i = start_vpn; i <= end_vpn; i++) {
179         x86_pte_t* curproc = &PTE_MOUNTED(VMS_SELF, i);
180         x86_pte_t* newproc = &PTE_MOUNTED(VMS_MOUNT_1, i);
181
182         cpu_flush_page((ptr_t)newproc);
183
184         if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
185             // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
186             cpu_flush_page((ptr_t)curproc);
187             cpu_flush_page((ptr_t)(i << 12));
188
189             *curproc = *curproc & ~PG_WRITE;
190             *newproc = *newproc & ~PG_WRITE;
191         } else {
192             // 如果是私有页,则将该页从新进程中移除。
193             *newproc = 0;
194         }
195     }
196 }
197
198 void
199 __copy_fdtable(struct proc_info* pcb)
200 {
201     for (size_t i = 0; i < VFS_MAX_FD; i++) {
202         struct v_fd* fd = __current->fdtable->fds[i];
203         if (!fd)
204             continue;
205         vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
206     }
207 }
208
209 pid_t
210 dup_proc()
211 {
212     struct proc_info* pcb = alloc_process();
213     pcb->intr_ctx = __current->intr_ctx;
214     pcb->parent = __current;
215
216     if (__current->cwd) {
217         pcb->cwd = __current->cwd;
218         vfs_ref_dnode(pcb->cwd);
219     }
220
221     __copy_fdtable(pcb);
222     region_copy(&__current->mm, &pcb->mm);
223
224     /*
225      *  store the return value for forked process.
226      *  this will be implicit carried over after kernel stack is copied.
227      */
228     store_retval(0);
229
230     copy_kernel_stack(pcb, VMS_SELF);
231
232     // 根据 mm_region 进一步配置页表
233
234     struct mm_region *pos, *n;
235     llist_for_each(pos, n, &pcb->mm.regions, head)
236     {
237         // 如果写共享,则不作处理。
238         if ((pos->attr & REGION_WSHARED)) {
239             continue;
240         }
241
242         ptr_t start_vpn = pos->start >> 12;
243         ptr_t end_vpn = pos->end >> 12;
244         __mark_region(start_vpn, end_vpn, pos->attr);
245     }
246
247     vmm_unmount_pd(VMS_MOUNT_1);
248
249     commit_process(pcb);
250
251     return pcb->pid;
252 }
253
254 extern void __kexec_end;
255
256 void
257 copy_kernel_stack(struct proc_info* proc, ptr_t usedMnt)
258 {
259     // copy the entire kernel page table
260     pid_t pid = proc->pid;
261     ptr_t pt_copy = __dup_pagetable(pid, usedMnt);
262
263     vmm_mount_pd(VMS_MOUNT_1, pt_copy); // 将新进程的页表挂载到挂载点#2
264
265     // copy the kernel stack
266     for (size_t i = KSTACK_START >> 12; i <= KSTACK_TOP >> 12; i++) {
267         volatile x86_pte_t* ppte = &PTE_MOUNTED(VMS_MOUNT_1, i);
268
269         /*
270             This is a fucking nightmare, the TLB caching keep the rewrite to PTE
271            from updating. Even the Nightmare Moon the Evil is far less nasty
272            than this. It took me hours of debugging to figure this out.
273
274             In the name of Celestia our glorious goddess, I will fucking HATE
275            the TLB for the rest of my LIFE!
276         */
277         cpu_flush_page((ptr_t)ppte);
278
279         x86_pte_t p = *ppte;
280         ptr_t ppa = vmm_dup_page(pid, PG_ENTRY_ADDR(p));
281         pmm_free_page(pid, PG_ENTRY_ADDR(p));
282         *ppte = (p & 0xfff) | ppa;
283     }
284
285     proc->page_table = pt_copy;
286 }