Message ID | 1431840030-2797-5-git-send-email-quan.xu@intel.com |
---|---|
State | New |
Headers | show |
On Sun, 17 May 2015, Quan Xu wrote: > This patch adds infrastructure for xen front drivers living in qemu, > so drivers don't need to implement common stuff on their own. It's > mostly xenbus management stuff: some functions to access XenStore, > setting up XenStore watches, callbacks on device discovery and state > changes, and handle event channel between the virtual machines. > > Call xen_fe_register() function to register XenDevOps, and make sure, > XenDevOps's flags is DEVOPS_FLAG_FE, which is flag bit to point out > the XenDevOps is Xen frontend. > > Signed-off-by: Quan Xu <quan.xu@intel.com> > --- > hw/xen/Makefile.objs | 2 +- > hw/xen/xen_frontend.c | 345 +++++++++++++++++++++++++++++++++++++++++++ > include/hw/xen/xen_backend.h | 8 + > 3 files changed, 354 insertions(+), 1 deletion(-) > create mode 100644 hw/xen/xen_frontend.c > > diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs > index 9ac9f7c..95eb9d0 100644 > --- a/hw/xen/Makefile.objs > +++ b/hw/xen/Makefile.objs > @@ -1,5 +1,5 @@ > # xen backend driver support > -common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o xen_pvdev.o > +common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o xen_frontend.o xen_pvdev.o > > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o > diff --git a/hw/xen/xen_frontend.c b/hw/xen/xen_frontend.c > new file mode 100644 > index 0000000..55af45a > --- /dev/null > +++ b/hw/xen/xen_frontend.c > @@ -0,0 +1,345 @@ > +/* > + * Xen frontend driver infrastructure > + * > + * Copyright (c) 2015 Intel Corporation > + * Authors: > + * Quan Xu <quan.xu@intel.com> > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <inttypes.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <sys/signal.h> > + > +#include "hw/hw.h" > +#include "sysemu/char.h" > +#include "qemu/log.h" > +#include "hw/xen/xen_backend.h" > +#include <xen/grant_table.h> > + > +int xenstore_dev; > + > +/* private */ > +static int debug; > + > +static void xen_fe_evtchn_event(void *opaque) > +{ > + struct XenDevice *xendev = opaque; > + evtchn_port_t port; > + > + port = xc_evtchn_pending(xendev->evtchndev); > + if (port != xendev->local_port) { > + return; > + } > + xc_evtchn_unmask(xendev->evtchndev, port); > + > + if (xendev->ops->event) { > + xendev->ops->event(xendev); > + } > +} This looks very similar to xen_be_evtchn_event. I think we should share the same function between frontends and backends. > +/* ------------------------------------------------------------- */ > + > +int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom) > +{ > + xendev->local_port = xc_evtchn_bind_unbound_port(xendev->evtchndev, > + remote_dom); > + if (xendev->local_port == -1) { > + xen_fe_printf(xendev, 0, "xc_evtchn_alloc_unbound failed\n"); > + return -1; > + } > + xen_fe_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); > + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), > + xen_fe_evtchn_event, NULL, xendev); > + return 0; > +} > + > +/* > + * Make sure, initialize the 'xendev->fe' in xendev->ops->init() or > + * xendev->ops->initialize() > + */ > +int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus) > +{ > + xs_transaction_t xbt = XBT_NULL; > + > + if (xendev->fe_state == xbus) { > + return 0; > + } > + > + xendev->fe_state = xbus; > + if (xendev->fe == NULL) { > + xen_fe_printf(NULL, 0, "xendev->fe is NULL\n"); > + return -1; > + } > + > +retry_transaction: > + xbt = xs_transaction_start(xenstore); > + if (xbt == XBT_NULL) { > + goto abort_transaction; > + } > + > + if (xenstore_write_int(xendev->fe, "state", xbus)) { > + goto abort_transaction; > + } > + > + if (!xs_transaction_end(xenstore, xbt, 0)) { > + if (errno == EAGAIN) { > + goto retry_transaction; > + } > + } > + > + return 0; > + > +abort_transaction: > + xs_transaction_end(xenstore, xbt, 1); > + return -1; > +} Given that this is xen_frontend.c, shouldn't this function be called xenbus_fe_switch_state? Also why is it so different from xen_be_set_state? I think it should be the same except for: xenstore_write_fe_int(xendev->fe, "state", state); > +/* > + * Simplify QEMU side, a thread is running in Xen backend, which will > + * connect frontend when the frontend is initialised. Call these initialised > + * functions. > + */ > +static int xen_fe_try_init(void *opaque) > +{ > + struct XenDevOps *ops = opaque; > + int rc = -1; > + > + if (ops->init) { > + rc = ops->init(NULL); > + } > + > + return rc; > +} > + > +static int xen_fe_try_initialise(struct XenDevice *xendev) > +{ > + int rc = 0, fe_state; > + > + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { > + fe_state = XenbusStateUnknown; > + } > + xendev->fe_state = fe_state; > + > + if (xendev->ops->initialise) { > + rc = xendev->ops->initialise(xendev); > + } > + if (rc != 0) { > + xen_fe_printf(xendev, 0, "initialise() failed\n"); > + return rc; > + } > + > + xenbus_switch_state(xendev, XenbusStateInitialised); > + return 0; > +} > + > +static void xen_fe_try_connected(struct XenDevice *xendev) > +{ > + if (!xendev->ops->connected) { > + return; > + } > + > + if (xendev->fe_state != XenbusStateConnected) { > + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { > + xen_fe_printf(xendev, 2, "frontend not ready, ignoring\n"); > + } else { > + xen_fe_printf(xendev, 2, "frontend not ready (yet)\n"); > + return; > + } > + } > + > + xendev->ops->connected(xendev); > +} > + > +static int xen_fe_check(struct XenDevice *xendev, uint32_t domid, > + int handle) > +{ > + int rc = 0; > + > + rc = xen_fe_try_initialise(xendev); > + if (rc != 0) { > + xen_fe_printf(xendev, 0, "xendev %s initialise error\n", > + xendev->name); > + goto err; > + } > + xen_fe_try_connected(xendev); > + > + return rc; > + > +err: > + xen_del_xendev(domid, handle); > + return -1; > +} This should be called xen_fe_try_initialise > +static char *xenstore_fe_get_backend(const char *type, int be_domid, > + uint32_t domid, int *hdl) > +{ > + char *name, *str, *ret = NULL; > + uint32_t i, cdev; > + int handle = 0; > + char path[XEN_BUFSIZE]; > + char **dev = NULL; > + > + name = xenstore_get_domain_name(domid); > + snprintf(path, sizeof(path), "frontend/%s/%d", type, be_domid); > + dev = xs_directory(xenstore, 0, path, &cdev); > + for (i = 0; i < cdev; i++) { > + handle = i; > + snprintf(path, sizeof(path), "frontend/%s/%d/%d", > + type, be_domid, handle); > + str = xenstore_read_str(path, "domain"); > + if (!strcmp(name, str)) { > + break; > + } This is an uncommon xenstore protocol. I don't recognize "frontend" and "domain". Usually: /local/domain/$DOMID_BACKEND/backend/$TYPE/$DOMID_FRONTEND/$VDEV/frontend contains: /local/domain/$DOMID_FRONTEND/device/$TYPE/$VDEV Vice versa: /local/domain/$FRONTEND_DOMID/device/$TYPE/$VDEV/backend contains: /local/domain/$BACKEND_DOMID/backend/$TYPE/$DOMID_FRONTEND/$VDEV Am I getting it wrong? Is there a reason to diverge from this? > + free(str); > + > + /* Not the backend domain */ > + if (handle == (cdev - 1)) { > + goto err; > + } > + } > + > + snprintf(path, sizeof(path), "frontend/%s/%d/%d", > + type, be_domid, handle); > + str = xenstore_read_str(path, "backend"); > + if (str != NULL) { > + ret = g_strdup(str); > + free(str); > + } > + > + *hdl = handle; > + free(dev); > + > + return ret; > +err: > + *hdl = -1; > + free(dev); > + return NULL; > +} > + > +static int xenstore_fe_scan(const char *type, uint32_t domid, > + struct XenDevOps *ops) > +{ > + struct XenDevice *xendev; > + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; > + unsigned int cdev, j; > + char *backend; > + char **dev = NULL; > + int rc; > + > + /* ops .init check, xendev is NOT initialized */ > + rc = xen_fe_try_init(ops); > + if (rc != 0) { > + return -1; > + } The init function should only be called when the corresponding xenstore entries are present. Here it looks like you are initializing the frontend unconditionally, even if /local/domain/0/device/$TYPE is missing. > + /* Get /local/domain/0/${type}/{} directory */ > + snprintf(path, sizeof(path), "frontend/%s", type); Why "frontend"? The string for frontends is usually "device". > + dev = xs_directory(xenstore, 0, path, &cdev); > + if (dev == NULL) { > + return 0; > + } It might actually be easier to scan /local/domain/$DOMID/backend instead of /local/domain/0/$TYPE, as I expect it to contain only one entry. Of course you would still need to double check that /local/domain/$DOMID_BACKEND/backend/$TYPE/$DOMID_FRONTEND contains the domain id of the domain QEMU is running on. > + for (j = 0; j < cdev; j++) { > + > + /* Get backend via domain name */ > + backend = xenstore_fe_get_backend(type, atoi(dev[j]), > + domid, &xenstore_dev); > + if (backend == NULL) { > + continue; > + } > + > + xendev = xen_fe_get_xendev(type, domid, xenstore_dev, backend, ops); > + free(backend); > + if (xendev == NULL) { > + xen_fe_printf(xendev, 0, "xendev is NULL.\n"); > + continue; > + } > + > + /* > + * Simplify QEMU side, a thread is running in Xen backend, which will > + * connect frontend when the frontend is initialised. > + */ > + if (xen_fe_check(xendev, domid, xenstore_dev) < 0) { > + xen_fe_printf(xendev, 0, "xendev fe_check error.\n"); > + continue; > + } > + > + /* Setup watch */ > + snprintf(token, sizeof(token), "be:%p:%d:%p", > + type, domid, xendev->ops); > + if (!xs_watch(xenstore, xendev->be, token)) { > + xen_fe_printf(xendev, 0, "xs_watch failed.\n"); > + continue; > + } > + } > + free(dev); > + return 0; > +} > + > +int xen_fe_register(const char *type, struct XenDevOps *ops) > +{ > + return xenstore_fe_scan(type, xen_domid, ops); > +} > + > +/* > + * msg_level: > + * 0 == errors (stderr + logfile). > + * 1 == informative debug messages (logfile only). > + * 2 == noisy debug messages (logfile only). > + * 3 == will flood your log (logfile only). > + */ > +void xen_fe_printf(struct XenDevice *xendev, int msg_level, > + const char *fmt, ...) > +{ > + va_list args; > + > + if (xendev) { > + if (msg_level > xendev->debug) { > + return; > + } > + qemu_log("xen fe: %s: ", xendev->name); > + if (msg_level == 0) { > + fprintf(stderr, "xen fe: %s: ", xendev->name); > + } > + } else { > + if (msg_level > debug) { > + return; > + } > + qemu_log("xen fe core: "); > + if (msg_level == 0) { > + fprintf(stderr, "xen fe core: "); > + } > + } > + va_start(args, fmt); > + qemu_log_vprintf(fmt, args); > + va_end(args); > + if (msg_level == 0) { > + va_start(args, fmt); > + vfprintf(stderr, fmt, args); > + va_end(args); > + } > + qemu_log_flush(); > +} As I wrote before, I would like to avoid having two separe xen printf functions. Please share the same function between frontends and backends.
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index 9ac9f7c..95eb9d0 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -1,5 +1,5 @@ # xen backend driver support -common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o xen_pvdev.o +common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o xen_frontend.o xen_pvdev.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o diff --git a/hw/xen/xen_frontend.c b/hw/xen/xen_frontend.c new file mode 100644 index 0000000..55af45a --- /dev/null +++ b/hw/xen/xen_frontend.c @@ -0,0 +1,345 @@ +/* + * Xen frontend driver infrastructure + * + * Copyright (c) 2015 Intel Corporation + * Authors: + * Quan Xu <quan.xu@intel.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include "hw/hw.h" +#include "sysemu/char.h" +#include "qemu/log.h" +#include "hw/xen/xen_backend.h" +#include <xen/grant_table.h> + +int xenstore_dev; + +/* private */ +static int debug; + +static void xen_fe_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) { + xendev->ops->event(xendev); + } +} + +/* ------------------------------------------------------------- */ + +int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom) +{ + xendev->local_port = xc_evtchn_bind_unbound_port(xendev->evtchndev, + remote_dom); + if (xendev->local_port == -1) { + xen_fe_printf(xendev, 0, "xc_evtchn_alloc_unbound failed\n"); + return -1; + } + xen_fe_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_fe_evtchn_event, NULL, xendev); + return 0; +} + +/* + * Make sure, initialize the 'xendev->fe' in xendev->ops->init() or + * xendev->ops->initialize() + */ +int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus) +{ + xs_transaction_t xbt = XBT_NULL; + + if (xendev->fe_state == xbus) { + return 0; + } + + xendev->fe_state = xbus; + if (xendev->fe == NULL) { + xen_fe_printf(NULL, 0, "xendev->fe is NULL\n"); + return -1; + } + +retry_transaction: + xbt = xs_transaction_start(xenstore); + if (xbt == XBT_NULL) { + goto abort_transaction; + } + + if (xenstore_write_int(xendev->fe, "state", xbus)) { + goto abort_transaction; + } + + if (!xs_transaction_end(xenstore, xbt, 0)) { + if (errno == EAGAIN) { + goto retry_transaction; + } + } + + return 0; + +abort_transaction: + xs_transaction_end(xenstore, xbt, 1); + return -1; +} + +/* + * Simplify QEMU side, a thread is running in Xen backend, which will + * connect frontend when the frontend is initialised. Call these initialised + * functions. + */ +static int xen_fe_try_init(void *opaque) +{ + struct XenDevOps *ops = opaque; + int rc = -1; + + if (ops->init) { + rc = ops->init(NULL); + } + + return rc; +} + +static int xen_fe_try_initialise(struct XenDevice *xendev) +{ + int rc = 0, fe_state; + + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { + fe_state = XenbusStateUnknown; + } + xendev->fe_state = fe_state; + + if (xendev->ops->initialise) { + rc = xendev->ops->initialise(xendev); + } + if (rc != 0) { + xen_fe_printf(xendev, 0, "initialise() failed\n"); + return rc; + } + + xenbus_switch_state(xendev, XenbusStateInitialised); + return 0; +} + +static void xen_fe_try_connected(struct XenDevice *xendev) +{ + if (!xendev->ops->connected) { + return; + } + + if (xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_fe_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_fe_printf(xendev, 2, "frontend not ready (yet)\n"); + return; + } + } + + xendev->ops->connected(xendev); +} + +static int xen_fe_check(struct XenDevice *xendev, uint32_t domid, + int handle) +{ + int rc = 0; + + rc = xen_fe_try_initialise(xendev); + if (rc != 0) { + xen_fe_printf(xendev, 0, "xendev %s initialise error\n", + xendev->name); + goto err; + } + xen_fe_try_connected(xendev); + + return rc; + +err: + xen_del_xendev(domid, handle); + return -1; +} + +static char *xenstore_fe_get_backend(const char *type, int be_domid, + uint32_t domid, int *hdl) +{ + char *name, *str, *ret = NULL; + uint32_t i, cdev; + int handle = 0; + char path[XEN_BUFSIZE]; + char **dev = NULL; + + name = xenstore_get_domain_name(domid); + snprintf(path, sizeof(path), "frontend/%s/%d", type, be_domid); + dev = xs_directory(xenstore, 0, path, &cdev); + for (i = 0; i < cdev; i++) { + handle = i; + snprintf(path, sizeof(path), "frontend/%s/%d/%d", + type, be_domid, handle); + str = xenstore_read_str(path, "domain"); + if (!strcmp(name, str)) { + break; + } + + free(str); + + /* Not the backend domain */ + if (handle == (cdev - 1)) { + goto err; + } + } + + snprintf(path, sizeof(path), "frontend/%s/%d/%d", + type, be_domid, handle); + str = xenstore_read_str(path, "backend"); + if (str != NULL) { + ret = g_strdup(str); + free(str); + } + + *hdl = handle; + free(dev); + + return ret; +err: + *hdl = -1; + free(dev); + return NULL; +} + +static int xenstore_fe_scan(const char *type, uint32_t domid, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + unsigned int cdev, j; + char *backend; + char **dev = NULL; + int rc; + + /* ops .init check, xendev is NOT initialized */ + rc = xen_fe_try_init(ops); + if (rc != 0) { + return -1; + } + + /* Get /local/domain/0/${type}/{} directory */ + snprintf(path, sizeof(path), "frontend/%s", type); + dev = xs_directory(xenstore, 0, path, &cdev); + if (dev == NULL) { + return 0; + } + + for (j = 0; j < cdev; j++) { + + /* Get backend via domain name */ + backend = xenstore_fe_get_backend(type, atoi(dev[j]), + domid, &xenstore_dev); + if (backend == NULL) { + continue; + } + + xendev = xen_fe_get_xendev(type, domid, xenstore_dev, backend, ops); + free(backend); + if (xendev == NULL) { + xen_fe_printf(xendev, 0, "xendev is NULL.\n"); + continue; + } + + /* + * Simplify QEMU side, a thread is running in Xen backend, which will + * connect frontend when the frontend is initialised. + */ + if (xen_fe_check(xendev, domid, xenstore_dev) < 0) { + xen_fe_printf(xendev, 0, "xendev fe_check error.\n"); + continue; + } + + /* Setup watch */ + snprintf(token, sizeof(token), "be:%p:%d:%p", + type, domid, xendev->ops); + if (!xs_watch(xenstore, xendev->be, token)) { + xen_fe_printf(xendev, 0, "xs_watch failed.\n"); + continue; + } + } + + free(dev); + return 0; +} + +int xen_fe_register(const char *type, struct XenDevOps *ops) +{ + return xenstore_fe_scan(type, xen_domid, ops); +} + +/* + * msg_level: + * 0 == errors (stderr + logfile). + * 1 == informative debug messages (logfile only). + * 2 == noisy debug messages (logfile only). + * 3 == will flood your log (logfile only). + */ +void xen_fe_printf(struct XenDevice *xendev, int msg_level, + const char *fmt, ...) +{ + va_list args; + + if (xendev) { + if (msg_level > xendev->debug) { + return; + } + qemu_log("xen fe: %s: ", xendev->name); + if (msg_level == 0) { + fprintf(stderr, "xen fe: %s: ", xendev->name); + } + } else { + if (msg_level > debug) { + return; + } + qemu_log("xen fe core: "); + if (msg_level == 0) { + fprintf(stderr, "xen fe core: "); + } + } + va_start(args, fmt); + qemu_log_vprintf(fmt, args); + va_end(args); + if (msg_level == 0) { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + } + qemu_log_flush(); +} diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 65476bf..5d80077 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -61,6 +61,7 @@ struct XenDevice { extern XenXC xen_xc; extern struct xs_handle *xenstore; extern const char *xen_protocol; +extern int xenstore_dev; /* xenstore helper functions */ int xenstore_write_str(const char *base, const char *node, const char *val); @@ -101,6 +102,13 @@ void xen_be_unbind_evtchn(struct XenDevice *xendev); int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) GCC_FMT_ATTR(3, 4); +void xen_fe_printf(struct XenDevice *xendev, int msg_level, + const char *fmt, ...) + GCC_FMT_ATTR(3, 4); +/* Xen frontend driver */ +int xen_fe_register(const char *type, struct XenDevOps *ops); +int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom); +int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus); /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */
This patch adds infrastructure for xen front drivers living in qemu, so drivers don't need to implement common stuff on their own. It's mostly xenbus management stuff: some functions to access XenStore, setting up XenStore watches, callbacks on device discovery and state changes, and handle event channel between the virtual machines. Call xen_fe_register() function to register XenDevOps, and make sure, XenDevOps's flags is DEVOPS_FLAG_FE, which is flag bit to point out the XenDevOps is Xen frontend. Signed-off-by: Quan Xu <quan.xu@intel.com> --- hw/xen/Makefile.objs | 2 +- hw/xen/xen_frontend.c | 345 +++++++++++++++++++++++++++++++++++++++++++ include/hw/xen/xen_backend.h | 8 + 3 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 hw/xen/xen_frontend.c