From 2cfcba7e6ed64a00ca01dcc7247ecfc5edacfdb2 Mon Sep 17 00:00:00 2001
From: Ian Moffett <ian@osmora.org>
Date: Wed, 13 Mar 2024 21:08:50 -0400
Subject: kernel: sched: Improve stack init code

Signed-off-by: Ian Moffett <ian@osmora.org>
---
 sys/include/sys/loader.h |  27 +++++----
 sys/kern/kern_sched.c    | 139 +++++++++++++++++++++++++----------------------
 2 files changed, 91 insertions(+), 75 deletions(-)

diff --git a/sys/include/sys/loader.h b/sys/include/sys/loader.h
index c1aa426..74a325c 100644
--- a/sys/include/sys/loader.h
+++ b/sys/include/sys/loader.h
@@ -33,17 +33,22 @@
 #include <sys/types.h>
 #include <vm/pmap.h>
 
-#define AT_NULL   0
-#define AT_IGNORE 1
-#define AT_EXECFD 2
-#define AT_PHDR   3
-#define AT_PHENT  4
-#define AT_PHNUM  5
-#define AT_PAGESZ 6
-#define AT_BASE   7
-#define AT_FLAGS  8
-#define AT_ENTRY  9
-#define AT_SECURE 10
+/* DANGER!: DO NOT CHANGE THESE DEFINES */
+#define AT_NULL 0
+#define AT_ENTRY 1
+#define AT_PHDR 2
+#define AT_PHENT 3
+#define AT_PHNUM 4
+#define AT_EXECPATH 5
+#define AT_SECURE 6
+#define AT_RANDOM 7
+#define AT_EXECFN 8
+
+#define STACK_PUSH(ptr, val) *(--ptr) = val
+#define AUXVAL(ptr, tag, val) __extension__ ({  \
+    STACK_PUSH(ptr, val);                       \
+    STACK_PUSH(ptr, tag);                       \
+});
 
 /* Auxiliary Vector */
 struct auxval {
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index 9231ea2..7be8241 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -48,6 +48,20 @@
 #define STACK_PAGES 8
 #define STACK_SIZE (STACK_PAGES*vm_get_page_size())
 
+/*
+ * The PHYS_TO_VIRT/VIRT_TO_PHYS macros convert
+ * addresses to lower and higher half addresses.
+ * Userspace addresses are on the lower half,
+ * therefore, we can just wrap over these to
+ * keep things simple.
+ *
+ * XXX: TODO: This won't work when not identity mapping
+ *            lowerhalf addresses. Once that is updated,
+ *            get rid of this.
+ */
+#define USER_TO_KERN(user) PHYS_TO_VIRT(user)
+#define KERN_TO_USER(kern) VIRT_TO_PHYS(kern)
+
 /*
  * Thread ready queue - all threads ready to be
  * scheduled should be added to this queue.
@@ -133,86 +147,87 @@ static uintptr_t
 sched_init_stack(void *stack_top, char *argvp[], char *envp[], struct auxval auxv)
 {
     uintptr_t *sp = stack_top;
-    size_t envp_len, argv_len, len;
-    uintptr_t old_sp = 0;
-    uintptr_t ret;
-
-    for (envp_len = 0; envp[envp_len] != NULL; ++envp_len) {
-        len = strlen(envp[envp_len]);
-        sp = (void *)((uintptr_t)sp - len - 1);
-        memcpy(sp, envp[envp_len], len);
+    void *env_ptr = NULL, *argv_ptr = NULL;
+    size_t argc, envc, len;
+
+    /* Copy argument and environment strings */
+    for (envc = 0; envp[envc] != NULL; ++envc) {
+        len = strlen(envp[envc]);
+        sp -= len - 1;
+        memcpy(sp, envp[envc], len);
     }
 
-    for (argv_len = 0; argvp[argv_len] != NULL; ++argv_len) {
-        len = strlen(argvp[argv_len]);
-        sp = (void *)((uintptr_t)sp - len - 1);
-        memcpy(sp, argvp[envp_len], len);
-    }
+    __assert(envc >= 1);
+    env_ptr = sp;
 
-    sp = (uint64_t *)(((uintptr_t)sp / 16) * 16);
-    if (((argv_len + envp_len + 1) & 1) != 0) {
-        sp--;
+    for (argc = 0; argvp[argc] != NULL; ++argc) {
+        len = strlen(argvp[argc]);
+        sp -= len - 1;
+        memcpy(sp, argvp[argc], len);
     }
 
-    *--sp = 0;
-    *--sp = 0;
-    *--sp = auxv.at_entry;
-    *--sp = AT_ENTRY;
-    *--sp = auxv.at_phent;
-    *--sp = AT_PHENT;
-    *--sp = auxv.at_phnum;
-    *--sp = AT_PHNUM;
-    *--sp = auxv.at_phdr;
-    *--sp = AT_PHDR;
-
-    old_sp = (uintptr_t)sp;
-
-    /* End of envp */
-    *--sp = 0;
-    sp -= envp_len;
-    for (int i = 0; i < envp_len; ++i) {
-        old_sp -= strlen(envp[i]) + 1;
-        sp[i] = old_sp;
+    __assert(argc >= 1);
+    argv_ptr = sp;
+
+    /* Ensure the stack is aligned */
+    sp = (void *)__ALIGN_DOWN((uintptr_t)sp, 16);
+    if (((argc + envc + 1) & 1) != 0)
+        --sp;
+
+    AUXVAL(sp, AT_NULL, 0x0);
+    AUXVAL(sp, AT_SECURE, 0x0);
+    AUXVAL(sp, AT_ENTRY, auxv.at_entry);
+    AUXVAL(sp, AT_PHDR, auxv.at_phdr);
+    AUXVAL(sp, AT_PHNUM, auxv.at_phnum);
+    STACK_PUSH(sp, 0);
+
+    /* Push environment string pointers */
+    for (int i = 0; i < envc; ++i) {
+        len = strlen(env_ptr);
+        sp -= len;
+
+        *sp = (uintptr_t)KERN_TO_USER((uintptr_t)env_ptr);
+        env_ptr = (char *)env_ptr + len;
     }
 
-    /* End of argvp */
-    *--sp = 0;
-    sp -= argv_len;
-    for (int i = 0; i < argv_len; ++i) {
-        old_sp -= strlen(argvp[i]) + 1;
-        sp[i] = old_sp;
+    /* Push argument string pointers */
+    STACK_PUSH(sp, 0);
+    for (int i = 0; i < argc; ++i) {
+        len = strlen(argv_ptr);
+        sp -= len;
+
+        *sp = (uintptr_t)KERN_TO_USER((uintptr_t)argv_ptr);
+        argv_ptr = (char *)argv_ptr + len;
     }
 
-    /* Argc */
-    *--sp = argv_len;
+    STACK_PUSH(sp, argc);
 
-    ret = (uintptr_t)stack_top;
-    ret -= (ret - (uintptr_t)sp);
-    return ret;
+    return (uintptr_t)sp;
 }
 
 static uintptr_t
-sched_create_stack(struct vas vas, bool user)
+sched_create_stack(struct vas vas, bool user, char *argvp[],
+                   char *envp[], struct auxval auxv)
 {
     int status;
-    uintptr_t user_stack;
+    uintptr_t stack;
     const vm_prot_t USER_STACK_PROT = PROT_WRITE | PROT_USER;
 
     if (!user) {
-        return (uintptr_t)dynalloc(STACK_SIZE);
+        stack = (uintptr_t)dynalloc(STACK_SIZE);
+        return sched_init_stack((void *)(stack + STACK_SIZE), argvp, envp, auxv);
     }
 
-    user_stack = vm_alloc_pageframe(STACK_PAGES);
-
-    status = vm_map_create(vas, user_stack, user_stack, USER_STACK_PROT,
-                           STACK_SIZE);
+    stack = vm_alloc_pageframe(STACK_PAGES);
+    status = vm_map_create(vas, stack, stack, USER_STACK_PROT, STACK_SIZE);
 
     if (status != 0) {
         return 0;
     }
 
-    memset(PHYS_TO_VIRT(user_stack), 0, STACK_SIZE);
-    return user_stack;
+    memset(USER_TO_KERN(stack), 0, STACK_SIZE);
+    stack = sched_init_stack((void *)USER_TO_KERN(stack + STACK_SIZE), argvp, envp, auxv);
+    return stack;
 }
 
 static struct proc *
@@ -220,8 +235,7 @@ sched_create_td(uintptr_t rip, char *argvp[], char *envp[], struct auxval auxv,
                 struct vas vas, bool is_user)
 {
     struct proc *td;
-    uintptr_t stack, sp;
-    void *stack_virt;
+    uintptr_t stack;
     struct trapframe *tf;
 
     tf = dynalloc(sizeof(struct trapframe));
@@ -229,14 +243,12 @@ sched_create_td(uintptr_t rip, char *argvp[], char *envp[], struct auxval auxv,
         return NULL;
     }
 
-    stack = sched_create_stack(vas, is_user);
+    stack = sched_create_stack(vas, is_user, argvp, envp, auxv);
     if (stack == 0) {
         dynfree(tf);
         return NULL;
     }
 
-    stack_virt = (is_user) ? PHYS_TO_VIRT(stack) : (void *)stack;
-
     td = dynalloc(sizeof(struct proc));
     if (td == NULL) {
         /* TODO: Free stack */
@@ -245,8 +257,7 @@ sched_create_td(uintptr_t rip, char *argvp[], char *envp[], struct auxval auxv,
     }
 
     memset(tf, 0, sizeof(struct trapframe));
-    sp = sched_init_stack((void *)((uintptr_t)stack_virt + STACK_SIZE),
-                          argvp, envp, auxv);
+    memset(td, 0, sizeof(struct proc));
 
     /* Setup process itself */
     td->pid = 0;        /* Don't assign PID until enqueued */
@@ -256,9 +267,9 @@ sched_create_td(uintptr_t rip, char *argvp[], char *envp[], struct auxval auxv,
 
     /* Setup trapframe */
     if (!is_user) {
-        init_frame(tf, rip, sp);
+        init_frame(tf, rip, (uintptr_t)stack);
     } else {
-        init_frame_user(tf, rip, VIRT_TO_PHYS(sp));
+        init_frame_user(tf, rip, KERN_TO_USER(stack));
     }
     return td;
 }
-- 
cgit v1.2.3