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 | |
18 | long 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 ; |
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 | |