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