Commit 647e7c9b authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan
Browse files

host-resource-trees: Update higher level alloc, map, unmap for trees.

To mirror what will happen inside an isolated container, when
code does a higher-level page alloc, it expects the pages to
be mapped in its virtual and physical address spaces. It also
expects to be able to do address -> cptr translations. This means
we need to insert the page memory object into the resource tree
during page alloc (and remove it when the pages are freed).

Similarly, for higher-level map/unmap, we need to update the resource
trees - as this is what will happen inside an isolated LCD, and
non-isolated code expects to be able to do address -> cptr
translation.

Almost done with this boring but important feature ...
parent d39579d0
......@@ -18,6 +18,11 @@
* For now, resource trees are per-thread (these are not the same
* thing as the global memory interval tree). So, we don't use any
* locks to protect them.
*
* We also expect non-isolated code to not be too tricky. For example,
* we don't expect it to insert host memory as RAM and VMALLOC memory
* simultaneously (one piece of physical memory, but also mapped via
* vmalloc). Non-isolated code is trusted after all ...
*/
#define LCD_RESOURCE_TREE_RAM_IDX 0
#define LCD_RESOURCE_TREE_DEV_MEM_IDX 0
......@@ -80,74 +85,6 @@ void lcd_destroy_free_resource_tree(struct lcd_resource_tree *t)
kfree(t);
}
/* LOW-LEVEL PAGE ALLOC -------------------------------------------------- */
int _lcd_alloc_pages_exact_node(int nid, unsigned int flags,
unsigned int order, cptr_t *slot_out)
{
struct lcd *lcd = current->lcd;
cptr_t slot;
int ret;
/*
* Allocate a slot in caller's cspace
*/
ret = lcd_cptr_alloc(&slot);
if (ret) {
LIBLCD_ERR("cptr alloc failed");
goto fail1;
}
/*
* Allocate pages
*/
ret = __lcd_alloc_pages_exact_node(lcd, slot, nid, flags, order);
if (ret) {
LIBLCD_ERR("alloc pages exact node failed");
goto fail2;
}
*slot_out = slot;
return 0;
fail2:
lcd_cptr_free(slot);
fail1:
return ret;
}
int _lcd_alloc_pages(unsigned int flags, unsigned int order,
cptr_t *slot_out)
{
struct lcd *lcd = current->lcd;
cptr_t slot;
int ret;
/*
* Allocate a slot in caller's cspace
*/
ret = lcd_cptr_alloc(&slot);
if (ret) {
LIBLCD_ERR("cptr alloc failed");
goto fail1;
}
/*
* Allocate pages
*/
ret = __lcd_alloc_pages(lcd, slot, flags, order);
if (ret) {
LIBLCD_ERR("alloc pages failed");
goto fail2;
}
*slot_out = slot;
return 0;
fail2:
lcd_cptr_free(slot);
fail1:
return ret;
}
/* LOW-LEVEL MAP -------------------------------------------------- */
static int mo_insert_in_tree(struct lcd_resource_tree *t,
struct lcd_memory_object *mo,
cptr_t mo_cptr)
......@@ -178,24 +115,25 @@ fail1:
return ret;
}
static int mo_insert_in_trees(struct lcd *lcd, struct lcd_memory_object *mo,
static int mo_insert_in_trees(struct task_struct *t,
struct lcd_memory_object *mo,
cptr_t mo_cptr)
{
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
return mo_insert_in_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
mo,
mo_cptr);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return mo_insert_in_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_DEV_MEM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_DEV_MEM_IDX],
mo,
mo_cptr);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return mo_insert_in_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_VMALLOC_MEM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_VMALLOC_MEM_IDX],
mo,
mo_cptr);
default:
......@@ -217,21 +155,21 @@ static int mo_in_tree(struct lcd_resource_tree *t,
&unused) == 0;
}
static int mo_in_trees(struct lcd *lcd, struct lcd_memory_object *mo)
static int mo_in_trees(struct task_struct *t, struct lcd_memory_object *mo)
{
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
return mo_in_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
mo);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return mo_in_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_DEV_MEM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_DEV_MEM_IDX],
mo);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return mo_in_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_VMALLOC_MEM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_VMALLOC_MEM_IDX],
mo);
default:
LCD_ERR("unexpected memory object type %d",
......@@ -240,52 +178,6 @@ static int mo_in_trees(struct lcd *lcd, struct lcd_memory_object *mo)
}
}
int _lcd_mmap(cptr_t mo, gpa_t base)
{
struct lcd_memory_object *mo;
struct cnode *cnode;
int ret;
/*
* Ignore gpa arg - non-isolated code cannot change physical
* mappings
*
*
* Get and lock the memory object
*/
ret = __lcd_get_memory_object(current->lcd, mo, &cnode, &mo);
if (ret) {
LIBLCD_ERR("error looking up memory object");
goto fail1;
}
/*
* Check if it's already in a resource tree
*/
if (mo_in_trees(current->lcd, mo)) {
LIBLCD_ERR("memory object already mapped");
goto fail2;
}
/*
* No; put it in the correct one, depending on its type
*/
ret = mo_insert_in_trees(current->lcd, mo, mo_cptr);
if (ret) {
LIBLCD_ERR("insert into resource tree failed");
goto fail3;
}
/*
* Release locks
*/
__lcd_put_memory_object(current->lcd, cnode, mo);
return 0;
fail3:
fail2:
__lcd_put_memory_object(current->lcd, cnode, mo);
fail1:
return ret;
}
static void mo_remove_from_tree(struct lcd_resource_tree *tree,
struct lcd_memory_object *mo)
{
......@@ -308,21 +200,22 @@ static void mo_remove_from_tree(struct lcd_resource_tree *tree,
kfree(node);
}
static void mo_remove_from_trees(struct lcd *lcd, struct lcd_memory_object *mo)
static void mo_remove_from_trees(struct task_struct *t,
struct lcd_memory_object *mo)
{
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
return mo_remove_from_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
mo);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return mo_remove_from_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_DEV_MEM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_DEV_MEM_IDX],
mo);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return mo_remove_from_tree(
lcd->lcd_resource_trees[LCD_RESOURCE_TREE_VMALLOC_MEM_IDX],
t->lcd_resource_trees[LCD_RESOURCE_TREE_VMALLOC_MEM_IDX],
mo);
default:
LCD_ERR("unexpected memory object type %d",
......@@ -331,12 +224,149 @@ static void mo_remove_from_trees(struct lcd *lcd, struct lcd_memory_object *mo)
}
}
void _lcd_munmap(cptr_t mo)
static int phys_addr_to_mo_cptr(struct task_struct *t,
unsigned long addr,
cptr_t *mo_cptr)
{
int ret;
struct lcd_resource_node *n;
/*
* Look in RAM/devmem tree
*/
ret = lcd_resource_tree_search(
t->lcd_resource_trees[LCD_RESOURCE_TREE_RAM_IDX],
addr,
&n);
if (ret) {
LIBLCD_ERR("address not found");
goto fail1;
}
/*
* Pull out memory object cptr
*/
mo_cptr = n->cptr;
return 0;
}
/* LOW-LEVEL PAGE ALLOC -------------------------------------------------- */
int _lcd_alloc_pages_exact_node(int nid, unsigned int flags,
unsigned int order, cptr_t *slot_out)
{
struct lcd *lcd = current->lcd;
cptr_t slot;
int ret;
/*
* Allocate a slot in caller's cspace
*/
ret = lcd_cptr_alloc(&slot);
if (ret) {
LIBLCD_ERR("cptr alloc failed");
goto fail1;
}
/*
* Allocate pages
*/
ret = __lcd_alloc_pages_exact_node(lcd, slot, nid, flags, order);
if (ret) {
LIBLCD_ERR("alloc pages exact node failed");
goto fail2;
}
*slot_out = slot;
return 0;
fail2:
lcd_cptr_free(slot);
fail1:
return ret;
}
int _lcd_alloc_pages(unsigned int flags, unsigned int order,
cptr_t *slot_out)
{
struct lcd *lcd = current->lcd;
cptr_t slot;
int ret;
/*
* Allocate a slot in caller's cspace
*/
ret = lcd_cptr_alloc(&slot);
if (ret) {
LIBLCD_ERR("cptr alloc failed");
goto fail1;
}
/*
* Allocate pages
*/
ret = __lcd_alloc_pages(lcd, slot, flags, order);
if (ret) {
LIBLCD_ERR("alloc pages failed");
goto fail2;
}
*slot_out = slot;
return 0;
fail2:
lcd_cptr_free(slot);
fail1:
return ret;
}
/* LOW-LEVEL MAP -------------------------------------------------- */
int __lcd_mmap(struct lcd *lcd, struct lcd_memory_object *mo,
cptr_t mo_cptr, gpa_t base)
{
int ret;
/*
* Check if it's already in a resource tree
*/
if (mo_in_trees(current, mo)) {
LIBLCD_ERR("memory object already mapped");
ret = -EINVAL;
goto fail1;
}
/*
* No; put it in the correct one, depending on its type
*/
ret = mo_insert_in_trees(current, mo, mo_cptr);
if (ret) {
LIBLCD_ERR("insert into resource tree failed");
goto fail2;
}
/*
* "Map" in physical address space (this is a no-op for
* non-isolated code right now)
*/
ret = __lcd_mem_object_map(lcd, mo, cap_cnode_metadata(cnode),
__gpa(0));
if (ret) {
LIBLCD_ERR("physical map failed");
goto fail3;
}
return 0;
fail3:
mo_remove_from_trees(current, mo);
fail2:
fail1:
return ret;
}
int _lcd_mmap(cptr_t mo, gpa_t base)
{
struct lcd_memory_object *mo;
struct cnode *cnode;
int ret;
/*
* Ignore gpa arg - non-isolated code cannot change physical
* mappings
*
*
* Get and lock the memory object
*/
ret = __lcd_get_memory_object(current->lcd, mo, &cnode, &mo);
......@@ -344,10 +374,59 @@ void _lcd_munmap(cptr_t mo)
LIBLCD_ERR("error looking up memory object");
goto fail1;
}
/*
* Do the map
*/
ret = __lcd_mmap(lcd, mo, mo_cptr, base);
if (ret) {
LIBLCD_ERR("error mapping mem object");
goto fail2;
}
/*
* Release locks
*/
__lcd_put_memory_object(current->lcd, cnode, mo);
return 0;
fail2:
__lcd_put_memory_object(current->lcd, cnode, mo);
fail1:
return ret;
}
void __lcd_munmap(struct lcd *lcd, struct lcd_memory_object *mo,
struct cnode *mo_cnode)
{
/*
* Remove from resource trees
*/
mo_remove_from_trees(current->lcd, mo);
mo_remove_from_trees(current, mo);
/*
* "Unmap" from physical address space (this is a no-op for
* non-isolated code right now)
*/
__lcd_mem_object_unmap(current->lcd, mo,
cap_cnode_metadata(cnode));
}
void _lcd_munmap(cptr_t mo_cptr)
{
struct lcd_memory_object *mo;
struct cnode *cnode;
int ret;
/*
* Get and lock the memory object
*/
ret = __lcd_get_memory_object(current->lcd, mo_cptr, &cnode, &mo);
if (ret) {
LIBLCD_ERR("error looking up memory object");
goto fail1;
}
/*
* Do the unmap
*/
__lcd_munmap(current->lcd, mo, cnode);
/*
* Release locks
*/
......@@ -376,19 +455,31 @@ struct page *lcd_alloc_pages_exact_node(int nid, unsigned int flags,
LIBLCD_ERR("lower level alloc failed");
goto fail1;
}
/*
* "Map" memory object in caller's physical address space
* (all this really does is put the allocated pages in the
* proper resource tree)
*/
ret = _lcd_mmap(slot, __gpa(0));
if (ret) {
LIBLCD_ERR("internal error: putting mem obj in tree");
goto fail2;
}
/*
* Look up memory object so we can get struct page pointer
*/
ret = __lcd_get_memory_object(lcd, slot, &cnode, &mo);
if (ret) {
LIBLCD_ERR("internal error: mem lookup failed");
goto fail2;
goto fail3;
}
p = mo->object;
__lcd_put_memory_object(lcd, cnode, mo);
return p;
fail3:
_lcd_munmap(slot);
fail2:
lcd_cap_delete(slot); /* frees pages, etc. */
fail1:
......@@ -411,19 +502,31 @@ struct page *lcd_alloc_pages(unsigned int flags, unsigned int order)
LIBLCD_ERR("lower level alloc failed");
goto fail1;
}
/*
* "Map" memory object in caller's physical address space
* (all this really does is put the allocated pages in the
* proper resource tree)
*/
ret = _lcd_mmap(slot, __gpa(0));
if (ret) {
LIBLCD_ERR("internal error: putting mem obj in tree");
goto fail2;
}
/*
* Look up memory object so we can get struct page pointer
*/
ret = __lcd_get_memory_object(lcd, slot, &cnode, &mo);
if (ret) {
LIBLCD_ERR("internal error: mem lookup failed");
goto fail2;
goto fail3;
}
p = mo->object;
__lcd_put_memory_object(lcd, cnode, mo);
return p;
fail3:
_lcd_munmap(slot);
fail2:
lcd_cap_delete(slot); /* frees pages, etc. */
fail1:
......@@ -453,6 +556,10 @@ void lcd_free_pages(struct page *base, unsigned int order)
}
if (order != actual_order)
LIBLCD_ERR("warning: order doesn't match actual order");
/*
* Remove pages from resource tree ("unmap")
*/
_lcd_munmap(page_cptr);
/*
* Free pages
*/
......@@ -470,46 +577,72 @@ int lcd_map_phys(cptr_t pages, unsigned int order, gpa_t *base_out)
* access to all physical memory. (We could maybe check that
* the order is correct.)
*
* XXX: It's significant that we don't call
* __lcd_map_memory_object. This means we won't update any
* mapping metadata for the non-isolated thread's pages
* capability, and so nothing will be done when the
* capability is revoked.
*
* Look up memory object so we can get struct page pointer,
* and then translate to physical address
*/
ret = __lcd_get_memory_object(lcd, slot, &cnode, &mo);
ret = __lcd_get_memory_object(current->lcd, slot, &cnode, &mo);
if (ret) {
LIBLCD_ERR("internal error: mem lookup failed");
goto fail1;
}
/*
* "Map" the pages (adds pages to proper resource tree)
*/
ret = __lcd_mmap(current->lcd, mo, cnode, __gpa(0));
if (ret) {
LIBLCD_ERR("error mapping pages in resource tree");
goto fail2;
}
p = mo->object;
__lcd_put_memory_object(lcd, cnode, mo);
/* guest physical == host physical for non-isolated */
*base_out = __gpa(hpa_val(va2hpa(page_address(p))));
/*
* Release memory object
*/
__lcd_put_memory_object(current->lcd, cnode, mo);
return 0;
fail2:
__lcd_put_memory_object(current->lcd, cnode, mo);
fail1:
return ret;
}
int lcd_map_virt(gpa_t base, unsigned int order, gva_t *gva_out)
{
int ret;
cptr_t mo_cptr;
/*
* On x86_64, all RAM is mapped already. Note that we
* don't bother checking whether the non-isolated thread
* actually has a capability to the RAM.
* On x86_64, all RAM is mapped already.
*
* But we still check to ensure non-isolated code has
* access to physical address via capabilities and has
* it mapped in its resource tree.
*
* XXX: For arch's with smaller virtual address spaces,
* we need to kmap or similar.
*
* guest virtual == host virtual for non-isolated
*/
ret = phys_addr_to_mo_cptr(current, gpa_val(base), &mo_cptr);
if (ret) {
LIBLCD_ERR("phys not mapped?");
goto fail1;
}
/*
* If we got a "hit", then there's no need to get the actual
* memory object (base is guest physical == host physical for
* non-isolated)
*/
*gva_out = __gva(hva_val(pa2hva(gpa_val(base))));
return 0;
fail1:
return ret;
}
int lcd_map_both(cptr_t pages, unsigned int order, gva_t *gva_out)
......@@ -530,32 +663,55 @@ int lcd_map_both(cptr_t pages, unsigned int order, gva_t *gva_out)
void lcd_unmap_phys(gpa_t base, unsigned int order)
{
int ret;
cptr_t mo_cptr;
/*
* No-op. No need to unmap in physical for the host.
* No real unmapping needs to be done, but we need to
* update the resource tree.
*
* We don't mark the non-isolated thread's capability metadata
* as is_mapped to begin with, so no need to unset it.
* Look up cptr for physical memory
*/
ret = phys_addr_to_mo_cptr(current, gpa_val(base), &mo_cptr);
if (ret) {
LIBLCD_ERR("phys not mapped?");
return;
}
/*
* Remove from resource tree
*/
_lcd_munmap(mo_cptr);
}
void lcd_unmap_virt(gva_t base, unsigned int order)
{
/*
* No-op.
* This is truly a no-op because all we did in lcd_map_virt
* was just check if the caller has the physical address in
* its resource tree.
*
* XXX: At least for x86_64. For arch's with smaller virtual
* address spaces, we need to kunmap or similar.
*
* We don't mark the non-isolated thread's capability metadata
* as is_mapped to begin with, so no need to unset it.
*/
}
void lcd_unmap_both(gva_t base, unsigned int order)
{
gpa_t base_gpa;
/*
* Get base as a physical address