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:
parent
a1146a5ce2
commit
9b39d618de
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -105,3 +105,25 @@ void sys_exec(Context* context, const char* pathname)
|
|||||||
|
|
||||||
return;
|
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;
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
@ -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: {
|
||||||
|
Loading…
Reference in New Issue
Block a user