From: Matthias Kruk Date: Mon, 4 May 2020 08:28:53 +0000 (+0900) Subject: kernel/core: Implement sys_execeve() syscall handler X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=622f9b17668f3f0241a3b0b3e44f5c210b31c4e7;p=corax kernel/core: Implement sys_execeve() syscall handler --- diff --git a/kernel/core/posixcall.c b/kernel/core/posixcall.c index 1bdec66..5186bf8 100644 --- a/kernel/core/posixcall.c +++ b/kernel/core/posixcall.c @@ -5,11 +5,13 @@ #include #include #include +#include #include #include #include #include #include +#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)