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>
18 __dup_pagetable(pid_t pid, uintptr_t mount_point)
20 void* ptd_pp = pmm_alloc_page(pid, PP_FGPERSIST);
21 vmm_set_mapping(VMS_SELF, PG_MOUNT_1, ptd_pp, PG_PREM_RW, VMAP_NULL);
23 x86_page_table* ptd = PG_MOUNT_1;
24 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
26 size_t kspace_l1inx = L1_INDEX(KERNEL_MM_BASE);
28 for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
30 x86_pte_t ptde = pptd->entry[i];
31 // 空或者是未在内存中的L1页表项直接照搬过去。
33 if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
39 void* pt_pp = pmm_alloc_page(pid, PP_FGPERSIST);
40 vmm_set_mapping(VMS_SELF, PG_MOUNT_2, pt_pp, PG_PREM_RW, VMAP_NULL);
42 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
43 x86_page_table* pt = PG_MOUNT_2;
45 for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
46 x86_pte_t pte = ppt->entry[j];
47 pmm_ref_page(pid, PG_ENTRY_ADDR(pte));
51 ptd->entry[i] = (uintptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
54 ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
60 __del_pagetable(pid_t pid, uintptr_t mount_point)
62 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
64 // only remove user address space
65 for (size_t i = 0; i < L1_INDEX(KERNEL_MM_BASE); i++) {
66 x86_pte_t ptde = pptd->entry[i];
67 if (!ptde || !(ptde & PG_PRESENT)) {
71 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
73 for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
74 x86_pte_t pte = ppt->entry[j];
75 // free the 4KB data page
76 if ((pte & PG_PRESENT)) {
77 pmm_free_page(pid, PG_ENTRY_ADDR(pte));
80 // free the L2 page table
81 pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
83 // free the L1 directory
84 pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
88 vmm_dup_vmspace(pid_t pid)
90 return __dup_pagetable(pid, VMS_SELF);
93 __DEFINE_LXSYSCALL(pid_t, fork)
98 __DEFINE_LXSYSCALL(pid_t, getpid)
100 return __current->pid;
103 __DEFINE_LXSYSCALL(pid_t, getppid)
105 return __current->parent->pid;
108 __DEFINE_LXSYSCALL(pid_t, getpgid)
110 return __current->pgid;
113 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
115 struct proc_info* proc = pid ? get_process(pid) : __current;
118 __current->k_status = EINVAL;
122 pgid = pgid ? pgid : proc->pid;
124 struct proc_info* gruppenfuhrer = get_process(pgid);
126 if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
127 __current->k_status = EINVAL;
131 llist_delete(&proc->grp_member);
132 llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
139 init_proc_user_space(struct proc_info* pcb)
141 vmm_mount_pd(VMS_MOUNT_1, pcb->page_table);
145 struct mm_region* mapped;
146 struct mmap_param param = { .vms_mnt = VMS_MOUNT_1,
147 .regions = &pcb->mm.regions,
148 .length = USTACK_SIZE,
149 .proct = PROT_READ | PROT_WRITE,
150 .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
151 .type = REGION_TYPE_STACK };
154 if ((status = mem_map(NULL, &mapped, USTACK_END, NULL, ¶m))) {
155 kprint_panic("fail to alloc user stack: %d", status);
158 // TODO other uspace initialization stuff
160 vmm_unmount_pd(VMS_MOUNT_1);
164 __mark_region(uintptr_t start_vpn, uintptr_t end_vpn, int attr)
166 for (size_t i = start_vpn; i <= end_vpn; i++) {
167 x86_pte_t* curproc = &PTE_MOUNTED(VMS_SELF, i);
168 x86_pte_t* newproc = &PTE_MOUNTED(VMS_MOUNT_1, i);
171 if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
172 // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
175 *curproc = *curproc & ~PG_WRITE;
176 *newproc = *newproc & ~PG_WRITE;
178 // 如果是私有页,则将该页从新进程中移除。
185 __copy_fdtable(struct proc_info* pcb)
187 for (size_t i = 0; i < VFS_MAX_FD; i++) {
188 struct v_fd* fd = __current->fdtable->fds[i];
191 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
198 struct proc_info* pcb = alloc_process();
199 pcb->mm.u_heap = __current->mm.u_heap;
200 pcb->intr_ctx = __current->intr_ctx;
201 pcb->parent = __current;
203 memcpy(pcb->fxstate, __current->fxstate, 512);
205 if (__current->cwd) {
206 pcb->cwd = __current->cwd;
207 vfs_ref_dnode(pcb->cwd);
211 region_copy(&__current->mm.regions, &pcb->mm.regions);
213 setup_proc_mem(pcb, VMS_SELF);
215 // 根据 mm_region 进一步配置页表
217 struct mm_region *pos, *n;
218 llist_for_each(pos, n, &pcb->mm.regions, head)
221 if ((pos->attr & REGION_WSHARED)) {
225 uintptr_t start_vpn = pos->start >> 12;
226 uintptr_t end_vpn = pos->end >> 12;
227 __mark_region(start_vpn, end_vpn, pos->attr);
230 vmm_unmount_pd(VMS_MOUNT_1);
233 pcb->intr_ctx.registers.eax = 0;
240 extern void __kernel_end;
243 setup_proc_mem(struct proc_info* proc, uintptr_t usedMnt)
245 // copy the entire kernel page table
246 pid_t pid = proc->pid;
247 void* pt_copy = __dup_pagetable(pid, usedMnt);
249 vmm_mount_pd(VMS_MOUNT_1, pt_copy); // 将新进程的页表挂载到挂载点#2
251 // copy the kernel stack
252 for (size_t i = KSTACK_START >> 12; i <= KSTACK_TOP >> 12; i++) {
253 volatile x86_pte_t* ppte = &PTE_MOUNTED(VMS_MOUNT_1, i);
256 This is a fucking nightmare, the TLB caching keep the rewrite to PTE
257 from updating. Even the Nightmare Moon the Evil is far less nasty
258 than this. It took me hours of debugging to figure this out.
260 In the name of Celestia our glorious goddess, I will fucking HATE
261 the TLB for the rest of my LIFE!
266 void* ppa = vmm_dup_page(pid, PG_ENTRY_ADDR(p));
267 pmm_free_page(pid, PG_ENTRY_ADDR(p));
268 *ppte = (p & 0xfff) | (uintptr_t)ppa;
271 // 我们不需要分配内核的区域,因为所有的内核代码和数据段只能通过系统调用来访问,任何非法的访问
272 // 都会导致eip落在区域外面,从而segmentation fault.
274 // 至于其他的区域我们暂时没有办法知道,因为那需要知道用户程序的信息。我们留到之后在处理。
275 proc->page_table = pt_copy;