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