/* * 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; }