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>
15 #include <sys/mm/mempart.h>
20 __dup_pagetable(pid_t pid, ptr_t mount_point)
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);
25 x86_page_table* ptd = (x86_page_table*)PG_MOUNT_1;
26 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
28 size_t kspace_l1inx = L1_INDEX(KERNEL_EXEC);
30 for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
32 x86_pte_t ptde = pptd->entry[i];
33 // 空或者是未在内存中的L1页表项直接照搬过去。
35 if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
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);
44 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
45 x86_page_table* pt = (x86_page_table*)PG_MOUNT_2;
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));
53 ptd->entry[i] = (ptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
56 ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
62 __del_pagetable(pid_t pid, ptr_t mount_point)
64 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
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)) {
73 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
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));
82 // free the L2 page table
83 pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
85 // free the L1 directory
86 pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
90 vmm_dup_vmspace(pid_t pid)
92 return __dup_pagetable(pid, VMS_SELF);
95 __DEFINE_LXSYSCALL(pid_t, fork)
100 __DEFINE_LXSYSCALL(pid_t, getpid)
102 return __current->pid;
105 __DEFINE_LXSYSCALL(pid_t, getppid)
107 return __current->parent->pid;
110 __DEFINE_LXSYSCALL(pid_t, getpgid)
112 return __current->pgid;
115 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
117 struct proc_info* proc = pid ? get_process(pid) : __current;
120 __current->k_status = EINVAL;
124 pgid = pgid ? pgid : proc->pid;
126 struct proc_info* gruppenfuhrer = get_process(pgid);
128 if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
129 __current->k_status = EINVAL;
133 llist_delete(&proc->grp_member);
134 llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
141 __stack_copied(struct mm_region* region)
143 mm_index((void**)®ion->proc_vms->stack, region);
147 init_proc_user_space(struct proc_info* pcb)
149 vmm_mount_pd(VMS_MOUNT_1, pcb->page_table);
153 struct mm_region* mapped;
154 struct mmap_param param = { .vms_mnt = VMS_MOUNT_1,
156 .mlen = USR_STACK_SIZE,
157 .proct = PROT_READ | PROT_WRITE,
158 .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
159 .type = REGION_TYPE_STACK };
162 if ((status = mem_map(NULL, &mapped, USR_STACK, NULL, ¶m))) {
163 kprint_panic("fail to alloc user stack: %d", status);
166 mapped->region_copied = __stack_copied;
167 mm_index((void**)&pcb->mm.stack, mapped);
169 // TODO other uspace initialization stuff
171 vmm_unmount_pd(VMS_MOUNT_1);
175 __mark_region(ptr_t start_vpn, ptr_t end_vpn, int attr)
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);
181 cpu_flush_page((ptr_t)newproc);
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));
188 *curproc = *curproc & ~PG_WRITE;
189 *newproc = *newproc & ~PG_WRITE;
191 // 如果是私有页,则将该页从新进程中移除。
198 __copy_fdtable(struct proc_info* pcb)
200 for (size_t i = 0; i < VFS_MAX_FD; i++) {
201 struct v_fd* fd = __current->fdtable->fds[i];
204 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
211 struct proc_info* pcb = alloc_process();
212 pcb->intr_ctx = __current->intr_ctx;
213 pcb->parent = __current;
215 if (__current->cwd) {
216 pcb->cwd = __current->cwd;
217 vfs_ref_dnode(pcb->cwd);
221 region_copy(&__current->mm, &pcb->mm);
224 * store the return value for forked process.
225 * this will be implicit carried over after kernel stack is copied.
229 copy_kernel_stack(pcb, VMS_SELF);
231 // 根据 mm_region 进一步配置页表
233 struct mm_region *pos, *n;
234 llist_for_each(pos, n, &pcb->mm.regions, head)
237 if ((pos->attr & REGION_WSHARED)) {
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);
246 vmm_unmount_pd(VMS_MOUNT_1);
253 extern void __kexec_end;
256 copy_kernel_stack(struct proc_info* proc, ptr_t usedMnt)
258 // copy the entire kernel page table
259 pid_t pid = proc->pid;
260 ptr_t pt_copy = __dup_pagetable(pid, usedMnt);
262 vmm_mount_pd(VMS_MOUNT_1, pt_copy); // 将新进程的页表挂载到挂载点#2
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);
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.
273 In the name of Celestia our glorious goddess, I will fucking HATE
274 the TLB for the rest of my LIFE!
276 cpu_flush_page((ptr_t)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;
284 proc->page_table = pt_copy;