1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/filesystem/dentry.hpp"
4#include "mos/filesystem/vfs.hpp"
5#include "mos/mm/mm.hpp"
6#include "mos/tasks/schedule.hpp"
7#include "mos/tasks/signal.hpp"
8
9#include <mos/lib/structures/hashmap.hpp>
10#include <mos/lib/structures/list.hpp>
11#include <mos/lib/structures/stack.hpp>
12#include <mos/lib/sync/spinlock.hpp>
13#include <mos/mm/cow.hpp>
14#include <mos/mm/paging/paging.hpp>
15#include <mos/mos_global.h>
16#include <mos/platform/platform.hpp>
17#include <mos/syslog/printk.hpp>
18#include <mos/tasks/process.hpp>
19#include <mos/tasks/task_types.hpp>
20#include <mos/tasks/thread.hpp>
21#include <mos_stdlib.hpp>
22#include <mos_string.hpp>
23
24Process *process_do_fork(Process *parent)
25{
26 MOS_ASSERT(Process::IsValid(parent));
27
28 auto child_p = Process::New(parent, name: parent->name);
29 if (unlikely(!child_p))
30 {
31 mEmerg << "failed to allocate process for fork";
32 return NULL;
33 }
34
35 child_p->working_directory = dentry_ref_up_to(dentry: parent->working_directory, root: root_dentry);
36
37 dEmph<process> << "process " << parent->pid << " forked to " << child_p->pid;
38
39 mm_lock_context_pair(ctx1: parent->mm, ctx2: child_p->mm);
40 list_foreach(vmap_t, vmap_p, parent->mm->mmaps)
41 {
42 PtrResult<vmap_t> child_vmap = [&]()
43 {
44 switch (vmap_p->type)
45 {
46 case VMAP_TYPE_SHARED: return mm_clone_vmap_locked(src_vmap: vmap_p, dst_ctx: child_p->mm); break;
47 case VMAP_TYPE_PRIVATE: return cow_clone_vmap_locked(target_mmctx: child_p->mm, source_vmap: vmap_p); break;
48 default: mos_panic("unknown vmap"); break;
49 }
50 }();
51
52 if (child_vmap.isErr())
53 {
54 mos_panic("failed to clone vmap");
55 }
56
57 dInfo2<process> << "fork vmap " << parent->pid << "->" << child_p->pid << ": " << get_vmap_type_str(type: vmap_p->type) << ", " << (void *) vmap_p << " -> "
58 << (void *) child_vmap.get();
59 vmap_finalise_init(vmap: child_vmap.get(), content: vmap_p->content, type: vmap_p->type);
60 }
61
62 mm_unlock_context_pair(ctx1: parent->mm, ctx2: child_p->mm);
63
64 // copy the parent's files
65 for (int i = 0; i < MOS_PROCESS_MAX_OPEN_FILES; i++)
66 {
67 const auto &file = parent->files[i];
68 if (IO::IsValid(io: file.io))
69 {
70 child_p->files[i].io = file.io->ref();
71 child_p->files[i].flags = file.flags;
72 }
73 }
74
75 child_p->signal_info = parent->signal_info;
76 waitlist_init(list: &child_p->signal_info.sigchild_waitlist);
77
78 // copy the thread
79 const auto parent_thread = current_thread;
80 const auto child_t = thread_allocate(owner: child_p, tflags: parent_thread->mode);
81 dInfo2<process> << "fork: thread " << parent_thread->tid << "->" << child_t->tid;
82 child_t->u_stack = parent_thread->u_stack;
83 child_t->name = parent_thread->name;
84 const ptr_t kstack_blk = phyframe_va(mm_get_free_pages(MOS_STACK_PAGES_KERNEL));
85 stack_init(stack: &child_t->k_stack, mem_region_bottom: (void *) kstack_blk, MOS_STACK_PAGES_KERNEL * MOS_PAGE_SIZE);
86 spinlock_acquire(&parent_thread->signal_info.lock);
87 child_t->signal_info.mask = parent_thread->signal_info.mask;
88 list_foreach(sigpending_t, sig, parent_thread->signal_info.pending)
89 {
90 sigpending_t *new_sig = mos::create<sigpending_t>();
91 linked_list_init(list_node(new_sig));
92 new_sig->signal = sig->signal;
93 list_node_prepend(head: &child_t->signal_info.pending, list_node(new_sig));
94 }
95 spinlock_release(&parent_thread->signal_info.lock);
96
97 platform_context_clone(from: parent_thread, to: child_t);
98
99 ProcessTable.insert(key: child_p->pid, value: child_p);
100 thread_complete_init(thread: child_t);
101 scheduler_add_thread(thread: child_t);
102 return child_p;
103}
104