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

Stubbed out microkernel, starting lcd loader.

parent 62815dba
/*
* Look up the cnode at c in cspace. No locking required by caller.
* Returns non-zero on error.
*/
int lcd_cnode_lookup(struct cspace *cspace, cptr_t c, struct cnode **out);
/*
* Confirms src_cptr and dest_cptr point at valid slots. Confirms src
* has grant rights to cap at src_cptr. Transfers cap to dest_cptr, updates
* cdt. No locking required by caller. Returns non-zero on error.
*/
int lcd_cnode_grant(struct cspace *src, struct cspace *dest,
cptr_t src_cptr, cptr_t dest_cptr, unsigned int rights);
/*
* microkernel.c
*
* Authors: Charles Jacobsen <charlesj@cs.utah.edu>
* Copyright: University of Utah
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <lcd-prototype/lcd.h>
#include <lcd-prototype/lcd-msg.h>
struct lcd_rvp {
struct list_head senders;
struct list_head receivers;
struct mutex *lock;
};
static struct list_head all_lcds;
/* API ROUTINES -------------------------------------------------- */
static void handle_api_cap_alloc(struct lcd_thread *t)
{
cptr_t c;
int ret;
ret = lcd_alloc_cap(t->lcd->cspace->cap_cache, &c);
if (ret) {
__lcd_set_m0(LCD_ENOMEM);
return;
}
__lcd_set_m0(0);
__lcd_set_m1(c);
return;
}
static int create_lcd(char *mname, struct lcd **out)
{
/*
* Allocate lcd struct
*/
}
static void handle_api_create_lcd1(struct lcd_thread *t)
{
char *mname;
u64 mname_len;
cptr_t *cptrs;
u64 cptr_len;
int ret;
struct lcd *lcd;
/*
* Get args
*
* XXX: More care required when type 1 lcd's moved to separate
* address space.
*/
if (lcd_max_m(t) < 5) {
__lcd_set_m0(t, LCD_EINVAL);
return;
}
mname = __lcd_m1(t);
mname_len = __lcd_m2(t);
cptrs = __lcd_m3(t);
cptr_len = __lcd_m4(t);
/*
* Make sure mname is NUL terminated
*/
mname[mnane_len] = 0;
/*
* Load module
*/
ret = request_module(mname);
if (ret)
goto fail1;
/*
* Create lcd
*/
ret = create_lcd(mname, &lcd);
if (ret)
goto fail2;
/*
* Add lcd to list of all lcd's
*/
list_add_head(&lcd->all_lcds, &all_lcds);
/*
* Install capabilities (including microkernel cap) in lcd's cspace
*/
ret = install_caps(lcd, cptrs, cptr_len);
if (ret)
goto fail3;
/*
* Spin up kthread, etc.
*/
ret = boot_lcd(lcd);
if (ret)
goto fail4;
__lcd_set_m0(0);
return;
fail4:
/* any cap tear down necessary? */
fail3:
/* tear down lcd */
fail2:
/* unload module? */
fail1:
__lcd_set_m0(LCD_EIO);
return;
}
static void handle_api_create_lcd2(struct lcd_thread *t)
{
/*
* Equivalent to type 1 for now, since same addr space ...
*/
}
static void handle_api_exit(struct lcd_thread *t)
{
/*
* */
}
static void api_call(struct lcd_thread *t)
{
/*
* Confirm there is at least one valid message register (containing
* the api call index)
*/
if (lcd_max_m(t->tcb) == 0) {
__lcd_set_r0(t, LCD_EINVAL);
return;
}
switch (__lcd_m0(t)) {
case LCD_API_CAP_ALLOC:
/* check capability */
break;
case LCD_API_CREATE_LCD1:
/* check capability */
break;
case LCD_API_CREATE_LCD2:
/* check capability */
break;
case LCD_API_EXIT:
/* check capability */
break;
default:
__lcd_set_r0(t, LCD_EINVAL);
return;
}
__lcd_set_r0(t, 0);
return;
}
/* IPC -------------------------------------------------- */
static void copy_msg_regs(struct lcd_thread *sender,
struct lcd_thread *receiver)
{
int i;
for (i = 0; i < lcd_max_m(sender->tcb); i++)
receiver->tcb->m[i] = sender->tcb->m[i];
}
static void copy_msg_cap(struct lcd_thread *sender, struct lcd_thread *receiver,
cptr_t sender_cptr, cptr_t receiver_cptr,
unsigned int rights)
{
int ret;
ret = lcd_cnode_grant(sender->cspace, receiver->cspace,
from_ptr, to_ptr, rights);
if (ret)
LCD_ERR("failed to transfer cap @ %d in lcd_thread %p to slot @ %d in lcd_thread %p",
sender_cptr, sender, receiver_cptr, receiver);
}
static void copy_msg_caps(struct lcd_thread *sender,
struct lcd_thread *receiver)
{
int i;
for (i = 0; i < lcd_max_oc(sender->tcb) &&
i < lcd_max_ic(receiver->tcb); i++) {
copy_msg_cap(sender, receiver, sender->tcb->oc[i],
sender->tcb->oc_rights[i], receiver->tcb->ic[i]);
}
}
static void transfer_msg(struct lcd_thread *sender, struct lcd_thread *receiver,
int is_call)
{
copy_msg_regs(sender, receiver);
copy_msg_caps(sender, receiver);
if (is_call) {
copy_msg_cap(sender, receiver, sender->call_cptr,
receiver->reply_cptr, LCD_CAP_RIGHT_SEND);
}
}
static struct lcd_rvp *lookup_rvp(struct lcd_thread *lcd_thread, cptr_t c,
unsigned int required_rights)
{
struct cnode *cnode;
ret = lcd_cnode_lookup(lcd_thread->lcd->cspace, c, &cnode);
if (ret)
return NULL;
if (cnode->type != LCD_CAP_TYPE_SYNC_EP)
return NULL;
/* TODO: Uncomment when rights are implemented in caps. */
/* if (cnode->rights & required_rights != required_rights) */
/* return NULL; */
return cnode->object;
}
static void lcd_do_send(struct lcd_thread *sender, struct lcd_rvp *rvp,
int is_call)
{
int ret;
struct lcd_thread *receiver;
ret = mutex_lock_interruptible(&rvp->lock);
if (ret) {
LCD_ERR("interrupted");
__lcd_set_r0(sender, LCD_EINTR);
return;
}
if (list_empty(&rvp->receivers)) {
/*
* No one receiving; put myself in rvp's sender list
*/
list_add_tail(&sender->rvp_queue, &rvp->senders);
/*
* Mark myself as calling, if needed, so receiver knows to
* copy special reply cap.
*/
sender->is_calling = is_call;
/*
* Unlock mutex before we sleep.
*/
mutex_unlock(&rvp->lock);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
/*
* Someone woke me up. Receiver should have transferred
* message, so we just return.
*/
__lcd_set_r0(sender, 0);
return;
}
/*
* Otherwise, someone waiting to receive. Remove from
* receiving queue.
*/
receiver = list_first_entry(&rvp->receivers, struct lcd_thread,
rvp_queue);
list_del_init(&receiver->rvp_queue);
/*
* Done with rvp for now.
*/
mutex_unlock(&rvp->lock);
/*
* Send message, and wake up receiver.
*/
transfer_msg(sender, receiver, is_call);
wake_up_process(receiver->thread);
/*
* Return success
*/
__lcd_set_r0(sender, 0);
return;
}
static void lcd_send(struct lcd_thread *sender)
{
cptr_t c;
struct lcd_rvp *rvp;
c = __lcd_r1(sender);
/*
* Check for api call.
*/
if (c == LCD_API_CPTR) {
api_call(sender);
return;
}
/*
* Look up rvp and confirm send rights
*
* TODO: Uncomment below when send rights implemented.
*/
rvp = lookup_rvp(sender, c); /*, LCD_CAP_RIGHT_SEND); */
if (!rvp) {
LCD_ERR("lookup for rvp at %llu failed", c);
__lcd_set_r0(sender, LCD_EINVAL);
return;
}
lcd_do_send(sender, rvp, 0);
return;
}
static void lcd_do_recv(struct lcd_thread *receiver, struct lcd_rvp *rvp,
{
int ret;
struct lcd_thread *sender;
ret = mutex_lock_interruptible(&rvp->lock);
if (ret) {
LCD_ERR("interrupted");
__lcd_set_r0(receiver, LCD_EINTR);
return;
}
if (list_empty(&rvp->senders)) {
/*
* No one sending; put myself in rvp's receiver list
*/
list_add_tail(&receiver->rvp_queue, &rvp->receivers);
/*
* Unlock mutex before we sleep.
*/
mutex_unlock(&rvp->lock);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
/*
* Someone woke me up. Sender should have transferred
* message, so we just return.
*/
__lcd_set_r0(receiver, 0);
return;
}
/*
* Otherwise, someone waiting to send. Remove from
* sending queue.
*/
sender = list_first_entry(&rvp->senders, struct lcd_thread,
rvp_queue);
list_del_init(&sender->rvp_queue);
/*
* Done with rvp for now.
*/
mutex_unlock(&rvp->lock);
/*
* Receive message, and wake up sender.
*/
transfer_msg(sender, receiver, sender->is_calling);
wake_up_process(sender->thread);
/*
* Return success
*/
__lcd_set_r0(receiver, 0);
return;
}
static void lcd_recv(struct lcd_thread *receiver)
{
cptr_t c;
struct lcd_rvp *rvp;
/*
* Look up rvp and confirm receive rights
*
* TODO: Uncomment below when receive rights implemented.
*/
c = __lcd_r1(receiver);
rvp = lookup_rvp(receiver, c); /*, LCD_CAP_RIGHT_RECV); */
if (!rvp) {
LCD_ERR("lookup for rvp at %llu failed", c);
__lcd_set_r0(receiver, LCD_EINVAL);
return;
}
lcd_do_recv(receiver, rvp);
return;
}
static void lcd_call(struct lcd_thread *sender)
{
cptr_t c;
struct lcd_rvp *rvp;
/*
* Look up rvp and confirm send rights
*
* TODO: Uncomment below when send rights implemented.
*/
c = __lcd_r1(sender);
rvp = lookup_rvp(sender, c); /*, LCD_CAP_RIGHT_SEND); */
if (!rvp) {
LCD_ERR("lookup for rvp at %llu failed", c);
__lcd_set_r0(sender, LCD_EINVAL);
return;
}
/*
* Do send
*/
lcd_do_send(sender, rvp, 1);
/*
* Listen on my rvp
*/
rvp = lookup_rvp(sender, sender->call_cptr); /*, LCD_CAP_RIGHT_RECV); */
if (!rvp) {
LCD_ERR("lookup for rvp at %llu failed", c);
__lcd_set_r0(sender, LCD_EINVAL);
return;
}
lcd_do_recv(sender, rvp);
return;
}
static void lcd_reply(struct lcd_thread *sender)
{
struct lcd_rvp *rvp;
/*
* Look up rvp and confirm send rights
*
* TODO: Uncomment below when send rights implemented.
*/
rvp = lookup_rvp(sender, sender->reply_cptr);/*, LCD_CAP_RIGHT_SEND); */
if (!rvp) {
LCD_ERR("lookup for reply rvp at %llu failed",
sender->reply_cptr);
__lcd_set_r0(sender, LCD_EINVAL);
return;
}
lcd_do_send(sender, rvp, 0);
return;
}
/* INTERRUPT -------------------------------------------------- */
/*
* The `interrupt' handler.
*/
void lcd_int(void)
{
struct lcd_thread *lcd_thread = current->lcd_thread;
/*
* Check that calling thread is an lcd thread
*/
if (!lcd_thread)
return;
/*
* Confirm there is at least one valid machine register (containing
* the system call index)
*/
if (lcd_max_r(lcd_thread->tcb) == 0) {
__lcd_set_r0(lcd_thread, LCD_EINVAL);
return;
}
switch (__lcd_r0(lcd_thread)) {
case LCD_SYSCALL_SEND:
lcd_send(lcd_thread);
break;
case LCD_SYSCALL_RECV:
lcd_recv(lcd_thread);
break;
case LCD_SYSCALL_CALL:
lcd_call(lcd_thread);
break;
case LCD_SYSCALL_REPLY:
lcd_reply(lcd_thread);
break;
default:
__lcd_set_r0(lcd_thread, LCD_EINVAL);
break;
}
/*
* Reset counters
*/
lcd_set_max_r(lcd_thread->tcb, 0);
lcd_set_max_m(lcd_thread->tcb, 0);
lcd_set_max_oc(lcd_thread->tcb, 0);
lcd_set_max_ic(lcd_thread->tcb, 0);
/*
* Clear is_call var
*/
lcd_thread->is_calling = 0;
return;
}
EXPORT_SYMBOL(lcd_int);
int __init microkernel_init(void)
{
LCD_MSG("microkernel starting");
return 0;
}
void __exit microkernel_exit(void)
{
LCD_MSG("microkernel exiting");
return;
}
module_init(microkernel_init);
module_exit(microkernel_exit);
/*
* Author: Charles Jacobsen <charlesj@cs.utah.edu>
* Copyright: University of Utah
*
*/
#ifndef LCD_PROTOTYPE_LCD_MSG_H
#define LCD_PROTOTYPE_LCD_MSG_H
#define LCD_ERR(fmt, args...) __lcd_err(__FILE__, __LINE__, fmt "\n" , ##args)
static inline void __lcd_err(char *file, int lineno, char *fmt, ...)
{
va_list args;
pr_err("lcd-proto: %s:%d: error: ", file, lineno);
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
}
#define LCD_MSG(fmt, args...) __lcd_msg(__FILE__, __LINE__, fmt "\n" , ##args)
static inline void __lcd_msg(char *file, int lineno, char *fmt, ...)
{
va_list args;
pr_info("lcd-proto: %s:%d: note: ", file, lineno);
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
}
#define LCD_WARN(fmt, args...) __lcd_warn(__FILE__, __LINE__, fmt "\n" , ##args)
static inline void __lcd_warn(char *file, int lineno, char *fmt, ...)
{
va_list args;
pr_warn("lcd-proto: %s:%d: warning: ", file, lineno);
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
}
#endif /* LCD_PROTOTYPE_LCD_MSG_H */
......@@ -60,6 +60,7 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <lcd-prototype/lcd-tcb.h>
#include <linux/mutex.h>
#define LCD_NUM_MACHINE_REGS 8
#define LCD_NUM_MESSAGE_REGS 8
......@@ -82,16 +83,26 @@ struct lcd_tcb {
};
struct lcd_thread {
struct *task_struct;
struct *lcd_tcb;
struct *lcd;
struct list_head threads;
struct list_head rvp_queue;
struct task_struct *thread; /* host thread */
struct lcd_tcb *tcb; /* this thread's registers, etc. */
struct lcd *lcd; /* containing lcd */
cptr_t call_cptr; /* cptr to rvp temp granted during call */
cptr_t reply_cptr; /* cptr to rvp that a reply will go thru */
struct list_head threads; /* list of lcd_threads inside an lcd */
struct list_head rvp_queue; /* list of lcd_threads in an rvp queue */
int is_calling; /* non-zero if thread is sending and send */
/* was part of a call */
};
struct lcd {
struct cspace *cspace;
struct list_head threads;
struct cspace *cspace; /* lcd's cspace, shared by all threads */
struct list_head threads; /* list of all contained threads */
struct list_head all_list; /* list of all lcds */
};
/* MACHINE REGISTERS ---------------------------------------- */
......@@ -111,18 +122,26 @@ static inline void lcd_update_max_r(struct lcd_tcb *tcb, unsigned int idx)
lcd_set_max_r(tcb, idx + 1);
}
#define LCD_MK_MACHINE_REG_ACCESS(idx) \
static inline u64 lcd_r##idx(void) \
static inline u64 __lcd_r##idx(struct lcd_thread *t) \
{ \
struct lcd_tcb *tcb = current->lcd_thread->tcb; \
struct lcd_tcb *tcb = t->tcb; \
BUILD_BUG_ON(idx >= LCD_NUM_MACHINE_REGS); \
return tcb->r[idx]; \
} \
static inline void lcd_set_r##idx(u64 val) \
static inline void __lcd_set_r##idx(struct lcd_thread *t, u64 val) \
{ \
struct lcd_tcb *tcb = current->lcd_thread->tcb; \
struct lcd_tcb *tcb = t->tcb; \
BUILD_BUG_ON(idx >= LCD_NUM_MACHINE_REGS); \
lcd_update_max_r(tcb, idx); \
tcb->r[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_MACHINE_REG_ACCESS(0);
LCD_MK_MACHINE_REG_ACCESS(1);
......@@ -150,18 +169,26 @@ static inline void lcd_update_max_m(struct lcd_tcb *tcb, unsigned int idx)
lcd_set_max_m(tcb, idx + 1);
}
#define LCD_MK_MESSAGE_REG_ACCESS(idx) \
static inline u64 lcd_m##idx(void) \
static inline u64 __lcd_m##idx(struct lcd_thread *t) \
{ \
struct lcd_tcb *tcb = current->lcd_thread->tcb; \
struct lcd_tcb *tcb = t->tcb; \
BUILD_BUG_ON(idx >= LCD_NUM_MESSAGE_REGS); \
return tcb->m[idx]; \
} \
static inline void lcd_set_m##idx(u64 val) \
static inline void __lcd_set_m##idx(struct lcd_thread *t, u64 val) \
{ \
struct lcd_tcb *tcb = current->lcd_thread->tcb; \
struct lcd_tcb *tcb = t->tcb; \
BUILD_BUG_ON(idx >= LCD_NUM_MESSAGE_REGS); \
lcd_update_max_m(tcb, idx); \
tcb->m[idx] = val; \
} \
static inline u64 lcd_m##idx(void) \
{ \
return __lcd_m##idx(current->lcd_thread); \
} \
static inline void lcd_set_m##idx(u64 val) \
{ \
__lcd_set_m##idx(current->lcd_thread, val); \
}
LCD_MK_MESSAGE_REG_ACCESS(0);
LCD_MK_MESSAGE_REG_ACCESS(1);
......@@ -188,26 +215,39 @@ static inline void lcd_update_max_oc(struct lcd_tcb *tcb, unsigned int idx)
if (idx >= lcd_max_oc(tcb))
lcd_set_max_oc(tcb, idx + 1);
}
#define LCD_MK_OUT_CAP_REG_ACCESS(idx) \
static inline cptr_t lcd_oc##idx(void) \
{ \
struct lcd_tcb *tcb = current->lcd_thread->tcb; \
BUILD_BUG_ON(idx >= LCD_NUM_OUT_CAP_REGS); \
return tcb->oc[idx]; \
} \
static inline unsigned int lcd_oc_rights##idx(void) \
#define LCD_MK_OUT_CAP_REG_ACCESS(idx) \
static inline cptr_t __lcd_oc##idx(struct lcd_thread *t) \
{ \
struct lcd_tcb *tcb = t->tcb; \
BUILD_BUG_ON(idx >= LCD_NUM_OUT_CAP_REGS); \