From 5ab0c12d54e7f47a64b476d41a7ed15104f79f5c Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Sun, 30 Jun 2024 23:57:39 -0400 Subject: kernel: exec: Add execve() Signed-off-by: Ian Moffett --- sys/include/sys/exec.h | 8 ++++ sys/include/sys/proc.h | 2 + sys/kern/kern_exec.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ sys/kern/kern_sched.c | 46 +++++++++++++++++++-- 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 sys/kern/kern_exec.c diff --git a/sys/include/sys/exec.h b/sys/include/sys/exec.h index 73eae01..f5fac97 100644 --- a/sys/include/sys/exec.h +++ b/sys/include/sys/exec.h @@ -72,7 +72,15 @@ struct exec_prog { char **envp; }; +struct execve_args { + const char *pathname; + char **argv; + char **envp; +}; + +int execve(struct proc *td, const struct execve_args *args); int elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog); + void elf_unload(struct proc *td, struct exec_prog *prog); void setregs(struct proc *td, struct exec_prog *prog, uintptr_t stack); diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h index 0c3cd33..ba931f3 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -56,6 +56,8 @@ struct proc { }; #define PROC_EXITING BIT(0) /* Exiting */ +#define PROC_EXEC BIT(1) /* Exec called (cleared by sched) */ +#define PROC_INEXEC BIT(2) /* Exec in progress */ struct proc *this_td(void); int md_fork(struct proc *p, struct proc *parent, uintptr_t ip); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c new file mode 100644 index 0000000..b21404b --- /dev/null +++ b/sys/kern/kern_exec.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Release the memory of the old stack + * + * @td: Target thread. + */ +static void +release_stack(struct proc *td) +{ + struct pcb *pcbp = &td->pcb; + uintptr_t base = td->stack_base; + + if (base >= VM_HIGHER_HALF) { + base -= VM_HIGHER_HALF; + vm_free_frame(base, PROC_STACK_PAGES); + } else { + vm_unmap(pcbp->addrsp, base, PROC_STACK_SIZE); + vm_free_frame(base, PROC_STACK_PAGES); + } +} + +int +execve(struct proc *td, const struct execve_args *args) +{ + int error; + struct exec_prog prog; + struct pcb *pcbp = &td->pcb; + uintptr_t stack_top, stack; + + if (td == NULL || args == NULL) + return -EINVAL; + if ((error = elf64_load(args->pathname, td, &prog)) != 0) + return error; + + /* Mark the thread as running exec */ + td->flags |= (PROC_EXEC | PROC_INEXEC); + + /* Create the new stack */ + stack = vm_alloc_frame(PROC_STACK_PAGES); + if (stack == 0) { + elf_unload(td, &prog); + return -ENOMEM; + } + + /* Release the old stack if it exists */ + if (td->stack_base != 0) + release_stack(td); + + /* Set new stack and map it to userspace */ + td->stack_base = stack; + vm_map(pcbp->addrsp, td->stack_base, td->stack_base, + (PROT_READ | PROT_WRITE | PROT_USER), PROC_STACK_SIZE); + + prog.argp = args->argv; + prog.envp = args->envp; + stack_top = td->stack_base + (PROC_STACK_SIZE - 1); + + /* Setup registers and stack */ + md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog); + setregs(td, &prog, stack_top); + + /* + * Done, reset flags and yield + * + * XXX: PROC_EXEC is unset by the scheduler so it + * can properly start the new exec'd program. + */ + td->flags &= ~PROC_INEXEC; + sched_enter(); +} diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index 9df541e..278eba2 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -74,6 +74,24 @@ sched_oneshot(bool now) timer.oneshot_us(usec); } +/* + * Save thread state and enqueue it back into one + * of the ready queues. + */ +static void +sched_save_td(struct proc *td, struct trapframe *tf) +{ + /* + * Save trapframe to process structure only + * if PROC_EXEC is not set. + */ + if (!ISSET(td->flags, PROC_EXEC)) { + memcpy(&td->tf, tf, sizeof(td->tf)); + } + + sched_enqueue_td(td); +} + static struct proc * sched_dequeue_td(void) { @@ -137,6 +155,21 @@ sched_switch(struct trapframe *tf) ci = this_cpu(); td = ci->curtd; + if (td != NULL) { + inexec = ISSET(td->flags, PROC_INEXEC); + + /* + * If both PROC_INEXEC and PROC_EXEC are set, + * an exec is in progress. However, if PROC_INEXEC is + * unset and PROC_EXEC is set, an exec has completed + * and we can unset PROC_EXEC and copy the new trapframe. + */ + if (ISSET(td->flags, PROC_EXEC) && !inexec) { + memcpy(tf, &td->tf, sizeof(*tf)); + td->flags &= ~PROC_EXEC; + } + } + /* * Get the next thread and use it only if it isn't * in the middle of an exit, exec, or whatever. @@ -147,6 +180,14 @@ sched_switch(struct trapframe *tf) return; } + /* + * If we are in the middle of an exec, don't use this + * thread. + */ + if (ISSET(next_td->flags, PROC_EXEC)) { + use_current = false; + } + /* * Don't use this thread if we are currently * exiting. @@ -156,10 +197,9 @@ sched_switch(struct trapframe *tf) } } while (!use_current); - /* Re-enqueue the old thread */ + /* Save the previous thread */ if (td != NULL) { - memcpy(&td->tf, tf, sizeof(td->tf)); - sched_enqueue_td(td); + sched_save_td(td, tf); } memcpy(tf, &next_td->tf, sizeof(*tf)); -- cgit v1.2.3