1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/filesystem/vfs.h"
4#include "mos/mm/cow.h"
5#include "mos/platform/platform.h"
6#include "mos/syslog/printk.h"
7#include "mos/tasks/elf.h"
8#include "mos/tasks/process.h"
9#include "mos/tasks/signal.h"
10#include "mos/tasks/task_types.h"
11#include "mos/tasks/thread.h"
12
13#include <mos/filesystem/fs_types.h>
14#include <mos/types.h>
15#include <mos_stdlib.h>
16#include <mos_string.h>
17
18long process_do_execveat(process_t *process, fd_t dirfd, const char *path, const char *const argv[], const char *const envp[], int flags)
19{
20 thread_t *const thread = current_thread;
21 process_t *const proc = current_process;
22
23 MOS_ASSERT(thread->owner == process); // why
24
25 MOS_UNUSED(flags); // not implemented: AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW
26 file_t *f = vfs_openat(fd: dirfd, path, flags: OPEN_READ | OPEN_EXECUTE);
27 if (IS_ERR(ptr: f))
28 return PTR_ERR(ptr: f);
29
30 io_ref(io: &f->io);
31 elf_header_t header;
32 if (!elf_read_and_verify_executable(file: f, header: &header))
33 {
34 pr_warn("failed to read elf header");
35 io_unref(io: &f->io);
36 return -ENOEXEC;
37 }
38
39 // backup argv and envp
40 const char **argv_copy = NULL;
41 const char **envp_copy = NULL;
42 const char *path_copy = strdup(src: path);
43
44 int argc = 0;
45 while (argv && argv[argc])
46 {
47 argc++;
48 argv_copy = krealloc(ptr: argv_copy, size: (argc + 1) * sizeof(char *));
49 argv_copy[argc - 1] = strdup(src: argv[argc - 1]);
50 }
51
52 if (!argv_copy)
53 {
54 argv_copy = kmalloc(sizeof(char *) * 2);
55 argv_copy[0] = strdup(src: path);
56 argv_copy[1] = NULL;
57 argc = 1;
58 }
59
60 argv_copy[argc] = NULL;
61
62 int envc = 0;
63 while (envp && envp[envc])
64 {
65 envc++;
66 envp_copy = krealloc(ptr: envp_copy, size: (envc + 1) * sizeof(char *));
67 envp_copy[envc - 1] = strdup(src: envp[envc - 1]);
68 }
69
70 if (!envp_copy)
71 {
72 envp_copy = kmalloc(sizeof(char *) * 1);
73 envp_copy[0] = NULL;
74 envc = 0;
75 }
76
77 envp_copy[envc] = NULL;
78
79 // !! ====== point of no return ====== !! //
80
81 if (proc->name)
82 kfree(ptr: proc->name);
83 if (thread->name)
84 kfree(ptr: thread->name);
85
86 proc->name = strdup(src: f->dentry->name); // set process name to the name of the executable
87 thread->name = strdup(src: f->dentry->name); // set thread name to the name of the executable
88
89 spinlock_acquire(&thread->state_lock);
90
91 list_foreach(thread_t, t, process->threads)
92 {
93 if (t != thread)
94 {
95 signal_send_to_thread(target: t, SIGKILL); // nice
96 thread_wait_for_tid(tid: t->tid);
97 spinlock_acquire(&t->state_lock);
98 thread_destroy(thread: t);
99 MOS_UNREACHABLE();
100 }
101 }
102
103 proc->main_thread = thread; // make current thread the only thread
104 platform_context_cleanup(thread);
105 spinlock_release(&thread->state_lock);
106
107 // free old memory
108 spinlock_acquire(&proc->mm->mm_lock);
109 list_foreach(vmap_t, vmap, proc->mm->mmaps)
110 {
111 spinlock_acquire(&vmap->lock);
112 vmap_destroy(vmap); // no need to unlock because it's destroyed
113 }
114 spinlock_release(&proc->mm->mm_lock);
115
116 // the userspace stack for the current thread will also be freed, so we create a new one
117 if (thread->mode == THREAD_MODE_USER)
118 {
119 const size_t ustack_size = MOS_STACK_PAGES_USER * MOS_PAGE_SIZE;
120 vmap_t *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);
121 stack_init(stack: &thread->u_stack, mem_region_bottom: (void *) stack_vmap->vaddr, size: ustack_size);
122 vmap_finalise_init(vmap: stack_vmap, content: VMAP_STACK, type: VMAP_TYPE_PRIVATE);
123 }
124
125 elf_startup_info_t startup_info = {
126 .argc = argc,
127 .argv = argv_copy,
128 .envc = envc,
129 .envp = envp_copy,
130 .auxv = { 0 },
131 .invocation = path_copy,
132 };
133
134 const bool filled = elf_do_fill_process(proc, file: f, 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(process: 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 platform_regs_t *const regs = platform_thread_regs(thread);
163 platform_return_to_userspace(regs);
164}
165