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>
19 __dup_pagetable(pid_t pid, ptr_t mount_point)
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);
24 x86_page_table* ptd = (x86_page_table*)PG_MOUNT_1;
25 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
27 size_t kspace_l1inx = L1_INDEX(KERNEL_MM_BASE);
29 for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
31 x86_pte_t ptde = pptd->entry[i];
32 // 空或者是未在内存中的L1页表项直接照搬过去。
34 if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
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);
43 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
44 x86_page_table* pt = (x86_page_table*)PG_MOUNT_2;
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));
52 ptd->entry[i] = (ptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
55 ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
61 __del_pagetable(pid_t pid, ptr_t mount_point)
63 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
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)) {
72 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
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));
81 // free the L2 page table
82 pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
84 // free the L1 directory
85 pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
89 vmm_dup_vmspace(pid_t pid)
91 return __dup_pagetable(pid, VMS_SELF);
94 __DEFINE_LXSYSCALL(pid_t, fork)
99 __DEFINE_LXSYSCALL(pid_t, getpid)
101 return __current->pid;
104 __DEFINE_LXSYSCALL(pid_t, getppid)
106 return __current->parent->pid;
109 __DEFINE_LXSYSCALL(pid_t, getpgid)
111 return __current->pgid;
114 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
116 struct proc_info* proc = pid ? get_process(pid) : __current;
119 __current->k_status = EINVAL;
123 pgid = pgid ? pgid : proc->pid;
125 struct proc_info* gruppenfuhrer = get_process(pgid);
127 if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
128 __current->k_status = EINVAL;
132 llist_delete(&proc->grp_member);
133 llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
140 __stack_copied(struct mm_region* region)
142 mm_index((void**)®ion->proc_vms->stack, region);
146 init_proc_user_space(struct proc_info* pcb)
148 vmm_mount_pd(VMS_MOUNT_1, pcb->page_table);
152 struct mm_region* mapped;
153 struct mmap_param param = { .vms_mnt = VMS_MOUNT_1,
156 .proct = PROT_READ | PROT_WRITE,
157 .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
158 .type = REGION_TYPE_STACK };
161 if ((status = mem_map(NULL, &mapped, USTACK_END, NULL, ¶m))) {
162 kprint_panic("fail to alloc user stack: %d", status);
165 mapped->region_copied = __stack_copied;
166 mm_index((void**)&pcb->mm.stack, mapped);
168 // TODO other uspace initialization stuff
170 vmm_unmount_pd(VMS_MOUNT_1);
174 __mark_region(ptr_t start_vpn, ptr_t end_vpn, int attr)
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);
180 cpu_flush_page((ptr_t)newproc);
182 if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
183 // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
184 cpu_flush_page((ptr_t)curproc);
185 cpu_flush_page((ptr_t)(i << 12));
187 *curproc = *curproc & ~PG_WRITE;
188 *newproc = *newproc & ~PG_WRITE;
190 // 如果是私有页,则将该页从新进程中移除。
197 __copy_fdtable(struct proc_info* pcb)
199 for (size_t i = 0; i < VFS_MAX_FD; i++) {
200 struct v_fd* fd = __current->fdtable->fds[i];
203 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
210 struct proc_info* pcb = alloc_process();
211 pcb->intr_ctx = __current->intr_ctx;
212 pcb->parent = __current;
214 if (__current->cwd) {
215 pcb->cwd = __current->cwd;
216 vfs_ref_dnode(pcb->cwd);
220 region_copy(&__current->mm, &pcb->mm);
223 * store the return value for forked process.
224 * this will be implicit carried over after kernel stack is copied.
228 copy_kernel_stack(pcb, VMS_SELF);
230 // 根据 mm_region 进一步配置页表
232 struct mm_region *pos, *n;
233 llist_for_each(pos, n, &pcb->mm.regions, head)
236 if ((pos->attr & REGION_WSHARED)) {
240 ptr_t start_vpn = pos->start >> 12;
241 ptr_t end_vpn = pos->end >> 12;
242 __mark_region(start_vpn, end_vpn, pos->attr);
245 vmm_unmount_pd(VMS_MOUNT_1);
252 extern void __kernel_end;
255 copy_kernel_stack(struct proc_info* proc, ptr_t usedMnt)
257 // copy the entire kernel page table
258 pid_t pid = proc->pid;
259 ptr_t pt_copy = __dup_pagetable(pid, usedMnt);
261 vmm_mount_pd(VMS_MOUNT_1, pt_copy); // 将新进程的页表挂载到挂载点#2
263 // copy the kernel stack
264 for (size_t i = KSTACK_START >> 12; i <= KSTACK_TOP >> 12; i++) {
265 volatile x86_pte_t* ppte = &PTE_MOUNTED(VMS_MOUNT_1, i);
268 This is a fucking nightmare, the TLB caching keep the rewrite to PTE
269 from updating. Even the Nightmare Moon the Evil is far less nasty
270 than this. It took me hours of debugging to figure this out.
272 In the name of Celestia our glorious goddess, I will fucking HATE
273 the TLB for the rest of my LIFE!
275 cpu_flush_page((ptr_t)ppte);
278 ptr_t ppa = vmm_dup_page(pid, PG_ENTRY_ADDR(p));
279 pmm_free_page(pid, PG_ENTRY_ADDR(p));
280 *ppte = (p & 0xfff) | ppa;
283 proc->page_table = pt_copy;