From: Peter Dinda Date: Thu, 5 May 2011 18:09:36 +0000 (-0500) Subject: Ported host device support from monolithic embedding X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=ec8e7606831a6768d67c47ca8f48119af64f3453;p=palacios-OLD.git Ported host device support from monolithic embedding --- diff --git a/linux_module/Makefile b/linux_module/Makefile index 1262bb9..00fcbc4 100644 --- a/linux_module/Makefile +++ b/linux_module/Makefile @@ -13,7 +13,8 @@ v3vee-objs := palacios.o \ palacios-dev.o \ palacios-vm.o \ palacios-mm.o \ - palacios-queue.o + palacios-queue.o \ + palacios-hashtable.o ifdef V3_CONFIG_CONSOLE v3vee-objs += palacios-console.o @@ -44,6 +45,20 @@ ifdef V3_CONFIG_SOCKET v3vee-objs += palacios-socket.o endif +ifdef V3_CONFIG_KEYED_STREAMS + v3vee-objs += palacios-keyed-stream.o +endif + +ifdef V3_CONFIG_HOST_DEVICE + v3vee-objs += palacios-host-dev.o +endif + +ifdef V3_CONFIG_GRAPHICS_CONSOLE + v3vee-objs += palacios-graphics-console.o +endif + + + v3vee-objs += ../libv3vee.a diff --git a/linux_module/palacios-host-dev-user.h b/linux_module/palacios-host-dev-user.h new file mode 100644 index 0000000..54b2e17 --- /dev/null +++ b/linux_module/palacios-host-dev-user.h @@ -0,0 +1,81 @@ +#ifndef _PALACIOS_HOST_DEV_USER_H_ +#define _PALACIOS_HOST_DEV_USER_H_ + +/* + * Palacios Host Device Interface + User-space interface + * (c) Peter Dinda, 2011 + */ + + +#define V3_VM_HOST_DEV_CONNECT 512+1 + +/* to detemine whether a host request is available, poll the fd for read */ + +/* make a request for reading/writing guest or injecting irq */ +/* the arguemnt is a pointer to a palacios_host_dev_user_op struct */ +/* return is negative on error, positive to indicate bytes read/written or irq injected*/ +#define V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL 1 + +/* find out the size of the current host request, if one is pending */ +/* you find out if one is pending by read poll/select on the fd */ +/* the argument is a pointer to a uint64_t that will give the total size */ +/* ioctl returns 1 if a request is ready, 0 if there is no request */ +/* -EFAULT if there is a an error */ +#define V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL 2 + +/* get the current host request, if one is available */ +/* the argument is a pointer to a palacios_host_dev_host_request_response */ +/* of the needed size */ +/* ioctl returns 1 if a request is ready+copied, 0 if there is no request */ +/* -EFAULT if there is a an error */ +#define V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL 3 + +/* write back the response to the previously pulled host request */ +/* the argument is a pointer to a palacios_host_dev_host_request_response */ +#define V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL 4 + + +#ifdef __KERNEL__ +#define USER __user +#else +#define USER +#endif + +struct palacios_host_dev_user_op { +#define PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST 1 +#define PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST 2 +#define PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST 3 + uint32_t type; // type of operation (from the #defs above) + void *gpa; // physical address in guest to read or write + void USER *data; // user address of data that will be read or written + uint64_t len; // number of bytes to move + + uint8_t irq; // irq to inject +}; + + +struct palacios_host_dev_host_request_response { + // data_len must remain the first field in this structure + uint64_t data_len; // size of the structure + occupied data + uint64_t len; // size of the structure in total +#define PALACIOS_HOST_DEV_HOST_REQUEST_READ_IO 1 +#define PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_IO 2 +#define PALACIOS_HOST_DEV_HOST_REQUEST_READ_MEM 3 +#define PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_MEM 4 +#define PALACIOS_HOST_DEV_HOST_REQUEST_READ_CONF 5 +#define PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_CONF 6 + uint32_t type; // one of the types given above + uint16_t port; // port number, for IO + void *gpa; // physical address in the guest for memory ops + uint64_t conf_addr; // address in the configuration for configuration ops + + uint64_t op_len; // response: bytes read/written to the device + // request: bytes to read/write + + uint8_t data[0]; // data (if any) + +} ; + + + +#endif diff --git a/linux_module/palacios-host-dev.c b/linux_module/palacios-host-dev.c new file mode 100644 index 0000000..686c962 --- /dev/null +++ b/linux_module/palacios-host-dev.c @@ -0,0 +1,1017 @@ +/* + * Host device interface + user-space device interface + * (c) 2011 Peter Dinda + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "palacios.h" +#include "palacios-host-dev.h" +#include "palacios-host-dev-user.h" + + + +/* + There are two things in this file: + + + 1. An implementation of the Palacios host device interface that will + accept any URL from Palacios. Currently, the only URL type it will + handle is user:, but it should be clear how to extend with + other types. + + Palacios opens a host device by issuing something like this: + + palacios_host_dev_open( impl="user:foo" busclass=pci, opaque ) + + This will attempt to rendezvous with the user space device The + rendevzous retry and timeout periods can be set below. + + 2. An implementation of user: urls - the host interface is mapped + into an RPC-like interface to/from user space via a file + interface. + + The user space gets a file descriptor like this: + + int vmfd = open("/dev/v3-vmX",...); + + int devfd = ioctl(vmfd,V3_HOST_DEV_CONNECT,"user:foo"); + + This will attempt to rendezvous with the host side. + + If it returns successfully, you can now issue synchronous, + blocking RPCs to the guest to read/write its memory and inject + irqs. This means that a user->host request is handled + immediately, and independently of any host->user request. + + struct palacios_host_dev_user_op op; + + // fill out op + op.type = PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST; + ... + + ioctl(devfd, V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL, &op); + + // return value is # bytes read or written; or 0 if irq injected + // negative value is error + + The interface from the host to the user side is asynchronous + blocking RPC. Any host device will have at most one outstanding + request from palacios. The implementation here stores the + request until the user side is ready to accept it. The user side + can check if there is a request via a poll/select or an ioctl. + The ioctl also returns the needed size for the request structure. + After the user side has a request, it is expected to process it, + perhaps making user->host requests as described above, and then + return a response. Only one host->user request should be in + progress at any time in the user space. + + What this looks like is: + + poll(...devfd for read...) or select(...devfd for read...) + + if (devfd is marked readable) { + uint64_t size; + + ioctl(devfd,V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL,&size); + // returns 1 if there is a request, 0 if not, negative on err + + struct palacios_host_dev_host_request_response *req; + + req = allocate req to be at least size bytes long + + ioctl(devfd,V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL,req) + // returns 1 if there is a request, 0 if not, negative on err + + // process request, perhaps using above user->host request + // build response structure + // resp.data_len == size of structure including relevant data at end + + ioctl(devfd,V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL,resp); + // returns 0 if there is no outstanding request (user error) + // 1 on success, negative on error + } + + +*/ + + +#define MAX_URL 256 + +#define RENDEZVOUS_WAIT_SECS 60 +#define RENDEZVOUS_RETRY_SECS 1 + + +struct palacios_host_device_user { + spinlock_t lock; + int connected; // is the user space connected to this? + int waiting; // am I waiting for a user-space response? + + int fd; // what is the user space fd? + + char url[MAX_URL]; // what is the url describing the device + + v3_guest_dev_t guestdev; // what is the palacios-side device + + wait_queue_head_t user_wait_queue; // user space processes waiting on us (should be only one) + wait_queue_head_t host_wait_queue; // host threads (should only be one) waiting on user space + + struct v3_guest *guest; // my guest + struct palacios_host_dev_host_request_response *req; // curent request + struct palacios_host_dev_host_request_response *resp; // curent response + + struct list_head node; // for adding me to the list of hostdevs this VM has +}; + + +/************************************************************************************** + Utility functions +*************************************************************************************/ + +static void palacios_host_dev_user_free(struct palacios_host_device_user *dev) +{ + if (dev->req) { + kfree(dev->req); + dev->req=0; + } + if (dev->resp) { + kfree(dev->resp); + dev->resp=0; + } + kfree(dev); +} + +static int palacios_resize_reqresp(struct palacios_host_dev_host_request_response **r, uint64_t data_size, int copy) +{ + if (!*r) { + // allocate it + *r = kmalloc(sizeof(struct palacios_host_dev_host_request_response)+data_size,GFP_KERNEL); + if (!*r) { + return -1; + } else { + (*r)->len=sizeof(struct palacios_host_dev_host_request_response)+data_size; + return 0; + } + } else { + //let it go if it's big enough + uint64_t cur_len = (*r)->len-sizeof(struct palacios_host_dev_host_request_response); + + if (data_size<=cur_len) { + // do nothing + return 0; + } else { + struct palacios_host_dev_host_request_response *new; + + if (!copy) { + kfree(*r); + *r=0; + } + new = kmalloc(sizeof(struct palacios_host_dev_host_request_response)+data_size,GFP_KERNEL); + if (!new) { + return -1; + } else { + new->len=sizeof(struct palacios_host_dev_host_request_response)+data_size; + if (copy) { + memcpy(new->data,(*r)->data,(*r)->data_len-sizeof(struct palacios_host_dev_host_request_response)); + new->data_len=(*r)->data_len; + kfree(*r); + } + *r=new; + return 0; + } + } + } +} + +static void cycle_request_response(struct palacios_host_device_user *dev) +{ + // wake up user side so that polls fall through + wake_up_interruptible(&(dev->user_wait_queue)); + // put us to sleep until the user side wakes us up + wait_event_interruptible((dev->host_wait_queue), (dev->waiting==0)); +} + +static void cycle_response_request(struct palacios_host_device_user *dev) +{ + // wake up host side + wake_up_interruptible(&(dev->host_wait_queue)); +} + + +/********************************************************************************************* + + Interface to user space + + *********************************************************************************************/ + + + +static unsigned int host_dev_poll(struct file * filp, + struct poll_table_struct * poll_tb) +{ + + struct palacios_host_device_user * dev = filp->private_data; + unsigned long f; + + if (!dev->connected) { + return -EFAULT; + } + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + // Yes, we have a request if you want it! + spin_unlock_irqrestore(&(dev->lock),f); + return POLLIN | POLLRDNORM; + } + + // No request yet, so we need to wait for one to show up. + + // register ourselves on the user wait queue + poll_wait(filp, &(dev->user_wait_queue), poll_tb); + + spin_unlock_irqrestore(&(dev->lock),f); + + // We will get called again when that queue is woken up + + return 0; +} + + +static int host_dev_release(struct inode * i, struct file * filp) +{ + struct palacios_host_device_user *dev = filp->private_data; + unsigned long f; + + printk("palacios: user side is closing host device \"%s\"\n",dev->url); + + spin_lock_irqsave(&(dev->lock), f); + dev->connected = 0; + spin_unlock_irqrestore(&(dev->lock), f); + + // it is the palacios->host interface's responsibility to ignore + // reads/writes until connected is true + + return 0; +} + +static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + + struct palacios_host_device_user *dev = fp->private_data; + + if (!dev->connected) { + return -EFAULT; + } + + switch (val) { + case V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL: { + + struct palacios_host_dev_user_op op; + + if (copy_from_user(&op,argp,sizeof(struct palacios_host_dev_user_op))) { + printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url); + return -EFAULT; + } + + switch (op.type) { + case PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST: { + void *temp = kmalloc(op.len,GFP_KERNEL); + + if (!temp) { + printk("palacios: unable to allocate enough for read guest request for host device \"%s\"\n",dev->url); + return -EFAULT; + } + + if (v3_host_dev_read_guest_mem(dev->guestdev, + dev, + op.gpa, + temp, + op.len) != op.len) { + printk("palacios: unable to read enough from guest for host device \"%s\"\n",dev->url); + kfree(temp); + return -EFAULT; + } + + if (copy_to_user(op.data,temp,op.len)) { + printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url); + kfree(temp); + return -EFAULT; + } + + kfree(temp); + + return op.len; + } + break; + + + case PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST: { + + void *temp = kmalloc(op.len,GFP_KERNEL); + + if (!temp) { + printk("palacios: unable to allocate enough for write guest request for host device \"%s\"\n",dev->url); + return -EFAULT; + } + + if (copy_from_user(temp,op.data,op.len)) { + printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url); + kfree(temp); + return -EFAULT; + } + + if (v3_host_dev_write_guest_mem(dev->guestdev, + dev, + op.gpa, + temp, + op.len) != op.len) { + printk("palacios: unable to write enough to guest for host device \"%s\"\n",dev->url); + kfree(temp); + return -EFAULT; + } + + kfree(temp); + + return op.len; + } + break; + + case PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST: { + + return v3_host_dev_raise_irq(dev->guestdev, dev, op.irq); + } + break; + + default: + printk("palacios: unknown user request to host device \"%s\"\n",dev->url); + return -EFAULT; + break; + } + } + break; + + case V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL: { + + unsigned long f; + + spin_lock_irqsave(&(dev->lock),f); + + if (!(dev->waiting)) { + spin_unlock_irqrestore(&(dev->lock),f); + return 0; // no request available now + } + + if (copy_to_user(argp,&(dev->req->data_len),sizeof(uint64_t))) { + printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return -EFAULT; // failed to copy! + } + + spin_unlock_irqrestore(&(dev->lock),f); + + return 1; // have request for you + + } + + break; + + case V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL: { + + unsigned long f; + + spin_lock_irqsave(&(dev->lock),f); + + if (!(dev->waiting)) { + spin_unlock_irqrestore(&(dev->lock),f); + return 0; // no request available now + } + + if (copy_to_user(argp,dev->req,dev->req->data_len)) { + printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return -EFAULT; // failed to copy! + } + + spin_unlock_irqrestore(&(dev->lock),f); + + return 1; // copied request for you + } + break; + + case V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL: { + + unsigned long f; + uint64_t user_datalen; + + spin_lock_irqsave(&(dev->lock),f); + + if (!(dev->waiting)) { + spin_unlock_irqrestore(&(dev->lock),f); + return 0; // no request outstanding, so we do not need a response! + } + + if (copy_from_user(&user_datalen,argp,sizeof(uint64_t))) { + printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return -EFAULT; // failed to copy! + } + + if (palacios_resize_reqresp(&(dev->resp),user_datalen,0)) { + printk("palacios: unable to resize to accept request of size %llu from user for host device \"%s\"\n",user_datalen,dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return -EFAULT; + } + + if (copy_from_user(dev->resp, argp, user_datalen)) { + printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return -EFAULT; // failed to copy! + } + + // now have valid response! + dev->waiting=0; + + // wake the palacios side up so that it sees it + cycle_response_request(dev); + + spin_unlock_irqrestore(&(dev->lock),f); + + return 1; // done + } + break; + + default: + printk("palacios: unknown ioctl for host device \"%s\"\n",dev->url); + return -EFAULT; + break; + } + +} + + +static struct file_operations host_dev_fops = { + .poll = host_dev_poll, + .release = host_dev_release, + .ioctl = host_dev_ioctl, +}; + + + + +int connect_host_dev(struct v3_guest * guest, char *url) +{ + struct palacios_host_device_user *dev; + unsigned long f1, f2; + int i; + + // currently only support user: types: + if (strncasecmp(url,"user:",5)) { + printk("palacios: do not currently support host devices of type in \"%s\"\n",url); + return -1; + } + + printk("palacios: attempting to rendezvous with palacios side of host device \"%s\"\n",url); + + // We will scan the list looking for the relevant + // URL. If we don't find it after a while, we give up + + for (i=0;ihostdev.lock),f1); + list_for_each_entry(dev,&(guest->hostdev.devs), node) { + if (!strncasecmp(url,dev->url,MAX_URL)) { + // found it + spin_lock_irqsave(&(dev->lock),f2); + if (dev->connected) { + printk("palacios: device for \"%s\" is already connected!\n",url); + spin_unlock_irqrestore(&(dev->lock),f2); + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + return -1; + } else { + dev->fd = anon_inode_getfd("v3-hostdev", &host_dev_fops, dev, 0); + if (dev->fd<0) { + printk("palacios: cannot create fd for device \"%s\"\n",url); + spin_unlock_irqrestore(&(dev->lock),f2); + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + return -1; + } + dev->connected=1; + dev->waiting=0; + if (dev->req) { + kfree(dev->req); + dev->req=0; + } + if (dev->resp) { + kfree(dev->resp); + dev->resp=0; + } + printk("palacios: connected fd for device \"%s\"\n",url); + spin_unlock_irqrestore(&(dev->lock),f2); + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + return dev->fd; + } + spin_unlock_irqrestore(&(dev->lock),f2); + } + } + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + + ssleep(RENDEZVOUS_RETRY_SECS); + } + + printk("palacios: timeout waiting for connection for device \"%s\"",url); + + return -1; + +} + + + + + + + + +/*************************************************************************************** + + Following this is the implementation of the palacios->host interface + +**************************************************************************************/ + +static v3_host_dev_t palacios_host_dev_open(char *url, + v3_bus_class_t bus, + v3_guest_dev_t gdev, + void *host_priv_data) +{ + struct v3_guest *guest= (struct v3_guest*)host_priv_data; + struct palacios_host_device_user *dev; + unsigned long f1,f2; + int i; + + /* + I will create the device in the list and then wait + for the user side to attach + */ + + + if (strncasecmp(url,"user:",5)) { + printk("palacios: do not currently support devices of type in \"%s\"\n",url); + return NULL; + } + + // Check to see if a device of this url already exists, which would be ugly + spin_lock_irqsave(&(guest->hostdev.lock),f1); + list_for_each_entry(dev,&(guest->hostdev.devs), node) { + if (!strncasecmp(url,dev->url,MAX_URL)) { + // found it + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + printk("palacios: a host device with url \"%s\" already exists in the guest!\n",url); + return NULL; + } + } + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + + + printk("palacios: creating host device \"%s\"\n",url); + + dev = kmalloc(sizeof(struct palacios_host_device_user),GFP_KERNEL); + + if (!dev) { + printk("palacios: cannot allocate for host device \"%s\"\n",url); + return NULL; + } + + memset(dev,0,sizeof(struct palacios_host_device_user)); + + strncpy(dev->url,url,MAX_URL); + + dev->guestdev=gdev; + + dev->guest=guest; + + spin_lock_init(&(dev->lock)); + + init_waitqueue_head(&(dev->user_wait_queue)); + init_waitqueue_head(&(dev->host_wait_queue)); + + printk("palacios: attempting to rendezvous with user side of host device \"%s\"\n",url); + + // Insert ourselves into the list + spin_lock_irqsave(&(guest->hostdev.lock),f1); + list_add(&(dev->node),&(guest->hostdev.devs)); + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + + + // Now wait until we are noticed! + for (i=0;ilock),f2); + if (dev->connected){ + printk("palacios: connection with user side established for host device \"%s\" fd=%d\n",dev->url,dev->fd); + spin_unlock_irqrestore(&(dev->lock),f2); + return dev; + } + spin_unlock_irqrestore(&(dev->lock),f2); + ssleep(RENDEZVOUS_RETRY_SECS); + } + + printk("palacios: timeout waiting for user side to connect to host device \"%s\"",url); + + // get us out of the list + spin_lock_irqsave(&(guest->hostdev.lock),f1); + list_del(&(dev->node)); + spin_unlock_irqrestore(&(guest->hostdev.lock),f1); + + palacios_host_dev_user_free(dev); + + return NULL; +} + +static int palacios_host_dev_close(v3_host_dev_t hostdev) +{ + unsigned long f1, f2; + + struct palacios_host_device_user *dev = (struct palacios_host_device_user *) hostdev; + + printk("palacios: closing host device \"%s\"\n",dev->url); + + spin_lock_irqsave(&(dev->guest->hostdev.lock),f1); + + spin_lock_irqsave(&(dev->lock),f2); + + if (dev->connected) { + dev->connected=0; + // After this, any user side request will return -EFAULT + } + + list_del(&(dev->node)); + + spin_unlock_irqrestore(&(dev->lock),f2); + spin_unlock_irqrestore(&(dev->guest->hostdev.lock),f1); + + palacios_host_dev_user_free(dev); + + return 0; +} + + + + +static uint64_t palacios_host_dev_read_io(v3_host_dev_t hostdev, + uint16_t port, + void *dest, + uint64_t len) +{ + struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev; + unsigned long f; + uint64_t op_len; + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + printk("palacios: guest issued i/o read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + if (!dev->connected) { + printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + // resize request and response in case they will need it + palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data + + dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_IO; + dev->req->port=port; + dev->req->op_len=len; + dev->req->gpa=0; + dev->req->conf_addr=0; + dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response); + + dev->waiting=1; + + spin_unlock_irqrestore(&(dev->lock),f); + + // hand over to the user space and wait for it to respond + cycle_request_response(dev); + + // We're back! So now we'll hand the response back to Palacios + + spin_lock_irqsave(&(dev->lock),f); + + op_len = dev->resp->op_len < len ? dev->resp->op_len : len ; + + memcpy(dest,dev->resp->data, op_len); + + spin_unlock_irqrestore(&(dev->lock),f); + + return op_len; +} + +static uint64_t palacios_host_dev_read_mem(v3_host_dev_t hostdev, + void * gpa, + void *dest, + uint64_t len) +{ + struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev; + unsigned long f; + uint64_t op_len; + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + printk("palacios: guest issued memory read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + if (!dev->connected) { + printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + // resize request and response in case they will need it + palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data + + dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_MEM; + dev->req->port=0; + dev->req->op_len=len; + dev->req->gpa=gpa; + dev->req->conf_addr=0; + dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response); + + dev->waiting=1; + + spin_unlock_irqrestore(&(dev->lock),f); + + // hand over to the user space and wait for it to respond + cycle_request_response(dev); + + // We're back! So now we'll hand the response back to Palacios + + spin_lock_irqsave(&(dev->lock),f); + + op_len = dev->resp->op_len < len ? dev->resp->op_len : len ; + + memcpy(dest,dev->resp->data, op_len); + + spin_unlock_irqrestore(&(dev->lock),f); + + return op_len; +} + +static uint64_t palacios_host_dev_read_conf(v3_host_dev_t hostdev, + uint64_t offset, + void *dest, + uint64_t len) +{ + struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev; + unsigned long f; + uint64_t op_len; + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + printk("palacios: guest issued config read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + if (!dev->connected) { + printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + // resize request and response in case they will need it + palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data + + dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_CONF; + dev->req->port=0; + dev->req->op_len=len; + dev->req->gpa=0; + dev->req->conf_addr=offset; + dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response); + + dev->waiting=1; + + spin_unlock_irqrestore(&(dev->lock),f); + + // hand over to the user space and wait for it to respond + cycle_request_response(dev); + + // We're back! So now we'll hand the response back to Palacios + + spin_lock_irqsave(&(dev->lock),f); + + op_len = dev->resp->op_len < len ? dev->resp->op_len : len ; + + memcpy(dest,dev->resp->data, op_len); + + spin_unlock_irqrestore(&(dev->lock),f); + + return op_len; +} + + +static uint64_t palacios_host_dev_write_io(v3_host_dev_t hostdev, + uint16_t port, + void *src, + uint64_t len) +{ + struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev; + unsigned long f; + uint64_t op_len; + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + printk("palacios: guest issued i/o write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + if (!dev->connected) { + printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + // resize request and response in case they will need it + palacios_resize_reqresp(&(dev->req),len,0); // make room for data + + dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_IO; + dev->req->port=port; + dev->req->op_len=len; + dev->req->gpa=0; + dev->req->conf_addr=0; + dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response)+len; + + memcpy(dev->req->data,src,len); + + dev->waiting=1; + + spin_unlock_irqrestore(&(dev->lock),f); + + // hand over to the user space and wait for it to respond + cycle_request_response(dev); + + // We're back! So now we'll hand the response back to Palacios + + spin_lock_irqsave(&(dev->lock),f); + + op_len = dev->resp->op_len < len ? dev->resp->op_len : len ; + + spin_unlock_irqrestore(&(dev->lock),f); + + return op_len; +} + + +static uint64_t palacios_host_dev_write_mem(v3_host_dev_t hostdev, + void * gpa, + void *src, + uint64_t len) +{ + struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev; + unsigned long f; + uint64_t op_len; + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + printk("palacios: guest issued memory write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + if (!dev->connected) { + printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + // resize request and response in case they will need it + palacios_resize_reqresp(&(dev->req),len,0); // make room for data + + dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_MEM; + dev->req->port=0; + dev->req->op_len=len; + dev->req->gpa=gpa; + dev->req->conf_addr=0; + dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response)+len; + + memcpy(dev->req->data,src,len); + + dev->waiting=1; + + spin_unlock_irqrestore(&(dev->lock),f); + + // hand over to the user space and wait for it to respond + cycle_request_response(dev); + + // We're back! So now we'll hand the response back to Palacios + + spin_lock_irqsave(&(dev->lock),f); + + op_len= dev->resp->op_len < len ? dev->resp->op_len : len ; + + spin_unlock_irqrestore(&(dev->lock),f); + + return op_len; +} + + +static int palacios_host_dev_ack_irq(v3_host_dev_t hostdev, uint8_t irq) +{ + // we don't care + return 0; +} + + + +static uint64_t palacios_host_dev_write_conf(v3_host_dev_t hostdev, + uint64_t offset, + void *src, + uint64_t len) +{ + struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev; + unsigned long f; + uint64_t op_len; + + spin_lock_irqsave(&(dev->lock),f); + + if (dev->waiting) { + printk("palacios: guest issued config write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + if (!dev->connected) { + printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url); + spin_unlock_irqrestore(&(dev->lock),f); + return 0; + } + + // resize request and response in case they will need it + palacios_resize_reqresp(&(dev->req),len,0); // make room for data + + dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_CONF; + dev->req->port=0; + dev->req->op_len=len; + dev->req->gpa=0; + dev->req->conf_addr=offset; + dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response)+len; + + memcpy(dev->req->data,src,len); + + dev->waiting=1; + + spin_unlock_irqrestore(&(dev->lock),f); + + // hand over to the user space and wait for it to respond + cycle_request_response(dev); + + // We're back! So now we'll hand the response back to Palacios + + spin_lock_irqsave(&(dev->lock),f); + + op_len = dev->resp->op_len < len ? dev->resp->op_len : len ; + + spin_unlock_irqrestore(&(dev->lock),f); + + return op_len; + } + + + + + + +static struct v3_host_dev_hooks palacios_host_dev_hooks = { + .open = palacios_host_dev_open, + .close = palacios_host_dev_close, + .read_io = palacios_host_dev_read_io, + .write_io = palacios_host_dev_write_io, + .read_mem = palacios_host_dev_read_mem, + .write_mem = palacios_host_dev_write_mem, + .read_config = palacios_host_dev_read_conf, + .write_config = palacios_host_dev_write_conf, + .ack_irq = palacios_host_dev_ack_irq, +}; + + + +int palacios_init_host_dev( void ) { + V3_Init_Host_Device_Support(&palacios_host_dev_hooks); + + return 0; +} diff --git a/linux_module/palacios-host-dev.h b/linux_module/palacios-host-dev.h new file mode 100644 index 0000000..d444b7d --- /dev/null +++ b/linux_module/palacios-host-dev.h @@ -0,0 +1,31 @@ +/* + * Palacios Host Device Interface + User-space interface + * (c) Peter Dinda, 2011 + */ + +#ifndef __PALACIOS_HOST_DEV_H__ +#define __PALACIOS_HOST_DEV_H__ + +#include +#include +#include "palacios-host-dev-user.h" + +struct v3_guest; + +/* + This is the collection of host devices that + a single guest has +*/ +struct palacios_host_dev { + spinlock_t lock; + struct list_head devs; +}; + + + +int connect_host_dev(struct v3_guest * guest, char *url); + +int palacios_init_host_dev( void ); + + +#endif