mirror of
https://github.com/drasko/codezero.git
synced 2026-01-14 11:53:15 +01:00
185 lines
4.6 KiB
C
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();
|
|
}
|
|
|
|
|