]> git.corax.cc Git - corax/commitdiff
kernel/core: Implement sys_execeve() syscall handler
authorMatthias Kruk <m@m10k.eu>
Mon, 4 May 2020 08:28:53 +0000 (17:28 +0900)
committerMatthias Kruk <m@m10k.eu>
Mon, 4 May 2020 08:28:53 +0000 (17:28 +0900)
kernel/core/posixcall.c

index 1bdec663eba33f029823c6f2b86f061c65d5a249..5186bf87c5d02235c80bfcaea7cf1fbd99f6e970 100644 (file)
@@ -5,11 +5,13 @@
 #include <corax/errno.h>
 #include <corax/syscall.h>
 #include <corax/types.h>
+#include <sys/mman.h>
 #include <arch.h>
 #include <process.h>
 #include <debug.h>
 #include <string.h>
 #include <sched.h>
+#include "elf.h"
 
 int sys_sigaction(stack_frame_t*);
 
@@ -293,20 +295,167 @@ int sys_execfve(stack_frame_t *stk)
        return(ret_val);
 }
 
+static int _load_elf_section(struct elf_hdr *hdr, struct elf_phdr *phdr, void *data)
+{
+       int ret_val;
+       pg_dir_t *pd;
+       void *vptr;
+       u32_t vaddr;
+       u32_t vsize;
+       int prot;
+
+       pd = (pg_dir_t*)data;
+       ret_val = -EINVAL;
+       prot = 0;
+
+       switch(phdr->p_type) {
+       case PT_LOAD:
+               /* loadable segment */
+
+               if(ELF_PHDR_R(phdr)) {
+                       prot |= PROT_READ;
+               }
+
+               if(ELF_PHDR_W(phdr)) {
+                       prot |= PROT_WRITE;
+               }
+
+               if(ELF_PHDR_X(phdr)) {
+                       prot |= PROT_EXEC;
+               }
+
+               /*
+                * ALIGN() achieves alignment by rounding the address up, but in
+                * the case of vaddr we want to round down to the alignment. On the
+                * other hand vsize must be rounded up.
+                */
+               vaddr = phdr->p_vaddr & ~(phdr->p_align - 1);
+               vsize = ALIGN(phdr->p_memsz, phdr->p_align);
+
+               ret_val = pg_dir_mmap(pd, (void*)vaddr, vsize, prot, 0, &vptr);
+
+               if(ret_val < 0) {
+                       break;
+               }
+
+               /* copy the ELF segment */
+               pg_dir_memcpy(pd, (void*)phdr->p_vaddr,
+                             NULL, (char*)hdr + phdr->p_offset,
+                             phdr->p_filesz);
+
+               /*
+                * If the size in memory is larger than the size in the ELF file,
+                * we have to zero the difference (think .BSS and such)
+                */
+               if(phdr->p_memsz > phdr->p_filesz) {
+                       pg_dir_memset(pd, (void*)(phdr->p_vaddr + phdr->p_filesz), 0,
+                                     phdr->p_filesz - phdr->p_memsz);
+               }
+
+               break;
+
+       default:
+               ret_val = 0;
+               break;
+       }
+
+       return(ret_val);
+}
+
 int sys_execeve(stack_frame_t *stk)
 {
-/*
+       int ret_val;
+       process_t *cproc;
+       task_t *ctask;
+       pg_dir_t *new_pd;
+       pg_dir_t *old_pd;
+       void *old_stack;
+       void *new_stack;
+       void *usr_stack;
+
        void *elfdata;
        size_t elfsize;
        char **argv;
        char **envp;
 
-       elfdata = stk->ebx;
-       elfsize = stk->ecx;
+       elfdata = (void*)stk->ebx;
+       elfsize = (size_t)stk->ecx;
        argv = (char**)stk->edx;
        envp = (char**)stk->esi;
-*/
-       return(-ENOSYS);
+       new_pd = NULL;
+
+       ret_val = elf_validate(elfdata, elfsize);
+
+       if(ret_val < 0) {
+               goto cleanup;
+       }
+
+       cproc = process_get_current();
+       ctask = task_get_current();
+
+       ret_val = pg_dir_create(&new_pd);
+
+       if(ret_val < 0) {
+               goto cleanup;
+       }
+
+       ret_val = elf_phdr_foreach(elfdata, elfsize, _load_elf_section, new_pd);
+
+       if(ret_val < 0) {
+               goto cleanup;
+       }
+
+       process_get_pagedir(cproc, &old_pd);
+       process_set_pagedir(cproc, new_pd);
+
+       /*
+        * Update CR3 in the stack frame so we switch to the correct
+        * page directory when returning from the syscall
+        */
+       stk->cr3 = (u32_t)pg_dir_get_pdbr(new_pd);
+
+       /*
+        * The kernel is currently executing on the kernel stack inside the
+        * page directory that we're about to free. We have to copy the old
+        * stack to the new one and update ESP, EBP.
+        */
+       old_stack = ctask->t_kstack;
+       new_stack = pg_dir_get_kstack(new_pd);
+
+       /* FIXME: Stack size may not be static */
+       usr_stack = (void*)((u32_t)pg_dir_get_ustack(new_pd) +
+                        CONFIG_USER_STACK_SIZE);
+
+       /* prepare the stack frame for the function */
+       usr_stack = setstackvfe(usr_stack, argv, envp);
+
+       /* make the task execute the new function upon return from the interrupt */
+       stk->eip = (u32_t)elf_entry(elfdata);
+       stk->ebp = (u32_t)usr_stack;
+       stk->esp = (u32_t)usr_stack;
+       stk->prevesp = (u32_t)usr_stack;
+
+       /*
+        * task_move_stack() will move the current stack and update the
+        * stack pointers. When this function returns, we will be executing
+        * on the kernel-mode stack from the new page directory.
+        */
+       task_move_stack(new_stack, old_stack, CONFIG_KERNEL_STACK_SIZE);
+
+       /* FIXME: Clean up the old page directory */
+
+       ret_val = 0;
+
+cleanup:
+       if(ret_val < 0) {
+               /* need to clean something up */
+
+               if(new_pd) {
+                       /* FIXME: Clean up the new page directory */
+               }
+       }
+
+       return(ret_val);
 }
 
 int sys_vfork(void)