Kernel, libc: Implement spawn()

This function is a Luna alternative to fork() and exec().

Why? Simply because I can't figure out for the life of me how to implement a working fork().

So meanwhile, we have spawn() as a replacement. exec() still exists, though.
This commit is contained in:
apio 2022-10-16 18:48:35 +02:00
parent a1146a5ce2
commit 9b39d618de
12 changed files with 79 additions and 19 deletions

View File

@ -152,7 +152,21 @@ int main()
return 1;
}
printf("Success!!");
printf("Success!!\n");
const char* execpath = "/bin/sym";
printf("Spawning %s\n", execpath);
pid_t child;
if ((child = spawn(execpath)) < 0)
{
perror("spawn");
return 1;
}
printf("Success!! Got PID %ld\n", child);
return 1;
}

View File

@ -18,7 +18,8 @@
#define SYS_fcntl 13
#define SYS_mprotect 14
#define SYS_clock 15
#define SYS_mkdir 17 // 16 is reserved for fork(), which is WIP in a pull request
#define SYS_spawn 16
#define SYS_mkdir 17
namespace Syscall
{
@ -43,4 +44,5 @@ void sys_exec(Context* context, const char* pathname);
void sys_fcntl(Context* context, int fd, int command, uintptr_t arg);
void sys_mprotect(Context* context, void* address, size_t size, int prot);
void sys_clock(Context* context);
void sys_spawn(Context* context, const char* pathname);
void sys_mkdir(Context* context, const char* filename);

View File

@ -13,7 +13,7 @@ namespace Scheduler
Task* create_user_task();
void load_user_task(const char* filename);
long load_user_task(const char* filename);
void task_exit(Context* context, int64_t status);
void task_misbehave(Context* context, int64_t status);

View File

@ -77,9 +77,9 @@ extern "C" void _start()
});
#ifdef RUN_TEST_AS_INIT
Scheduler::load_user_task(STRINGIZE_VALUE_OF(RUN_TEST_AS_INIT));
ASSERT(Scheduler::load_user_task(STRINGIZE_VALUE_OF(RUN_TEST_AS_INIT)) > 0);
#else
Scheduler::load_user_task("/bin/init");
ASSERT(Scheduler::load_user_task("/bin/init") > 0);
#endif
kinfoln("Prepared scheduler tasks");

View File

@ -28,6 +28,7 @@ void Syscall::entry(Context* context)
case SYS_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
case SYS_clock: sys_clock(context); break;
case SYS_mkdir: sys_mkdir(context, (const char*)context->rdi); break;
case SYS_spawn: sys_spawn(context, (const char*)context->rdi); break;
default: context->rax = -ENOSYS; break;
}
VMM::exit_syscall_context();

View File

@ -123,40 +123,40 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
long ELFLoader::check_elf_image(VFS::Node* node)
{
Elf64_Ehdr elf_ehdr;
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0)
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < (long)sizeof(elf_ehdr))
{
kwarnln("Unable to read ELF header");
return -1;
return -ENOEXEC;
}
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
{
kwarnln("ELF file has invalid magic, skipping");
return -1;
return -ENOEXEC;
}
if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64)
{
kwarnln("ELF file is not ELF64, skipping");
return -1;
return -ENOEXEC;
}
if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
{
kwarnln("ELF file is not little-endian, skipping");
return -1;
return -ENOEXEC;
}
if (elf_ehdr.e_type != ET_EXEC)
{
kwarnln("not supported: ELF file is not an executable");
return -1;
return -ENOEXEC;
}
if (elf_ehdr.e_machine != EM_MACH)
{
kwarnln("Unsupported target machine");
return -1;
return -ENOEXEC;
}
if (elf_ehdr.e_phnum == 0)
{
kwarnln("ELF file has no PHDRS");
return -1;
return -ENOEXEC;
}
int i;
int loadable_sections = 0;
@ -170,12 +170,12 @@ long ELFLoader::check_elf_image(VFS::Node* node)
if (!phdr.p_vaddr)
{
kerrorln("segment address is NULL, this is invalid :(");
return -1;
return -ENOEXEC;
}
if (Memory::is_kernel_address(phdr.p_vaddr) || Memory::is_kernel_address(phdr.p_vaddr + phdr.p_memsz))
{
kerrorln("trying to load ELF into kernel memory");
return -1;
return -ENOEXEC;
}
loadable_sections++;
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE;
@ -184,7 +184,7 @@ long ELFLoader::check_elf_image(VFS::Node* node)
if (!loadable_sections)
{
kwarnln("No loadable sections");
return -1;
return -ENOEXEC;
}
return memusage;
}

