Beispiel #1
0
/**
 * 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;
}
Beispiel #2
0
/**
 * 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);
}
Beispiel #3
0
/**
 * 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;
}