Commit 2b829db5 authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan

Improved capability module.

parent b796391c
......@@ -19,10 +19,11 @@ static int test01(void)
struct cspace *cspace;
if (lcd_mk_cspace(&cspace))
LCD_FAIL("mk cspace");
lcd_rm_cspace(cspace);
lcd_rm_cspace(&cspace);
LCD_PASS();
}
#if 0
static int test02(void)
{
struct cspace *cspace;
......@@ -155,18 +156,20 @@ static int test05(void)
LCD_PASS();
}
#endif
int api_tests(void)
{
if (test01())
return -1;
if (test02())
return -1;
if (test03())
return -1;
if (test04())
return -1;
if (test05())
return -1;
/* if (test02()) */
/* return -1; */
/* if (test03()) */
/* return -1; */
/* if (test04()) */
/* return -1; */
/* if (test05()) */
/* return -1; */
LCD_MSG("all api tests passed!");
return 0;
}
......@@ -6,6 +6,34 @@
#include "defs.h"
#include "../include/common.h"
int lcd_cap_init(void)
{
mutex_init(&__lcd_cap_lock);
return 0;
}
static void init_cspace_cnodes(struct cspace *cspace)
{
int i;
struct cnode *cnode;
INIT_LIST_HEAD(&cspace->free_list);
for (i = 0; i < LCD_NUM_CAPS; i++) {
cnode = &cspace->cnodes[i];
INIT_LIST_HEAD(&cnode->free_list);
INIT_LIST_HEAD(&cnode->children);
INIT_LIST_HEAD(&cnode->child_list);
list_add(&cnode->free_list, &cspace->free_list);
cnode->cspace = cspace;
}
return;
}
int lcd_mk_cspace(struct cspace **cspace_ptr)
{
struct cspace *cspace;
......@@ -16,13 +44,11 @@ int lcd_mk_cspace(struct cspace **cspace_ptr)
if (!cspace)
return -ENOMEM;
/*
* Sets cnode types to unformed, and free_slot to 0
* Zero out all data (including cnodes). Sets cnode types to free with
* no rights. Initialize free list and cnodes.
*/
memset(cspace, 0, sizeof(*cspace));
/*
* Initialize lock
*/
mutex_init(&cspace->lock);
init_cspace_cnodes(cspace);
/*
* Done
*/
......@@ -30,56 +56,267 @@ int lcd_mk_cspace(struct cspace **cspace_ptr)
return 0;
}
int lcd_cap_insert(struct cspace *cspace, cptr_t cptr, void *object,
enum lcd_cap_type type)
static int __lcd_cnode_alloc(struct cspace *cspace, cptr_t *cptr)
{
struct cnode *cnode = NULL;
int ret;
if (list_empty(&cspace->free_list)) {
return -ENOMEM;
} else {
/*
* Get first available cnode, and remove it from the list.
*/
cnode = list_first_entry(&cspace->free_list, struct cspace,
free_list);
/*
* Remove from free list
*/
list_del_init(&cnode->free_list);
/*
* Mark it as unformed
*/
lcd_cnode_set_type(cnode, LCD_CAP_TYPE_UNFORMED);
/*
* Calc index
*/
*cptr = (cnode - (cspace->cnodes)) / sizeof(struct cnode);
return 0;
}
}
int lcd_cnode_alloc(struct cspace *cspace, cptr_t *cptr)
{
struct cnode *cnode;
int ret;
/*
* Lookup cnode
* LOCK cap
*/
ret = lcd_lock_cspace(cspace);
if (!ret) {
cnode = __lcd_cnode_lookup(cspace, cptr);
lcd_unlock_cspace(cspace);
} else {
ret = lcd_cap_lock();
if (ret)
return ret;
}
ret = __lcd_cnode_alloc(cspace, cptr);
/*
* Set vals
* UNLOCK cap
*/
if (cnode && cnode->type == LCD_CAP_TYPE_UNFORMED) {
lcd_cap_unlock();
return ret;
}
int __lcd_cnode_lookup(struct cspace *cspace, cptr_t cptr, struct cnode **out)
{
struct cnode *c;
int ret;
if (cptr >= LCD_NUM_CAPS)
return -EINVAL;
c = &cspace->cnodes[cptr];
*out = c;
return 0;
}
static int __lcd_cnode_insert(struct cnode *cnode, void *object,
enum lcd_cap_type type, int rights)
{
if (!lcd_cnode_is_unformed(cnode))
return -EINVAL;
else {
cnode->object = object;
cnode->type = type;
cnode->type = type;
cnode->rights = rights;
return 0;
} else {
return -EINVAL;
}
}
int lcd_cap_grant(struct cspace *src_cspace, struct cspace *dest_cspace,
cptr_t src_cptr, cptr_t dest_cptr)
int lcd_cnode_insert(struct cspace *cspace, cptr_t cptr,
void *object, enum lcd_cap_type type, int rights)
{
struct cnode *src_cnode;
struct cnode *cnode;
int ret;
/*
* Get source capability data
* LOCK cap
*/
ret = lcd_cnode_lookup(src_cspace, src_cptr, &src_cnode);
ret = lcd_cap_lock();
if (ret)
return ret;
if (!src_cnode)
ret = __lcd_cnode_lookup(cspace, cptr, &cnode);
if (ret)
goto out;
ret = __lcd_cnode_insert(cnode, object, type, rights);
out:
/*
* UNLOCK cap
*/
lcd_cap_unlock();
return ret;
}
static int __lcd_cnode_insert_for_grant(struct cnode *src_cnode,
struct cnode *dest_cnode, int rights)
{
int ret;
ret = __lcd_cnode_insert(dest_cnode, src_cnode->object,
src_cnode->type, rights);
if (ret)
return ret;
/*
* Add dest_cnode to src_cnode's children.
*/
list_add(&dest_cnode->child_list, &src_cnode->children);
return 0;
}
static int __lcd_cnode_grant(struct cspace *src_cspace,
struct cspace *dest_cspace, cptr_t src_cptr,
cptr_t dest_cptr, int rights)
{
struct cnode *src_cnode;
struct cnode *dest_cnode;
int dest_rights;
int ret;
if (src_cspace == dest_cspace)
return -EINVAL;
ret = __lcd_cnode_lookup(src_cspace, src_cptr, &src_cnode);
if (ret)
return ret;
if (!__lcd_cnode_can_grant(src_cnode))
return -EINVAL;
if (!__lcd_cap_can_grant(src_cnode))
ret = __lcd_cnode_lookup(dest_cspace, dest_cptr, &dest_cnode);
if (ret)
return ret;
if (!__lcd_cnode_is_occupied(src_cnode) ||
!__lcd_cnode_is_unformed(dest_cnode))
return -EINVAL;
dest_rights = lcd_cnode_rights(src_cnode) & rights;
ret = __lcd_insert_for_grant(src_cnode, dest_cnode, dest_rights);
return ret;
}
int lcd_cnode_grant(struct cspace *src_cspace, struct cspace *dest_cspace,
cptr_t src_cptr, cptr_t dest_cptr, int rights)
{
int ret;
/*
* LOCK cap
*/
ret = lcd_lock_cap();
if (ret)
return ret;
ret = __lcd_cnode_grant(src_cspace, dest_cspace, src_cptr, dest_cptr,
rights);
/*
* UNLOCK cap
*/
lcd_unlock_cap();
return ret;
}
static void __lcd_cnode_do_revoke(struct cnode *parent, int rights)
{
int ret;
struct cnode *child;
struct list_head *ptr;
list_for_each(ptr, &parent->children) {
child = list_entry(ptr, struct cnode, child_list);
/*
* Revoke child's rights
*/
child->rights &= ~rights;
/*
* Recur on its children
*
* XXX: Be aware of stack overflow
*/
__lcd_cnode_do_revoke(child, rights);
}
return;
}
static int __lcd_cnode_revoke(struct cspace *cspace, cptr_t cptr, int rights)
{
int ret;
struct cnode *parent;
ret = __lcd_cnode_lookup(cspace, cptr, &parent);
if (ret)
return ret;
__lcd_cnode_do_revoke(parent, rights);
return 0;
}
int lcd_cnode_revoke(struct cspace *cspace, cptr_t cptr, int rights)
{
int ret;
/*
* Insert into destination
* LOCK cap
*/
return lcd_cap_insert(dest_cspace, dest_cptr, src_cnode->object,
src_cnode->type);
ret = lcd_lock_cap();
if (ret)
return ret;
ret = __lcd_cnode_revoke(cspace, cptr, rights);
/*
* UNLOCK cap
*/
lcd_unlock_cap();
return ret;
}
void lcd_rm_cspace(struct cspace *cspace)
static void __lcd_cnode_do_free(struct cnode *parent)
{
struct cnode *child;
struct list_head *ptr;
struct list_head *n;
list_for_each_safe(ptr, n, &parent->children) {
child = list_entry(ptr, struct cnode, child_list);
/*
* Recur on child's children
*
* XXX: Be aware of stack overflow
*/
__lcd_cnode_do_free(cspace, child);
/*
* Remove from parent
*/
list_del_init(&child->child_list);
}
/*
* Mark parent as free
*/
__lcd_cnode_set_type(parent, LCD_CAP_TYPE_FREE);
/*
* Add to containing cspace free list
*/
list_add(&parent->free_list, &parent->cspace->free_list);
return;
}
void __lcd_cnode_free(struct cnode *cnode)
{
__lcd_cnode_do_free(cnode);
return;
}
void __lcd_rm_cnode(struct cnode *cnode)
{
if (lcd_cnode_is_occupied(cnode))
__lcd_cnode_do_free(cnode);
return;
}
void __lcd_rm_cspace(struct cspace **cspace_ptr)
{
int i;
struct cspace *cspace;
cspace = *cspace_ptr;
for (i = 0; i < LCD_NUM_CAPS; i++)
__lcd_rm_cnode(&cspace->cnodes[i]);
kfree(cspace);
*cspace_ptr = NULL;
return;
}
......@@ -15,31 +15,128 @@
/* CAPABILITIES -------------------------------------------------- */
/*
* This simple prototype uses a global lock on an lcd's cspace. No
* rights checking. The lcd need only have a valid cnode mapped by
* the cptr. Capability grants just copy cnode data from one cspace
* to another. Capabilities cannot be revoked, so if a cnode is valid,
* it can be used freely. Cnodes are allocated in a linear order, so
* no need to do fancy free lists, etc.
* A cspace is a simple static array of cnodes. Each cnode can have 0 or more
* derived children, in a linked list. This simple prototype uses a global
* mutex for the entire capability module. All routines marked as unsafe
* should be executed with the lock held.
*
* Unlike seL4, objects are not deallocated automatically when no one has a
* capability to it. Here is a typical sequence that exhibits the current
* features:
*
* // Create cspaces for two lcds.
* lcd_mk_cspace(&cspace1);
* lcd_mk_cspace(&cspace2);
*
* // Create a rendezvous point.
* rvp = mk_rvp();
*
* // Put rvp in cspace1, give lcd1 all rights (lcd1 is the owner). The
* // corresponding cnode will be parentless.
* lcd_cnode_alloc(cspace1, &cptr1);
* lcd_cnode_insert(cspace1, cptr1, rvp, LCD_CAP_RIGHT_ALL);
*
* // lcd1 grants write rights to lcd2. Internally, lcd1's cnode
* // will be the parent of lcd2's cnode.
* lcd_cnode_grant(cspace1, cptr1, cspace2, cptr2, rvp,
* LCD_CAP_RIGHT_WRITE);
*
* // lcd2 tries to send on rvp
* lcd_cap_lock();
* cnode2 = __lcd_cnode_lookup(cspace2, cptr2);
* if (__lcd_cnode_can_write(cnode2) && __lcd_cnode_sync_ep(cnode2))
* ... do send ...
* else
* ... fail ...
* lcd_cap_unlock();
*
* // lcd1 revokes write rights to all derived children (lcd2)
* lcd_cnode_revoke(cspace1, cptr1, LCD_CAP_RIGHT_WRITE);
*
* // lcd1 destroys the rvp. This revokes all rights given to lcd1,
* // and transitively, all rights to lcd2 (if they hadn't been
* // revoked already). Since the hypervisor created the object, it
* // will free the allocated memory for the rvp.
* lcd_cap_lock();
* ret = __lcd_cnode_lookup(cspace1, cptr1, &cnode1);
* if (ret)
* ... look up error ...
* if (__lcd_cnode_is_owner(cnode1) && __lcd_cnode_sync_ep(cnode1))
* rvp = cnode->object;
* free_rvp(rvp);
* __lcd_cnode_free(cspace1, cnode1);
* else
* ... fail ...
* lcd_cap_unlock();
*
* // lcd1 and lcd2 are torn down
* lcd_cap_lock();
* __lcd_rm_cspace(cspace1);
* __lcd_rm_cspace(cspace2);
* lcd_cap_unlock();
*
* All routines must exec in process context (no interrupt context,
* since processes will sleep when trying to acquire the mutex).
*/
enum lcd_cap_type {
LCD_CAP_TYPE_UNFORMED = 0,
LCD_CAP_TYPE_SYNC_EP = 1,
LCD_CAP_TYPE_FREE = 0,
LCD_CAP_TYPE_UNFORMED = 1,
LCD_CAP_TYPE_SYNC_EP = 2,
};
enum lcd_cap_right {
LCD_CAP_RIGHT_READ = (1 << 0),
LCD_CAP_RIGHT_WRITE = (1 << 1),
LCD_CAP_RIGHT_GRANT = (1 << 2),
LCD_CAP_RIGHT_OWNER = (1 << 3),
};
#define LCD_CAP_RIGHT_ALL ((1 << 4) - 1)
struct cspace;
struct cnode {
enum lcd_cap_type type;
int rights;
struct mutex lock;
/* Pointer to containing cspace */
struct cspace *cspace;
/* Linked list of free cnodes */
struct list_head free_list;
/* Pointer to linked list of my children */
struct list_head children;
/* Linked list of siblings */
struct list_head child_list;
/* The contained object */
void *object;
};
#define LCD_NUM_CAPS 32
struct cspace {
struct mutex lock;
struct cnode cnodes[LCD_NUM_CAPS];
int free_slot;
struct list_head free_list;
};
extern struct mutex __lcd_cap_lock;
/**
* Initializes capability module.
*/
int lcd_cap_init(void);
/**
* Lock entire capability module (all cspaces)
*/
static inline int lcd_cap_lock(void)
{
return mutex_lock_interruptible(&__lcd_cap_lock);
}
/**
* Unlock entire capability module (all cspaces)
*/
static inline void lcd_cap_unlock(void)
{
return mutex_unlock(&__lcd_cap_lock);
}
/**
* Return current's cspace
*/
......@@ -48,123 +145,109 @@ static inline struct cspace * lcd_cspace(void)
return current->lcd->cspace;
}
/**
* [SAFE]
* Allocates memory and initializes the contents of a cspace.
*/
int lcd_mk_cspace(struct cspace **cspace_ptr);
/**
* Acquires/releases global cspace lock. Must wrap calls to unsafe
* insert, lookup, etc. with lock/unlock. Lock returns zero if
* lock successfully taken (no signals).
* [SAFE, USES LOCK]
* Allocates an empty cnode slot, and returns a cptr to it. Returns
* non-zero if no free slots available, or couldn't get lock.
*
* (It is necessary to separate allocation and insertion since an lcd
* can specify where it wants incoming capabilities inserted, via ipc.)
*/
static inline int lcd_lock_cspace(struct cspace *cspace)
{
return mutex_lock_interruptible(&cspace->lock);
}
static inline void lcd_unlock_cspace(struct cspace *cspace)
{
mutex_unlock(&cspace->lock);
}
int lcd_cnode_alloc(struct cspace *cspace, cptr_t *cptr);
/**
* (SAFE) Allocates an empty cnode slot, and returns a cptr to it. Returns
* non-zero if no free slots available.
* [UNSAFE]
* Look up cnode in cspace at cptr. Returns non-zero if cptr is out of range.
*/
static inline int lcd_cap_alloc(struct cspace *cspace, cptr_t *cptr)
{
int ret = lcd_lock_cspace(cspace);
if (!ret) {
if (cspace->free_slot < LCD_NUM_CAPS)
*cptr = cspace->free_slot++;
else
ret = -ENOMEM;
lcd_unlock_cspace(cspace);
}
return ret;
}
int __lcd_cnode_lookup(struct cspace *cspace, cptr_t cptr, struct cnode **out);
/**
* (SAFE) Insert object into cspace.
* [SAFE, USES LOCK]
* Insert object into cspace with type and rights. Fails if the type of
* the cnode is not unformed (cnode is free or occupied), or if it fails to
* get the lock (interrupted).
*
* (Use lcd_cnode_grant to transfer a capability from one cspace to another,
* and update the parent's children.)
*/
int lcd_cap_insert(struct cspace *cspace, cptr_t cptr,
void *object, enum lcd_cap_type type);
int lcd_cnode_insert(struct cspace *cspace, cptr_t cptr,
void *object, enum lcd_cap_type type, int rights);
/**
* (UNSAFE) Look up cnode in cspace at cptr.
* [SAFE, USES LOCK]
* Transfer capability from one cspace to another. Fails if we can't get
* the lock; if source is not occupied, or dest is not unformed; if source
* doesn't have grant rights; or if the source and destination cspaces are the
* same.
*/
static inline struct cnode *__lcd_cnode_lookup(struct cspace *cspace,
cptr_t cptr)
{
BUG_ON(cspace->free_slot > LCD_NUM_CAPS);
if (cptr < cspace->free_slot)
return &cspace->cnodes[cptr];
else
return NULL;
}
int lcd_cnode_grant(struct cspace *src_cspace, struct cspace *dest_cspace,
cptr_t src_cptr, cptr_t dest_cptr, int rights);
/**
* (SAFE) Look up cnode in cspace at cptr. Returns non-zero if lock
* failed. out set to null if cnode is marked as unformed.
* [SAFE, USES LOCK]
* Revoke rights from all derived cnodes. Fails if we can't lock. A cnode
* is not freed if its rights drop to nothing.
*/
static inline int lcd_cnode_lookup(struct cspace *cspace, cptr_t cptr,
struct cnode **out)
{
struct cnode *c = NULL;
int ret = lcd_lock_cspace(cspace);
if (!ret) {
c = __lcd_cnode_lookup(cspace, cptr);
if (c && c->type == LCD_CAP_TYPE_UNFORMED)
c = NULL;
lcd_unlock_cspace(cspace);
}
*out = c;
return ret;
}
int lcd_cnode_revoke(struct cspace *cspace, cptr_t cptr, int rights);
/**
* (SAFE) Transfer capability from one cspace to another.
* [UNSAFE]
* Frees the slot containing cnode in cspace, and recursively frees derived
* children in other cspaces (so this also acts as a total revoke).
*/
int __lcd_cnode_free(struct cspace *cspace, struct cnode *cnode);
/**
* [UNSAFE]
* Deallocates cspace. Caller should ensure no one is trying to
* use the cspace (e.g., via grant, insert, etc.). This will revoke all
* cnodes derived from this cspace.
*/
int lcd_cap_grant(struct cspace *src_cspace, struct cspace *dest_cspace,
cptr_t src_cptr, cptr_t dest_cptr);
void __lcd_rm_cspace(struct cspace *cspace);
/**
* (SAFE) Get capability type.
* [UNSAFE]
* Quick inlines for determining rights and type. These should be wrapped
* in a lookup/release.