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