diff --git a/conts/libl4thread/include/l4thread/addr.h b/conts/libl4thread/include/l4thread/addr.h new file mode 100644 index 0000000..e7b4519 --- /dev/null +++ b/conts/libl4thread/include/l4thread/addr.h @@ -0,0 +1,27 @@ +/* + * Address allocation pool + * + * Copyright (C) 2007 Bahadir Balban + */ +#ifndef __ADDR_H__ +#define __ADDR_H__ + +#include + +/* Address pool to allocate from a range of addresses */ +struct address_pool { + struct id_pool *idpool; + unsigned long start; + unsigned long end; +}; + +int address_pool_init_with_idpool(struct address_pool *pool, + struct id_pool *idpool, + unsigned long start, unsigned long end); +int address_pool_init(struct address_pool *pool, + unsigned long start, unsigned long end, + int size); +void *address_new(struct address_pool *pool, int npages); +int address_del(struct address_pool *, void *addr, int npages); + +#endif /* __ADDR_H__ */ diff --git a/conts/libl4thread/include/l4thread/tcb.h b/conts/libl4thread/include/l4thread/tcb.h new file mode 100644 index 0000000..1c81111 --- /dev/null +++ b/conts/libl4thread/include/l4thread/tcb.h @@ -0,0 +1,35 @@ +/* + * Thread control block. + * + * Copyright (C) 2009 B Labs Ltd. + */ +#ifndef __LIB_TCB_H__ +#define __LIB_TCB_H__ + +#include + +/* Keeps all the struct utcb_descs belonging to a thread group together. */ +struct utcb_head { + struct link list; +}; + +/* A simple thread control block for the thread library. */ +struct l4t_tcb { + struct link list; + int tid; + struct utcb_head *utcb_head; + unsigned long utcb_addr; +}; + +/* This struct keeps track of all the threads handled by the thread lib. */ +struct l4t_global_list { + int total; + struct link list; +}; + +struct l4t_tcb *l4t_find_task(int tid); +struct l4t_tcb *l4t_tcb_alloc_init(struct l4t_tcb *parent, unsigned int flags); +void l4t_global_add_task(struct l4t_tcb *task); +void l4t_global_remove_task(struct l4t_tcb *task); + +#endif /* __LIB_TCB_H__ */ diff --git a/conts/libl4thread/include/l4thread/thread.h b/conts/libl4thread/include/l4thread/thread.h new file mode 100644 index 0000000..319233e --- /dev/null +++ b/conts/libl4thread/include/l4thread/thread.h @@ -0,0 +1,18 @@ +/* + * Thread creation userspace helpers + * + * Copyright (C) 2009 B Labs Ltd. + */ +#ifndef __LIB_THREAD_H__ +#define __LIB_THREAD_H__ + +int set_stack_params(unsigned long stack_top, + unsigned long stack_bottom, + unsigned long stack_size); +int set_utcb_params(unsigned long utcb_start, unsigned long utcb_end); + +int l4thread_create(struct task_ids *ids, unsigned int flags, + int (*func)(void *), void *arg); +void l4thread_kill(struct task_ids *ids); + +#endif /* __LIB_THREAD_H__ */ diff --git a/conts/libl4thread/include/l4thread/utcb-common.h b/conts/libl4thread/include/l4thread/utcb-common.h new file mode 100644 index 0000000..e5f5fef --- /dev/null +++ b/conts/libl4thread/include/l4thread/utcb-common.h @@ -0,0 +1,25 @@ +/* + * UTCB handling common helper routines + * + * Copyright (C) 2009 B Labs Ltd. + */ +#ifndef __UTCB_COMMON_H__ +#define __UTCB_COMMON_H__ + +#include + +struct utcb_desc { + struct link list; + unsigned long utcb_base; + struct id_pool *slots; +}; + +int utcb_pool_init(unsigned long utcb_start, unsigned long utcb_end); + +unsigned long utcb_new_slot(struct utcb_desc *desc); +int utcb_delete_slot(struct utcb_desc *desc, unsigned long address); + +struct utcb_desc *utcb_new_desc(void); +int utcb_delete_desc(struct utcb_desc *desc); + +#endif /* __UTCB_COMMON_H__ */ diff --git a/conts/libl4thread/include/l4thread/utcb.h b/conts/libl4thread/include/l4thread/utcb.h new file mode 100644 index 0000000..f16f310 --- /dev/null +++ b/conts/libl4thread/include/l4thread/utcb.h @@ -0,0 +1,14 @@ +/* + * UTCB handling helper routines + * + * Copyright (C) 2009 B Labs Ltd. + */ +#ifndef __LIB_UTCB_H__ +#define __LIB_UTCB_H__ + +#include + +unsigned long get_utcb_addr(struct l4t_tcb *task); +int delete_utcb_addr(struct l4t_tcb *task); + +#endif /* __LIB_UTCB_H__ */ diff --git a/conts/libl4thread/src/addr.c b/conts/libl4thread/src/addr.c new file mode 100644 index 0000000..90c67dd --- /dev/null +++ b/conts/libl4thread/src/addr.c @@ -0,0 +1,57 @@ +/* + * This module allocates an unused address range from + * a given memory region defined as the pool range. + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include + +/* + * Initializes an address pool, but uses an already + * allocated id pool for it. + */ +int address_pool_init_with_idpool(struct address_pool *pool, + struct id_pool *idpool, + unsigned long start, unsigned long end) +{ + pool->idpool = idpool; + pool->start = start; + pool->end = end; + + return 0; +} + +int address_pool_init(struct address_pool *pool, + unsigned long start, unsigned long end, + int size) +{ + if ((pool->idpool = id_pool_new_init((end - start) / size)) < 0) + return (int)pool->idpool; + pool->start = start; + pool->end = end; + return 0; +} + +void *address_new(struct address_pool *pool, int npages) +{ + unsigned int pfn; + + if ((int)(pfn = ids_new_contiguous(pool->idpool, npages)) < 0) + return 0; + + return (void *)__pfn_to_addr(pfn) + pool->start; +} + +int address_del(struct address_pool *pool, void *addr, int npages) +{ + unsigned long pfn = __pfn(page_align(addr) - pool->start); + + if (ids_del_contiguous(pool->idpool, pfn, npages) < 0) { + printf("%s: Invalid address range returned to " + "virtual address pool.\n", __FUNCTION__); + return -1; + } + return 0; +} + diff --git a/conts/libl4thread/src/tcb.c b/conts/libl4thread/src/tcb.c new file mode 100644 index 0000000..d0125bb --- /dev/null +++ b/conts/libl4thread/src/tcb.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +/* Global task list. */ +struct l4t_global_list l4t_global_tasks = { + .list = { &l4t_global_tasks.list, &l4t_global_tasks.list }, + .total = 0, +}; + +/* Function definitions */ +void l4t_global_add_task(struct l4t_tcb *task) +{ + BUG_ON(!list_empty(&task->list)); + list_insert_tail(&task->list, &l4t_global_tasks.list); + l4t_global_tasks.total++; +} + +void l4t_global_remove_task(struct l4t_tcb *task) +{ + BUG_ON(list_empty(&task->list)); + list_remove_init(&task->list); + BUG_ON(--l4t_global_tasks.total < 0); +} + +struct l4t_tcb *l4t_find_task(int tid) +{ + struct l4t_tcb *t; + + list_foreach_struct(t, &l4t_global_tasks.list, list) + if (t->tid == tid) + return t; + return 0; +} + +struct l4t_tcb *l4t_tcb_alloc_init(struct l4t_tcb *parent, unsigned int flags) +{ + struct l4t_tcb *task; + + if (!(task = kzalloc(sizeof(struct l4t_tcb)))) + return PTR_ERR(-ENOMEM); + + link_init(&task->list); + + if ((parent) && (flags & TC_SHARE_SPACE)) + task->utcb_head = parent->utcb_head; + else { + if (!(task->utcb_head = kzalloc(sizeof(struct utcb_head)))) + return PTR_ERR(-ENOMEM); + link_init(&task->utcb_head->list); + } + + return task; +} diff --git a/conts/libl4thread/src/thread.c b/conts/libl4thread/src/thread.c new file mode 100644 index 0000000..87e84b4 --- /dev/null +++ b/conts/libl4thread/src/thread.c @@ -0,0 +1,185 @@ +/* + * Thread creation userspace helpers + * + * Copyright (C) 2009 B Labs Ltd. + */ +#include +#include +#include +#include +#include +#include +#include + +/* Symbolic constants and macros */ +#define IS_STACK_SETUP() (lib_stack_size) + +/* Extern declarations */ +extern void setup_new_thread(void); + +/* Static variable definitions */ +static unsigned long lib_stack_top_addr; +static unsigned long lib_stack_bot_addr; +static unsigned long lib_stack_size; + +/* Function definitions */ +int set_stack_params(unsigned long stack_top, + unsigned long stack_bottom, + unsigned long stack_size) +{ + /* Ensure that arguments are valid. */ + if (IS_STACK_SETUP()) { + printf("libl4thread: You have already called: %s.\n", + __FUNCTION__); + return -EPERM; + } + if (!stack_top || !stack_bottom) { + printf("libl4thread: Stack address range cannot contain " + "0x0 as a start and/or end address(es).\n"); + return -EINVAL; + } + // FIXME: Aligning should be taken into account. + /* + * Stack grows downward so the top of the stack will have + * the lowest numbered address. + */ + if (stack_top >= stack_bottom) { + printf("libl4thread: Stack bottom address must be bigger " + "than stack top address.\n"); + return -EINVAL; + } + if (!stack_size) { + printf("libl4thread: Stack size cannot be zero.\n"); + return -EINVAL; + } + /* stack_size at least must be equal to the difference. */ + if ((stack_bottom - stack_top) < stack_size) { + printf("libl4thread: The given range size is lesser than " + "the stack size(0x%x).\n", stack_size); + return -EINVAL; + } + /* Arguments passed the validity tests. */ + + /* Initialize internal variables */ + lib_stack_bot_addr = stack_bottom; + lib_stack_top_addr = stack_top; + lib_stack_size = stack_size; + + return 0; +} + +int l4thread_create(struct task_ids *ids, unsigned int flags, + int (*func)(void *), void *arg) +{ + struct exregs_data exregs; + unsigned long utcb_addr; + struct l4t_tcb *parent, *child; + int err; + + /* A few controls before granting access to thread creation */ + // FIXME: check if utcb region is setup + if (!IS_STACK_SETUP()) { + printf("libl4thread: Stack and/or utcb have not been " + "set up.\n"); + return -EPERM; + } + + /* Is there enough stack space for the new thread? */ + if (lib_stack_top_addr >= lib_stack_bot_addr) { + printf("libl4thread: No stack space left.\n"); + return -ENOMEM; + } + + // FIXME: Check if there is enough utcb space + + if (!(TC_SHARE_SPACE & flags)) { + printf("libl4thread: Only allows shared space thread " + "creation.\n"); + + return -EINVAL; + } + + /* Get parent's ids and find the tcb belonging to it. */ + l4_getid(ids); + if (!(parent = l4t_find_task(ids->tid))) + return-ESRCH; + + /* Allocate tcb for the child. */ + if (!(child = l4t_tcb_alloc_init(parent, flags))) { + printf("libl4thread: No heap space left.\n"); + return -ENOMEM; + } + + /* Get a utcb addr for this thread */ + if (!(utcb_addr = get_utcb_addr(child))) { + printf("libl4thread: No utcb address left.\n"); + // FIXME: Check if there is any memory leak + kfree(child); + return -ENOMEM; + } + + /* Create thread */ + if ((err = l4_thread_control(THREAD_CREATE | flags, ids)) < 0) { + printf("libl4thread: l4_thread_control(THREAD_CREATE) " + "failed with (%d).\n", err); + // FIXME: Check if there is any memory leak + kfree(child); + delete_utcb_addr(child); + return err; + } + + /* Setup new thread pc, sp, utcb */ + memset(&exregs, 0, sizeof(exregs)); + exregs_set_stack(&exregs, align((lib_stack_bot_addr - 1), 8)); + exregs_set_pc(&exregs, (unsigned long)setup_new_thread); + exregs_set_utcb(&exregs, (unsigned long)utcb_addr); + + if ((err = l4_exchange_registers(&exregs, ids->tid)) < 0) { + printf("libl4thread: l4_exchange_registers failed with " + "(%d).\n", err); + // FIXME: Check if there is any memory leak + return err; + } + + /* First word of new stack is arg */ + ((unsigned long *)align((lib_stack_bot_addr - 1), 8))[0] = + (unsigned long)arg; + /* Second word of new stack is function address */ + ((unsigned long *)align((lib_stack_bot_addr - 1), 8))[-1] = + (unsigned long)func; + /* Update the stack address */ + lib_stack_bot_addr -= lib_stack_size; + + /* Add child to the global task list */ + child->tid = ids->tid; + l4t_global_add_task(child); + + /* Start the new thread */ + if ((err = l4_thread_control(THREAD_RUN, ids)) < 0) { + printf("libl4thread: l4_thread_control(THREAD_RUN) " + "failed with (%d).\n", err); + return err; + } + + return 0; +} + +void l4thread_kill(struct task_ids *ids) +{ + struct l4t_tcb *task; + + /* Find the task to be killed. */ + task = l4t_find_task(ids->tid); + + /* Delete the utcb address. */ + delete_utcb_addr(task); + + /* Remove child from the global task list. */ + l4t_global_remove_task(task); + + // FIXME: We assume that main thread never leaves the scene + kfree(task); + + /* Finally, destroy the thread. */ + l4_thread_control(THREAD_DESTROY, ids); +} diff --git a/conts/libl4thread/src/utcb-common.c b/conts/libl4thread/src/utcb-common.c new file mode 100644 index 0000000..38df843 --- /dev/null +++ b/conts/libl4thread/src/utcb-common.c @@ -0,0 +1,89 @@ +/* + * UTCB management in Codezero + * + * Copyright © 2009 B Labs Ltd + */ +#include +#include +#include +#include + +/* Globally disjoint utcb virtual region pool */ +static struct address_pool utcb_region_pool; + +int utcb_pool_init(unsigned long utcb_start, unsigned long utcb_end) +{ + int err; + + /* Initialise the global utcb virtual address pool */ + if ((err = address_pool_init(&utcb_region_pool, + utcb_start, utcb_end, + UTCB_SIZE) < 0)) { + printf("UTCB address pool initialisation failed.\n"); + return err; + } + + return 0; +} + +static inline void *utcb_new_address(int npages) +{ + return address_new(&utcb_region_pool, npages); +} + +static inline int utcb_delete_address(void *utcb_address, int npages) +{ + return address_del(&utcb_region_pool, utcb_address, npages); +} + +/* Return an empty utcb slot in this descriptor */ +unsigned long utcb_new_slot(struct utcb_desc *desc) +{ + int slot; + + if ((slot = id_new(desc->slots)) < 0) + return 0; + else + return desc->utcb_base + (unsigned long)slot * UTCB_SIZE; +} + +int utcb_delete_slot(struct utcb_desc *desc, unsigned long address) +{ + BUG_ON(id_del(desc->slots, (address - desc->utcb_base) + / UTCB_SIZE) < 0); + return 0; +} + +struct utcb_desc *utcb_new_desc(void) +{ + struct utcb_desc *d; + + /* Allocate a new descriptor */ + if (!(d = kzalloc(sizeof(*d)))) + return 0; + + link_init(&d->list); + + /* We currently assume UTCB is smaller than PAGE_SIZE */ + BUG_ON(UTCB_SIZE > PAGE_SIZE); + + /* Initialise utcb slots */ + d->slots = id_pool_new_init(PAGE_SIZE / UTCB_SIZE); + + /* Obtain a new and unique utcb base */ + /* FIXME: Use variable size than a page */ + d->utcb_base = (unsigned long)utcb_new_address(1); + + return d; +} + +int utcb_delete_desc(struct utcb_desc *desc) +{ + /* Return descriptor address */ + utcb_delete_address((void *)desc->utcb_base, 1); + + /* Free the descriptor */ + kfree(desc); + + return 0; +} diff --git a/conts/libl4thread/src/utcb.c b/conts/libl4thread/src/utcb.c new file mode 100644 index 0000000..4dd7fef --- /dev/null +++ b/conts/libl4thread/src/utcb.c @@ -0,0 +1,148 @@ +/* + * UTCB handling helper routines + * + * Copyright (C) 2009 B Labs Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Extern declarations */ +extern struct l4t_global_list l4t_global_tasks; + +/* Function definitions */ +unsigned long get_utcb_addr(struct l4t_tcb *task) +{ + struct utcb_desc *udesc; + unsigned long slot; + + /* Setting this up twice is a bug. */ + BUG_ON(task->utcb_addr); + + /* Search for an empty utcb slot already allocated to this space. */ + list_foreach_struct(udesc, &task->utcb_head->list, list) + if ((slot = utcb_new_slot(udesc))) + goto found; + + /* Allocate a new utcb memory region and return its base. */ + udesc = utcb_new_desc(); + slot = utcb_new_slot(udesc); + list_insert(&udesc->list, &task->utcb_head->list); +found: + task->utcb_addr = slot; + + return slot; +} + +int delete_utcb_addr(struct l4t_tcb *task) +{ + struct utcb_desc *udesc; + + list_foreach_struct(udesc, &task->utcb_head->list, list) { + /* FIXME: Use variable alignment than a page */ + /* Detect matching slot */ + if (page_align(task->utcb_addr) == udesc->utcb_base) { + + /* Delete slot from the descriptor */ + utcb_delete_slot(udesc, task->utcb_addr); + + /* Is the desc completely empty now? */ + if (id_is_empty(udesc->slots)) { + /* Unlink desc from its list */ + list_remove_init(&udesc->list); + /* Delete the descriptor */ + utcb_delete_desc(udesc); + } + return 0; /* Finished */ + } + } + BUG(); +} + +static int set_utcb_addr(void) +{ + struct exregs_data exregs; + struct task_ids ids; + struct l4t_tcb *task; + struct utcb_desc *udesc; + int err; + + /* Create a task. */ + if (IS_ERR(task = l4t_tcb_alloc_init(0, 0))) + return -ENOMEM; + + /* Add child to the global task list. */ + list_insert_tail(&task->list, &l4t_global_tasks.list); + l4t_global_tasks.total++; + + udesc = utcb_new_desc(); + task->utcb_addr = utcb_new_slot(udesc); + list_insert(&udesc->list, &task->utcb_head->list); + + memset(&exregs, 0, sizeof(exregs)); + exregs_set_utcb(&exregs, (unsigned long)task->utcb_addr); + + l4_getid(&ids); + if ((err = l4_exchange_registers(&exregs, ids.tid)) < 0) { + printf("libl4thread: l4_exchange_registers failed with " + "(%d).\n", err); + return err; + } + + return 0; +} + +int set_utcb_params(unsigned long utcb_start, unsigned long utcb_end) +{ + int err; + + /* Ensure that arguments are valid. */ + // FIXME: Check if set_utcb_params is called + /*if (IS_UTCB_SETUP()) {*/ + /*printf("libl4thread: You have already called: %s.\n",*/ + /*__FUNCTION__);*/ + /*return -EPERM;*/ + /*}*/ + if (!utcb_start || !utcb_end) { + printf("libl4thread: Utcb address range cannot contain " + "0x0 as a start and/or end address(es).\n"); + return -EINVAL; + } + /* Check if the start address is aligned on UTCB_SIZE. */ + if (utcb_start & !UTCB_SIZE) { + printf("libl4thread: Utcb start address must be aligned " + "on UTCB_SIZE(0x%x).\n", UTCB_SIZE); + return -EINVAL; + } + /* The range must be a valid one. */ + if (utcb_start >= utcb_end) { + printf("libl4thread: Utcb end address must be bigger " + "than utcb start address.\n"); + return -EINVAL; + } + /* + * This check guarantees two things: + * 1. The range must be multiple of UTCB_SIZE, at least one item. + * 2. utcb_end is aligned on UTCB_SIZE + */ + if ((utcb_end - utcb_start) % UTCB_SIZE) { + printf("libl4thread: The given range size must be multiple " + "of the utcb size(%d).\n", UTCB_SIZE); + return -EINVAL; + } + /* Arguments passed the validity tests. */ + + /* Init utcb virtual address pool */ + utcb_pool_init(utcb_start, utcb_end); + + /* The very first thread's utcb address is assigned. */ + if ((err = set_utcb_addr()) < 0) + return err; + + return 0; +}