Commit e49732d2 authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan

Adds support for creating a kLCD from another kLCD.

This is a boring but important commit that we need for
our glue code example (that is still in progress).

The main motiviation is: One kLCD (like a booting kLCD) may
want to create other threads to run kernel modules, and it
may want to grant them capabilities to resources. (Before
this, this wasn't possible - all kLCDs were isolated from
each other.) Another alternative (possibly better) for the
future is to allow threads to share cspaces. But we're
probably not there yet.

Adds the following to the kLIBLCD interface:

-- klcd_create_klcd - essentially creates another kernel
   thread and initializes the lcd-specific stuff like its
   cspace; the creator can then use lcd_cap_grant to grant
   this thread capabilities

-- klcd_create_module_klcd - creates a klcd (using
   klcd_create_klcd) and loads a kernel module for the klcd
   to run

-- klcd_destroy_module_klcd - destroys klcd (the kernel
   thread, etc.) and unloads the module

Adds test and an example group of modules that show how to use
this.
parent 5147626a
......@@ -39,8 +39,9 @@ int klcd_send(cptr_t endpoint);
int klcd_recv(cptr_t endpoint);
int klcd_call(cptr_t endpoint);
int klcd_reply(void);
int klcd_create(cptr_t *slot_out, gpa_t stack);
int klcd_config(cptr_t lcd, gva_t pc, gva_t sp, gpa_t gva_root);
int klcd_create(cptr_t *slot_out);
int klcd_config(cptr_t lcd, gva_t pc, gva_t sp, gpa_t gva_root,
gpa_t stack_page);
int klcd_run(cptr_t lcd);
int klcd_cap_grant(cptr_t lcd, cptr_t src, cptr_t dest);
int klcd_cap_page_grant_map(cptr_t lcd, cptr_t page, cptr_t dest, gpa_t gpa);
......@@ -254,15 +255,15 @@ static inline int lcd_reply(void)
/**
* Allocates lcd and does minimal initialization of hardware virtual
* machine and lcd's cspace. Returns non-zero on error. Stack should be
* the guest physical address where stack/utcb should be mapped (the
* microkernel will allocate a page for the stack/utcb - it can't trust
* the caller, and the microkernel needs safe access to the utcb during
* ipc).
* machine and lcd's cspace. Returns non-zero on error.
*
* (The microkernel will allocate a page for the stack/utcb - it can't
* trust the caller, and the microkernel needs safe access to the utcb during
* ipc.)
*/
static inline int lcd_create(cptr_t *slot_out, gpa_t stack)
static inline int lcd_create(cptr_t *slot_out)
{
return klcd_create(slot_out, stack);
return klcd_create(slot_out);
}
/**
* Configure lcd environment.
......@@ -276,10 +277,14 @@ static inline int lcd_create(cptr_t *slot_out, gpa_t stack)
* lcd_cap_page_grant_map.
*
* The lcd's cspace is configured using lcd_cap_grant.
*
* Stack should be the guest physical address where stack/utcb should be
* mapped.
*/
static inline int lcd_config(cptr_t lcd, gva_t pc, gva_t sp, gpa_t gva_root)
static inline int lcd_config(cptr_t lcd, gva_t pc, gva_t sp, gpa_t gva_root,
gpa_t stack_page)
{
return klcd_config(lcd, pc, sp, gva_root);
return klcd_config(lcd, pc, sp, gva_root, stack_page);
}
/**
* Runs / resumes an lcd.
......@@ -300,7 +305,7 @@ static inline int lcd_run(cptr_t lcd)
*
* Yes, I'm breaking from seL4 here by allowing grant to happen outside
* of ipc.
*
* It's for one special case: When the caller is
* setting up an lcd and needs to map pages inside it, we need to put
* capabilities to those pages in the lcd's cspace. Why? Because the microkernel
......@@ -477,6 +482,31 @@ void lcd_dstore_put(struct dstore_node *n);
/* EXTRAS -------------------------------------------------- */
/**
* Create a non-isolated `LCD'. This essentially initializes everything
* except the low-level hardware VM container.
*
* The klcd can be configured etc. just like a regular lcd (program counter,
* stack - though you should probably use the same stack that was set up
* by the kernel when the kthread was created, and so on). You can grant
* the klcd caps too.
*/
int klcd_create_klcd(cptr_t *klcd_out);
/**
* Create a non-isolated LCD. This basically loads a kernel module (with
* name mname) and creates a kernel thread to run its module init. This
* allows the caller to grant capabilities to a non-isolated thread.
*/
int klcd_create_module_klcd(cptr_t *slot_out, char *mname);
/**
* Destroys non-isolated LCD / thread (it will have to wait until that
* thread exits the module's init, so this may block!). Unloads the
* module with name module_name. It's up to you to make sure you pass the
* same module name that you used in klcd_create_module_klcd.
*/
void klcd_destroy_module_klcd(cptr_t klcd, char *module_name);
/**
* When provided with an endpoint connected to a module loader, this routine
......
......@@ -121,5 +121,26 @@ config LCD_TEST_MOD_IPC2_LCD2
# --------------------------------------------------
config LCD_TEST_MOD_CREATE_KLCD
bool "Create and load a klcd example"
depends on LCD_DOMAINS
default y
select LCD_TEST_MOD_CREATE_KLCD_BOOT
select LCD_TEST_MOD_CREATE_KLCD_KLCD
---help---
Simple example showing how to load a kLCD.
config LCD_TEST_MOD_CREATE_KLCD_BOOT
tristate
depends on LCD_TEST_MOD_CREATE_KLCD
default m
config LCD_TEST_MOD_CREATE_KLCD_KLCD
tristate
depends on LCD_TEST_MOD_CREATE_KLCD
default m
# --------------------------------------------------
endif
......@@ -18,7 +18,8 @@ lcd-domains-y += kliblcd/init.o kliblcd/mem.o \
kliblcd/ipc.o kliblcd/enterexit.o \
kliblcd/create.o kliblcd/cap.o \
kliblcd/cptr_cache.o \
kliblcd/dstore.o
kliblcd/dstore.o \
kliblcd/create_klcd.o
# Unit tests module
obj-$(CONFIG_LCD_DOMAINS_TESTS) += tests/
......
......@@ -89,6 +89,7 @@ enum lcd_cap_type {
LCD_CAP_TYPE_PAGE,
LCD_CAP_TYPE_KPAGE,
LCD_CAP_TYPE_LCD,
LCD_CAP_TYPE_KLCD,
LCD_CAP_TYPE_CNODE,
};
......@@ -227,6 +228,10 @@ void __lcd_cnode_put(struct cnode *c);
#define LCD_STATUS_RUNNING 2
#define LCD_STATUS_DEAD 4
enum lcd_type {
LCD_TYPE_ISOLATED,
LCD_TYPE_NONISOLATED,
};
enum lcd_xmit_status {
LCD_XMIT_INVALID = 0, /* when lcd is not in queue */
......@@ -245,6 +250,10 @@ struct lcd {
* (Pray for me that I will not get deadlocks)
*/
struct mutex lock;
/*
* Type (isolated, non-isolated (klcd))
*/
enum lcd_type type;
/*
* Status
*/
......@@ -302,6 +311,11 @@ struct lcd {
*/
char console_buff[LCD_CONSOLE_BUFF_SIZE];
unsigned console_cursor;
/*
* KLCD specifics
* ==============
*/
int (*klcd_main)(void);
};
/* similar to task structs */
......@@ -353,10 +367,16 @@ int __klcd_page_zalloc(struct lcd *klcd, cptr_t c, hpa_t *hpa_out,
int __klcd_pages_zalloc(struct lcd *klcd, cptr_t *slots,
hpa_t *hp_base_out, hva_t *hv_base_out,
unsigned order);
int __lcd_create(struct lcd *caller, cptr_t slot, gpa_t stack);
int __klcd_get_module(char *module_name, struct module **m);
void __klcd_put_module(char *module_name);
int __klcd_do_call_endpoint(cptr_t lcd);
int __klcd_create_klcd(struct lcd *caller, cptr_t slot);
int __lcd_create(struct lcd *caller, cptr_t slot);
int __lcd_create__(struct lcd **out);
int __lcd_config(struct lcd *caller, cptr_t lcd, gva_t pc, gva_t sp,
gpa_t gva_root);
gpa_t gva_root, gpa_t stack_page);
int __lcd_run(struct lcd *caller, cptr_t lcd);
int __lcd_cap_grant_cheat(struct lcd *caller, cptr_t lcd, cptr_t src,
cptr_t dest);
......@@ -390,6 +410,7 @@ void __lcd_page_check(struct lcd *lcd, struct page *p, int is_mapped,
void __lcd_kpage_check(struct lcd *lcd, struct page *p, int is_mapped,
gpa_t where_mapped);
void __lcd_check(struct lcd *owning_lcd, struct lcd *owned_lcd);
void __lcd_check_klcd(struct lcd *owning_lcd, struct lcd *owned_klcd);
/*
* These are called by code in cap.c when the last capability goes
......@@ -399,8 +420,8 @@ void __lcd_sync_endpoint_destroy(struct lcd_sync_endpoint *e);
void __lcd_page_destroy(struct page *p);
void __lcd_kpage_destroy(struct page *p);
void __lcd_destroy(struct lcd *owned_lcd);
void __lcd_destroy__(struct lcd *owned_lcd);
void __lcd_destroy_klcd(struct lcd *owned_klcd);
......
......@@ -21,7 +21,7 @@
#include <linux/mutex.h>
#include "../internal.h"
int klcd_create(cptr_t *slot_out, gpa_t stack)
int klcd_create(cptr_t *slot_out)
{
int ret;
/*
......@@ -30,7 +30,7 @@ int klcd_create(cptr_t *slot_out, gpa_t stack)
ret = lcd_alloc_cptr(slot_out);
if (ret)
goto fail1;
ret = __lcd_create(current->lcd, *slot_out, stack);
ret = __lcd_create(current->lcd, *slot_out);
if (ret)
goto fail2;
......@@ -42,9 +42,10 @@ fail1:
return ret;
}
int klcd_config(cptr_t lcd, gva_t pc, gva_t sp, gpa_t gva_root)
int klcd_config(cptr_t lcd, gva_t pc, gva_t sp, gpa_t gva_root,
gpa_t stack_page)
{
return __lcd_config(current->lcd, lcd, pc, sp, gva_root);
return __lcd_config(current->lcd, lcd, pc, sp, gva_root, stack_page);
}
int klcd_run(cptr_t lcd)
......@@ -58,7 +59,7 @@ int klcd_run(cptr_t lcd)
* Loads module into host address space, and stores pointer to
* struct module in lcd.
*/
static int get_module(char *module_name, struct module **m)
int __klcd_get_module(char *module_name, struct module **m)
{
int ret;
struct module *m1;
......@@ -102,6 +103,35 @@ fail1:
return ret;
}
void __klcd_put_module(char *module_name)
{
int ret;
struct module *m;
/*
* Delete module
*
* We need to look it up so we can do a put
*/
mutex_lock(&module_mutex);
m = find_module(module_name);
mutex_unlock(&module_mutex);
if (!m) {
LCD_ERR("couldn't find module");
goto fail;
}
module_put(m);
ret = do_sys_delete_module(module_name, 0, 1);
if (ret) {
LCD_ERR("deleting module");
goto fail;
}
goto out;
out:
fail:
return;
}
static int get_module_pages(hva_t hva, unsigned long size,
struct list_head *mpage_list)
{
......@@ -257,7 +287,7 @@ int klcd_load_module(char *mname, cptr_t mloader_endpoint,
/*
* Load module in host
*/
ret = get_module(mname, &m);
ret = __klcd_get_module(mname, &m);
if (ret)
goto fail1;
/*
......@@ -294,32 +324,16 @@ fail0:
void klcd_unload_module(struct lcd_info *mi, cptr_t mloader_endpoint)
{
int ret;
/*
* module loader endpoint ignored; use standard module loading system
*/
struct module *m;
/*
*
* Remove module pages from capability system
*/
free_module_pages(&mi->mpages_list);
/*
* Delete module
*
* We need to look it up so we can do a put
*/
mutex_lock(&module_mutex);
m = find_module(mi->mname);
mutex_unlock(&module_mutex);
if (!m) {
LCD_ERR("couldn't find module");
goto free_mi;
}
module_put(m);
ret = do_sys_delete_module(mi->mname, 0, 1);
if (ret)
LCD_ERR("deleting module");
free_mi:
__klcd_put_module(mi->mname);
/*
* Free lcd module info
*/
......@@ -1277,7 +1291,7 @@ fail1:
return ret;
}
static int do_call_endpoint(cptr_t lcd)
int __klcd_do_call_endpoint(cptr_t lcd)
{
cptr_t call_endpoint;
int ret;
......@@ -1323,7 +1337,7 @@ int klcd_create_module_lcd(cptr_t *slot_out, char *mname,
/*
* Create an empty lcd
*/
ret = lcd_create(slot_out, LCD_STACK_GPA);
ret = lcd_create(slot_out);
if (ret) {
LCD_ERR("lcd create failed");
goto fail0;
......@@ -1351,7 +1365,8 @@ int klcd_create_module_lcd(cptr_t *slot_out, char *mname,
*/
ret = lcd_config(*slot_out, (*mi)->init,
gva_add(LCD_STACK_GVA, LCD_STACK_SIZE - 1), /* stack */
LCD_GV_PAGING_MEM_GPA);
LCD_GV_PAGING_MEM_GPA,
LCD_STACK_GPA);
if (ret) {
LCD_ERR("failed to config lcd");
goto fail3;
......@@ -1359,7 +1374,7 @@ int klcd_create_module_lcd(cptr_t *slot_out, char *mname,
/*
* Provide the lcd with a call endpoint
*/
ret = do_call_endpoint(*slot_out);
ret = __klcd_do_call_endpoint(*slot_out);
if (ret) {
LCD_ERR("failed to set up call endpoint");
goto fail4;
......
/**
* create_klcd.c - Code for creating a non-isolated LCD.
*
* In some cases, a klcd may want to create another klcd - basically,
* create another thread and grant it capabilities. lcd_enter/lcd_exit
* are insufficient for this. (You could imagine that perhaps instead
* the klcd would load a kernel module that runs non-isolated and the
* thread that runs the module's init does lcd_enter first thing. But then
* the creator can't grant caps to that thread, or talk to it in any
* way via the microkernel interfaces.)
*
* Authors:
* Charlie Jacobsen <charlesj@cs.utah.edu>
*/
#include <linux/slab.h>
#include <linux/mm.h>
#include <asm/page.h>
#include <lcd-domains/kliblcd.h>
#include <lcd-domains/utcb.h>
#include <lcd-domains/types.h>
#include <linux/mutex.h>
#include "../internal.h"
int klcd_create_klcd(cptr_t *slot_out)
{
int ret;
/*
* Alloc cptr
*/
ret = lcd_alloc_cptr(slot_out);
if (ret)
goto fail1;
ret = __klcd_create_klcd(current->lcd, *slot_out);
if (ret)
goto fail2;
return 0;
fail2:
lcd_free_cptr(*slot_out);
fail1:
return ret;
}
/* KLCD CREATE WITH MODULE ---------------------------------------- */
int klcd_create_module_klcd(cptr_t *slot_out, char *mname)
{
int ret;
struct module *m;
/*
* Create a klcd
*/
ret = klcd_create_klcd(slot_out);
if (ret) {
LCD_ERR("klcd create failed");
goto fail0;
}
/*
* Load module
*/
ret = __klcd_get_module(mname, &m);
if (ret) {
LCD_ERR("error getting module");
goto fail1;
}
/*
* Configure klcd
*
* NOTE: For now, the microkernel ignores everything
* except the program counter, so we just pass junk for those
* other arguments.
*/
ret = lcd_config(*slot_out,
__gva(hva_val(va2hva(m->init))),
__gva(0),
__gpa(0),
__gpa(0));
if (ret) {
LCD_ERR("failed to config lcd");
goto fail2;
}
/*
* Provide the lcd with a call endpoint
*/
ret = __klcd_do_call_endpoint(*slot_out);
if (ret) {
LCD_ERR("failed to set up call endpoint");
goto fail3;
}
/*
* Done!
*/
return 0;
fail3:
fail2:
/*
* Remove module from host
*/
__klcd_put_module(mname);
fail1:
/*
* Should destroy lcd since this is the one and only capability to
* it.
*/
lcd_cap_delete(*slot_out);
fail0:
return ret;
}
void klcd_destroy_module_klcd(cptr_t klcd, char *module_name)
{
/*
* We *must* delete the lcd first before unloading the module.
* Otherwise, if the lcd is still running, it will use the freed
* module pages.
*/
lcd_cap_delete(klcd);
__klcd_put_module(module_name);
}
/* EXPORTS -------------------------------------------------- */
EXPORT_SYMBOL(klcd_create_klcd);
EXPORT_SYMBOL(klcd_create_module_klcd);
EXPORT_SYMBOL(klcd_destroy_module_klcd);
......@@ -455,6 +455,17 @@ static void free_cnode_object(struct cspace *cspace, struct cnode *cnode)
}
__lcd_destroy(lcd);
break;
case LCD_CAP_TYPE_KLCD:
/*
* Similar to LCD case.
*/
lcd = cnode->object;
if (unlikely(cspace_to_lcd(cspace) == current->lcd)) {
cnode->type = LCD_CAP_TYPE_FREE;
__lcd_cnode_put(cnode);
}
__lcd_destroy_klcd(lcd);
break;
default:
/* we don't expect to see invalid, free, or cnode types here */
LCD_ERR("unexpected cnode type %d", cnode->type);
......@@ -481,6 +492,9 @@ static void update_microkernel(struct cspace *cspace, struct cnode *cnode)
case LCD_CAP_TYPE_LCD:
__lcd_check(cspace_to_lcd(cspace), cnode->object);
break;
case LCD_CAP_TYPE_KLCD:
__lcd_check_klcd(cspace_to_lcd(cspace), cnode->object);
break;
default:
/* we don't expect to see invalid, free, or cnode types here */
LCD_ERR("unexpected cnode type %d", cnode->type);
......
......@@ -43,9 +43,10 @@ static int lookup_lcd(struct cspace *cspace, cptr_t slot, struct cnode **cnode)
if (ret)
goto fail1;
/*
* Confirm it's an lcd
* Confirm it's an lcd or klcd
*/
if ((*cnode)->type != LCD_CAP_TYPE_LCD) {
if ((*cnode)->type != LCD_CAP_TYPE_LCD &&
(*cnode)->type != LCD_CAP_TYPE_KLCD) {
LCD_ERR("not an lcd");
goto fail2;
}
......@@ -271,6 +272,9 @@ int __lcd_create__(struct lcd **out)
int ret;
/*
* Alloc lcd data structure
*
* (Because we're doing a zalloc, this will set the type, status,
* and so on to "defaults".)
*/
lcd = kzalloc(sizeof(*lcd), GFP_KERNEL);
if (!lcd) {
......@@ -311,7 +315,7 @@ fail1:
static int lcd_kthread_main(void *data);
int __lcd_create(struct lcd *caller, cptr_t slot, gpa_t stack)
int __lcd_create(struct lcd *caller, cptr_t slot)
{
struct lcd *lcd;
hva_t addr;
......@@ -340,16 +344,6 @@ int __lcd_create(struct lcd *caller, cptr_t slot, gpa_t stack)
LCD_ERR("alloc page");
goto fail3;
}
LCD_MSG("stack page is %p",
virt_to_page(hva2va(addr)));
/*
* Map in lcd
*/
ret = gp_map(lcd, stack, hva2hpa(addr));
if (ret) {
LCD_ERR("map");
goto fail4;
}
/*
* Store ref in lcd struct's utcb
*/
......@@ -360,7 +354,7 @@ int __lcd_create(struct lcd *caller, cptr_t slot, gpa_t stack)
lcd->kthread = kthread_create(lcd_kthread_main, NULL, "lcd");
if (!lcd->kthread) {
LCD_ERR("failed to create kthread");
goto fail5;
goto fail4;
}
/*
* Bump reference count on kthread
......@@ -376,18 +370,16 @@ int __lcd_create(struct lcd *caller, cptr_t slot, gpa_t stack)
ret = __lcd_cap_insert(&caller->cspace, slot, lcd, LCD_CAP_TYPE_LCD);
if (ret) {
LCD_ERR("insert");
goto fail6;
goto fail5;
}
/*
* Done
*/
return 0;
fail6:
fail5:
kthread_stop(lcd->kthread); /* never ran, so should die */
put_task_struct(lcd->kthread);
fail5:
gp_unmap(lcd, stack);
fail4:
free_page(hva_val(addr));
fail3:
......@@ -398,25 +390,19 @@ fail1:
return ret;
}
int __lcd_config(struct lcd *caller, cptr_t lcd, gva_t pc, gva_t sp,
gpa_t gva_root)
static int __lcd_config_lcd(struct lcd *caller, struct lcd *lcd_struct,
gva_t pc, gva_t sp,
gpa_t gva_root, gpa_t stack_page)
{
struct lcd *lcd_struct;
struct cnode *cnode;
int ret;
/*
* Look up and lock
*/
ret = get_lcd(&caller->cspace, lcd, &cnode, &lcd_struct);
if (ret)
goto fail1;
hva_t stack_page_addr;
/*
* If lcd is not an embryo, fail
*/
if (!lcd_status_embryo(lcd_struct)) {
LCD_ERR("cannot config: lcd not an embryo");
ret = -EINVAL;
goto fail2;
goto fail1;
}
/*
* Set pc, sp, gva_root
......@@ -424,6 +410,15 @@ int __lcd_config(struct lcd *caller, cptr_t lcd, gva_t pc, gva_t sp,
lcd_arch_set_pc(lcd_struct->lcd_arch, pc);
lcd_arch_set_sp(lcd_struct->lcd_arch, sp);
lcd_arch_set_gva_root(lcd_struct->lcd_arch, gva_root);
/*
* Map stack page in guest physical
*/
stack_page_addr = va2hva(lcd_struct->utcb);
ret = gp_map(lcd_struct, stack_page, hva2hpa(stack_page_addr));
if (ret) {
LCD_ERR("map");
goto fail2;
}
/*
* Make sure lcd_arch has valid state
*/
......@@ -432,19 +427,81 @@ int __lcd_config(struct lcd *caller, cptr_t lcd, gva_t pc, gva_t sp,
LCD_ERR("bad lcd_arch state");
goto fail3;
}
return 0;
fail3:
gp_unmap(lcd_struct, stack_page);
fail2:
fail1:
return ret;
}
int __lcd_config_klcd(struct lcd *caller, struct lcd *lcd_struct,
gva_t pc, gva_t sp,
gpa_t gva_root, gpa_t stack_page)
{
/*
* Set status to configed
* For now, we ignore everything except the program counter.
*
* The program counter is assumed to be a host virtual
* address to a kernel module's init.
*
* (Recall that for klcd's, guest addresses = host addresses.)
*/
set_lcd_status(lcd_struct, LCD_STATUS_CONFIGED);
lcd_struct->klcd_main = hva2va(__hva(gva_val(pc)));
return 0;
}
int __lcd_config(struct lcd *caller, cptr_t lcd, gva_t pc, gva_t sp,
gpa_t gva_root, gpa_t stack_page)
{
struct lcd *lcd_struct;
struct cnode *cnode;
int ret;
/*
* Unlock
* Look up and lock
*/
put_lcd(cnode, lcd_struct);
ret = get_lcd(&caller->cspace, lcd, &cnode, &lcd_struct);
if (ret)
goto fail1;
/*
* Switch on the type of the lcd (a regular lcd, klcd, ...)
*/
switch (cnode->type) {
return 0;
case LCD_CAP_TYPE_LCD:
ret = __lcd_config_lcd(caller, lcd_struct, pc, sp, gva_root,
stack_page);
break;
case LCD_CAP_TYPE_KLCD:
ret = __lcd_config_klcd(caller, lcd_struct, pc, sp, gva_root,
stack_page);
break;
default:
/* shouldn't happend */
LCD_ERR("unexpected cnode type: %d",
cnode->type);
goto fail2;
}
if (ret) {
LCD_ERR("error config'ing lcd, ret = %d", ret);
goto fail2;
}
/*
* Set status to configed
*/
set_lcd_status(lcd_struct, LCD_STATUS_CONFIGED);
fail3:
ret = 0;
goto out;
out:
fail2:
/*
* Unlock
*/
put_lcd(cnode, lcd_struct);
fail1:
return ret;
......@@ -529,6 +586,10 @@ int __klcd_enter(void)
* Mark lcd as running
*/
set_lcd_status(lcd, LCD_STATUS_RUNNING);
/*
* Set type as non-isolated
*/
lcd->type = LCD_TYPE_NONISOLATED;
return 0;
......@@ -588,6 +649,79 @@ lock_skip:
current->lcd = NULL;
}
/* CREATE A KLCD (NON-ISOLATED LCD) ------------------------------ */
int __klcd_create_klcd(struct lcd *caller, cptr_t slot)
{
struct lcd *lcd;
hva_t addr;
int ret;