| 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/vector.hpp> |
| 16 | #include <mos_stdlib.hpp> |
| 17 | #include <mos_string.hpp> |
| 18 | |
| 19 | long process_do_execveat(fd_t dirfd, const char *path, const char *const argv[], const char *const envp[], int flags) |
| 20 | { |
| 21 | auto thread = current_thread; |
| 22 | auto proc = current_process; |
| 23 | |
| 24 | MOS_UNUSED(flags); // not implemented: AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW |
| 25 | auto f = vfs_openat(fd: dirfd, path, flags: OPEN_READ | OPEN_EXECUTE); |
| 26 | if (f.isErr()) |
| 27 | return f.getErr(); |
| 28 | |
| 29 | auto file = f.get(); |
| 30 | |
| 31 | file->ref(); |
| 32 | elf_header_t ; |
| 33 | if (!elf_read_and_verify_executable(file: f.get(), header: &header)) |
| 34 | { |
| 35 | pr_warn("failed to read elf header" ); |
| 36 | file->unref(); |
| 37 | return -ENOEXEC; |
| 38 | } |
| 39 | |
| 40 | // backup argv and envp |
| 41 | mos::vector<mos::string> argv_copy; |
| 42 | mos::vector<mos::string> envp_copy; |
| 43 | mos::string path_copy = path; |
| 44 | |
| 45 | int argc = 0; |
| 46 | while (argv && argv[argc]) |
| 47 | { |
| 48 | argc++; |
| 49 | argv_copy.push_back(value: argv[argc - 1]); |
| 50 | } |
| 51 | |
| 52 | if (argv_copy.empty()) |
| 53 | argv_copy = { path }; |
| 54 | |
| 55 | int envc = 0; |
| 56 | while (envp && envp[envc]) |
| 57 | { |
| 58 | envc++; |
| 59 | envp_copy.push_back(value: envp[envc - 1]); |
| 60 | } |
| 61 | |
| 62 | // !! ====== point of no return ====== !! // |
| 63 | |
| 64 | proc->name = f->dentry->name; // set process name to the name of the executable |
| 65 | thread->name = f->dentry->name; // set thread name to the name of the executable |
| 66 | |
| 67 | spinlock_acquire(&thread->state_lock); |
| 68 | |
| 69 | for (const auto &t : proc->thread_list) |
| 70 | { |
| 71 | if (t != thread) |
| 72 | { |
| 73 | signal_send_to_thread(target: t, SIGKILL); // nice |
| 74 | thread_wait_for_tid(tid: t->tid); |
| 75 | spinlock_acquire(&t->state_lock); |
| 76 | thread_destroy(thread: t); |
| 77 | MOS_UNREACHABLE(); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | proc->main_thread = thread; // make current thread the only thread |
| 82 | platform_context_cleanup(thread); |
| 83 | spinlock_release(&thread->state_lock); |
| 84 | |
| 85 | // free old memory |
| 86 | spinlock_acquire(&proc->mm->mm_lock); |
| 87 | list_foreach(vmap_t, vmap, proc->mm->mmaps) |
| 88 | { |
| 89 | spinlock_acquire(&vmap->lock); |
| 90 | vmap_destroy(vmap); // no need to unlock because it's destroyed |
| 91 | } |
| 92 | spinlock_release(&proc->mm->mm_lock); |
| 93 | |
| 94 | // the userspace stack for the current thread will also be freed, so we create a new one |
| 95 | if (thread->mode == THREAD_MODE_USER) |
| 96 | { |
| 97 | const size_t ustack_size = MOS_STACK_PAGES_USER * MOS_PAGE_SIZE; |
| 98 | auto stack_vmap = cow_allocate_zeroed_pages(handle: proc->mm, npages: ustack_size / MOS_PAGE_SIZE, MOS_ADDR_USER_STACK, flags: VM_USER_RW); |
| 99 | if (stack_vmap.isErr()) |
| 100 | { |
| 101 | pr_emerg("failed to allocate stack for new process" ); |
| 102 | process_exit(proc: std::move(t&: proc), exit_code: 0, SIGKILL); |
| 103 | MOS_UNREACHABLE(); |
| 104 | } |
| 105 | |
| 106 | stack_init(stack: &thread->u_stack, mem_region_bottom: (void *) stack_vmap->vaddr, size: ustack_size); |
| 107 | vmap_finalise_init(vmap: stack_vmap.get(), content: VMAP_STACK, type: VMAP_TYPE_PRIVATE); |
| 108 | } |
| 109 | |
| 110 | elf_startup_info_t startup_info = { |
| 111 | .invocation = path_copy, |
| 112 | .auxv = {}, |
| 113 | .argv = argv_copy, |
| 114 | .envp = envp_copy, |
| 115 | }; |
| 116 | |
| 117 | const bool filled = elf_do_fill_process(proc, file, elf: header, info: &startup_info); |
| 118 | file->unref(); |
| 119 | |
| 120 | if (unlikely(!filled)) |
| 121 | { |
| 122 | pr_emerg("failed to fill process, execve failed" ); |
| 123 | process_exit(proc: std::move(t&: proc), exit_code: 0, SIGKILL); |
| 124 | MOS_UNREACHABLE(); |
| 125 | } |
| 126 | |
| 127 | memzero(s: proc->signal_info.handlers, n: sizeof(proc->signal_info.handlers)); // reset signal handlers |
| 128 | |
| 129 | // close any files that are FD_CLOEXEC |
| 130 | for (int i = 0; i < MOS_PROCESS_MAX_OPEN_FILES; i++) |
| 131 | { |
| 132 | const auto &fd = proc->files[i]; |
| 133 | if (IO::IsValid(io: fd.io) && (fd.flags & FD_FLAGS_CLOEXEC)) |
| 134 | process_detach_fd(process: proc, fd: i); |
| 135 | } |
| 136 | |
| 137 | return 0; |
| 138 | } |
| 139 | |