View File

@ -105,3 +105,25 @@ void sys_exec(Context* context, const char* pathname)
return;
}
void sys_spawn(Context* context, const char* pathname)
{
char* kpathname = Syscall::strdup_from_user(pathname);
if (!kpathname)
{
context->rax = -EFAULT;
return;
}
kinfoln("spawn(): spawning %s", kpathname);
Task* current_task = Scheduler::current_task();
context->rax = Scheduler::load_user_task(kpathname);
kfree(kpathname);
VMM::switch_to_user_address_space(current_task->address_space);
return;
}

View File

@ -1,10 +1,12 @@
#define MODULE "sched"
#include "thread/Scheduler.h"
#include "errno.h"
#include "interrupts/Interrupts.h"
#include "kassert.h"
#include "log/Log.h"
#include "memory/MemoryManager.h"
#include "memory/PMM.h"
#include "memory/VMM.h"
#include "misc/hang.h"
#include "misc/utils.h"
@ -109,16 +111,18 @@ Task* Scheduler::create_user_task()
return new_task;
}
void Scheduler::load_user_task(const char* filename)
long Scheduler::load_user_task(const char* filename)
{
kinfoln("Loading user task: %s", filename);
Interrupts::push_and_disable();
if (ELFLoader::check_elf_image_from_filesystem(filename) < 0)
long result;
if ((result = ELFLoader::check_elf_image_from_filesystem(filename)) < 0)
{
kerrorln("Failed to load %s from initrd", filename);
Interrupts::pop();
return;
return result;
}
if ((uint64_t)result > PMM::get_free()) { return -ENOMEM; }
Task* new_task = new Task;
ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context));
@ -134,6 +138,12 @@ void Scheduler::load_user_task(const char* filename)
new_task->image = image;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
if (!new_task->allocated_stack)
{
delete new_task;
ELFLoader::release_elf_image(image);
return -ENOMEM;
}
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
new_task->regs.cs = 0x18 | 0x03;
new_task->regs.ss = 0x20 | 0x03;
@ -153,6 +163,7 @@ void Scheduler::load_user_task(const char* filename)
new_task->id, new_task->regs.rsp, task_num);
VMM::switch_back_to_kernel_address_space();
Interrupts::pop();
return (long)new_task->id;
}
void Scheduler::reset_task(Task* task, ELFImage* new_image)

View File

@ -18,6 +18,9 @@ extern "C"
/* Prints a message to standard error and aborts the program. */
__lc_noreturn void __luna_abort(const char* message);
/* Creates a new process from the executable at pathname and returns its PID. */
pid_t spawn(const char* pathname);
#ifdef __cplusplus
}
#endif

View File

@ -17,6 +17,7 @@
#define SYS_fcntl 13
#define SYS_mprotect 14
#define SYS_clock 15
#define SYS_spawn 16
#define SYS_mkdir 17
#ifndef __want_syscalls

View File

@ -22,4 +22,9 @@ extern "C"
fputs(message, stderr);
abort();
}
pid_t spawn(const char* pathname)
{
return syscall(SYS_spawn, pathname);
}
}

View File

@ -39,6 +39,7 @@ extern "C"
case SYS_close:
case SYS_exec:
case SYS_mkdir:
case SYS_spawn:
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
case SYS_munmap:
case SYS_open: {