chore: make things more general
[lunaix-os.git] / lunaix-os / kernel / process.c
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>
12
13 LOG_MODULE("PROC")
14
15 void*
16 __dup_pagetable(pid_t pid, uintptr_t mount_point)
17 {
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);
20
21     x86_page_table* ptd = PG_MOUNT_1;
22     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
23
24     for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
25         // 没有必要拷贝临时挂载点
26         if (PG_MOUNT_RANGE(i)) {
27             ptd->entry[i] = 0;
28             continue;
29         }
30
31         x86_pte_t ptde = pptd->entry[i];
32         if (!ptde || !(ptde & PG_PRESENT)) {
33             ptd->entry[i] = ptde;
34             continue;
35         }
36
37         void* pt_pp = pmm_alloc_page(pid, PP_FGPERSIST);
38         vmm_set_mapping(
39           PD_REFERENCED, PG_MOUNT_2, pt_pp, PG_PREM_RW, VMAP_NULL);
40
41         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
42         x86_page_table* pt = PG_MOUNT_2;
43
44         for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
45             x86_pte_t pte = ppt->entry[j];
46             pmm_ref_page(pid, PG_ENTRY_ADDR(pte));
47             pt->entry[j] = pte;
48         }
49
50         ptd->entry[i] = (uintptr_t)pt_pp | PG_PREM_RW;
51     }
52
53     ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
54
55     return ptd_pp;
56 }
57
58 void
59 __del_pagetable(pid_t pid, uintptr_t mount_point)
60 {
61     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
62
63     for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
64         x86_pte_t ptde = pptd->entry[i];
65         if (!ptde || !(ptde & PG_PRESENT)) {
66             continue;
67         }
68
69         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
70
71         for (size_t j = 0; j < PG_MAX_ENTRIES; j++) {
72             x86_pte_t pte = ppt->entry[j];
73             // free the 4KB data page
74             if ((pte & PG_PRESENT)) {
75                 pmm_free_page(pid, PG_ENTRY_ADDR(pte));
76             }
77         }
78         // free the L2 page table
79         pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
80     }
81     // free the L1 directory
82     pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
83 }
84
85 void*
86 vmm_dup_vmspace(pid_t pid)
87 {
88     return __dup_pagetable(pid, PD_REFERENCED);
89 }
90
91 __DEFINE_LXSYSCALL(pid_t, fork)
92 {
93     return dup_proc();
94 }
95
96 __DEFINE_LXSYSCALL(pid_t, getpid)
97 {
98     return __current->pid;
99 }
100
101 __DEFINE_LXSYSCALL(pid_t, getppid)
102 {
103     return __current->parent->pid;
104 }
105
106 __DEFINE_LXSYSCALL(pid_t, getpgid)
107 {
108     return __current->pgid;
109 }
110
111 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
112 {
113     struct proc_info* proc = pid ? get_process(pid) : __current;
114
115     if (!proc) {
116         __current->k_status = LXINVL;
117         return -1;
118     }
119
120     pgid = pgid ? pgid : proc->pid;
121
122     struct proc_info* gruppenfuhrer = get_process(pgid);
123
124     if (!gruppenfuhrer || proc->pgid == proc->pid) {
125         __current->k_status = LXINVL;
126         return -1;
127     }
128
129     llist_delete(&proc->grp_member);
130     llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
131
132     proc->pgid = pgid;
133     return 0;
134 }
135
136 void
137 init_proc(struct proc_info* pcb)
138 {
139     memset(pcb, 0, sizeof(*pcb));
140
141     pcb->pid = alloc_pid();
142     pcb->created = clock_systime();
143     pcb->state = PROC_CREATED;
144     pcb->pgid = pcb->pid;
145 }
146
147 void
148 __mark_region(uintptr_t start_vpn, uintptr_t end_vpn, int attr)
149 {
150     for (size_t i = start_vpn; i < end_vpn; i++) {
151         x86_pte_t* curproc = &PTE_MOUNTED(PD_REFERENCED, i);
152         x86_pte_t* newproc = &PTE_MOUNTED(PD_MOUNT_2, i);
153         cpu_invplg(newproc);
154
155         if (attr == REGION_RSHARED) {
156             // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
157             cpu_invplg(curproc);
158             *curproc = *curproc & ~PG_WRITE;
159             *newproc = *newproc & ~PG_WRITE;
160         } else {
161             // 如果是私有页,则将该页从新进程中移除。
162             *newproc = 0;
163         }
164     }
165 }
166
167 pid_t
168 dup_proc()
169 {
170     struct proc_info pcb;
171     init_proc(&pcb);
172     pcb.mm = __current->mm;
173     pcb.intr_ctx = __current->intr_ctx;
174     pcb.parent = __current;
175
176     region_copy(&__current->mm.regions, &pcb.mm.regions);
177
178 #ifdef USE_KERNEL_PG
179     setup_proc_mem(&pcb, PD_MOUNT_1); //挂载点#1是当前进程的页表
180 #else
181     setup_proc_mem(&pcb, PD_REFERENCED);
182 #endif
183
184     // 根据 mm_region 进一步配置页表
185     if (!__current->mm.regions) {
186         goto not_copy;
187     }
188
189     struct mm_region *pos, *n;
190     llist_for_each(pos, n, &pcb.mm.regions->head, head)
191     {
192         // 如果写共享,则不作处理。
193         if ((pos->attr & REGION_WSHARED)) {
194             continue;
195         }
196
197         uintptr_t start_vpn = PG_ALIGN(pos->start) >> 12;
198         uintptr_t end_vpn = PG_ALIGN(pos->end) >> 12;
199         __mark_region(start_vpn, end_vpn, pos->attr);
200     }
201
202 not_copy:
203     vmm_unmount_pd(PD_MOUNT_2);
204
205     // 正如同fork,返回两次。
206     pcb.intr_ctx.registers.eax = 0;
207
208     push_process(&pcb);
209
210     return pcb.pid;
211 }
212
213 extern void __kernel_end;
214
215 void
216 setup_proc_mem(struct proc_info* proc, uintptr_t usedMnt)
217 {
218     // copy the entire kernel page table
219     pid_t pid = proc->pid;
220     void* pt_copy = __dup_pagetable(pid, usedMnt);
221
222     vmm_mount_pd(PD_MOUNT_2, pt_copy); // 将新进程的页表挂载到挂载点#2
223
224     // copy the kernel stack
225     for (size_t i = KSTACK_START >> 12; i <= KSTACK_TOP >> 12; i++) {
226         volatile x86_pte_t* ppte = &PTE_MOUNTED(PD_MOUNT_2, i);
227
228         /*
229             This is a fucking nightmare, the TLB caching keep the rewrite to PTE
230            from updating. Even the Nightmare Moon the Evil is far less nasty
231            than this. It took me hours of debugging to figure this out.
232
233             In the name of Celestia our glorious goddess, I will fucking HATE
234            the TLB for the rest of my LIFE!
235         */
236         cpu_invplg(ppte);
237
238         x86_pte_t p = *ppte;
239         void* ppa = vmm_dup_page(pid, PG_ENTRY_ADDR(p));
240         *ppte = (p & 0xfff) | (uintptr_t)ppa;
241     }
242
243     // 我们不需要分配内核的区域,因为所有的内核代码和数据段只能通过系统调用来访问,任何非法的访问
244     // 都会导致eip落在区域外面,从而segmentation fault.
245
246     // 定义用户栈区域,但是不分配实际的物理页。我们会在Page fault
247     // handler里面实现动态分配物理页的逻辑。(虚拟内存的好处!)
248     // FIXME: 这里应该放到spawn_proc里面。
249     // region_add(proc, USTACK_END, USTACK_SIZE, REGION_PRIVATE |
250     // REGION_RW);
251
252     // 至于其他的区域我们暂时没有办法知道,因为那需要知道用户程序的信息。我们留到之后在处理。
253     proc->page_table = pt_copy;
254 }