/** * The hook called before 'do_execve' successfully return. * What we need to do here are: * 1. create a new host container if the process doesn't have one yet; * 2. create the stack for syscall stub; * 3. unmap umUcore kernel area in the child and erasing the info in the page table; * 4. copy arguments to the user stack of the child, and free the kernel's copy; * 5. call 'userspace'. * If everything is done, the current thread becomes a monitor thread forever. * @param argc the number of arguments * @param kargv the copy of the arguments in the kernel */ int do_execve_arch_hook(int argc, char **kargv) { if (current->arch.host == NULL) { current->arch.host = kmalloc(sizeof(struct host_container)); if (current->arch.host == NULL) return -1; } void *stub_stack = boot_alloc_page(); if (stub_stack == NULL) goto free_host; int ret = start_userspace(stub_stack); if (ret <= 0) goto free_stub_stack; current->arch.host->stub_stack = stub_stack; current->arch.host->host_pid = ret; current->arch.host->nr_threads = 1; /* unmap kernel area */ if (host_munmap(current, (void *)KERNBASE, KERNTOP - KERNBASE) < 0) panic("unmap kernel area failed\n"); /* erase kernel maps in the page table */ int valid_size = KERNBASE / PTSIZE * sizeof(pte_t); memset((void *)((int)(current->mm->pgdir) + valid_size), 0, PGSIZE - valid_size); /* Copy arguments */ current->arch.regs.is_user = 1; uintptr_t stacktop = USTACKTOP - argc * PGSIZE; char **uargv = (char **)(stacktop - argc * sizeof(char *)); int i, addr; for (i = 0; i < argc; i++) { addr = stacktop + i * PGSIZE; assert(copy_to_user (current->mm, uargv + i, &addr, sizeof(int))); assert(copy_to_user (current->mm, (void *)addr, kargv[i], strlen(kargv[i]) + 1)); } stacktop = (uintptr_t) uargv - sizeof(int); copy_to_user(current->mm, (void *)stacktop, &argc, sizeof(int)); /* The copy of the args in the kernel will never be used again and we will not return, * so free them here. */ while (argc > 0) { kfree(kargv[--argc]); } userspace(&(current->arch.regs)); /* should never comes here */ free_stub_stack: free_page(kva2page(stub_stack)); free_host: kfree(current->arch.host); current->arch.host = NULL; return -1; }
/** * Remap the specified address to a new page with new permission. * @param pgdir page directory * @param la linear address */ void tlb_update (pde_t *pgdir, uintptr_t la) { la = ROUNDDOWN (la, PGSIZE); pte_t* pte = get_pte (pgdir, la, 0); if (pte == 0 || (*pte & PTE_P) == 0) panic ("invalid tlb flushing\n"); uint32_t pa = PDE_ADDR(*pte); /* A tricky method to make the page table right under most circumstances. * Please consult the internal documentation for details. */ int r = 1, w = 1, x = 1; if (Get_PTE_A(pte) == 0) r = x = w = 0; else if (Get_PTE_W(pte) == 0 || Get_PTE_D(pte) == 0) w = 0; /* Make sure that the page is invalid before mapping * It is better to use 'mprotect' here actually. */ tlb_invalidate (pgdir, la); struct proc_struct *proc = find_proc_by_pgdir (pgdir); if (current != NULL && proc != NULL) { /* Map the page to the container process found using the stub code */ if (host_mmap (proc, (void*)la, PGSIZE, (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | (x ? PROT_EXEC : 0), MAP_SHARED | MAP_FIXED, ginfo->mem_fd, pa) == MAP_FAILED) panic ("map in child failed.\n"); } else { /* Map the page to the host process */ struct mmap_arg_struct args = { .addr = la, .len = PGSIZE, .prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | (x ? PROT_EXEC : 0), .flags = MAP_SHARED | MAP_FIXED, .fd = ginfo->mem_fd, .offset = pa, }; syscall1 (__NR_mmap, (long)&args); } } /** * unmap the page specified by @la in the container process corresponding to @pgdir * @param pgdir page directory * @param la the logical address of the page to be flushed */ void tlb_invalidate (pde_t *pgdir, uintptr_t la) { struct proc_struct *proc = find_proc_by_pgdir (pgdir); if (current != NULL && proc != NULL) { if (host_munmap (proc, (void*)la, PGSIZE) < 0) panic ("unmap in child failed\n"); } else { syscall2 (__NR_munmap, la, PGSIZE); } } /** * invalidate [USERBASE, USERTOP). * used by tests or do_execve if a 'clean' space is needed (though not neccesary). */ void tlb_invalidate_user (void) { syscall2 (__NR_munmap, USERBASE, USERTOP - USERBASE); }
/** * Make a copy of the current thread/process, giving the parent the child's pid and the child 0. * This is called in do_fork after all structures in the child's PCB are ready. * @param clone_flags we need this to determine whether we're creating a 'thread' or a 'process' * @param proc the PCB of the child * @param user_stack the stack used by the child * @param tf the struct containing 'fn' and 'arg' * @return 0 if succeeds, or -1 otherwise */ int copy_thread(uint32_t clone_flags, struct proc_struct *proc, uintptr_t user_stack, struct trapframe *tf) { int pid; void *stub_stack; /* If 'do_fork' is called by the kernel, 'current' should be idle, * and we're actually creating a kernel thread . * If 'do_fork' is called by the user (i.e. syscall), 'current' is a user PCB, * and we need to create another user process. */ if (RUN_AS_USER) { if (clone_flags & CLONE_VM) { /* Use current host process as its container */ proc->arch.host = current->arch.host; proc->arch.host->nr_threads++; } else { /* Create a new child process */ proc->arch.host = kmalloc(sizeof(struct host_container)); if (proc->arch.host == NULL) goto exit; stub_stack = boot_alloc_page(); if (stub_stack == NULL) goto exit_free_host; pid = start_userspace(stub_stack); if (pid < 0) goto exit_free_stub; /* Initialize host process descriptor */ proc->arch.forking = 0; proc->arch.host->host_pid = pid; proc->arch.host->nr_threads = 1; proc->arch.host->stub_stack = (struct stub_stack *)stub_stack; /* unmap kernel area. */ if (host_munmap (proc, (void *)KERNBASE, KERNTOP - KERNBASE) < 0) panic("unmap kernel area failed \n"); } /* The child should have the same regs as the parent's. */ memcpy(&(proc->arch.regs.regs), &(current->arch.regs.regs), sizeof(struct user_regs_struct)); /* make the child return 0 for the syscall */ proc->arch.regs.regs.eax = 0; proc->arch.regs.regs.esp = user_stack; /* The current thread will run in 'userspace' */ proc->context.switch_buf->__ebx = (uint32_t) userspace; proc->context.switch_buf->__ebp = (uint32_t) & (proc->arch.regs); } else { /* For kernel thread */ proc->context.switch_buf->__ebx = (uint32_t) (tf->fn); proc->context.switch_buf->__ebp = (uint32_t) (tf->arg); } /* All new threads/processes start running from 'kernel_thread_entry' * for the 'processes' actually means 'monitor threads' to the kernel. */ proc->context.switch_buf->__eip = (uint32_t) kernel_thread_entry; proc->context.switch_buf->__esp = proc->kstack + KSTACKSIZE; return 0; exit_free_stub: free_page(kva2page(stub_stack)); exit_free_host: kfree(proc->arch.host); exit: return -1; }