kernel: Add an exec() system call
All checks were successful
continuous-integration/drone/push Build is passing

Doesn't support arguments or environment for now.
This commit is contained in:
apio 2023-03-16 22:44:58 +01:00
parent 08c888eaae
commit 8c72e9a49a
Signed by: apio
GPG Key ID: B8A7D06E42258954
10 changed files with 163 additions and 44 deletions

View File

@ -7,3 +7,4 @@ function(luna_app SOURCE_FILE APP_NAME)
endfunction()
luna_app(app.c app)
luna_app(hello.c hello)

View File

@ -2,6 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
@ -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");
}

6
apps/hello.c Normal file
View File

@ -0,0 +1,6 @@
#include <stdio.h>
int main()
{
printf("Hello world!\n");
}

View File

@ -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

View File

@ -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;

45
kernel/src/sys/exec.cpp Normal file
View File

@ -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 <bits/modes.h>
#include <luna/CString.h>
#include <luna/ScopeGuard.h>
Result<u64> 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, &current->regs, sizeof(*regs));
kinfoln("exec: done");
return 0;
}

View File

@ -4,6 +4,7 @@
#include "arch/CPU.h"
#include "arch/MMU.h"
#include "memory/MemoryManager.h"
#include "thread/ThreadImage.h"
#include <luna/Alignment.h>
#include <luna/ScopeGuard.h>
#include <luna/Stack.h>
@ -99,25 +100,6 @@ namespace Scheduler
return new_kernel_thread_impl(thread);
}
static Result<void> 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<void> new_userspace_thread(SharedPtr<VFS::Inode> 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());

View File

@ -0,0 +1,70 @@
#include "thread/ThreadImage.h"
#include "memory/MemoryManager.h"
#include "thread/Thread.h"
static Result<void> 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<OwnedPtr<ThreadImage>> ThreadImage::try_load_from_elf(SharedPtr<VFS::Inode> inode)
{
auto image = TRY(make_owned<ThreadImage>());
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);
}

View File

@ -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 <luna/LinkedList.h>
#include <luna/OwnedPtr.h>
#include <luna/Result.h>
#include <luna/Stack.h>
class Thread;
class ThreadImage
{
public:
static Result<OwnedPtr<ThreadImage>> try_load_from_elf(SharedPtr<VFS::Inode> inode);
void apply(Thread* thread);
private:
OwnedPtr<UserVM> m_vm_allocator;
PageDirectory* m_directory { nullptr };
Stack m_user_stack;
Stack m_kernel_stack;
ELFData m_loaded_image_data;
};

View File

@ -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
{