1 #include <klibc/string.h>
2 #include <lunaix/clock.h>
3 #include <lunaix/common.h>
4 #include <lunaix/mm/pmm.h>
5 #include <lunaix/mm/region.h>
6 #include <lunaix/mm/vmm.h>
7 #include <lunaix/process.h>
8 #include <lunaix/spike.h>
9 #include <lunaix/status.h>
10 #include <lunaix/syscall.h>
11 #include <lunaix/syslog.h>
16 __dup_pagetable(pid_t pid, uintptr_t mount_point)
18 void* ptd_pp = pmm_alloc_page(pid, PP_FGPERSIST);
19 vmm_set_mapping(PD_REFERENCED, PG_MOUNT_1, ptd_pp, PG_PREM_RW, VMAP_NULL);
21 x86_page_table* ptd = PG_MOUNT_1;
22 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
24 for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
25 x86_pte_t ptde = pptd->entry[i];
26 if (!ptde || !(ptde & PG_PRESENT)) {
31 void* pt_pp = pmm_alloc_page(pid, PP_FGPERSIST);
33 PD_REFERENCED, PG_MOUNT_2, pt_pp, PG_PREM_RW, VMAP_NULL);
35 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
36 x86_page_table* pt = PG_MOUNT_2;
38 for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
39 x86_pte_t pte = ppt->entry[j];
40 pmm_ref_page(pid, PG_ENTRY_ADDR(pte));
44 ptd->entry[i] = (uintptr_t)pt_pp | PG_PREM_RW;
47 ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
53 __del_pagetable(pid_t pid, uintptr_t mount_point)
55 x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
57 for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
58 x86_pte_t ptde = pptd->entry[i];
59 if (!ptde || !(ptde & PG_PRESENT)) {
63 x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
65 for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
66 x86_pte_t pte = ppt->entry[j];
67 // free the 4KB data page
68 if ((pte & PG_PRESENT)) {
69 pmm_free_page(pid, PG_ENTRY_ADDR(pte));
72 // free the L2 page table
73 pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
75 // free the L1 directory
76 pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
80 dup_pagetable(pid_t pid)
82 return __dup_pagetable(pid, PD_REFERENCED);
85 __DEFINE_LXSYSCALL(pid_t, fork)
90 __DEFINE_LXSYSCALL(pid_t, getpid)
92 return __current->pid;
95 __DEFINE_LXSYSCALL(pid_t, getppid)
97 return __current->parent->pid;
100 __DEFINE_LXSYSCALL(pid_t, getpgid)
102 return __current->pgid;
105 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
107 struct proc_info* proc = pid ? get_process(pid) : __current;
110 __current->k_status = LXINVL;
114 pgid = pgid ? pgid : proc->pid;
116 struct proc_info* gruppenfuhrer = get_process(pgid);
118 if (!gruppenfuhrer || proc->pgid == proc->pid) {
119 __current->k_status = LXINVL;
123 llist_delete(&proc->grp_member);
124 llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
131 init_proc(struct proc_info* pcb)
133 memset(pcb, 0, sizeof(*pcb));
135 pcb->pid = alloc_pid();
136 pcb->created = clock_systime();
137 pcb->state = PROC_CREATED;
138 pcb->pgid = pcb->pid;
144 struct proc_info pcb;
146 pcb.mm = __current->mm;
147 pcb.intr_ctx = __current->intr_ctx;
148 pcb.parent = __current;
151 setup_proc_mem(&pcb, PD_MOUNT_1); //挂载点#1是当前进程的页表
153 setup_proc_mem(&pcb, PD_REFERENCED);
156 // 根据 mm_region 进一步配置页表
157 if (!__current->mm.regions) {
161 llist_init_head(&pcb.mm.regions);
162 struct mm_region *pos, *n;
163 llist_for_each(pos, n, &__current->mm.regions->head, head)
165 region_add(&pcb, pos->start, pos->end, pos->attr);
168 if ((pos->attr & REGION_WSHARED)) {
172 uintptr_t start_vpn = PG_ALIGN(pos->start) >> 12;
173 uintptr_t end_vpn = PG_ALIGN(pos->end) >> 12;
174 for (size_t i = start_vpn; i < end_vpn; i++) {
175 x86_pte_t* curproc = &PTE_MOUNTED(PD_MOUNT_1, i);
176 x86_pte_t* newproc = &PTE_MOUNTED(PD_MOUNT_2, i);
179 if (pos->attr == REGION_RSHARED) {
180 // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
182 *curproc = *curproc & ~PG_WRITE;
183 *newproc = *newproc & ~PG_WRITE;
185 // 如果是私有页,则将该页从新进程中移除。
192 vmm_unmount_pd(PD_MOUNT_2);
195 pcb.intr_ctx.registers.eax = 0;
202 extern void __kernel_end;
205 setup_proc_mem(struct proc_info* proc, uintptr_t usedMnt)
207 // copy the entire kernel page table
208 pid_t pid = proc->pid;
209 void* pt_copy = __dup_pagetable(pid, usedMnt);
211 vmm_mount_pd(PD_MOUNT_2, pt_copy); // 将新进程的页表挂载到挂载点#2
213 // copy the kernel stack
214 for (size_t i = KSTACK_START >> 12; i <= KSTACK_TOP >> 12; i++) {
215 volatile x86_pte_t* ppte = &PTE_MOUNTED(PD_MOUNT_2, i);
218 This is a fucking nightmare, the TLB caching keep the rewrite to PTE
219 from updating. Even the Nightmare Moon the Evil is far less nasty
220 than this. It took me hours of debugging to figure this out.
222 In the name of Celestia our glorious goddess, I will fucking HATE
223 the TLB for the rest of my LIFE!
228 void* ppa = vmm_dup_page(pid, PG_ENTRY_ADDR(p));
229 *ppte = (p & 0xfff) | (uintptr_t)ppa;
232 // 我们不需要分配内核的区域,因为所有的内核代码和数据段只能通过系统调用来访问,任何非法的访问
233 // 都会导致eip落在区域外面,从而segmentation fault.
235 // 定义用户栈区域,但是不分配实际的物理页。我们会在Page fault
236 // handler里面实现动态分配物理页的逻辑。(虚拟内存的好处!)
237 // FIXME: 这里应该放到spawn_proc里面。
238 // region_add(proc, USTACK_END, USTACK_SIZE, REGION_PRIVATE | REGION_RW);
240 // 至于其他的区域我们暂时没有办法知道,因为那需要知道用户程序的信息。我们留到之后在处理。
242 proc->page_table = pt_copy;