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

libcap-integration: Add support for sharing vmalloc memory.

We were doing this before (memory for module bits is vmalloc'd),
but we used a capability per page. This leads to a handful of
cptr's we have to keep track of, rather than just a couple.
Moreover, when I tune page allocation inside the LCD, I may
have to vmalloc memory (if the required chunks are big
enough).

So, one vmalloc allocation should be represented with a
single capability. (We sacrifice some access control granularity.)
parent be3834c3
......@@ -27,27 +27,14 @@ static void page_revoke(struct cspace *cspace, struct cnode *cnode,
struct lcd_mapping_metadata *m;
struct lcd_memory_object *mo = (struct lcd_memory_object *)object;
/*
* Check if page is mapped; if not, nothing to do.
*
* (m can be null in the edge case that we're revoking this
* capability before we had a chance to store the metadata.)
* Unmap memory object
*/
m = cap_cnode_metadata(cnode);
if (!m || !m->is_mapped)
goto out;
__lcd_do_unmap_memory_object(cap_cspace_getowner(cspace), mo, m);
/*
* Unmap it
*
* XXX: This assumes the cspace is for an isolated LCD. For now,
* this is true, because the liblcd interface doesn't allow kLCDs
* to map memory. (They don't need to for RAM because it's already
* mapped in their address space. But they would for dev mem.)
* Free metadata
*/
__lcd_unmap_pages(cap_cspace_getowner(cspace), m->where_mapped,
1 << mo->order);
out:
kfree(m);
if (m)
kfree(m);
}
static void volunteered_page_revoke(struct cspace *cspace, struct cnode *cnode,
......@@ -56,8 +43,14 @@ static void volunteered_page_revoke(struct cspace *cspace, struct cnode *cnode,
page_revoke(cspace, cnode, object);
}
static void dev_mem_revoke(struct cspace *cspace, struct cnode *cnode,
void *object)
static void volunteered_dev_mem_revoke(struct cspace *cspace,
struct cnode *cnode, void *object)
{
page_revoke(cspace, cnode, object);
}
static void volunteered_vmalloc_mem_revoke(struct cspace *cspace,
struct cnode *cnode, void *object)
{
page_revoke(cspace, cnode, object);
}
......@@ -156,8 +149,8 @@ static void volunteered_page_delete(struct cspace *cspace, struct cnode *cnode,
kfree(object);
}
static void dev_mem_delete(struct cspace *cspace, struct cnode *cnode,
void *object)
static void volunteered_dev_mem_delete(struct cspace *cspace,
struct cnode *cnode, void *object)
{
/*
* The device memory itself (e.g., high memory, ISA memory) shouldn't
......@@ -171,6 +164,16 @@ static void dev_mem_delete(struct cspace *cspace, struct cnode *cnode,
kfree(object);
}
static void volunteered_vmalloc_mem_delete(struct cspace *cspace,
struct cnode *cnode, void *object)
{
/*
* Just like volunteered pages, the original volunteer is
* responsible for freeing the memory.
*/
kfree(object);
}
static void sync_endpoint_delete(struct cspace *cspace, struct cnode *cnode,
void *object)
{
......@@ -212,9 +215,16 @@ static struct type_ops_id mk_type_ops_id[LCD_MICROKERNEL_NUM_CAP_TYPES] = {
},
{
{
.name = "device memory",
.delete = dev_mem_delete,
.revoke = dev_mem_revoke,
.name = "volunteered device memory",
.delete = volunteered_dev_mem_delete,
.revoke = volunteered_dev_mem_revoke,
}
},
{
{
.name = "volunteered vmalloc memory",
.delete = volunteered_vmalloc_mem_delete,
.revoke = volunteered_vmalloc_mem_revoke,
}
},
{
......
......@@ -111,7 +111,8 @@ static inline hpa_t hva2hpa(hva_t hva)
enum lcd_microkernel_type_id {
LCD_MICROKERNEL_TYPE_ID_PAGE,
LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE,
LCD_MICROKERNEL_TYPE_ID_DEV_MEM,
LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM,
LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM,
LCD_MICROKERNEL_TYPE_ID_SYNC_EP,
LCD_MICROKERNEL_TYPE_ID_LCD,
LCD_MICROKERNEL_TYPE_ID_KLCD,
......
......@@ -209,8 +209,8 @@ void __lcd_put_memory_object(struct lcd *caller, struct cnode *cnode,
/* MAPPING -------------------------------------------------- */
static int memory_object_hpa(struct lcd_memory_object *mo,
hpa_t *hpa_base)
static int contiguous_memory_object_hpa(struct lcd_memory_object *mo,
hpa_t *hpa_base)
{
hpa_t hpa_base;
......@@ -219,7 +219,7 @@ static int memory_object_hpa(struct lcd_memory_object *mo,
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
*hpa_base = va2hpa(page_address(mo->object));
break;
case LCD_MICROKERNEL_TYPE_ID_DEV_MEM:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
*hpa_base = hpa_val((unsigned long)mo->object);
break;
default:
......@@ -231,66 +231,78 @@ static int memory_object_hpa(struct lcd_memory_object *mo,
return 0;
}
int __lcd_map_pages(struct lcd *caller, gpa_t gpa, hpa_t hpa,
unsigned int num_pages)
static int isolated_map_vmalloc_mem(struct lcd *lcd,
struct lcd_memory_object *vmalloc_mo,
gpa_t base)
{
int ret, err_ret;
hva_t vmalloc_base, hva;
gpa_t gpa;
unsigned long i, j;
struct page *p;
/*
* Only isolated VMs should be landing here.
* The base host virtual address is stored in the object
*/
BUG_ON(caller->type != LCD_ISOLATED);
/*
* Create, and do not overwrite
vmalloc_base = __hva((unsigned long)vmalloc_mo->object);
/*
* Map each page, one at a time
*/
return lcd_arch_ept_map_range(caller->lcd_arch, gpa, hpa, num_pages);
}
for (i = 0; i < (1UL << mo->order); i++) {
hva = hva_add(vmalloc_base, i * PAGE_SIZE);
gpa = gpa_add(base, i * PAGE_SIZE);
/*
* Get the corresponding host page
*/
p = vmalloc_to_page(hva2va(hva));
/*
* Map it in the LCD's guest physical
*/
ret = lcd_arch_ept_map(lcd->lcd_arch, gpa,
va2hpa(page_address(p)),
1, 0);
if (ret) {
LCD_ERR("error mapping vmalloc page in LCD");
goto fail1;
}
}
return 0;
void __lcd_unmap_pages(struct lcd *caller, gpa_t gpa, unsigned int num_pages)
{
fail1:
/*
* Only isolated VMs should be landing here.
* Unmap pages that were mapped
*/
BUG_ON(caller->type != LCD_ISOLATED);
int ret;
ret = lcd_arch_ept_unmap_range(caller->lcd_arch, gpa, num_pages);
if (ret)
LCD_DEBUG(LCD_DEBUG_ERR,
"some error unmapping");
for (j = 0; j < i; j++) {
hva = hva_add(vmalloc_base, j * PAGE_SIZE);
gpa = gpa_add(base, j * PAGE_SIZE);
/*
* Get the corresponding host page
*/
p = vmalloc_to_page(hva2va(hva));
/*
* Unmap it from the LCD's guest physical
*/
err_ret = lcd_arch_ept_unmap(lcd->lcd_arch, gpa);
if (err_ret)
LCD_ERR("double fault: error unmapping vmalloc page in LCD");
}
return ret;
}
int __lcd_map_memory_object(struct lcd *caller, cptr_t mo_cptr, gpa_t base)
static int isolated_map_contiguous_mem(struct lcd *lcd,
struct lcd_memory_object *mo,
gpa_t base)
{
int ret;
struct lcd_memory_object *mo;
struct lcd_mapping_metadata *meta;
struct cnode *cnode;
hpa_t base_hpa;
/*
* Look up memory object in caller's cspace
*/
ret = __lcd_get_memory_object(caller->cspace, mo_cptr, &cnode, &mo);
if (ret)
goto fail1;
meta = cap_cnode_metadata(cnode);
/*
* If memory object is already mapped, fail
*/
if (!meta) {
LCD_ERR("lookup before meta set?");
ret = -EIO;
goto fail2;
}
if (meta->is_mapped) {
LCD_ERR("memory object already mapped");
ret = -EINVAL;
goto fail2;
}
hpa_t hpa_base;
/*
* Get host physical address of start of memory object
*/
ret = memory_object_hpa(mo, &hpa_base);
ret = contiguous_memory_object_hpa(mo, &hpa_base);
if (ret) {
LCD_ERR("invalid memory object");
goto fail2;
goto fail1;
}
/*
* Map memory object.
......@@ -300,16 +312,115 @@ int __lcd_map_memory_object(struct lcd *caller, cptr_t mo_cptr, gpa_t base)
* with it because we allow the LCD to use the PAT to control
* caching, and we always map memory as WB in guest physical.
*/
ret = __lcd_map_pages(lcd, gpa, hpa_base, 1 << mo->order)
ret = lcd_arch_ept_map_range(lcd->lcd_arch, base, hpa_base,
1 << mo->order);
if (ret) {
LCD_ERR("map");
goto fail2;
}
fail2:
fail1:
return ret;
}
static int isolated_map_memory_object(struct lcd *lcd,
struct lcd_memory_object *mo,
struct lcd_mapping_metadata *meta,
gpa_t base)
{
/*
* The mapping process depends on the memory object type
* (physical memory, vmalloc memory, etc.)
*/
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return isolated_map_contiguous_mem(lcd, mo, meta, base);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return isolated_map_vmalloc_mem(lcd, mo, meta, base);
default:
LCD_ERR("unexpected memory object type %d", mo->sub_type);
return -EINVAL;
}
}
int __lcd_do_map_memory_object(struct lcd *lcd,
struct lcd_memory_object *mo,
struct lcd_mapping_metadata *meta,
gpa_t base)
{
int ret;
/*
* If memory object is already mapped, fail
*/
if (!meta) {
LCD_ERR("lookup before meta set?");
ret = -EIO;
goto out;
}
if (meta->is_mapped) {
LCD_ERR("memory object already mapped");
ret = -EINVAL;
goto out;
}
/*
* We need to handle mapping differently depending on
* the LCD type (isolated vs non-isolated)
*/
switch (lcd->type) {
case LCD_TYPE_ISOLATED:
ret = isolated_map_memory_object(lcd, mo, meta, base);
break;
case LCD_TYPE_NONISOLATED:
case LCD_TYPE_TOP:
/*
* For now, map is a no-op for non-isolated code. (All host
* physical is available to non-isolated code. Recall that
* this function is about mapping in physical, not virtual.)
*/
ret = 0;
goto out;
default:
LCD_ERR("unrecognized lcd type %d", lcd->type);
ret = -EINVAL;
break;
}
if (ret)
goto out;
/*
* Mark page as mapped, and where it's mapped
*/
meta->is_mapped = 1;
meta->where_mapped = gpa;
meta->where_mapped = base;
ret = 0;
goto out;
out:
return ret;
}
int __lcd_map_memory_object(struct lcd *caller, cptr_t mo_cptr, gpa_t base)
{
int ret;
struct lcd_mapping_metadata *meta;
struct lcd_memory_object *mo;
struct cnode *cnode;
/*
* Look up memory object and metadata in caller's cspace
*/
ret = __lcd_get_memory_object(caller->cspace, mo_cptr, &cnode, &mo);
if (ret)
goto fail1;
meta = cap_cnode_metadata(cnode);
/*
* Do the map
*/
ret = __lcd_do_map_memory_object(caller, mo, meta, base);
if (ret)
goto fail3;
/*
* Release cnode, etc.
*/
......@@ -317,46 +428,140 @@ int __lcd_map_memory_object(struct lcd *caller, cptr_t mo_cptr, gpa_t base)
return 0;
fail3:
fail2:
__lcd_put_memory_object(caller, cnode, mo);
fail1:
return ret;
}
}
void __lcd_unmap_memory_object(struct lcd *caller, cptr_t mo_cptr)
/* UNMAPPING -------------------------------------------------- */
static void isolated_unmap_vmalloc_mem(struct lcd *lcd,
struct lcd_memory_object *vmalloc_mo,
struct lcd_mapping_metadata *meta)
{
int ret;
gpa_t gpa;
struct lcd_memory_object *mo;
struct lcd_mapping_metadata *meta;
struct cnode *cnode;
/*
* Look up memory object in caller's cspace
* Unmapping is easier, because we mapped the vmalloc memory
* in one contiguous chunk.
*/
ret = __lcd_get_memory_object(caller->cspace, mo_cptr, &cnode, &mo);
ret = lcd_arch_ept_unmap_range(lcd->lcd_arch,
meta->where_mapped,
vmalloc_mo->order);
if (ret)
goto fail1;
meta = cap_cnode_metadata(cnode);
LCD_DEBUG(LCD_DEBUG_ERR,
"some error unmapping");
}
static void isolated_unmap_contiguous_mem(struct lcd *lcd,
struct lcd_memory_object *mo,
struct lcd_mapping_metadata *meta)
{
/*
* Unmap memory object
*/
ret = lcd_arch_ept_unmap_range(lcd->lcd_arch, meta->where_mapped,
1UL << mo->order);
if (ret)
LCD_DEBUG(LCD_DEBUG_ERR,
"some error unmapping");
}
static void isolated_unmap_memory_object(struct lcd *lcd,
struct lcd_memory_object *mo,
struct lcd_mapping_metadata *meta)
{
/*
* The unmapping process depends on the memory object type
* (physical memory, vmalloc memory, etc.)
*/
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return isolated_unmap_contiguous_mem(lcd, mo, meta);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return isolated_unmap_vmalloc_mem(lcd, mo, meta);
default:
LCD_ERR("unexpected memory object type %d", mo->sub_type);
return -EINVAL;
}
}
void __lcd_do_unmap_memory_object(struct lcd *caller,
struct lcd_memory_object *mo,
struct lcd_mapping_metadata *meta)
{
/*
* If memory object is not mapped, fail
* If memory object is not mapped, silently do nothing
*/
if (!meta) {
LCD_ERR("lookup before meta set?");
goto fail2;
LCD_DEBUG(LCD_DEBUG_MSG,
"lookup before meta set?");
goto out;
}
if (!meta->is_mapped) {
LCD_DEBUG(LCD_DEBUG_ERR,
LCD_DEBUG(LCD_DEBUG_MSG,
"memory object not mapped");
goto fail2;
goto out;
}
/*
* Unmap memory object
* We need to handle unmapping differently depending on
* the LCD type (isolated vs non-isolated)
*/
__lcd_unmap_pages(caller, meta->where_mapped, 1 << mo->order);
switch (lcd->type) {
case LCD_TYPE_ISOLATED:
ret = isolated_unmap_memory_object(lcd, mo, meta);
break;
case LCD_TYPE_NONISOLATED:
case LCD_TYPE_TOP:
/*
* For now, unmap is a no-op for non-isolated code. (All host
* physical is available to non-isolated code. Recall that
* this function is about unmapping in physical, not virtual.)
*/
ret = 0;
goto out;
default:
LCD_ERR("unrecognized lcd type %d", lcd->type);
ret = -EINVAL;
break;
}
if (ret)
goto out;
/*
* Mark memory object as not mapped
*/
meta->is_mapped = 0;
ret = 0;
goto out;
out:
return ret;
}
void __lcd_unmap_memory_object(struct lcd *caller, cptr_t mo_cptr)
{
int ret;
gpa_t gpa;
struct lcd_memory_object *mo;
struct lcd_mapping_metadata *meta;
struct cnode *cnode;
/*
* Look up memory object in caller's cspace
*/
ret = __lcd_get_memory_object(caller->cspace, mo_cptr, &cnode, &mo);
if (ret)
goto fail1;
meta = cap_cnode_metadata(cnode);
/*
* Do the unmap
*/
ret = __lcd_do_unmap_memory_object(caller, mo, meta);
if (ret)
goto fail2;
/*
* Release cnode, etc.
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment