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; 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; return 1;
} }

View File

@ -18,7 +18,8 @@
#define SYS_fcntl 13 #define SYS_fcntl 13
#define SYS_mprotect 14 #define SYS_mprotect 14
#define SYS_clock 15 #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 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_fcntl(Context* context, int fd, int command, uintptr_t arg);
void sys_mprotect(Context* context, void* address, size_t size, int prot); void sys_mprotect(Context* context, void* address, size_t size, int prot);
void sys_clock(Context* context); void sys_clock(Context* context);
void sys_spawn(Context* context, const char* pathname);
void sys_mkdir(Context* context, const char* filename); void sys_mkdir(Context* context, const char* filename);

View File

@ -13,7 +13,7 @@ namespace Scheduler
Task* create_user_task(); 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_exit(Context* context, int64_t status);
void task_misbehave(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 #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 #else
Scheduler::load_user_task("/bin/init"); ASSERT(Scheduler::load_user_task("/bin/init") > 0);
#endif #endif
kinfoln("Prepared scheduler tasks"); 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_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
case SYS_clock: sys_clock(context); break; case SYS_clock: sys_clock(context); break;
case SYS_mkdir: sys_mkdir(context, (const char*)context->rdi); 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; default: context->rax = -ENOSYS; break;
} }
VMM::exit_syscall_context(); 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) long ELFLoader::check_elf_image(VFS::Node* node)
{ {
Elf64_Ehdr elf_ehdr; 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"); kwarnln("Unable to read ELF header");
return -1; return -ENOEXEC;
} }
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0) if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
{ {
kwarnln("ELF file has invalid magic, skipping"); kwarnln("ELF file has invalid magic, skipping");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64) if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64)
{ {
kwarnln("ELF file is not ELF64, skipping"); kwarnln("ELF file is not ELF64, skipping");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB) if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
{ {
kwarnln("ELF file is not little-endian, skipping"); kwarnln("ELF file is not little-endian, skipping");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_type != ET_EXEC) if (elf_ehdr.e_type != ET_EXEC)
{ {
kwarnln("not supported: ELF file is not an executable"); kwarnln("not supported: ELF file is not an executable");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_machine != EM_MACH) if (elf_ehdr.e_machine != EM_MACH)
{ {
kwarnln("Unsupported target machine"); kwarnln("Unsupported target machine");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_phnum == 0) if (elf_ehdr.e_phnum == 0)
{ {
kwarnln("ELF file has no PHDRS"); kwarnln("ELF file has no PHDRS");
return -1; return -ENOEXEC;
} }
int i; int i;
int loadable_sections = 0; int loadable_sections = 0;
@ -170,12 +170,12 @@ long ELFLoader::check_elf_image(VFS::Node* node)
if (!phdr.p_vaddr) if (!phdr.p_vaddr)
{ {
kerrorln("segment address is NULL, this is invalid :("); 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)) 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"); kerrorln("trying to load ELF into kernel memory");
return -1; return -ENOEXEC;
} }
loadable_sections++; loadable_sections++;
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE; 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) if (!loadable_sections)
{ {
kwarnln("No loadable sections"); kwarnln("No loadable sections");
return -1; return -ENOEXEC;
} }
return memusage; return memusage;
} }

View File

@ -103,5 +103,27 @@ void sys_exec(Context* context, const char* pathname)
kfree(kpathname); kfree(kpathname);
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; return;
} }

View File

@ -1,10 +1,12 @@
#define MODULE "sched" #define MODULE "sched"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "errno.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "kassert.h" #include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "misc/utils.h" #include "misc/utils.h"
@ -109,16 +111,18 @@ Task* Scheduler::create_user_task()
return new_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); kinfoln("Loading user task: %s", filename);
Interrupts::push_and_disable(); 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); kerrorln("Failed to load %s from initrd", filename);
Interrupts::pop(); Interrupts::pop();
return; return result;
} }
if ((uint64_t)result > PMM::get_free()) { return -ENOMEM; }
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context)); 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->image = image;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages( 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? 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.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
new_task->regs.cs = 0x18 | 0x03; new_task->regs.cs = 0x18 | 0x03;
new_task->regs.ss = 0x20 | 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); new_task->id, new_task->regs.rsp, task_num);
VMM::switch_back_to_kernel_address_space(); VMM::switch_back_to_kernel_address_space();
Interrupts::pop(); Interrupts::pop();
return (long)new_task->id;
} }
void Scheduler::reset_task(Task* task, ELFImage* new_image) 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. */ /* Prints a message to standard error and aborts the program. */
__lc_noreturn void __luna_abort(const char* message); __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 #ifdef __cplusplus
} }
#endif #endif

View File

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

View File

@ -22,4 +22,9 @@ extern "C"
fputs(message, stderr); fputs(message, stderr);
abort(); 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_close:
case SYS_exec: case SYS_exec:
case SYS_mkdir: case SYS_mkdir:
case SYS_spawn:
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break; case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
case SYS_munmap: case SYS_munmap:
case SYS_open: { case SYS_open: {