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()
|
endfunction()
|
||||||
|
|
||||||
luna_app(app.c app)
|
luna_app(app.c app)
|
||||||
|
luna_app(hello.c hello)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -32,4 +33,7 @@ int main()
|
|||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
printf("date: %s", ctime(&now));
|
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/Timer.cpp
|
||||||
src/arch/PCI.cpp
|
src/arch/PCI.cpp
|
||||||
src/thread/Thread.cpp
|
src/thread/Thread.cpp
|
||||||
|
src/thread/ThreadImage.cpp
|
||||||
src/thread/Scheduler.cpp
|
src/thread/Scheduler.cpp
|
||||||
src/sys/Syscall.cpp
|
src/sys/Syscall.cpp
|
||||||
src/sys/exit.cpp
|
src/sys/exit.cpp
|
||||||
@ -27,6 +28,7 @@ set(SOURCES
|
|||||||
src/sys/mmap.cpp
|
src/sys/mmap.cpp
|
||||||
src/sys/usleep.cpp
|
src/sys/usleep.cpp
|
||||||
src/sys/open.cpp
|
src/sys/open.cpp
|
||||||
|
src/sys/exec.cpp
|
||||||
src/sys/file.cpp
|
src/sys/file.cpp
|
||||||
src/sys/id.cpp
|
src/sys/id.cpp
|
||||||
src/sys/mkdir.cpp
|
src/sys/mkdir.cpp
|
||||||
|
@ -327,8 +327,8 @@ namespace MMU
|
|||||||
|
|
||||||
PageDirectory* const table = translate_physical(directory);
|
PageDirectory* const table = translate_physical(directory);
|
||||||
|
|
||||||
// Let's iterate over every top-level entry, skipping the last two entries (recursive mapping and kernel pages)
|
// Let's iterate over every top-level entry in the lower half
|
||||||
for (u64 i = 0; i < 510; i++)
|
for (u64 i = 0; i < 256; i++)
|
||||||
{
|
{
|
||||||
PageTableEntry& l4 = table->entries[i];
|
PageTableEntry& l4 = table->entries[i];
|
||||||
if (!l4.present) continue;
|
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/CPU.h"
|
||||||
#include "arch/MMU.h"
|
#include "arch/MMU.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "thread/ThreadImage.h"
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/ScopeGuard.h>
|
#include <luna/ScopeGuard.h>
|
||||||
#include <luna/Stack.h>
|
#include <luna/Stack.h>
|
||||||
@ -99,25 +100,6 @@ namespace Scheduler
|
|||||||
|
|
||||||
return new_kernel_thread_impl(thread);
|
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)
|
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode)
|
||||||
{
|
{
|
||||||
Thread* const thread = TRY(new_thread());
|
Thread* const thread = TRY(new_thread());
|
||||||
@ -126,30 +108,11 @@ namespace Scheduler
|
|||||||
|
|
||||||
auto guard = make_scope_guard([&] { delete thread; });
|
auto guard = make_scope_guard([&] { delete thread; });
|
||||||
|
|
||||||
thread->vm_allocator = TRY(UserVM::try_create());
|
auto image = TRY(ThreadImage::try_load_from_elf(inode));
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
guard.deactivate();
|
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(),
|
kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
|
||||||
thread->sp(), thread->kernel_stack.top());
|
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) \
|
#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(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
|
enum Syscalls
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user