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>
16 #include <sys/mm/mempart.h>
21 __dup_pagetable(pid_t pid, ptr_t mount_point)
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);
26 x86_page_table* ptd = (x86_page_table*)PG_MOUNT_1;
27 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
29 size_t kspace_l1inx = L1_INDEX(KERNEL_EXEC);
31 for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
33 x86_pte_t ptde = pptd->entry[i];
34 // 空或者是未在内存中的L1页表项直接照搬过去。
36 if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
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);
45 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
46 x86_page_table* pt = (x86_page_table*)PG_MOUNT_2;
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));
54 ptd->entry[i] = (ptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
57 ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
63 __del_pagetable(pid_t pid, ptr_t mount_point)
65 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
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)) {
74 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
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));
83 // free the L2 page table
84 pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
86 // free the L1 directory
87 pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
91 vmm_dup_vmspace(pid_t pid)
93 return __dup_pagetable(pid, VMS_SELF);
96 __DEFINE_LXSYSCALL(pid_t, fork)
101 __DEFINE_LXSYSCALL(pid_t, getpid)
103 return __current->pid;
106 __DEFINE_LXSYSCALL(pid_t, getppid)
108 return __current->parent->pid;
111 __DEFINE_LXSYSCALL(pid_t, getpgid)
113 return __current->pgid;
116 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
118 struct proc_info* proc = pid ? get_process(pid) : __current;
121 __current->k_status = EINVAL;
125 pgid = pgid ? pgid : proc->pid;
127 struct proc_info* gruppenfuhrer = get_process(pgid);
129 if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
130 __current->k_status = EINVAL;
134 llist_delete(&proc->grp_member);
135 llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
142 __stack_copied(struct mm_region* region)
144 mm_index((void**)®ion->proc_vms->stack, region);
148 init_proc_user_space(struct proc_info* pcb)
150 vmm_mount_pd(VMS_MOUNT_1, pcb->page_table);
154 struct mm_region* mapped;
155 struct mmap_param param = { .vms_mnt = VMS_MOUNT_1,
158 .proct = PROT_READ | PROT_WRITE,
159 .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
160 .type = REGION_TYPE_STACK };
163 if ((status = mem_map(NULL, &mapped, USTACK_END, NULL, ¶m))) {
164 kprint_panic("fail to alloc user stack: %d", status);
167 mapped->region_copied = __stack_copied;
168 mm_index((void**)&pcb->mm.stack, mapped);
170 // TODO other uspace initialization stuff
172 vmm_unmount_pd(VMS_MOUNT_1);
176 __mark_region(ptr_t start_vpn, ptr_t end_vpn, int attr)
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);
182 cpu_flush_page((ptr_t)newproc);
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));
189 *curproc = *curproc & ~PG_WRITE;
190 *newproc = *newproc & ~PG_WRITE;
192 // 如果是私有页,则将该页从新进程中移除。
199 __copy_fdtable(struct proc_info* pcb)
201 for (size_t i = 0; i < VFS_MAX_FD; i++) {
202 struct v_fd* fd = __current->fdtable->fds[i];
205 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
212 struct proc_info* pcb = alloc_process();
213 pcb->intr_ctx = __current->intr_ctx;
214 pcb->parent = __current;
216 if (__current->cwd) {
217 pcb->cwd = __current->cwd;
218 vfs_ref_dnode(pcb->cwd);
222 region_copy(&__current->mm, &pcb->mm);
225 * store the return value for forked process.
226 * this will be implicit carried over after kernel stack is copied.
230 copy_kernel_stack(pcb, VMS_SELF);
232 // 根据 mm_region 进一步配置页表
234 struct mm_region *pos, *n;
235 llist_for_each(pos, n, &pcb->mm.regions, head)
238 if ((pos->attr & REGION_WSHARED)) {
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);
247 vmm_unmount_pd(VMS_MOUNT_1);
254 extern void __kexec_end;
257 copy_kernel_stack(struct proc_info* proc, ptr_t usedMnt)
259 // copy the entire kernel page table
260 pid_t pid = proc->pid;
261 ptr_t pt_copy = __dup_pagetable(pid, usedMnt);
263 vmm_mount_pd(VMS_MOUNT_1, pt_copy); // 将新进程的页表挂载到挂载点#2
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);
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.
274 In the name of Celestia our glorious goddess, I will fucking HATE
275 the TLB for the rest of my LIFE!
277 cpu_flush_page((ptr_t)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;
285 proc->page_table = pt_copy;