#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*);
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)