kernel: Add an exec() system call
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Doesn't support arguments or environment for now.
This commit is contained in:
parent
08c888eaae
commit
8c72e9a49a
@ -7,3 +7,4 @@ function(luna_app SOURCE_FILE APP_NAME)
|
||||
endfunction()
|
||||
|
||||
luna_app(app.c app)
|
||||
luna_app(hello.c hello)
|
||||
|
@ -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
6
apps/hello.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello world!\n");
|
||||
}
|
@ -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
|
||||
|
@ -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
45
kernel/src/sys/exec.cpp
Normal 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, ¤t->regs, sizeof(*regs));
|
||||
|
||||
kinfoln("exec: done");
|
||||
|
||||
return 0;
|
||||
}
|
@ -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());
|
||||
|
70
kernel/src/thread/ThreadImage.cpp
Normal file
70
kernel/src/thread/ThreadImage.cpp
Normal 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);
|
||||
}
|
28
kernel/src/thread/ThreadImage.h
Normal file
28
kernel/src/thread/ThreadImage.h
Normal 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;
|
||||
};
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user