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