From 05d8438f340638d044796d776f2f554fff45a1a3 Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Mon, 1 Dec 2008 13:53:50 +0200 Subject: [PATCH] Task initally exec'ing with success. Some errors need to be investigated. - Directory creation, file read/write is OK. - Cannot reuse old task's fds. They are not recycled for some reason. - Problems with fork/clone/exit. They fail for a reason. --- include/l4/api/space.h | 2 - include/l4/api/thread.h | 1 + include/l4/glue/arm/memory.h | 3 +- include/l4/macros.h | 1 + src/api/space.c | 10 --- src/api/syscall.c | 6 +- src/api/thread.c | 45 ++++++++++++ tasks/libposix/README | 10 +-- tasks/mm0/include/exit.h | 6 +- tasks/mm0/include/task.h | 7 +- tasks/mm0/src/execve.c | 38 ++++++++--- tasks/mm0/src/exit.c | 42 +++++++++--- tasks/mm0/src/pagers.c | 18 ++++- tasks/mm0/src/task.c | 129 ++++++++++++++++++++++++++--------- 14 files changed, 239 insertions(+), 79 deletions(-) diff --git a/include/l4/api/space.h b/include/l4/api/space.h index 9656c17..2bdacf4 100644 --- a/include/l4/api/space.h +++ b/include/l4/api/space.h @@ -1,7 +1,5 @@ #ifndef __API_SPACE_H__ #define __API_SPACE_H__ -#define UNMAP_ALL_SPACE 0xFFFFFFFF - #endif /* __API_SPACE_H__ */ diff --git a/include/l4/api/thread.h b/include/l4/api/thread.h index 4d2761c..6681d6a 100644 --- a/include/l4/api/thread.h +++ b/include/l4/api/thread.h @@ -21,5 +21,6 @@ #define THREAD_SUSPEND 0x0002 #define THREAD_RESUME 0x0003 #define THREAD_DESTROY 0x0004 +#define THREAD_RECYCLE 0x0005 #endif /* __THREAD_H__ */ diff --git a/include/l4/glue/arm/memory.h b/include/l4/glue/arm/memory.h index 8529161..572d6e4 100644 --- a/include/l4/glue/arm/memory.h +++ b/include/l4/glue/arm/memory.h @@ -39,6 +39,8 @@ /* Align to given size */ #define align(addr, size) (((unsigned int)(addr)) & (~(size-1))) +#define align_up(addr, size) ((((unsigned long)(addr)) + \ + ((size) - 1)) & (~((size) - 1))) /* The bytes left until the end of the page that x is in */ #define TILL_PAGE_ENDS(x) (PAGE_SIZE - ((unsigned long)(x) & PAGE_MASK)) @@ -57,7 +59,6 @@ #define BITWISE_GETWORD(x) ((x) >> WORD_BITS_LOG2) /* Divide by 32 */ #define BITWISE_GETBIT(x) (1 << ((x) % WORD_BITS)) -#define align_up(addr, size) ((((unsigned long)(addr)) + ((size) - 1)) & (~((size) - 1))) /* Endianness conversion */ static inline void be32_to_cpu(unsigned int x) diff --git a/include/l4/macros.h b/include/l4/macros.h index 0983cee..d5cc67b 100644 --- a/include/l4/macros.h +++ b/include/l4/macros.h @@ -37,6 +37,7 @@ #define SZ_1K 1024 #define SZ_4K 0x1000 #define SZ_16K 0x4000 +#define SZ_32K 0x8000 #define SZ_64K 0x10000 #define SZ_1MB 0x100000 #define SZ_8MB (8*SZ_1MB) diff --git a/src/api/space.c b/src/api/space.c index bc3861b..ca074b4 100644 --- a/src/api/space.c +++ b/src/api/space.c @@ -59,16 +59,6 @@ int sys_unmap(syscall_context_t *regs) else if (!(target = find_task(tid))) return -ESRCH; - /* - * These special values mean unmap all the mappings - * from task space except the kernel mappings - */ - if (virtual == UNMAP_ALL_SPACE && - npages == UNMAP_ALL_SPACE) { - remove_mapping_pgd_all_user(target->pgd); - return 0; - } - for (int i = 0; i < npages; i++) { ret = remove_mapping_pgd(virtual + i * PAGE_SIZE, target->pgd); if (ret) diff --git a/src/api/syscall.c b/src/api/syscall.c index 415884e..2b87701 100644 --- a/src/api/syscall.c +++ b/src/api/syscall.c @@ -87,7 +87,7 @@ void do_exchange_registers(struct ktcb *task, struct exregs_data *exregs) * the register context of a thread. The thread's registers can be * set only when the thread is in user mode. A newly created thread * that is the copy of another thread (forked or cloned) will also - * be given its user mode context on the first chance to execute so + * be given its user mode context on the first chance to execute so * such threads can also be modified by this call before execution. * * A thread executing in the kernel cannot be modified since this @@ -119,6 +119,9 @@ int sys_exchange_registers(syscall_context_t *regs) goto out; } +#if 0 + A suspended thread implies it cannot do any harm + even if it is in kernel mode. /* * The thread must be in user mode for its context * to be modified. @@ -127,6 +130,7 @@ int sys_exchange_registers(syscall_context_t *regs) err = -EPERM; goto out; } +#endif /* Copy registers */ do_exchange_registers(task, exregs); diff --git a/src/api/thread.c b/src/api/thread.c index cd7ed3f..987a2c7 100644 --- a/src/api/thread.c +++ b/src/api/thread.c @@ -49,6 +49,47 @@ int thread_suspend(struct task_ids *ids) return ret; } +int arch_clear_thread(struct ktcb *task) +{ + memset(&task->context, 0, sizeof(task->context)); + task->context.spsr = ARM_MODE_USR; + + /* Clear the page tables */ + remove_mapping_pgd_all_user(task->pgd); + + return 0; +} + +int thread_recycle(struct task_ids *ids) +{ + struct ktcb *task; + int ret; + + if (!(task = find_task(ids->tid))) + return -ESRCH; + + if ((ret = thread_suspend(ids)) < 0) + return ret; + + /* + * If there are any sleepers on any of the task's + * waitqueues, we need to wake those tasks up. + */ + wake_up_all(&task->wqh_send, 0); + wake_up_all(&task->wqh_recv, 0); + + /* + * The thread cannot have a pager waiting for it + * since we ought to be the pager. + */ + BUG_ON(task->wqh_pager.sleepers > 0); + + /* Clear the task's tcb */ + arch_clear_thread(task); + + return 0; +} + int thread_destroy(struct task_ids *ids) { struct ktcb *task; @@ -292,6 +333,10 @@ int sys_thread_control(syscall_context_t *regs) break; case THREAD_DESTROY: ret = thread_destroy(ids); + break; + case THREAD_RECYCLE: + ret = thread_recycle(ids); + break; default: ret = -EINVAL; } diff --git a/tasks/libposix/README b/tasks/libposix/README index 53f83a2..e19eb8d 100644 --- a/tasks/libposix/README +++ b/tasks/libposix/README @@ -41,6 +41,7 @@ shmat shmget shmdt mmap +munmap read readdir write @@ -51,19 +52,18 @@ creat mkdir mknod fork +clone +execve +exit +getpid Functions to be supported in the near future are: -munmap link unlink -getpid -execve -clone wait kill -exit sbrk getenv setenv diff --git a/tasks/mm0/include/exit.h b/tasks/mm0/include/exit.h index 08e53b9..1ddd871 100644 --- a/tasks/mm0/include/exit.h +++ b/tasks/mm0/include/exit.h @@ -7,9 +7,7 @@ #ifndef __EXIT_H__ #define __EXIT_H__ -#define EXIT_THREAD_DESTROY (1 << 0) -#define EXIT_UNMAP_ALL_SPACE (1 << 1) +void do_exit(struct tcb *task, int status); +int execve_recycle_task(struct tcb *task); - -void do_exit(struct tcb *task, unsigned int flags, int status); #endif /* __EXIT_H__ */ diff --git a/tasks/mm0/include/task.h b/tasks/mm0/include/task.h index 4eb0987..bfc2287 100644 --- a/tasks/mm0/include/task.h +++ b/tasks/mm0/include/task.h @@ -25,7 +25,7 @@ /* POSIX minimum is 4Kb */ #define DEFAULT_ENV_SIZE SZ_4K -#define DEFAULT_STACK_SIZE SZ_16K +#define DEFAULT_STACK_SIZE SZ_32K #define DEFAULT_UTCB_SIZE PAGE_SIZE #define TCB_NO_SHARING 0 @@ -94,8 +94,6 @@ struct tcb { unsigned long stack_end; unsigned long heap_start; unsigned long heap_end; - unsigned long env_start; - unsigned long env_end; unsigned long args_start; unsigned long args_end; @@ -122,7 +120,8 @@ struct tcb *find_task(int tid); void global_add_task(struct tcb *task); void global_remove_task(struct tcb *task); void task_map_prefault_utcb(struct tcb *mapper, struct tcb *owner); -int task_mmap_segments(struct tcb *task, struct vm_file *file, struct exec_file_desc *efd); +int task_mmap_segments(struct tcb *task, struct vm_file *file, struct exec_file_desc *efd, + struct args_struct *args, struct args_struct *env); int task_setup_registers(struct tcb *task, unsigned int pc, unsigned int sp, l4id_t pager); struct tcb *tcb_alloc_init(unsigned int flags); diff --git a/tasks/mm0/src/execve.c b/tasks/mm0/src/execve.c index b6ff116..0d244f2 100644 --- a/tasks/mm0/src/execve.c +++ b/tasks/mm0/src/execve.c @@ -128,7 +128,7 @@ int do_execve(struct tcb *sender, char *filename, struct args_struct *args, * TODO: Set up parents for children's children */ list_for_each_entry(thread, &tgleader->children, child_ref) - do_exit(thread, EXIT_THREAD_DESTROY, 0); + do_exit(thread, 0); } else { /* Otherwise group leader is same as sender */ tgleader = sender; @@ -139,22 +139,30 @@ int do_execve(struct tcb *sender, char *filename, struct args_struct *args, new_task->spid = tgleader->spid; new_task->tgid = tgleader->tgid; new_task->pagerid = tgleader->pagerid; + new_task->utcb = tgleader->utcb; /* * Release all task resources, do everything done in * exit() except destroying the actual thread. */ - do_exit(tgleader, EXIT_UNMAP_ALL_SPACE, 0); + if ((err = execve_recycle_task(tgleader)) < 0) { + vm_file_put(vmfile); + kfree(new_task); + return err; + } /* Map task's new segment markers as virtual memory regions */ - if ((err = task_mmap_segments(new_task, vmfile, &efd)) < 0) { + if ((err = task_mmap_segments(new_task, vmfile, &efd, args, env)) < 0) { vm_file_put(vmfile); kfree(new_task); return err; } /* Set up task registers via exchange_registers() */ - task_setup_registers(new_task, 0, 0, new_task->pagerid); + task_setup_registers(new_task, 0, new_task->args_start, new_task->pagerid); + + /* Add new task to global list */ + global_add_task(new_task); /* Start the task */ task_start(new_task); @@ -186,7 +194,7 @@ Dynamic Linking. * - Jump to the entry point of main executable. */ #endif - return -1; + return 0; } /* @@ -253,6 +261,7 @@ int copy_user_buf(struct tcb *task, void *buf, char *user, int maxlength, void *mapped = 0; int (*copy_func)(void *, void *, int count); + /* This bit determines what size copier function to use. */ if (elem_size == sizeof(char)) copy_func = strncpy_page; else if (elem_size == sizeof(unsigned long)) @@ -376,28 +385,37 @@ out: int sys_execve(struct tcb *sender, char *pathname, char *argv[], char *envp[]) { int ret; - char *path = kzalloc(PATH_MAX); + char *path; struct args_struct args; struct args_struct env; + if (!(path = kzalloc(PATH_MAX))) + return -ENOMEM; + + memset(&args, 0, sizeof(args)); + memset(&env, 0, sizeof(env)); + /* Copy the executable path string */ if ((ret = copy_user_string(sender, path, pathname, PATH_MAX)) < 0) return ret; printf("%s: Copied pathname: %s\n", __FUNCTION__, path); /* Copy the args */ - if ((ret = copy_user_args(sender, &args, argv, ARGS_MAX)) < 0) + if (argv && ((ret = copy_user_args(sender, &args, argv, ARGS_MAX)) < 0)) goto out1; /* Copy the env */ - if ((ret = copy_user_args(sender, &env, envp, ARGS_MAX - args.size))) + if (envp && ((ret = copy_user_args(sender, &env, envp, + ARGS_MAX - args.size)) < 0)) goto out2; ret = do_execve(sender, path, &args, &env); - kfree(env.argv); + if (env.argv) + kfree(env.argv); out2: - kfree(args.argv); + if (args.argv) + kfree(args.argv); out1: kfree(path); return ret; diff --git a/tasks/mm0/src/exit.c b/tasks/mm0/src/exit.c index 6ef5889..2949308 100644 --- a/tasks/mm0/src/exit.c +++ b/tasks/mm0/src/exit.c @@ -68,7 +68,37 @@ int task_close_files(struct tcb *task) return err; } -void do_exit(struct tcb *task, unsigned int flags, int status) +/* Prepare old task's environment for new task */ +int execve_recycle_task(struct tcb *task) +{ + int err; + struct task_ids ids = { + .tid = task->tid, + .spid = task->spid, + .tgid = task->tgid, + }; + + /* Flush all IO on task's files and close fds */ + task_close_files(task); + + /* Vfs still knows the thread */ + + /* Keep the utcb on vfs */ + + /* Ask the kernel to recycle the thread */ + if ((err = l4_thread_control(THREAD_RECYCLE, &ids)) < 0) { + printf("%s: Suspending thread %d failed with %d.\n", + __FUNCTION__, task->tid, err); + return err; + } + + /* Destroy the locally known tcb */ + tcb_destroy(task); + + return 0; +} + +void do_exit(struct tcb *task, int status) { struct task_ids ids = { .tid = task->tid, @@ -89,14 +119,8 @@ void do_exit(struct tcb *task, unsigned int flags, int status) /* Free task's local tcb */ tcb_destroy(task); - /* Ask the kernel to reset this thread's page tables */ - if (flags & EXIT_UNMAP_ALL_SPACE) - l4_unmap((void *)UNMAP_ALL_SPACE, - UNMAP_ALL_SPACE, task->tid); - /* Ask the kernel to delete the thread from its records */ - if (flags & EXIT_THREAD_DESTROY) - l4_thread_control(THREAD_DESTROY, &ids); + l4_thread_control(THREAD_DESTROY, &ids); /* TODO: Wake up any waiters about task's destruction */ #if 0 @@ -111,6 +135,6 @@ void do_exit(struct tcb *task, unsigned int flags, int status) void sys_exit(struct tcb *task, int status) { - do_exit(task, EXIT_THREAD_DESTROY, status); + do_exit(task, status); } diff --git a/tasks/mm0/src/pagers.c b/tasks/mm0/src/pagers.c index b61745e..aa3513b 100644 --- a/tasks/mm0/src/pagers.c +++ b/tasks/mm0/src/pagers.c @@ -223,8 +223,15 @@ int bootfile_release_pages(struct vm_object *vm_obj) list_del(&p->list); BUG_ON(p->refcnt); - /* Free the page structure */ - kfree(p); + /* Reinitialise the page */ + page_init(p); + + /* + * We don't free the page because it doesn't + * come from the page allocator + */ + // free_page((void *)page_to_phys(p)); + /* Reduce object page count */ BUG_ON(--vm_obj->npages < 0); @@ -276,6 +283,10 @@ struct vm_pager bootfile_pager = { }, }; +void bootfile_destroy_priv_data(struct vm_file *bootfile) +{ + +} /* From bare boot images, create mappable device files */ int init_boot_files(struct initdata *initdata) @@ -292,6 +303,8 @@ int init_boot_files(struct initdata *initdata) boot_file->priv_data = img; boot_file->length = img->phys_end - img->phys_start; boot_file->type = VM_FILE_BOOTFILE; + boot_file->destroy_priv_data = + bootfile_destroy_priv_data; /* Initialise the vm object */ boot_file->vm_obj.flags = VM_OBJ_FILE; @@ -300,6 +313,7 @@ int init_boot_files(struct initdata *initdata) /* Add the file to initdata's bootfile list */ list_add_tail(&boot_file->list, &initdata->boot_file_list); } + return 0; } diff --git a/tasks/mm0/src/task.c b/tasks/mm0/src/task.c index 71c39c7..6937d73 100644 --- a/tasks/mm0/src/task.c +++ b/tasks/mm0/src/task.c @@ -236,8 +236,6 @@ int copy_tcb(struct tcb *to, struct tcb *from, unsigned int flags) to->stack_end = from->stack_end; to->heap_start = from->heap_start; to->heap_end = from->heap_end; - to->env_start = from->env_start; - to->env_end = from->env_end; to->args_start = from->args_start; to->args_end = from->args_end; to->map_start = from->map_start; @@ -343,6 +341,87 @@ struct tcb *task_create(struct tcb *parent, struct task_ids *ids, return task; } +/* + * Copy argument and environment strings into task's stack in a + * format that is expected by the C runtime. + * + * e.g. uclibc expects stack state: + * + * (low) |->argc|argv[0]|argv[1]|...|argv[argc] = 0|envp[0]|...|NULL| (high) + * + */ +int task_args_to_user(char *user_stack, struct args_struct *args, + struct args_struct *env) +{ + BUG_ON((unsigned long)user_stack & 7); + + /* Copy argc */ + *((int *)user_stack) = args->argc; + user_stack += sizeof(int); + + /* Copy argument strings one by one */ + for (int i = 0; i < args->argc; i++) { + strcpy(user_stack, args->argv[i]); + user_stack += strlen(args->argv[i]) + 1; + } + /* Put the null terminator integer */ + *((int *)user_stack) = 0; + user_stack = user_stack + sizeof(int); + + /* Copy environment strings one by one */ + for (int i = 0; i < env->argc; i++) { + strcpy(user_stack, env->argv[i]); + user_stack += strlen(env->argv[i]) + 1; + } + + return 0; +} + +int task_map_stack(struct vm_file *f, struct exec_file_desc *efd, struct tcb *task, + struct args_struct *args, struct args_struct *env) +{ + /* First set up task's stack markers */ + unsigned long stack_used = align_up(args->size + env->size, 8); + unsigned long arg_pages = __pfn(page_align_up(stack_used)); + char *args_on_stack; + void *mapped; + + task->stack_end = USER_AREA_END; + task->stack_start = USER_AREA_END - DEFAULT_STACK_SIZE; + task->args_end = task->stack_end; + task->args_start = task->stack_end - stack_used; + + BUG_ON(stack_used > DEFAULT_STACK_SIZE); + + /* + * mmap task's stack as anonymous memory. + * TODO: Add VMA_GROWSDOWN here so the stack can expand. + */ + if (IS_ERR(mapped = do_mmap(0, 0, task, task->stack_start, + VM_READ | VM_WRITE | + VMA_PRIVATE | VMA_ANONYMOUS, + __pfn(task->stack_end - + task->stack_start)))) { + printf("do_mmap: Mapping stack failed with %d.\n", + (int)mapped); + return (int)mapped; + } + + /* Map the stack's part that will contain args and environment */ + args_on_stack = + pager_validate_map_user_range2(task, + (void *)task->args_start, + stack_used, VM_READ | VM_WRITE); + + /* Copy arguments and env */ + task_args_to_user(args_on_stack, args, env); + + /* Unmap task's those stack pages from pager */ + pager_unmap_pages(args_on_stack, arg_pages); + + return 0; +} + /* * If bss comes consecutively after the data section, prefault the * last page of the data section and zero out the bit that contains @@ -411,10 +490,11 @@ int task_map_bss(struct vm_file *f, struct exec_file_desc *efd, struct tcb *task return 0; } -int task_mmap_segments(struct tcb *task, struct vm_file *file, struct exec_file_desc *efd) +int task_mmap_segments(struct tcb *task, struct vm_file *file, struct exec_file_desc *efd, + struct args_struct *args, struct args_struct *env) { void *mapped; - struct vm_file *shm; + //struct vm_file *shm; int err; /* mmap task's text to task's address space. */ @@ -442,33 +522,19 @@ int task_mmap_segments(struct tcb *task, struct vm_file *file, struct exec_file_ return err; } - /* mmap task's environment as anonymous memory. */ - if (IS_ERR(mapped = do_mmap(0, 0, task, task->env_start, - VM_READ | VM_WRITE | - VMA_PRIVATE | VMA_ANONYMOUS, - __pfn(task->env_end - task->env_start)))) { - printf("do_mmap: Mapping environment failed with %d.\n", - (int)mapped); - return (int)mapped; + /* mmap task's stack, writing in the arguments and environment */ + if ((err = task_map_stack(file, efd, task, args, env)) < 0) { + printf("%s: Mapping task's stack has failed.\n", + __FUNCTION__); + return err; } - /* mmap task's stack as anonymous memory. */ - if (IS_ERR(mapped = do_mmap(0, 0, task, task->stack_start, - VM_READ | VM_WRITE | - VMA_PRIVATE | VMA_ANONYMOUS, - __pfn(task->stack_end - - task->stack_start)))) { - printf("do_mmap: Mapping stack failed with %d.\n", - (int)mapped); - return (int)mapped; - } - - /* Task's utcb */ - task->utcb = utcb_new_address(); - - /* Create a shared memory segment available for shmat() */ - if (IS_ERR(shm = shm_new((key_t)task->utcb, __pfn(DEFAULT_UTCB_SIZE)))) - return (int)shm; + /* + * Task already has recycled task's utcb. It will attach to it + * when it starts in userspace. + */ + //if (IS_ERR(shm = shm_new((key_t)task->utcb, __pfn(DEFAULT_UTCB_SIZE)))) + // return (int)shm; return 0; } @@ -483,7 +549,8 @@ int task_setup_registers(struct tcb *task, unsigned int pc, if (!sp) sp = align(task->stack_end - 1, 8); if (!pc) - pc = task->entry; + if (!(pc = task->entry)) + pc = task->text_start; if (!pager) pager = self_tid(); @@ -492,7 +559,7 @@ int task_setup_registers(struct tcb *task, unsigned int pc, exregs_set_pc(&exregs, pc); exregs_set_pager(&exregs, pager); - if ((err = l4_exchange_registers(&exregs, task->tid) < 0)) { + if ((err = l4_exchange_registers(&exregs, task->tid)) < 0) { printf("l4_exchange_registers failed with %d.\n", err); return err; }