1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/filesystem/vfs.hpp" |
4 | #include "mos/mm/cow.hpp" |
5 | #include "mos/platform/platform.hpp" |
6 | #include "mos/syslog/printk.hpp" |
7 | #include "mos/tasks/elf.hpp" |
8 | #include "mos/tasks/process.hpp" |
9 | #include "mos/tasks/signal.hpp" |
10 | #include "mos/tasks/task_types.hpp" |
11 | #include "mos/tasks/thread.hpp" |
12 | |
13 | #include <mos/filesystem/fs_types.h> |
14 | #include <mos/types.hpp> |
15 | #include <mos_stdlib.hpp> |
16 | #include <mos_string.hpp> |
17 | |
18 | long process_do_execveat(fd_t dirfd, const char *path, const char *const argv[], const char *const envp[], int flags) |
19 | { |
20 | auto thread = current_thread; |
21 | auto proc = current_process; |
22 | |
23 | MOS_UNUSED(flags); // not implemented: AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW |
24 | auto f = vfs_openat(fd: dirfd, path, flags: open_flags(OPEN_READ | OPEN_EXECUTE)); |
25 | if (f.isErr()) |
26 | return f.getErr(); |
27 | |
28 | io_ref(io: &f->io); |
29 | elf_header_t ; |
30 | if (!elf_read_and_verify_executable(file: f.get(), header: &header)) |
31 | { |
32 | pr_warn("failed to read elf header" ); |
33 | io_unref(io: &f->io); |
34 | return -ENOEXEC; |
35 | } |
36 | |
37 | // backup argv and envp |
38 | const char **argv_copy = NULL; |
39 | const char **envp_copy = NULL; |
40 | const char *path_copy = strdup(src: path); |
41 | |
42 | int argc = 0; |
43 | while (argv && argv[argc]) |
44 | { |
45 | argc++; |
46 | argv_copy = krealloc(ptr: argv_copy, size: (argc + 1) * sizeof(char *)); |
47 | argv_copy[argc - 1] = strdup(src: argv[argc - 1]); |
48 | } |
49 | |
50 | if (!argv_copy) |
51 | { |
52 | argv_copy = kcalloc<const char *>(n_members: 2); |
53 | argv_copy[0] = strdup(src: path); |
54 | argv_copy[1] = NULL; |
55 | argc = 1; |
56 | } |
57 | |
58 | argv_copy[argc] = NULL; |
59 | |
60 | int envc = 0; |
61 | while (envp && envp[envc]) |
62 | { |
63 | envc++; |
64 | envp_copy = krealloc(ptr: envp_copy, size: (envc + 1) * sizeof(char *)); |
65 | envp_copy[envc - 1] = strdup(src: envp[envc - 1]); |
66 | } |
67 | |
68 | if (!envp_copy) |
69 | { |
70 | envp_copy = kcalloc<const char *>(n_members: 1); |
71 | envp_copy[0] = NULL; |
72 | envc = 0; |
73 | } |
74 | |
75 | envp_copy[envc] = NULL; |
76 | |
77 | // !! ====== point of no return ====== !! // |
78 | |
79 | proc->name = f->dentry->name; // set process name to the name of the executable |
80 | thread->name = f->dentry->name; // set thread name to the name of the executable |
81 | |
82 | spinlock_acquire(&thread->state_lock); |
83 | |
84 | for (const auto &t : proc->thread_list) |
85 | { |
86 | if (t != thread) |
87 | { |
88 | signal_send_to_thread(target: t, SIGKILL); // nice |
89 | thread_wait_for_tid(tid: t->tid); |
90 | spinlock_acquire(&t->state_lock); |
91 | thread_destroy(thread: std::move(t: t)); |
92 | MOS_UNREACHABLE(); |
93 | } |
94 | } |
95 | |
96 | proc->main_thread = thread; // make current thread the only thread |
97 | platform_context_cleanup(thread); |
98 | spinlock_release(&thread->state_lock); |
99 | |
100 | // free old memory |
101 | spinlock_acquire(&proc->mm->mm_lock); |
102 | list_foreach(vmap_t, vmap, proc->mm->mmaps) |
103 | { |
104 | spinlock_acquire(&vmap->lock); |
105 | vmap_destroy(vmap); // no need to unlock because it's destroyed |
106 | } |
107 | spinlock_release(&proc->mm->mm_lock); |
108 | |
109 | // the userspace stack for the current thread will also be freed, so we create a new one |
110 | if (thread->mode == THREAD_MODE_USER) |
111 | { |
112 | const size_t ustack_size = MOS_STACK_PAGES_USER * MOS_PAGE_SIZE; |
113 | auto stack_vmap = cow_allocate_zeroed_pages(handle: proc->mm, npages: ustack_size / MOS_PAGE_SIZE, MOS_ADDR_USER_STACK, hints: VALLOC_DEFAULT, flags: VM_USER_RW); |
114 | if (stack_vmap.isErr()) |
115 | { |
116 | pr_emerg("failed to allocate stack for new process" ); |
117 | process_exit(proc: std::move(t&: proc), exit_code: 0, SIGKILL); |
118 | MOS_UNREACHABLE(); |
119 | } |
120 | |
121 | stack_init(stack: &thread->u_stack, mem_region_bottom: (void *) stack_vmap->vaddr, size: ustack_size); |
122 | vmap_finalise_init(vmap: stack_vmap.get(), content: VMAP_STACK, type: VMAP_TYPE_PRIVATE); |
123 | } |
124 | |
125 | elf_startup_info_t startup_info = { |
126 | .invocation = path_copy, |
127 | .auxv = { .count: 0 }, |
128 | .argc = argc, |
129 | .argv = argv_copy, |
130 | .envc = envc, |
131 | .envp = envp_copy, |
132 | }; |
133 | |
134 | const bool filled = elf_do_fill_process(proc, file: f.get(), elf: header, info: &startup_info); |
135 | io_unref(io: &f->io); |
136 | |
137 | // free old argv and envp |
138 | for (int i = 0; i < argc; i++) |
139 | kfree(ptr: argv_copy[i]); |
140 | kfree(ptr: argv_copy); |
141 | for (int i = 0; i < envc; i++) |
142 | kfree(ptr: envp_copy[i]); |
143 | kfree(ptr: envp_copy); |
144 | kfree(ptr: path_copy); |
145 | |
146 | if (unlikely(!filled)) |
147 | { |
148 | pr_emerg("failed to fill process, execve failed" ); |
149 | process_exit(proc: std::move(t&: proc), exit_code: 0, SIGKILL); |
150 | MOS_UNREACHABLE(); |
151 | } |
152 | |
153 | memzero(s: proc->signal_info.handlers, n: sizeof(proc->signal_info.handlers)); // reset signal handlers |
154 | |
155 | // close any files that are FD_CLOEXEC |
156 | for (int i = 0; i < MOS_PROCESS_MAX_OPEN_FILES; i++) |
157 | { |
158 | if (io_valid(io: proc->files[i].io) && (proc->files[i].flags & FD_FLAGS_CLOEXEC)) |
159 | process_detach_fd(process: proc, fd: i); |
160 | } |
161 | |
162 | return 0; |
163 | } |
164 | |