Merge branch 'master' into prog-loader
[lunaix-os.git] / lunaix-os / kernel / process / process.c
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>
14
15 LOG_MODULE("PROC")
16
17 void*
18 __dup_pagetable(pid_t pid, uintptr_t mount_point)
19 {
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);
22
23     x86_page_table* ptd = PG_MOUNT_1;
24     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
25
26     size_t kspace_l1inx = L1_INDEX(KERNEL_MM_BASE);
27
28     for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) {
29
30         x86_pte_t ptde = pptd->entry[i];
31         // 空或者是未在内存中的L1页表项直接照搬过去。
32         // 内核地址空间直接共享过去。
33         if (!ptde || i >= kspace_l1inx || !(ptde & PG_PRESENT)) {
34             ptd->entry[i] = ptde;
35             continue;
36         }
37
38         // 复制L2页表
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);
41
42         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
43         x86_page_table* pt = PG_MOUNT_2;
44
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));
48             pt->entry[j] = pte;
49         }
50
51         ptd->entry[i] = (uintptr_t)pt_pp | PG_ENTRY_FLAGS(ptde);
52     }
53
54     ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd_pp);
55
56     return ptd_pp;
57 }
58
59 void
60 __del_pagetable(pid_t pid, uintptr_t mount_point)
61 {
62     x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12));
63
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)) {
68             continue;
69         }
70
71         x86_page_table* ppt = (x86_page_table*)(mount_point | (i << 12));
72
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));
78             }
79         }
80         // free the L2 page table
81         pmm_free_page(pid, PG_ENTRY_ADDR(ptde));
82     }
83     // free the L1 directory
84     pmm_free_page(pid, PG_ENTRY_ADDR(pptd->entry[PG_MAX_ENTRIES - 1]));
85 }
86
87 void*
88 vmm_dup_vmspace(pid_t pid)
89 {
90     return __dup_pagetable(pid, VMS_SELF);
91 }
92
93 __DEFINE_LXSYSCALL(pid_t, fork)
94 {
95     return dup_proc();
96 }
97
98 __DEFINE_LXSYSCALL(pid_t, getpid)
99 {
100     return __current->pid;
101 }
102
103 __DEFINE_LXSYSCALL(pid_t, getppid)
104 {
105     return __current->parent->pid;
106 }
107
108 __DEFINE_LXSYSCALL(pid_t, getpgid)
109 {
110     return __current->pgid;
111 }
112
113 __DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
114 {
115     struct proc_info* proc = pid ? get_process(pid) : __current;
116
117     if (!proc) {
118         __current->k_status = EINVAL;
119         return -1;
120     }
121
122     pgid = pgid ? pgid : proc->pid;
123
124     struct proc_info* gruppenfuhrer = get_process(pgid);
125
126     if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
127         __current->k_status = EINVAL;
128         return -1;
129     }
130
131     llist_delete(&proc->grp_member);
132     llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
133
134     proc->pgid = pgid;
135     return 0;
136 }
137
138 void
139 __stack_copied(struct mm_region* region)
140 {
141     mm_index((void**)&region->proc_vms->stack, region);
142 }
143
144 void
145 init_proc_user_space(struct proc_info* pcb)
146 {
147     vmm_mount_pd(VMS_MOUNT_1, pcb->page_table);
148
149     /*---  分配用户栈  ---*/
150
151     struct mm_region* mapped;
152     struct mmap_param param = { .vms_mnt = VMS_MOUNT_1,
153                                 .pvms = &pcb->mm,
154                                 .mlen = USTACK_SIZE,
155                                 .proct = PROT_READ | PROT_WRITE,
156                                 .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
157                                 .type = REGION_TYPE_STACK };
158
159     int status = 0;
160     if ((status = mem_map(NULL, &mapped, USTACK_END, NULL, &param))) {
161         kprint_panic("fail to alloc user stack: %d", status);
162     }
163
164     mapped->region_copied = __stack_copied;
165     mm_index((void**)&pcb->mm.stack, mapped);
166
167     // TODO other uspace initialization stuff
168
169     vmm_unmount_pd(VMS_MOUNT_1);
170 }
171
172 void
173 __mark_region(uintptr_t start_vpn, uintptr_t end_vpn, int attr)
174 {
175     for (size_t i = start_vpn; i <= end_vpn; i++) {
176         x86_pte_t* curproc = &PTE_MOUNTED(VMS_SELF, i);
177         x86_pte_t* newproc = &PTE_MOUNTED(VMS_MOUNT_1, i);
178         cpu_invplg(newproc);
179
180         if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
181             // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
182             cpu_invplg(curproc);
183             cpu_invplg(i << 12);
184             *curproc = *curproc & ~PG_WRITE;
185             *newproc = *newproc & ~PG_WRITE;
186         } else {
187             // 如果是私有页,则将该页从新进程中移除。
188             *newproc = 0;
189         }
190     }
191 }
192
193 void
194 __copy_fdtable(struct proc_info* pcb)
195 {
196     for (size_t i = 0; i < VFS_MAX_FD; i++) {
197         struct v_fd* fd = __current->fdtable->fds[i];
198         if (!fd)
199             continue;
200         vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
201     }
202 }
203
204 pid_t
205 dup_proc()
206 {
207     struct proc_info* pcb = alloc_process();
208     pcb->intr_ctx = __current->intr_ctx;
209     pcb->parent = __current;
210
211     memcpy(pcb->fxstate, __current->fxstate, 512);
212
213     if (__current->cwd) {
214         pcb->cwd = __current->cwd;
215         vfs_ref_dnode(pcb->cwd);
216     }
217
218     __copy_fdtable(pcb);
219     region_copy(&__current->mm, &pcb->mm);
220
221     setup_proc_mem(pcb, VMS_SELF);
222
223     // 根据 mm_region 进一步配置页表
224
225     struct mm_region *pos, *n;
226     llist_for_each(pos, n, &pcb->mm.regions, head)
227     {
228         // 如果写共享,则不作处理。
229         if ((pos->attr & REGION_WSHARED)) {
230             continue;
231         }
232
233         uintptr_t start_vpn = pos->start >> 12;
234         uintptr_t end_vpn = pos->end >> 12;
235         __mark_region(start_vpn, end_vpn, pos->attr);
236     }
237
238     vmm_unmount_pd(VMS_MOUNT_1);
239
240     // 正如同fork,返回两次。
241     pcb->intr_ctx.registers.eax = 0;
242
243     commit_process(pcb);
244
245     return pcb->pid;
246 }
247
248 extern void __kernel_end;
249
250 void
251 setup_proc_mem(struct proc_info* proc, uintptr_t usedMnt)
252 {
253     // copy the entire kernel page table
254     pid_t pid = proc->pid;
255     void* pt_copy = __dup_pagetable(pid, usedMnt);
256
257     vmm_mount_pd(VMS_MOUNT_1, pt_copy); // 将新进程的页表挂载到挂载点#2
258
259     // copy the kernel stack
260     for (size_t i = KSTACK_START >> 12; i <= KSTACK_TOP >> 12; i++) {
261         volatile x86_pte_t* ppte = &PTE_MOUNTED(VMS_MOUNT_1, i);
262
263         /*
264             This is a fucking nightmare, the TLB caching keep the rewrite to PTE
265            from updating. Even the Nightmare Moon the Evil is far less nasty
266            than this. It took me hours of debugging to figure this out.
267
268             In the name of Celestia our glorious goddess, I will fucking HATE
269            the TLB for the rest of my LIFE!
270         */
271         cpu_invplg(ppte);
272
273         x86_pte_t p = *ppte;
274         void* ppa = vmm_dup_page(pid, PG_ENTRY_ADDR(p));
275         pmm_free_page(pid, PG_ENTRY_ADDR(p));
276         *ppte = (p & 0xfff) | (uintptr_t)ppa;
277     }
278
279     // 我们不需要分配内核的区域,因为所有的内核代码和数据段只能通过系统调用来访问,任何非法的访问
280     // 都会导致eip落在区域外面,从而segmentation fault.
281
282     // 至于其他的区域我们暂时没有办法知道,因为那需要知道用户程序的信息。我们留到之后在处理。
283     proc->page_table = pt_copy;
284 }