Luna/kernel/src/sys/exec.cpp

137 lines
3.8 KiB
C++

#define MODULE "exec"
#include "errno.h"
#include "interrupts/Interrupts.h"
#include "kassert.h"
#include "memory/MemoryManager.h"
#include "memory/PMM.h"
#include "memory/VMM.h"
#include "std/stdlib.h"
#include "std/string.h"
#include "sys/Syscall.h"
#include "sys/elf/ELFLoader.h"
#include "thread/Scheduler.h"
#include "utils/Addresses.h"
void sys_fork(Context* context) // FIXME: Even though both processes's address spaces are the same in content, writing
// to one should not affect the other.
{
kinfoln("fork(): attempting fork");
Task* parent = Scheduler::current_task();
Task* child = Scheduler::create_user_task();
memcpy(&child->regs, &parent->regs, sizeof(Context));
if (parent->floating_saved)
{
memcpy(child->floating_region, parent->floating_region, sizeof(parent->floating_region));
child->floating_saved = true;
}
for (int i = 0; i < TASK_MAX_FDS; i++) { child->files[i] = parent->files[i]; }
size_t stack_bytes = get_top_of_stack(parent->allocated_stack, TASK_PAGES_IN_STACK) - parent->regs.rsp;
child->regs.rsp -= stack_bytes;
memcpy((void*)child->regs.rsp, (void*)parent->regs.rsp, stack_bytes);
child->address_space = parent->address_space.clone();
child->regs.rax = 0;
context->rax = child->id;
kinfoln("fork(): forked parent %d into child %d", parent->id, child->id);
return;
}
void sys_exec(Context* context, const char* pathname)
{
char* kpathname = Syscall::strdup_from_user(pathname);
if (!kpathname)
{
context->rax = -EFAULT;
return;
}
kinfoln("exec(): executing %s", kpathname);
VFS::Node* program = VFS::resolve_path(kpathname);
if (!program)
{
kfree(kpathname);
context->rax = -ENOENT;
return;
}
if (program->type == VFS_DIRECTORY)
{
kfree(kpathname);
context->rax = -EISDIR;
return;
}
long memusage;
if ((memusage = ELFLoader::check_elf_image(program)) < 0)
{
kfree(kpathname);
context->rax = -ENOEXEC;
return;
}
uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER);
if (!allocated_stack)
{
kfree(kpathname);
context->rax = -ENOMEM;
return;
}
uint64_t allocated_stack_phys = VMM::get_physical(allocated_stack);
if ((uint64_t)memusage > PMM::get_free())
{
kfree(kpathname);
MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK);
context->rax = -ENOMEM;
return;
}
Interrupts::disable();
ASSERT(!Interrupts::are_enabled()); // This part is pretty sensitive.
Task* task = Scheduler::current_task();
ASSERT(task);
if (task->address_space.is_cloned())
{
kdbgln("Detaching cloned address space, %p, %s", (void*)task->address_space.get_pml4(),
task->address_space.is_cloned() ? "is cloned" : "is not cloned");
task->address_space.detach();
VMM::switch_to_user_address_space(task->address_space);
kdbgln("Detached cloned address space, %p, %s", (void*)task->address_space.get_pml4(),
task->address_space.is_cloned() ? "is cloned" : "is not cloned");
}
// At this point, pretty much nothing can fail.
ELFImage* image = ELFLoader::load_elf_from_vfs(program);
ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
// wrong.
task->allocated_stack = allocated_stack;
for (uint64_t i = 0; i < TASK_PAGES_IN_STACK; i++)
{
VMM::map(allocated_stack + (i * PAGE_SIZE), allocated_stack_phys + (i * PAGE_SIZE), MAP_READ_WRITE | MAP_USER);
}
Scheduler::reset_task(task, image);
set_context_from_task(*task, context);
kfree(kpathname);
return;
}