f0870282e1185a72314ce4cce0726a6c77d43a1a
[lunaix-os.git] / lunaix-os / kernel / process / fork.c
1 #include <lunaix/mm/region.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/mm/vmm.h>
4 #include <lunaix/mm/pmm.h>
5 #include <lunaix/mm/mmap.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 #include <lunaix/signal.h>
12
13 #include <sys/abi.h>
14 #include <sys/mm/mm_defs.h>
15
16 #include <klibc/string.h>
17
18 LOG_MODULE("FORK")
19
20 static void
21 region_maybe_cow(struct mm_region* region)
22 {
23     int attr = region->attr;
24     if ((attr & REGION_WSHARED)) {
25         return;
26     }
27
28     ptr_t start_vpn = PN(region->start);
29     ptr_t end_vpn = PN(region->end);
30     for (size_t i = start_vpn; i <= end_vpn; i++) {
31         x86_pte_t* curproc = &PTE_MOUNTED(VMS_SELF, i);
32         x86_pte_t* newproc = &PTE_MOUNTED(VMS_MOUNT_1, i);
33
34         cpu_flush_page((ptr_t)newproc);
35
36         if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
37             // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
38             cpu_flush_page((ptr_t)curproc);
39             cpu_flush_page((ptr_t)(i << 12));
40
41             *curproc = *curproc & ~PG_WRITE;
42             *newproc = *newproc & ~PG_WRITE;
43         } else {
44             // 如果是私有页,则将该页从新进程中移除。
45             *newproc = 0;
46         }
47     }
48 }
49
50 static inline void
51 __dup_fdtable(struct proc_info* pcb)
52 {
53     for (size_t i = 0; i < VFS_MAX_FD; i++) {
54         struct v_fd* fd = __current->fdtable->fds[i];
55         if (!fd)
56             continue;
57         vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
58     }
59 }
60
61
62 static void
63 __dup_kernel_stack(struct thread* thread, ptr_t vm_mnt)
64 {
65     ptr_t kstack_pn = PN(current_thread->kstack);
66
67     // copy the kernel stack
68     for (size_t i = 0; i < PN(KSTACK_SIZE); i++) {
69         volatile x86_pte_t* orig_ppte = &PTE_MOUNTED(VMS_SELF, kstack_pn);
70         x86_pte_t p = *orig_ppte;
71         ptr_t kstack = kstack_pn * PG_SIZE;
72
73         if (guardian_page(p)) {
74             vmm_set_mapping(vm_mnt, kstack, 0, 0, VMAP_GUARDPAGE);
75         } else {
76             ptr_t ppa = vmm_dup_page(PG_ENTRY_ADDR(p));
77             vmm_set_mapping(vm_mnt, kstack, ppa, p & 0xfff, 0);
78         }
79
80         kstack_pn--;
81     }
82 }
83
84 /*
85     Duplicate the current active thread to the forked process's
86     main thread.
87
88     This is not the same as "fork a thread within the same 
89     process". In fact, it is impossible to do such "thread forking"
90     as the new forked thread's kernel and user stack must not
91     coincide with the original thread (because the same vm space)
92     thus all reference to the stack space are staled which could 
93     lead to undefined behaviour.
94
95 */
96
97 static struct thread*
98 dup_active_thread(ptr_t vm_mnt, struct proc_info* duped_pcb) 
99 {
100     struct thread* th = alloc_thread(duped_pcb);
101     if (!th) {
102         return NULL;
103     }
104
105     th->intr_ctx = current_thread->intr_ctx;
106     th->kstack = current_thread->kstack;
107
108     signal_dup_context(&th->sigctx);
109
110     /*
111      *  store the return value for forked process.
112      *  this will be implicit carried over after kernel stack is copied.
113      */
114     store_retval_to(th, 0);
115
116     __dup_kernel_stack(th, vm_mnt);
117
118     if (!current_thread->ustack) {
119         goto done;
120     }
121
122     struct mm_region* old_stack = current_thread->ustack;
123     struct mm_region *pos, *n;
124     llist_for_each(pos, n, vmregions(duped_pcb), head)
125     {
126         // remove stack of other threads.
127         if (!stack_region(pos)) {
128             continue;
129         }
130
131         if (!same_region(pos, old_stack)) {
132             mem_unmap_region(vm_mnt, pos);
133         }
134         else {
135             th->ustack = pos;
136         }
137     }
138
139     assert(th->ustack);
140
141 done:
142     return th;
143 }
144
145 pid_t
146 dup_proc()
147 {
148     struct proc_info* pcb = alloc_process();
149     if (!pcb) {
150         syscall_result(ENOMEM);
151         return -1;
152     }
153     
154     pcb->parent = __current;
155
156     // FIXME need a more elagent refactoring
157     if (__current->cmd) {
158         pcb->cmd_len = __current->cmd_len;
159         pcb->cmd = valloc(pcb->cmd_len);
160         memcpy(pcb->cmd, __current->cmd, pcb->cmd_len);
161     }
162
163     if (__current->cwd) {
164         pcb->cwd = __current->cwd;
165         vfs_ref_dnode(pcb->cwd);
166     }
167
168     __dup_fdtable(pcb);
169     procvm_dup(pcb);
170
171     vmm_mount_pd(VMS_MOUNT_1, vmroot(pcb));
172
173     struct thread* main_thread = dup_active_thread(VMS_MOUNT_1, pcb);
174     if (!main_thread) {
175         syscall_result(ENOMEM);
176         vmm_unmount_pd(VMS_MOUNT_1);
177         delete_process(pcb);
178         return -1;
179     }
180
181     // 根据 mm_region 进一步配置页表
182     struct mm_region *pos, *n;
183     llist_for_each(pos, n, &pcb->mm->regions, head)
184     {
185         region_maybe_cow(pos);
186     }
187
188     vmm_unmount_pd(VMS_MOUNT_1);
189
190     commit_process(pcb);
191     commit_thread(main_thread);
192
193     return pcb->pid;
194 }
195
196 __DEFINE_LXSYSCALL(pid_t, fork)
197 {
198     return dup_proc();
199 }