From 8c72e9a49a63f0f6ed25a26656bb2ef0f11446b8 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 16 Mar 2023 22:44:58 +0100 Subject: [PATCH] kernel: Add an exec() system call Doesn't support arguments or environment for now. --- apps/CMakeLists.txt | 1 + apps/app.c | 4 ++ apps/hello.c | 6 +++ kernel/CMakeLists.txt | 2 + kernel/src/arch/x86_64/MMU.cpp | 4 +- kernel/src/sys/exec.cpp | 45 ++++++++++++++++++++ kernel/src/thread/Scheduler.cpp | 45 ++------------------ kernel/src/thread/ThreadImage.cpp | 70 +++++++++++++++++++++++++++++++ kernel/src/thread/ThreadImage.h | 28 +++++++++++++ libluna/include/luna/Syscall.h | 2 +- 10 files changed, 163 insertions(+), 44 deletions(-) create mode 100644 apps/hello.c create mode 100644 kernel/src/sys/exec.cpp create mode 100644 kernel/src/thread/ThreadImage.cpp create mode 100644 kernel/src/thread/ThreadImage.h diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 4ab52d3b..80c8a777 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -7,3 +7,4 @@ function(luna_app SOURCE_FILE APP_NAME) endfunction() luna_app(app.c app) +luna_app(hello.c hello) diff --git a/apps/app.c b/apps/app.c index 24b05a9c..b443fa34 100644 --- a/apps/app.c +++ b/apps/app.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -32,4 +33,7 @@ int main() time_t now = time(NULL); printf("date: %s", ctime(&now)); + + // FIXME: Add libc wrapper. + syscall(SYS_exec, "/bin/hello"); } diff --git a/apps/hello.c b/apps/hello.c new file mode 100644 index 00000000..2eb09b6e --- /dev/null +++ b/apps/hello.c @@ -0,0 +1,6 @@ +#include + +int main() +{ + printf("Hello world!\n"); +} diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index bccf58c5..082542e2 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -19,6 +19,7 @@ set(SOURCES src/arch/Timer.cpp src/arch/PCI.cpp src/thread/Thread.cpp + src/thread/ThreadImage.cpp src/thread/Scheduler.cpp src/sys/Syscall.cpp src/sys/exit.cpp @@ -27,6 +28,7 @@ set(SOURCES src/sys/mmap.cpp src/sys/usleep.cpp src/sys/open.cpp + src/sys/exec.cpp src/sys/file.cpp src/sys/id.cpp src/sys/mkdir.cpp diff --git a/kernel/src/arch/x86_64/MMU.cpp b/kernel/src/arch/x86_64/MMU.cpp index cc91b6c3..5d8376eb 100644 --- a/kernel/src/arch/x86_64/MMU.cpp +++ b/kernel/src/arch/x86_64/MMU.cpp @@ -327,8 +327,8 @@ namespace MMU PageDirectory* const table = translate_physical(directory); - // Let's iterate over every top-level entry, skipping the last two entries (recursive mapping and kernel pages) - for (u64 i = 0; i < 510; i++) + // Let's iterate over every top-level entry in the lower half + for (u64 i = 0; i < 256; i++) { PageTableEntry& l4 = table->entries[i]; if (!l4.present) continue; diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp new file mode 100644 index 00000000..fe0362fb --- /dev/null +++ b/kernel/src/sys/exec.cpp @@ -0,0 +1,45 @@ +#include "ELF.h" +#include "Log.h" +#include "fs/VFS.h" +#include "memory/MemoryManager.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" +#include "thread/ThreadImage.h" +#include +#include +#include + +Result sys_exec(Registers* regs, SyscallArgs args) +{ + auto path = TRY(MemoryManager::strdup_from_user(args[0])); + + auto inode = TRY(VFS::resolve_path(path.chars())); + + // Not executable + if ((inode->mode() & S_IXUSR) != S_IXUSR) return err(EACCES); + + kinfoln("exec: attempting to replace current image with %s", path.chars()); + + auto current = Scheduler::current(); + + auto image = TRY(ThreadImage::try_load_from_elf(inode)); + + // From now on, nothing should fail. + + kinfoln("exec: image load ok, will now replace existing process image"); + + // FIXME: Close only O_CLOEXEC file descriptors. + for (int i = 0; i < FD_MAX; i++) { current->fd_table[i] = {}; } + + MMU::delete_userspace_page_directory(current->directory); + + image->apply(current); + + MMU::switch_page_directory(current->directory); + + memcpy(regs, ¤t->regs, sizeof(*regs)); + + kinfoln("exec: done"); + + return 0; +} diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 0f1eb7cb..dbe3237b 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -4,6 +4,7 @@ #include "arch/CPU.h" #include "arch/MMU.h" #include "memory/MemoryManager.h" +#include "thread/ThreadImage.h" #include #include #include @@ -99,25 +100,6 @@ namespace Scheduler return new_kernel_thread_impl(thread); } - - static Result create_stacks(Stack& user_stack, Stack& kernel_stack) - { - const u64 THREAD_STACK_BASE = 0x10000; - - TRY(MemoryManager::alloc_at(THREAD_STACK_BASE, 4, MMU::ReadWrite | MMU::NoExecute | MMU::User)); - - auto guard = make_scope_guard([&] { MemoryManager::unmap_owned(THREAD_STACK_BASE, 4); }); - - const u64 kernel_stack_base = TRY(MemoryManager::alloc_for_kernel(4, MMU::ReadWrite | MMU::NoExecute)); - - guard.deactivate(); - - user_stack = { THREAD_STACK_BASE, 4 * ARCH_PAGE_SIZE }; - kernel_stack = { kernel_stack_base, 4 * ARCH_PAGE_SIZE }; - - return {}; - } - Result new_userspace_thread(SharedPtr inode) { Thread* const thread = TRY(new_thread()); @@ -126,30 +108,11 @@ namespace Scheduler auto guard = make_scope_guard([&] { delete thread; }); - thread->vm_allocator = TRY(UserVM::try_create()); - - PageDirectory* const directory = TRY(MMU::create_page_directory_for_userspace()); - - auto directory_guard = make_scope_guard([&] { - MMU::switch_page_directory(MMU::kernel_page_directory()); - MemoryManager::free_frame((u64)directory); - }); - - MMU::switch_page_directory(directory); - - thread->init_regs_user(); - - const ELFData data = TRY(ELFLoader::load(inode)); - - thread->set_ip(data.entry); - - TRY(create_stacks(thread->stack, thread->kernel_stack)); - thread->set_sp(thread->stack.top()); - - thread->directory = directory; + auto image = TRY(ThreadImage::try_load_from_elf(inode)); guard.deactivate(); - directory_guard.deactivate(); + + image->apply(thread); kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(), thread->sp(), thread->kernel_stack.top()); diff --git a/kernel/src/thread/ThreadImage.cpp b/kernel/src/thread/ThreadImage.cpp new file mode 100644 index 00000000..1037a47b --- /dev/null +++ b/kernel/src/thread/ThreadImage.cpp @@ -0,0 +1,70 @@ +#include "thread/ThreadImage.h" +#include "memory/MemoryManager.h" +#include "thread/Thread.h" + +static Result create_stacks(Stack& user_stack, Stack& kernel_stack) +{ + const u64 THREAD_STACK_BASE = 0x10000; + + TRY(MemoryManager::alloc_at(THREAD_STACK_BASE, 4, MMU::ReadWrite | MMU::NoExecute | MMU::User)); + + auto guard = make_scope_guard([&] { MemoryManager::unmap_owned(THREAD_STACK_BASE, 4); }); + + const u64 kernel_stack_base = TRY(MemoryManager::alloc_for_kernel(4, MMU::ReadWrite | MMU::NoExecute)); + + guard.deactivate(); + + user_stack = { THREAD_STACK_BASE, 4 * ARCH_PAGE_SIZE }; + kernel_stack = { kernel_stack_base, 4 * ARCH_PAGE_SIZE }; + + return {}; +} + +Result> ThreadImage::try_load_from_elf(SharedPtr inode) +{ + auto image = TRY(make_owned()); + + auto vm_allocator = TRY(UserVM::try_create()); + + auto old_directory = MMU::get_page_directory(); + + auto new_directory = TRY(MMU::create_page_directory_for_userspace()); + + MMU::switch_page_directory(new_directory); + + auto guard = make_scope_guard([=] { + MMU::delete_userspace_page_directory(new_directory); + MMU::switch_page_directory(old_directory); + }); + + const ELFData data = TRY(ELFLoader::load(inode)); + + Stack user_stack; + Stack kernel_stack; + TRY(create_stacks(user_stack, kernel_stack)); + + guard.deactivate(); + + image->m_directory = new_directory; + image->m_kernel_stack = kernel_stack; + image->m_user_stack = user_stack; + image->m_loaded_image_data = data; + image->m_vm_allocator = move(vm_allocator); + + return image; +} + +void ThreadImage::apply(Thread* thread) +{ + thread->init_regs_user(); + + thread->set_ip(m_loaded_image_data.entry); + + thread->kernel_stack = m_kernel_stack; + thread->stack = m_user_stack; + thread->set_sp(m_user_stack.top()); + + thread->directory = m_directory; + + thread->vm_allocator = move(m_vm_allocator); +} diff --git a/kernel/src/thread/ThreadImage.h b/kernel/src/thread/ThreadImage.h new file mode 100644 index 00000000..ced6b989 --- /dev/null +++ b/kernel/src/thread/ThreadImage.h @@ -0,0 +1,28 @@ +#pragma once + +#include "ELF.h" +#include "arch/CPU.h" +#include "arch/MMU.h" +#include "fs/VFS.h" +#include "memory/UserVM.h" +#include +#include +#include +#include + +class Thread; + +class ThreadImage +{ + public: + static Result> try_load_from_elf(SharedPtr inode); + + void apply(Thread* thread); + + private: + OwnedPtr m_vm_allocator; + PageDirectory* m_directory { nullptr }; + Stack m_user_stack; + Stack m_kernel_stack; + ELFData m_loaded_image_data; +}; diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 0e65143e..e7e0f05b 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -2,7 +2,7 @@ #define enumerate_syscalls(_e) \ _e(exit) _e(console_write) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) \ - _e(write) _e(lseek) _e(mkdir) + _e(write) _e(lseek) _e(mkdir) _e(exec) enum Syscalls {