Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Ported host device support from monolithic embedding
Peter Dinda [Thu, 5 May 2011 18:09:36 +0000 (13:09 -0500)]
linux_module/Makefile
linux_module/palacios-host-dev-user.h [new file with mode: 0644]
linux_module/palacios-host-dev.c [new file with mode: 0644]
linux_module/palacios-host-dev.h [new file with mode: 0644]

index 1262bb9..00fcbc4 100644 (file)
@@ -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 (file)
index 0000000..54b2e17
--- /dev/null
@@ -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 (file)
index 0000000..686c962
--- /dev/null
@@ -0,0 +1,1017 @@
+/* 
+ * Host device interface + user-space device interface
+ * (c) 2011 Peter Dinda
+ */
+
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include <interfaces/vmm_host_dev.h>
+
+#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:<name>, 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;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) { 
+       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_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;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) { 
+       spin_lock_irqsave(&(dev->lock),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 (file)
index 0000000..d444b7d
--- /dev/null
@@ -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 <linux/spinlock.h>
+#include <linux/list.h>
+#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