Files
codezero/tasks/mm0/src/utcb.c
2009-06-02 13:19:17 +03:00

185 lines
4.6 KiB
C

/*
* Management of task utcb regions and own utcb.
*
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#include <l4lib/arch/utcb.h>
#include <l4/macros.h>
#include INC_GLUE(memlayout.h)
#include <mmap.h>
#include <utcb.h>
#include <lib/malloc.h>
#include <vm_area.h>
/*
* UTCB management in Codezero
*/
/* Globally disjoint utcb virtual region pool */
static struct address_pool utcb_region_pool;
int utcb_pool_init()
{
int err;
/* Initialise the global shm virtual address pool */
if ((err = address_pool_init(&utcb_region_pool,
UTCB_AREA_START, UTCB_AREA_END)) < 0) {
printf("UTCB address pool initialisation failed.\n");
return err;
}
return 0;
}
void *utcb_new_address(int npages)
{
return address_new(&utcb_region_pool, npages);
}
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;
}
unsigned long task_new_utcb_desc(struct tcb *task)
{
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);
/* Add descriptor to tcb's chain */
list_insert(&d->list, &task->utcb_head->list);
/* Obtain and return first slot */
return utcb_new_slot(d);
}
int task_delete_utcb_desc(struct tcb *task, struct utcb_desc *d)
{
/* Unlink desc from its list */
list_remove_init(&d->list);
/* Unmap the descriptor region */
do_munmap(task, d->utcb_base, 1);
/* Return descriptor address */
utcb_delete_address((void *)d->utcb_base, 1);
/* Free the descriptor */
kfree(d);
return 0;
}
/*
* Upon fork, the utcb descriptor list is origaced by a new one, since it is a new
* address space. A new utcb is allocated and mmap'ed for the child task
* running in the newly created address space.
*
* The original privately mmap'ed regions for thread-local utcbs remain
* as copy-on-write on the new task, just like mmap'ed the stacks for cloned
* threads in the parent address space.
*
* Upon clone, naturally the utcb descriptor chain and vm_areas remain to be
* shared. A new utcb slot is allocated either by using an empty one in one of
* the existing mmap'ed utcb regions, or by mmaping a new utcb region.
*/
int task_setup_utcb(struct tcb *task)
{
struct utcb_desc *udesc;
unsigned long slot;
void *err;
/* Setting this up twice is a bug */
BUG_ON(task->utcb_address);
/* 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 out;
/* Allocate a new utcb memory region and return its base */
slot = task_new_utcb_desc(task);
out:
/* Check if utcb is already mapped (in case of multiple threads) */
if (!find_vma(slot, &task->vm_area_head->list)) {
/* Map this region as private to current task */
if (IS_ERR(err = do_mmap(0, 0, task, slot,
VMA_ANONYMOUS | VMA_PRIVATE |
VMA_FIXED | VM_READ | VM_WRITE, 1))) {
printf("UTCB: mmapping failed with %d\n", err);
return (int)err;
}
}
/* Assign task's utcb address */
task->utcb_address = slot;
// printf("UTCB created at 0x%x.\n", slot);
return 0;
}
/*
* Deletes a utcb slot by first deleting the slot entry, the descriptor
* address if emptied, the mapping of the descriptor, and the descriptor itself
*/
int task_destroy_utcb(struct tcb *task)
{
struct utcb_desc *udesc;
// printf("UTCB: Destroying 0x%x\n", task->utcb_address);
/* Find the utcb descriptor slot first */
list_foreach_struct(udesc, &task->utcb_head->list, list) {
/* FIXME: Use variable alignment than a page */
/* Detect matching slot */
if (page_align(task->utcb_address) == udesc->utcb_base) {
/* Delete slot from the descriptor */
utcb_delete_slot(udesc, task->utcb_address);
/* Is the desc completely empty now? */
if (id_is_empty(udesc->slots))
/* Delete the descriptor */
task_delete_utcb_desc(task, udesc);
return 0; /* Finished */
}
}
BUG();
}