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

libcap-integration: Re-factors page alloc and map, and LCD run loop.

Code for arbitrary host addresses is also materializing (via the
more abstract struct lcd_memory_object).
parent 261e6b5d
......@@ -28,15 +28,18 @@ static void page_revoke(struct cspace *cspace, struct cnode *cnode,
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.)
*/
m = cap_cnode_metadata(cnode);
if (!m->is_mapped)
if (!m || !m->is_mapped)
goto out;
/*
* Unmap it
*/
__lcd_unmap(cap_cspace_getowner(cspace), m->where_mapped,
mo->order);
__lcd_unmap_pages(cap_cspace_getowner(cspace), m->where_mapped,
1 << mo->order);
out:
kfree(m);
......
/*
* console.c -- Primitive putchar for LCDs
*
* Copyright: University of Utah
*/
......@@ -92,7 +92,7 @@ int __lcd_create_no_vm(struct lcd **out)
/*
* Create a kernel thread (won't run till we wake it up)
*/
lcd->kthread = kthread_create(lcd_kthread_main, NULL, "lcd");
lcd->kthread = kthread_create(__lcd_kthread_main, NULL, "lcd");
if (!lcd->kthread) {
LCD_ERR("failed to create kthread");
goto fail2;
......@@ -220,7 +220,7 @@ static int lookup_lcd(struct cspace *cspace, cptr_t slot, struct cnode **cnode)
return 0;
fail2:
__lcd_cnode_put(*cnode);
cap_cnode_put(*cnode);
fail1:
return ret;
}
......@@ -245,7 +245,7 @@ int __lcd_get(struct lcd *caller, cptr_t lcd, struct cnode **cnode,
goto fail2;
}
return 0; /* caller should match with put_lcd */
return 0; /* caller should match with __lcd_put */
fail2:
cap_cnode_put(*cnode);
......@@ -291,7 +291,7 @@ static int config_lcd(struct lcd *caller, struct lcd *lcd_struct,
* Map utcb page in guest physical
*/
utcb_page_addr = va2hva(lcd_struct->utcb);
ret = gp_map(lcd_struct, utcb_page, hva2hpa(utcb_page_addr));
ret = __lcd_map(lcd_struct, utcb_page, hva2hpa(utcb_page_addr));
if (ret) {
LCD_ERR("map");
goto fail2;
......
......@@ -131,10 +131,10 @@ struct lcd_mapping_metadata {
* This is used as a wrapper around struct page and arbitrary
* host physical addresses, so that we can track the size of the
* memory region. This is the object that is inserted into an LCD's
* cspace (rather than a struct page, say). We don't store an extra
* type field because this information is stored in the cspace.
* cspace (rather than a struct page, say).
*/
struct lcd_memory_object {
enum lcd_microkernel_type_id sub_type;
void *object;
unsigned int order;
};
......
/*
* mem.c -- Architecture-independent memory related stuff - page allocation,
* mapping, etc.
*
* Copyright: University of Utah
*/
#include <linux/slab.h>
#include "internal.h"
/* ALLOC -------------------------------------------------- */
static void do_insert_pages(struct lcd *caller, struct page *p,
unsigned int order)
{
struct lcd_memory_object *mo;
struct lcd_mapping_metadata *meta;
struct cnode *cnode;
/*
* Set up metadata
*/
mo = kmalloc(sizeof(*mo));
if (!mo) {
LCD_ERR("kmalloc error");
ret = -ENOMEM;
goto fail1;
}
meta = kzalloc(sizeof(*meta)); /* set is_mapped = 0 */
if (!meta) {
LCD_ERR("kmalloc error");
ret = -ENOMEM;
goto fail2;
}
mo->sub_type = LCD_MICROKERNEL_TYPE_ID_PAGE;
mo->object = p;
mo->order = order;
/*
* Insert into caller's cspace
*/
ret = cap_insert(caller->cspace, slot,
mo,
__lcd_get_libcap_type(LCD_MICROKERNEL_TYPE_ID_PAGE));
if (ret) {
LCD_ERR("insert");
goto fail3;
}
/*
* ================================================
* After this point, mo is managed by libcap, and will be
* freed when the last capability to it goes away. In particular,
* if we fail below and do cap_delete, this will automatically
* delete mo (in the delete callback). This is subtle, so be
* careful.
*
* Store metadata
*/
ret = cap_cnode_get(caller->cspace, slot, &cnode);
if (ret) {
LCD_ERR("metadata");
/* don't use "label" chain */
cap_delete(caller->cspace, slot);
kfree(mo);
return ret;
}
cap_cnode_set_metadata(cnode, meta);
cap_cnode_put(cnode);
/*
* ==================================================
* After this point, meta is now also managed implicitly
* by libcap.
*/
return 0;
fail3:
kfree(meta);
fail2:
kfree(mo);
fail1:
return ret;
}
int __lcd_alloc_pages_exact_node(struct lcd *caller, cptr_t slot, int nid,
unsigned int flags, unsigned int order)
{
int ret;
struct page *p;
/*
* Allocate zero'd pages on node
*/
p = alloc_pages_exact_node(nid, flags | __GFP_ZERO, order);
if (!p) {
LCD_ERR("alloc failed");
ret = -ENOMEM;
goto fail1;
}
/*
* Insert into caller's cspace
*/
ret = do_insert_pages(caller, p, order);
if (ret) {
LCD_ERR("failed to insert page capability into caller's cspace");
goto fail2;
}
return 0;
fail2:
__free_pages(p, order);
fail1:
return ret;
}
int __lcd_alloc_pages(struct lcd *caller, cptr_t slot,
unsigned int flags, unsigned int order)
{
int ret;
struct page *p;
/*
* Allocate zero'd pages
*/
p = alloc_pages(flags | __GFP_ZERO, order);
if (!p) {
LCD_ERR("alloc failed");
ret = -ENOMEM;
goto fail1;
}
/*
* Insert into caller's cspace
*/
ret = do_insert_pages(caller, p, order);
if (ret) {
LCD_ERR("failed to insert page capability into caller's cspace");
goto fail2;
}
return 0;
fail2:
__free_pages(p, order);
fail1:
return ret;
}
/* LOOKUP -------------------------------------------------- */
static int lookup_memory_object(struct cspace *cspace, cptr_t slot,
struct cnode **cnode)
{
int ret;
cap_type_t t;
/*
* Look up
*/
ret = cap_cnode_get(cspace, slot, cnode);
if (ret)
goto fail1;
/*
* Confirm it's a memory object
*/
t = cap_cnode_type(*cnode);
if (t != __lcd_get_libcap_type(LCD_MICROKERNEL_TYPE_ID_PAGE) &&
t != __lcd_get_libcap_type(LCD_MICROKERNEL_TYPE_ID_KPAGE) &&
t != __lcd_get_libcap_type(LCD_MICROKERNEL_TYPE_ID_DEV_MEM)) {
LCD_ERR("not a page");
goto fail2;
}
return 0;
fail2:
cap_cnode_put(*cnode);
fail1:
return ret;
}
int __lcd_get_memory_object(struct lcd *caller, cptr_t mo_cptr,
struct cnode **cnode,
struct lcd_memory_object **mo_out)
{
int ret;
/*
* Look up and lock cnode containing memory object
*/
ret = lookup_memory_object(cspace, mo_cptr, cnode);
if (ret)
goto fail1;
*mo_out = cap_cnode_object(*cnode);
/*
* XXX: for now, we assume we won't be touching any of the
* memory in a way that could race with others - so
* it's not necessary to take some other lock.
*/
return 0; /* caller should match with __lcd_put_memory_object */
fail1:
return ret;
}
void __lcd_put_memory_object(struct lcd *caller, struct cnode *cnode,
struct lcd_memory_object *mo)
{
/*
* (no unlock on memory object - see __lcd_get_memory_object)
*
* Release cnode
*/
cap_cnode_put(cnode);
}
/* MAPPING -------------------------------------------------- */
static int memory_object_hpa(struct lcd_memory_object *mo,
hpa_t *hpa_base)
{
hpa_t hpa_base;
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
*hpa_base = va2hpa(page_address(mo->object));
break;
case LCD_MICROKERNEL_TYPE_ID_DEV_MEM:
*hpa_base = hpa_val((unsigned long)mo->object);
break;
default:
LCD_ERR("unexpected memory object type: %d\n",
mo->sub_type);
return -EINVAL;
}
return 0;
}
int __lcd_map_pages(struct lcd *caller, gpa_t gpa, hpa_t hpa,
unsigned int num_pages)
{
/*
* Only isolated VMs should be landing here.
*/
BUG_ON(caller->type != LCD_ISOLATED);
/*
* Create, and do not overwrite
*/
return lcd_arch_ept_map_range(caller->lcd_arch, gpa, hpa, num_pages);
}
void __lcd_unmap_pages(struct lcd *caller, gpa_t gpa, unsigned int num_pages)
{
/*
* Only isolated VMs should be landing here.
*/
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");
}
int __lcd_map_memory_object(struct lcd *caller, cptr_t mo_cptr, 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;
}
/*
* Get host physical address of start of memory object
*/
ret = memory_object_hpa(mo, &hpa_base);
if (ret) {
LCD_ERR("invalid memory object");
goto fail2;
}
/*
* Map memory object.
*
* XXX: No matter what the memory object is, we map it
* the same way. This is kind of subtle. We can get away
* 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)
if (ret) {
LCD_ERR("map");
goto fail2;
}
/*
* Mark page as mapped, and where it's mapped
*/
meta->is_mapped = 1;
meta->where_mapped = gpa;
/*
* Release cnode, etc.
*/
__lcd_put_memory_object(caller, cnode, mo);
return 0;
fail2:
__lcd_put_memory_object(caller, cnode, mo);
fail1:
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);
/*
* If memory object is not mapped, fail
*/
if (!meta) {
LCD_ERR("lookup before meta set?");
goto fail2;
}
if (!meta->is_mapped) {
LCD_DEBUG(LCD_DEBUG_ERR,
"memory object not mapped");
goto fail2;
}
/*
* Unmap memory object
*/
__lcd_unmap_pages(caller, meta->where_mapped, 1 << mo->order);
/*
* Mark memory object as not mapped
*/
meta->is_mapped = 0;
/*
* Release cnode, etc.
*/
__lcd_put_memory_object(caller, cnode, mo);
return;
fail2:
__lcd_put_memory_object(caller, cnode, mo);
fail1:
return;
}
/*
* run.c -- Code for running LCDs
*
* Copyright: University of Utah
*/
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include "internal.h"
static int __lcd_put_char(struct lcd *lcd)
{
char c;
/*
* Char is in r0
*/
c = __lcd_debug_reg(lcd->utcb);
/*
* Put char in lcd's console buff
*/
lcd->console_buff[lcd->console_cursor] = c;
/*
* Bump cursor and check
*/
lcd->console_cursor++;
if (c == 0) {
/*
* End of string; printk to host and reset buff
*/
printk(KERN_INFO "message from lcd %p: %s\n",
lcd, lcd->console_buff);
lcd->console_cursor = 0;
return 0;
}
if (lcd->console_cursor >= LCD_CONSOLE_BUFF_SIZE - 1) {
/*
* Filled buffer; empty it.
*/
lcd->console_buff[LCD_CONSOLE_BUFF_SIZE - 1] = 0;
printk(KERN_INFO "(incomplete) message from lcd %p: %s\n",
lcd, lcd->console_buff);
lcd->console_cursor = 0;
return 0;
}
return 0;
}
static int do_cap_delete(struct lcd *lcd)
{
cptr_t slot;
/*
* Get cptr
*/
slot = __lcd_cr0(lcd->utcb);
__lcd_cap_delete(&lcd->cspace, slot);
return 0;
}
static int do_create_sync_endpoint(struct lcd *lcd)
{
cptr_t slot;
slot = __lcd_cr0(lcd->utcb);
return __lcd_create_sync_endpoint(lcd, slot);
}
static int handle_syscall(struct lcd *lcd, int *lcd_ret)
{
int syscall_id;
cptr_t endpoint;
int ret;
syscall_id = lcd_arch_get_syscall_num(lcd->lcd_arch);
LCD_DEBUG(LCD_DEBUG_VERB,
"got syscall %d from lcd %p", syscall_id, lcd);
switch (syscall_id) {
case LCD_SYSCALL_EXIT:
/*
* Return value to signal exit
*/
*lcd_ret = (int)__lcd_r0(lcd->utcb);
ret = 1;
break;
case LCD_SYSCALL_PUTCHAR:
/*
* Put char and possibly print on host
*/
ret = __lcd_put_char(lcd);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_SEND:
/*
* Get endpoint
*/
endpoint = __lcd_cr0(lcd->utcb);
/*
* Null out that register so that it's not transferred
* during ipc
*/
__lcd_set_cr0(lcd->utcb, LCD_CPTR_NULL);
/*
* Do send, and set ret val
*/
ret = __lcd_send(lcd, endpoint);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_RECV:
/*
* Get endpoint
*/
endpoint = __lcd_cr0(lcd->utcb);
/*
* Null out that register so that it's not transferred
* during ipc
*/
__lcd_set_cr0(lcd->utcb, LCD_CPTR_NULL);
/*
* Do recv
*/
ret = __lcd_recv(lcd, endpoint);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_CALL:
/*
* Get endpoint
*/
endpoint = __lcd_cr0(lcd->utcb);
/*
* Null out that register so that it's not transferred
* during ipc
*/
__lcd_set_cr0(lcd->utcb, LCD_CPTR_NULL);
/*
* Do call
*/
ret = __lcd_call(lcd, endpoint);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_REPLY:
/*
* No endpoint arg; just do reply
*/
ret = __lcd_reply(lcd);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_PAGE_ALLOC:
/*
* Do page alloc
*/
ret = __lcd_alloc_pages(lcd, __lcd_cr0(lcd->utcb),
GFP_KERNEL, 0);
__lcd_set_cr0(lcd->utcb, LCD_CPTR_NULL);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_PAGE_MAP:
/*
* Do page map
*/
ret = do_page_map(lcd);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_PAGE_UNMAP:
/*
* Do page unmap
*/
ret = do_page_unmap(lcd);
lcd_arch_set_syscall_ret(lcd->lcd_arch, ret);
break;
case LCD_SYSCALL_CAP_DELETE:
/*
* Do cap delete
*/
ret = do_cap_delete(lcd);