From f8b3567042343cda1662dde3500235b0634bdc93 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 12 Oct 2022 17:42:01 +0200 Subject: [PATCH] Kernel: Add an exec() syscall Very bare-bones for now. Doesn't support arguments or environment (we don't have that stuff right now), and the executable is not a valid ELF, it terminates the task. But it's a start! --- kernel/include/sys/Syscall.h | 4 +- kernel/include/thread/Scheduler.h | 2 + kernel/src/sys/Syscall.cpp | 1 + kernel/src/sys/exec.cpp | 67 +++++++++++++++++++++++++++++++ kernel/src/thread/Scheduler.cpp | 5 --- 5 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 kernel/src/sys/exec.cpp diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index 91f265f7..9d9a1ce8 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -15,6 +15,7 @@ #define SYS_read 10 #define SYS_close 11 #define SYS_seek 12 +#define SYS_exec 13 namespace Syscall { @@ -33,4 +34,5 @@ void sys_munmap(Context* context, void* address, size_t size); void sys_open(Context* context, const char* filename, int flags); void sys_read(Context* context, int fd, size_t size, char* buffer); void sys_close(Context* context, int fd); -void sys_seek(Context* context, int fd, long offset, int whence); \ No newline at end of file +void sys_seek(Context* context, int fd, long offset, int whence); +void sys_exec(Context* context, const char* pathname); \ No newline at end of file diff --git a/kernel/include/thread/Scheduler.h b/kernel/include/thread/Scheduler.h index 9621cf47..0b0321ac 100644 --- a/kernel/include/thread/Scheduler.h +++ b/kernel/include/thread/Scheduler.h @@ -1,6 +1,8 @@ #pragma once #include "thread/Task.h" +#define TASK_PAGES_IN_STACK 4 + namespace Scheduler { void init(); diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index 14c79802..5c3a21e6 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -21,6 +21,7 @@ void Syscall::entry(Context* context) case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break; case SYS_close: sys_close(context, (int)context->rdi); break; case SYS_seek: sys_seek(context, (int)context->rdi, (long)context->rsi, (int)context->rdx); break; + case SYS_exec: sys_exec(context, (const char*)context->rdi); default: context->rax = -ENOSYS; break; } } \ No newline at end of file diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp new file mode 100644 index 00000000..f7a81160 --- /dev/null +++ b/kernel/src/sys/exec.cpp @@ -0,0 +1,67 @@ +#define MODULE "exec" + +#include "assert.h" +#include "errno.h" +#include "interrupts/Interrupts.h" +#include "memory/MemoryManager.h" +#include "sys/elf/ELFLoader.h" +#include "thread/Scheduler.h" + +void sys_exec(Context* context, const char* pathname) +{ + if (!pathname) + { + context->rax = -EINVAL; // FIXME: This should probably return EFAULT. + return; + } + + kinfoln("exec(): executing %s", pathname); + + VFS::Node* program = VFS::resolve_path(pathname); + if (!program) + { + context->rax = -ENOENT; + return; + } + + uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); + if (!allocated_stack) + { + context->rax = -ENOMEM; + return; + } + + // FIXME: This should be done later, but since there is a very high chance that loading the executed program's ELF + // image will overwrite ours, we have to do it here. + ELFLoader::release_elf_image(Scheduler::current_task()->image); + + // FIXME: Check the ELF image is valid before loading it into memory. This will allow us to first check, then free + // the previous image, then load, which should reduce the chances of loading failing to almost zero. + ELFImage* image = ELFLoader::load_elf_from_filesystem(pathname); + if (!image) + { + MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK); + Scheduler::current_task()->image = nullptr; + kwarnln("exec(): ERROR: Failed to load program. Previous program has already been freed, thus cannot " + "return to it."); + return Scheduler::task_exit(context, -255); + } + + Interrupts::disable(); + ASSERT(!Interrupts::are_enabled()); // This part is pretty sensitive. + + Task* task = Scheduler::current_task(); + ASSERT(task); + + // At this point, pretty much nothing can fail. + + MemoryManager::release_pages((void*)task->allocated_stack, TASK_PAGES_IN_STACK); + + task->allocated_stack = allocated_stack; + + Scheduler::reset_task(task, image); + + set_context_from_task(*task, context); + + return; +} \ No newline at end of file diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 4b8d2426..05136040 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -15,8 +15,6 @@ #include "thread/PIT.h" #include "thread/Task.h" -#define TASK_PAGES_IN_STACK 4 - static uint64_t task_num = 0; static Task idle_task; @@ -160,15 +158,12 @@ void Scheduler::reset_task(Task* task, ELFImage* new_image) task->state = task->Running; task->regs.rip = new_image->entry; task->image = new_image; - task->allocated_stack = (uint64_t)MemoryManager::get_pages( - TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right? task->regs.rsp = Utilities::get_top_of_stack(task->allocated_stack, TASK_PAGES_IN_STACK); task->regs.cs = 0x18 | 0x03; task->regs.ss = 0x20 | 0x03; task->regs.ds = 0x20 | 0x03; task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts task->task_sleep = 0; - task->task_time = 0; task->cpu_time = 0; kinfoln("Resetting task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", task->regs.rip, task->id, task->regs.rsp, task_num);