Commit 9edaa6a6 authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan

Partially through starting ipc.

Will probably switch back to single lcd, multiple addr space, etc.

Merging muktesh's caps.
parent f32f560d
/* NOT CURRENTLY USED */
#ifndef LCD_DOMAINS_IPC_H
#define LCD_DOMAINS_IPC_H
#include <linux/list.h>
#include <linux/mutex.h>
struct sync_endpoint {
struct list_head senders;
struct list_head receivers;
struct mutex lock;
};
#endif /* LCD_DOMAINS_IPC_H */
......@@ -4,35 +4,31 @@
#include <linux/module.h>
#include <asm/lcd-domains-arch.h>
#include <linux/sched.h>
#include <linux/waitqueue.h>
/*
* lcd_thread_status
* =================
*
* Similar to status used for task_struct's.
*
* LCD_THREAD_UNFORMED = still setting up, or status not set yet
* LCD_THREAD_SUSPENDED = lcd_thread is going to sleep / is asleep
* LCD_THREAD_RUNNABLE = set status to this to wake up lcd_thread
* LCD_THREAD_RUNNING = lcd_thread is running, or is about to run
* LCD_THREAD_KILL = set status to this to kill lcd_thread (as soon as
* possible)
* LCD_THREAD_DEAD = lcd_thread is not running, most parts have been
* destroyed (only hanging around to provide
* status info)
*/
enum lcd_thread_status {
LCD_THREAD_UNFORMED = 0,
LCD_THREAD_SUSPENDED = 1,
LCD_THREAD_RUNNABLE = 2,
LCD_THREAD_RUNNING = 3,
LCD_THREAD_KILL = 4,
LCD_THREAD_DEAD = 5,
emum lcd_thread_status {
LCD_THREAD_DEAD,
LCD_THREAD_ALIVE,
};
emum lcd_xmit_status {
LCD_XMIT_INVALID = 0,
LCD_XMIT_SUCCESS = 1,
LCD_XMIT_FAILED = 2,
};
struct lcd;
struct lcd_thread_proxy;
struct lcd_thread {
/*
* Reference counting
*/
atomic_t usage;
/*
* Status
*/
int status;
/*
* My corresponding kthread
*/
......@@ -47,17 +43,30 @@ struct lcd_thread {
*/
struct list_head lcd_threads;
/*
* Message Passing
* ===============
*
* Thread control block, accessible by lcd_thread while running inside
* lcd (mapped in the bottom of its stack). Contains message
* registers, etc.
*/
struct lcd_utcb *utcb;
/*
* Status (see above)
* Transmission status. When a send/recv is completed, set to
* success/fail.
*/
int status;
atomic_t xmit_status;
/*
* We use a `proxy' object for rendezvous point recv/send queues.
*
* For now, a thread is only allowed to do one blocking send or
* one blocking receive (hence only one proxy).
*/
struct lcd_thread_proxy proxy;
/*
*
* Arch-dependent state of lcd_thread
* ==================================
*/
struct lcd_arch_thread *lcd_arch_thread;
};
......@@ -161,6 +170,10 @@ struct lcd_thread {
#define LCD_GV_MEM_START LCD_ARCH_TOP
struct lcd {
/*
* Reference counting
*/
atomic_t usage;
/*
* Display name
*/
......@@ -217,26 +230,95 @@ struct lcd {
};
/**
* -- Loads module_name into host kernel. (Note: The module loading code
* expects underscores, _, rather than hyphens. If the module's name
* in the file system is some-module.ko, use the name some_module.)
* -- Sets up the address space with the module mapped inside it in the lcd
* -- Sets up an initial thread for running inside the lcd (stored in
* the struct lcd's init_thread)
* Message Passing
* ===============
*
* Synchronous endpoints contain a send/receive queue for lcd_thread's
* (similar to an seL4 endpoint). Also known as `rendezvous point'.
*/
struct lcd_sync_endpoint {
atomic_t usage;
struct list_head senders;
struct list_head receivers;
struct mutex lock;
};
/**
* An lcd_thread_proxy represents an lcd_thread in an endpoint's queue.
*
* Call lcd_thread_start on the lcd's init thread to start executing the
* module's init inside the lcd.
* This is similar to a wait_queue_t that represents a task in a wait queue.
*
* Call lcd_destroy after killing the lcd's init thread to tear down the
* lcd and remove the module from the host.
* We do this so that an lcd_thread can receive on multiple endpoints. It
* also conveniently wraps a back pointer to the endpoint so we can safely
* remove the lcd_thread_proxy (using the endpoint's lock) from the queue.
*/
struct lcd_thread_proxy {
struct lcd_thread *lcd_thread;
struct lcd_sync_endpoint *endpoint;
struct list_head proxies;
};
/* REFERENCE COUNTING -------------------------------------------------- */
/**
* TODO: This will be shifted into cspaces soon.
*
* lcd_thread_kill will block until the underlying kernel thread exits (the
* kthread checks whether it should stop each time the lcd thread exits from
* the lcd -- due to an interrupt, etc.).
* The put inlines are built so that the ptr is automatically set to NULL.
* This prevents use after free.
*/
int lcd_create(char *module_name, struct lcd **out);
void lcd_destroy(struct lcd *lcd);
int lcd_thread_start(struct lcd_thread *t);
int lcd_thread_kill(struct lcd_thread *t);
extern void __put_lcd_thread(struct lcd_thread *t);
static inline void __get_lcd_thread(struct lcd_thread *t)
{
atomic_inc(&t->usage);
}
static inline void get_lcd_thread(struct lcd_thread **ptr, struct lcd_thread *t)
{
__get_lcd_thread(t);
*ptr = t;
}
static inline void put_lcd_thread(struct lcd_thread **ptr)
{
BUG_ON(!atomic_read(&(*ptr)->usage));
if (atomic_dec_and_test(&(*ptr)->usage))
__put_lcd_thread(*ptr);
*ptr = NULL;
}
extern void __put_lcd(struct lcd *lcd);
static inline void __get_lcd(struct lcd *lcd)
{
atomic_inc(&lcd->usage);
}
static inline void get_lcd(struct lcd **ptr, struct lcd *lcd)
{
__get_lcd(lcd);
*ptr = lcd;
}
static inline void put_lcd(struct lcd **ptr)
{
BUG_ON(!atomic_read(&(*ptr)->usage));
if (atomic_dec_and_test(&(*ptr)->usage))
__put_lcd(*ptr);
*ptr = NULL;
}
extern void __put_lcd_sync_endpoint(struct lcd_sync_endpoint *e);
static inline void __get_lcd_sync_endpoint(struct lcd_sync_endpoint *e)
{
atomic_inc(&e->usage);
}
static inline void get_lcd_sync_endpoint(struct lcd_sync_endpoint **ptr,
struct lcd_sync_endpoint *e)
{
__get_lcd_sync_endpoint(e);
*ptr = e;
}
static inline void put_lcd_sync_endpoint(struct lcd_sync_endpoint **ptr);
{
BUG_ON(!atomic_read(&(*ptr)->usage));
if (atomic_dec_and_test(&(*ptr)->usage))
__put_lcd_sync_endpoint(*ptr);
*ptr = NULL;
}
#endif /* LCD_DOMAINS_LCD_DOMAINS_H */
#ifndef LCD_DOMAINS_UTCB_H
#define LCD_DOMAINS_UTCB_H
#define LCD_NUM_REGS 8
/*
* User thread control block
* =========================
*
* This is accessible (mapped) inside the lcd.
* Each lcd_thread has one of these. This is where it puts its
* data for a message. mr are general registers and are just
* copied during a send/recv. cr are capability pointer registers, and
* the corresponding capabilities are granted during a send/recv (like
* seL4).
*
* NOTE: This needs to fit inside a page (4 KBs). It goes at the bottom
* of the lcd_thread's stack (so it should be reasonably small).
*/
struct lcd_utcb {
u64 mr[8];
u64 mr[LCD_NUM_REGS];
cptr_t cr[LCD_NUM_REGS];
};
/* GENERAL REGISTERS ---------------------------------------- */
#define LCD_MK_REG_ACCESS(idx) \
static inline u64 __lcd_r##idx(struct lcd_thread *t) \
{ \
BUILD_BUG_ON(idx >= LCD_NUM_REGS); \
return t->utcb.mr[idx]; \
} \
static inline void __lcd_set_r##idx(struct lcd_thread *t, u64 val) \
{ \
BUILD_BUG_ON(idx >= LCD_NUM_REGS); \
t->utcb.mr[idx] = val; \
} \
static inline u64 lcd_r##idx(void) \
{ \
return __lcd_r##idx(current->lcd_thread); \
} \
static inline void lcd_set_r##idx(u64 val) \
{ \
__lcd_set_r##idx(current->lcd_thread, val); \
} \
LCD_MK_REG_ACCESS(0);
LCD_MK_REG_ACCESS(1);
LCD_MK_REG_ACCESS(2);
LCD_MK_REG_ACCESS(3);
LCD_MK_REG_ACCESS(4);
LCD_MK_REG_ACCESS(5);
LCD_MK_REG_ACCESS(6);
LCD_MK_REG_ACCESS(7);
/* CPTR REGISTERS ---------------------------------------- */
#define LCD_MK_CAP_REG_ACCESS(idx) \
static inline cptr_t __lcd_cr##idx(struct lcd_thread *t) \
{ \
BUILD_BUG_ON(idx >= LCD_NUM_REGS); \
return t->utcb.cr[idx]; \
} \
static inline u64 __lcd_set_cr##idx(struct lcd_thread *t, cptr_t val) \
{ \
BUILD_BUG_ON(idx >= LCD_NUM_REGS); \
t->utcb.cr[idx] = val; \
} \
static inline cptr_t lcd_cr##idx(void) \
{ \
return __lcd_cr##idx(current->lcd_thread); \
} \
static inline u64 lcd_set_cr##idx(cptr_t val) \
{ \
__lcd_set_cr##idx(current->lcd_thread, val); \
} \
LCD_MK_CAP_REG_ACCESS(0);
LCD_MK_CAP_REG_ACCESS(1);
LCD_MK_CAP_REG_ACCESS(2);
LCD_MK_CAP_REG_ACCESS(3);
LCD_MK_CAP_REG_ACCESS(4);
LCD_MK_CAP_REG_ACCESS(5);
LCD_MK_CAP_REG_ACCESS(6);
LCD_MK_CAP_REG_ACCESS(7);
#endif /* LCD_DOMAINS_UTCB_H */
......@@ -228,6 +228,8 @@ static void account_kernel_stack(unsigned long *stack, int account)
void free_task(struct task_struct *tsk)
{
if (tsk->lcd_thread)
put_lcd_thread(&tsk->lcd_thread);
account_kernel_stack(tsk->stack, -1);
arch_release_thread_stack(tsk->stack);
free_thread_stack(tsk->stack);
......
This diff is collapsed.
......@@ -26,42 +26,11 @@
#include <lcd-domains/lcd-domains.h>
#include <lcd-domains/syscall.h>
#include <lcd-domains/ipc.h>
#include <lcd-domains/common.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LCD driver");
/* DEBUG -------------------------------------------------- */
#define LCD_DEBUG 0
#define LCD_ERR(msg...) __lcd_err(__FILE__, __LINE__, msg)
static inline void __lcd_err(char *file, int lineno, char *fmt, ...)
{
va_list args;
printk(KERN_ERR "lcd-domains: %s:%d: error: ", file, lineno);
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
}
#define LCD_MSG(msg...) __lcd_msg(__FILE__, __LINE__, msg)
static inline void __lcd_msg(char *file, int lineno, char *fmt, ...)
{
va_list args;
printk(KERN_ERR "lcd-domains: %s:%d: note: ", file, lineno);
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
}
#define LCD_WARN(msg...) __lcd_warn(__FILE__, __LINE__, msg)
static inline void __lcd_warn(char *file, int lineno, char *fmt, ...)
{
va_list args;
printk(KERN_ERR "lcd-domains: %s:%d: warning: ", file, lineno);
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
}
/* MEMORY ALLOCATION -------------------------------------------------- */
/**
......@@ -1284,19 +1253,58 @@ fail1:
return ret;
}
void lcd_destroy(struct lcd *lcd)
static void __lcd_destroy(struct lcd *lcd)
{
int ret;
/*
* Assume we have no running lcd_thread's ...
* Finish
*/
kfree(lcd);
}
void __put_lcd(struct lcd *lcd)
{
__lcd_destroy(lcd);
}
static int lcd_destroy_ok(struct lcd *lcd)
{
int ret = 0;
/**
* If there are still threads in the lcd, fail. They need to die
* first. (And we aren't going to do it for the caller, to make our
* life simpler.)
*/
ret = mutex_lock_interruptible(&lcd->lcd_threads.lock);
if (ret) {
LCD_ERR("interrupted, failing ...");
goto out;
}
if (!list_empty(&lcd->lcd_threads.list)) {
ret = -EINVAL;
LCD_ERR("threads inside lcd, cannot destroy lcd");
}
mutex_unlock(&lcd->lcd_threads.lock);
out:
return ret;
}
static void lcd_destroy(struct lcd *lcd)
{
/*
* We assume caller has done lcd_destroy_ok, and proceed assuming
* there are no threads in the lcd.
*
* ORDER IS IMPORTANT:
*
* (1) unmap the module
*
* This must happen before (4), otherwise the lcd arch tear down
* will free the module pages from under us before we do (2).
*
* (2) delete the module from the host
*
* (3) tear down guest virtual paging
* (3)
*
* (4) free up any pages still in the guest physical addr space
* XXX: This will attempt to free the underlying host page.
......@@ -1350,15 +1358,18 @@ void lcd_destroy(struct lcd *lcd)
* Tear down lcd_arch (ept, ...)
*/
lcd_arch_destroy(lcd->lcd_arch);
/*
* Finish
/*
* XXX: just call destroy for now. In the future, we could possibly
* loop over any lcd_threads and kill them before destroying the
* LCD. For now, we assume all threads are dead and gone.
*/
kfree(lcd);
__lcd_destroy(lcd);
}
static int lcd_setup_initial_thread(struct lcd *lcd);
int lcd_create(char *module_name, struct lcd **out)
static int lcd_create(char *module_name, struct lcd **out)
{
int ret;
struct lcd *lcd;
......@@ -1400,6 +1411,10 @@ int lcd_create(char *module_name, struct lcd **out)
*out = lcd;
/*
* TODO: Put lcd and initial thread in caller's cspace
*/
return 0;
fail4:
......@@ -1412,6 +1427,11 @@ fail1:
/* LCD THREAD EXECUTION -------------------------------------------------- */
static inline void set_lcd_thread_status(struct lcd_thread *t, int status)
{
set_mb(t->status, status);
}
static int lcd_handle_syscall(struct lcd_thread *t)
{
int syscall_id;
......@@ -1420,9 +1440,12 @@ static int lcd_handle_syscall(struct lcd_thread *t)
LCD_MSG("got syscall %d", syscall_id);
switch (syscall_id) {
case LCD_SYSCALL_SEND:
return lcd_handle_send(t);
case LCD_SYSCALL_RECV:
return lcd_handle_recv(t);
case LCD_SYSCALL_YIELD:
return 1;
break;
default:
LCD_ERR("unimplemented syscall %d", syscall_id);
return -ENOSYS;
......@@ -1508,41 +1531,74 @@ int lcd_thread_start(struct lcd_thread *t)
return 0;
}
int lcd_thread_kill(struct lcd_thread *t)
void __put_lcd_thread(struct lcd_thread *t)
{
/*
* Free t
*/
kfree(t);
}
static void __lcd_thread_kill(struct lcd_thread *t)
{
int ret;
/*
* Stop the kernel thread and get return value.
*/
ret = kthread_stop(t->kthread);
/*
* Decrement the kthread's reference count. Host kernel will clean
* up the rest.
* up the rest (and eventually decrement t's ref count).
*/
put_task_struct(t->kthread);
/*
* The LCD still owns the lcd thread's stack, so we won't free it.
* Mark t as dead. If an endpoint sees this, it will remove t from
* its queue rather than attempt a send/recv.
*/
set_lcd_thread_status(t, LCD_THREAD_DEAD);
/*
* *Now* we will attempt to remove t from any queue it may be in.
*/
if (t->proxy.endpoint)
lcd_endpoint_dequeue(t->proxy, t->proxy.endpoint);
/*
* Now we can safely assume t is not in any endpoint queue, so its
* utcb will not be used. We set it to NULL. BUT the LCD still owns
* the page that contains the utcb (and stack), so we won't free
* the host physical memory.
*
* XXX: We need to update permissions for the utcb/stack page for those
* LCDs that have access to it. Now that it isn't associated with a
* thread, we should allow the page to be freed.
*/
t->utcb = NULL;
/*
* Tear down the lcd arch thread
*/
lcd_arch_destroy_thread(t->lcd_arch_thread);
}
void lcd_thread_kill(struct lcd_thread *t)
{
/*
* Remove from LCD's list of threads
*/
if (mutex_lock_interruptible(&t->lcd->lcd_threads.lock)) {
LCD_ERR("interrupted, skipping list removal ...");
goto finally;
LCD_ERR("interrupted, caller should kill lcd ...");
goto out;
}
list_del(&t->lcd_threads);
mutex_unlock(&t->lcd->lcd_threads.lock);
/*
* Decrement lcd's ref count
*/
put_lcd(&t->lcd);
finally:
/*
* Free t
* Decrement the ref the lcd had on t
*/
kfree(t);
return ret;
put_lcd_thread(&t);
}
/* LCD THREAD CREATE / DESTROY -------------------------------------------- */
......@@ -1583,16 +1639,15 @@ static int lcd_add_thread(struct lcd *lcd, gva_t pc, gva_t stack_gva,
/*
* Alloc lcd_thread
*/
t = kzalloc(sizeof(*t), GFP_KERNEL); /* status unformed */
t = kzalloc(sizeof(*t), GFP_KERNEL); /* sets status and flags to 0 */
if (!t) {
LCD_ERR("failed to alloc lcd_thread");
ret = -ENOMEM;
goto fail1;
}
/*
* Add t to lcd's list
* Add t to lcd's list, and bump ref counts
*/
t->lcd = lcd;
INIT_LIST_HEAD(&t->lcd_threads);
if (mutex_lock_interruptible(&lcd->lcd_threads.lock)) {
LCD_ERR("interrupted, cleaning up...");
......@@ -1602,11 +1657,24 @@ static int lcd_add_thread(struct lcd *lcd, gva_t pc, gva_t stack_gva,
list_add(&t->lcd_threads, &lcd->lcd_threads.list);
mutex_unlock(&lcd->lcd_threads.lock);
get_lcd(&t->lcd, lcd); /* matched with put in lcd_thread_kill / fail3 */
get_lcd_thread(&t); /* matched with put in lcd_thread_kill / fail2 */
/*
* Set up utcb (at bottom of stack -- masking off lower bits)
*
* XXX: We need to restrict any LCD that has a capability to the
* utcb/stack page from freeing it underneath us.
*/
t->utcb = hva2va(stack_hva);
t->utcb = (void *)(((u64)t->utcb) & PAGE_MASK);
/*
* Set up thread proxy
*/
t->proxy.lcd_thread = t;
t->proxy.endpoint = NULL;
INIT_LIST_HEAD(t->proxy.proxies);
/*
* Alloc corresponding lcd_arch_thread
*/
......@@ -1653,13 +1721,20 @@ static int lcd_add_thread(struct lcd *lcd, gva_t pc, gva_t stack_gva,
goto fail8;
}
/*
* Bumpg reference count on kthread
* Bump reference count on kthread
*/
get_task_struct(t->kthread);
/*
* Store back reference
* Store back reference, and bump reference count on lcd_thread. This
* ref count won't decrement until the kernel thread is free'd (in
* kernel/fork.c).
*/
t->kthread->lcd_thread = t;
get_lcd_thread(&t->kthread->lcd_thread, t); /* put in kernel/fork.c */
/*
* Mark as alive
*/
set_lcd_thread_status(t, LCD_THREAD_ALIVE);
/*
* DONE!
......@@ -1683,8 +1758,10 @@ fail3:
list_del(&t->lcd_threads);
mutex_unlock(&lcd->lcd_threads.lock);
put_lcd(&t->lcd);
fail2:
kfree(t);
put_lcd_thread(&t);
fail1:
fail0:
return ret;
......@@ -1852,8 +1929,10 @@ module_exit(lcd_exit);
EXPORT_SYMBOL(lcd_create);
EXPORT_SYMBOL(lcd_destroy);
EXPORT_SYMBOL(__put_lcd);
EXPORT_SYMBOL(lcd_thread_start);
EXPORT_SYMBOL(lcd_thread_kill);
EXPORT_SYMBOL(__put_lcd_thread);
/* DEBUGGING ---------------------------------------- */
......
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