summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-06-30 23:57:39 -0400
committerIan Moffett <ian@osmora.org>2024-07-01 00:01:41 -0400
commit5ab0c12d54e7f47a64b476d41a7ed15104f79f5c (patch)
treef659411bb964561c9b5cea780b9c503332711fd1
parenta0540658f66feb32c1abe71d9d7589b854e9aaa5 (diff)
kernel: exec: Add execve()
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r--sys/include/sys/exec.h8
-rw-r--r--sys/include/sys/proc.h2
-rw-r--r--sys/kern/kern_exec.c108
-rw-r--r--sys/kern/kern_sched.c46
4 files changed, 161 insertions, 3 deletions
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 <sys/exec.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <vm/vm.h>
+#include <vm/map.h>
+#include <vm/physmem.h>
+#include <machine/pcb.h>
+
+/*
+ * 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.
@@ -148,6 +181,14 @@ sched_switch(struct trapframe *tf)
}
/*
+ * 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));