diff --git a/apps/src/init.c b/apps/src/init.c index d46dc841..320af7af 100644 --- a/apps/src/init.c +++ b/apps/src/init.c @@ -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; } diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index adc6c060..48648926 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -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); \ No newline at end of file diff --git a/kernel/include/thread/Scheduler.h b/kernel/include/thread/Scheduler.h index ed2d7533..832dec01 100644 --- a/kernel/include/thread/Scheduler.h +++ b/kernel/include/thread/Scheduler.h @@ -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); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 13030466..468ebcc3 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -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"); diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index eb4f66e8..e04ce8b2 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -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(); diff --git a/kernel/src/sys/elf/ELFLoader.cpp b/kernel/src/sys/elf/ELFLoader.cpp index 0dd659ae..24ca582a 100644 --- a/kernel/src/sys/elf/ELFLoader.cpp +++ b/kernel/src/sys/elf/ELFLoader.cpp @@ -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; } diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index aed792f2..74cf52a4 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -103,5 +103,27 @@ void sys_exec(Context* context, const char* pathname) 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; } \ No newline at end of file diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 3544bb9d..0896e5ce 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -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) diff --git a/libs/libc/include/luna.h b/libs/libc/include/luna.h index cfcfafb4..758574af 100644 --- a/libs/libc/include/luna.h +++ b/libs/libc/include/luna.h @@ -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 diff --git a/libs/libc/include/luna/syscall.h b/libs/libc/include/luna/syscall.h index a6a86df5..81414fba 100644 --- a/libs/libc/include/luna/syscall.h +++ b/libs/libc/include/luna/syscall.h @@ -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 diff --git a/libs/libc/src/luna.cpp b/libs/libc/src/luna.cpp index 7c7af050..c8576179 100644 --- a/libs/libc/src/luna.cpp +++ b/libs/libc/src/luna.cpp @@ -22,4 +22,9 @@ extern "C" fputs(message, stderr); abort(); } + + pid_t spawn(const char* pathname) + { + return syscall(SYS_spawn, pathname); + } } \ No newline at end of file diff --git a/libs/libc/src/unistd.cpp b/libs/libc/src/unistd.cpp index 6e5d4490..25c42e09 100644 --- a/libs/libc/src/unistd.cpp +++ b/libs/libc/src/unistd.cpp @@ -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: {