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!
This commit is contained in:
parent
34c35163e4
commit
f8b3567042
@ -15,6 +15,7 @@
|
|||||||
#define SYS_read 10
|
#define SYS_read 10
|
||||||
#define SYS_close 11
|
#define SYS_close 11
|
||||||
#define SYS_seek 12
|
#define SYS_seek 12
|
||||||
|
#define SYS_exec 13
|
||||||
|
|
||||||
namespace Syscall
|
namespace Syscall
|
||||||
{
|
{
|
||||||
@ -34,3 +35,4 @@ void sys_open(Context* context, const char* filename, int flags);
|
|||||||
void sys_read(Context* context, int fd, size_t size, char* buffer);
|
void sys_read(Context* context, int fd, size_t size, char* buffer);
|
||||||
void sys_close(Context* context, int fd);
|
void sys_close(Context* context, int fd);
|
||||||
void sys_seek(Context* context, int fd, long offset, int whence);
|
void sys_seek(Context* context, int fd, long offset, int whence);
|
||||||
|
void sys_exec(Context* context, const char* pathname);
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "thread/Task.h"
|
#include "thread/Task.h"
|
||||||
|
|
||||||
|
#define TASK_PAGES_IN_STACK 4
|
||||||
|
|
||||||
namespace Scheduler
|
namespace Scheduler
|
||||||
{
|
{
|
||||||
void init();
|
void init();
|
||||||
|
@ -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_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_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_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;
|
default: context->rax = -ENOSYS; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
67
kernel/src/sys/exec.cpp
Normal file
67
kernel/src/sys/exec.cpp
Normal file
@ -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;
|
||||||
|
}
|
@ -15,8 +15,6 @@
|
|||||||
#include "thread/PIT.h"
|
#include "thread/PIT.h"
|
||||||
#include "thread/Task.h"
|
#include "thread/Task.h"
|
||||||
|
|
||||||
#define TASK_PAGES_IN_STACK 4
|
|
||||||
|
|
||||||
static uint64_t task_num = 0;
|
static uint64_t task_num = 0;
|
||||||
|
|
||||||
static Task idle_task;
|
static Task idle_task;
|
||||||
@ -160,15 +158,12 @@ void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
|||||||
task->state = task->Running;
|
task->state = task->Running;
|
||||||
task->regs.rip = new_image->entry;
|
task->regs.rip = new_image->entry;
|
||||||
task->image = new_image;
|
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.rsp = Utilities::get_top_of_stack(task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
task->regs.cs = 0x18 | 0x03;
|
task->regs.cs = 0x18 | 0x03;
|
||||||
task->regs.ss = 0x20 | 0x03;
|
task->regs.ss = 0x20 | 0x03;
|
||||||
task->regs.ds = 0x20 | 0x03;
|
task->regs.ds = 0x20 | 0x03;
|
||||||
task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
|
task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
|
||||||
task->task_sleep = 0;
|
task->task_sleep = 0;
|
||||||
task->task_time = 0;
|
|
||||||
task->cpu_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,
|
kinfoln("Resetting task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", task->regs.rip, task->id,
|
||||||
task->regs.rsp, task_num);
|
task->regs.rsp, task_num);
|
||||||
|
Loading…
Reference in New Issue
Block a user