summaryrefslogtreecommitdiffstats
path: root/hw/xen
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xen')
-rw-r--r--hw/xen/Makefile.objs2
-rw-r--r--hw/xen/trace-events26
-rw-r--r--hw/xen/xen-backend.c165
-rw-r--r--hw/xen/xen-bus-helper.c184
-rw-r--r--hw/xen/xen-bus.c1199
-rw-r--r--hw/xen/xen-common.c2
-rw-r--r--hw/xen/xen-legacy-backend.c (renamed from hw/xen/xen_backend.c)80
-rw-r--r--hw/xen/xen_devconfig.c2
-rw-r--r--hw/xen/xen_pt.c8
-rw-r--r--hw/xen/xen_pt_config_init.c6
-rw-r--r--hw/xen/xen_pt_graphics.c18
-rw-r--r--hw/xen/xen_pt_msi.c2
-rw-r--r--hw/xen/xen_pvdev.c20
13 files changed, 1660 insertions, 54 deletions
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs
index 9ea5c73423..84df60a928 100644
--- a/hw/xen/Makefile.objs
+++ b/hw/xen/Makefile.objs
@@ -1,5 +1,5 @@
# xen backend driver support
-common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o xen-common.o
+common-obj-$(CONFIG_XEN) += xen-legacy-backend.o xen_devconfig.o xen_pvdev.o xen-common.o xen-bus.o xen-bus-helper.o xen-backend.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_graphics.o xen_pt_msi.o
diff --git a/hw/xen/trace-events b/hw/xen/trace-events
index c7e7a3b523..f6944624b2 100644
--- a/hw/xen/trace-events
+++ b/hw/xen/trace-events
@@ -12,3 +12,29 @@ xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id:
xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
xen_domid_restrict(int err) "err: %u"
+
+# include/hw/xen/xen-bus.c
+xen_bus_realize(void) ""
+xen_bus_unrealize(void) ""
+xen_bus_enumerate(void) ""
+xen_bus_type_enumerate(const char *type) "type: %s"
+xen_bus_backend_create(const char *type, const char *path) "type: %s path: %s"
+xen_bus_add_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s"
+xen_bus_remove_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s"
+xen_bus_watch(const char *token) "token: %s"
+xen_device_realize(const char *type, char *name) "type: %s name: %s"
+xen_device_unrealize(const char *type, char *name) "type: %s name: %s"
+xen_device_backend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s"
+xen_device_backend_online(const char *type, char *name, bool online) "type: %s name: %s -> %u"
+xen_device_backend_changed(const char *type, char *name) "type: %s name: %s"
+xen_device_frontend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s"
+xen_device_frontend_changed(const char *type, char *name) "type: %s name: %s"
+xen_device_unplug(const char *type, char *name) "type: %s name: %s"
+
+# include/hw/xen/xen-bus-helper.c
+xs_node_create(const char *node) "%s"
+xs_node_destroy(const char *node) "%s"
+xs_node_vprintf(char *path, char *value) "%s %s"
+xs_node_vscanf(char *path, char *value) "%s %s"
+xs_node_watch(char *path) "%s"
+xs_node_unwatch(char *path) "%s"
diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c
new file mode 100644
index 0000000000..da065f81b7
--- /dev/null
+++ b/hw/xen/xen-backend.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-bus.h"
+
+typedef struct XenBackendImpl {
+ const char *type;
+ XenBackendDeviceCreate create;
+ XenBackendDeviceDestroy destroy;
+} XenBackendImpl;
+
+struct XenBackendInstance {
+ QLIST_ENTRY(XenBackendInstance) entry;
+ const XenBackendImpl *impl;
+ XenBus *xenbus;
+ char *name;
+ XenDevice *xendev;
+};
+
+static GHashTable *xen_backend_table_get(void)
+{
+ static GHashTable *table;
+
+ if (table == NULL) {
+ table = g_hash_table_new(g_str_hash, g_str_equal);
+ }
+
+ return table;
+}
+
+static void xen_backend_table_add(XenBackendImpl *impl)
+{
+ g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl);
+}
+
+static const XenBackendImpl *xen_backend_table_lookup(const char *type)
+{
+ return g_hash_table_lookup(xen_backend_table_get(), type);
+}
+
+void xen_backend_register(const XenBackendInfo *info)
+{
+ XenBackendImpl *impl = g_new0(XenBackendImpl, 1);
+
+ g_assert(info->type);
+
+ if (xen_backend_table_lookup(info->type)) {
+ error_report("attempt to register duplicate Xen backend type '%s'",
+ info->type);
+ abort();
+ }
+
+ if (!info->create) {
+ error_report("backend type '%s' has no creator", info->type);
+ abort();
+ }
+
+ impl->type = info->type;
+ impl->create = info->create;
+ impl->destroy = info->destroy;
+
+ xen_backend_table_add(impl);
+}
+
+static QLIST_HEAD(, XenBackendInstance) backend_list;
+
+static void xen_backend_list_add(XenBackendInstance *backend)
+{
+ QLIST_INSERT_HEAD(&backend_list, backend, entry);
+}
+
+static XenBackendInstance *xen_backend_list_find(XenDevice *xendev)
+{
+ XenBackendInstance *backend;
+
+ QLIST_FOREACH(backend, &backend_list, entry) {
+ if (backend->xendev == xendev) {
+ return backend;
+ }
+ }
+
+ return NULL;
+}
+
+static void xen_backend_list_remove(XenBackendInstance *backend)
+{
+ QLIST_REMOVE(backend, entry);
+}
+
+void xen_backend_device_create(XenBus *xenbus, const char *type,
+ const char *name, QDict *opts, Error **errp)
+{
+ const XenBackendImpl *impl = xen_backend_table_lookup(type);
+ XenBackendInstance *backend;
+ Error *local_error = NULL;
+
+ if (!impl) {
+ return;
+ }
+
+ backend = g_new0(XenBackendInstance, 1);
+ backend->xenbus = xenbus;
+ backend->name = g_strdup(name);
+
+ impl->create(backend, opts, &local_error);
+ if (local_error) {
+ error_propagate(errp, local_error);
+ g_free(backend->name);
+ g_free(backend);
+ return;
+ }
+
+ backend->impl = impl;
+ xen_backend_list_add(backend);
+}
+
+XenBus *xen_backend_get_bus(XenBackendInstance *backend)
+{
+ return backend->xenbus;
+}
+
+const char *xen_backend_get_name(XenBackendInstance *backend)
+{
+ return backend->name;
+}
+
+void xen_backend_set_device(XenBackendInstance *backend,
+ XenDevice *xendev)
+{
+ g_assert(!backend->xendev);
+ backend->xendev = xendev;
+}
+
+XenDevice *xen_backend_get_device(XenBackendInstance *backend)
+{
+ return backend->xendev;
+}
+
+
+bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp)
+{
+ XenBackendInstance *backend = xen_backend_list_find(xendev);
+ const XenBackendImpl *impl;
+
+ if (!backend) {
+ return false;
+ }
+
+ impl = backend->impl;
+ impl->destroy(backend, errp);
+
+ xen_backend_list_remove(backend);
+ g_free(backend->name);
+ g_free(backend);
+
+ return true;
+}
diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c
new file mode 100644
index 0000000000..5f7a4b2612
--- /dev/null
+++ b/hw/xen/xen-bus-helper.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/xen/xen.h"
+#include "hw/xen/xen-bus.h"
+#include "hw/xen/xen-bus-helper.h"
+#include "qapi/error.h"
+
+#include <glib/gprintf.h>
+
+struct xs_state {
+ enum xenbus_state statenum;
+ const char *statestr;
+};
+#define XS_STATE(state) { state, #state }
+
+static struct xs_state xs_state[] = {
+ XS_STATE(XenbusStateUnknown),
+ XS_STATE(XenbusStateInitialising),
+ XS_STATE(XenbusStateInitWait),
+ XS_STATE(XenbusStateInitialised),
+ XS_STATE(XenbusStateConnected),
+ XS_STATE(XenbusStateClosing),
+ XS_STATE(XenbusStateClosed),
+ XS_STATE(XenbusStateReconfiguring),
+ XS_STATE(XenbusStateReconfigured),
+};
+
+#undef XS_STATE
+
+const char *xs_strstate(enum xenbus_state state)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xs_state); i++) {
+ if (xs_state[i].statenum == state) {
+ return xs_state[i].statestr;
+ }
+ }
+
+ return "INVALID";
+}
+
+void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, struct xs_permissions perms[],
+ unsigned int nr_perms, Error **errp)
+{
+ trace_xs_node_create(node);
+
+ if (!xs_write(xsh, tid, node, "", 0)) {
+ error_setg_errno(errp, errno, "failed to create node '%s'", node);
+ return;
+ }
+
+ if (!xs_set_permissions(xsh, tid, node, perms, nr_perms)) {
+ error_setg_errno(errp, errno, "failed to set node '%s' permissions",
+ node);
+ }
+}
+
+void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, Error **errp)
+{
+ trace_xs_node_destroy(node);
+
+ if (!xs_rm(xsh, tid, node)) {
+ error_setg_errno(errp, errno, "failed to destroy node '%s'", node);
+ }
+}
+
+void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, va_list ap)
+{
+ char *path, *value;
+ int len;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+ len = g_vasprintf(&value, fmt, ap);
+
+ trace_xs_node_vprintf(path, value);
+
+ if (!xs_write(xsh, tid, path, value, len)) {
+ error_setg_errno(errp, errno, "failed to write '%s' to '%s'",
+ value, path);
+ }
+
+ g_free(value);
+ g_free(path);
+}
+
+void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xs_node_vprintf(xsh, tid, node, key, errp, fmt, ap);
+ va_end(ap);
+}
+
+int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, va_list ap)
+{
+ char *path, *value;
+ int rc;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+ value = xs_read(xsh, tid, path, NULL);
+
+ trace_xs_node_vscanf(path, value);
+
+ if (value) {
+ rc = vsscanf(value, fmt, ap);
+ } else {
+ error_setg_errno(errp, errno, "failed to read from '%s'",
+ path);
+ rc = EOF;
+ }
+
+ free(value);
+ g_free(path);
+
+ return rc;
+}
+
+int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, fmt);
+ rc = xs_node_vscanf(xsh, tid, node, key, errp, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
+ char *token, Error **errp)
+{
+ char *path;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+
+ trace_xs_node_watch(path);
+
+ if (!xs_watch(xsh, path, token)) {
+ error_setg_errno(errp, errno, "failed to watch node '%s'", path);
+ }
+
+ g_free(path);
+}
+
+void xs_node_unwatch(struct xs_handle *xsh, const char *node,
+ const char *key, const char *token, Error **errp)
+{
+ char *path;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+
+ trace_xs_node_unwatch(path);
+
+ if (!xs_unwatch(xsh, path, token)) {
+ error_setg_errno(errp, errno, "failed to unwatch node '%s'", path);
+ }
+
+ g_free(path);
+}
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
new file mode 100644
index 0000000000..3aeccec69c
--- /dev/null
+++ b/hw/xen/xen-bus.c
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qemu/uuid.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/xen/xen.h"
+#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-bus.h"
+#include "hw/xen/xen-bus-helper.h"
+#include "monitor/monitor.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+static char *xen_device_get_backend_path(XenDevice *xendev)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+ const char *backend = xendev_class->backend;
+
+ if (!backend) {
+ backend = type;
+ }
+
+ return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s",
+ xenbus->backend_id, backend, xendev->frontend_id,
+ xendev->name);
+}
+
+static char *xen_device_get_frontend_path(XenDevice *xendev)
+{
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+ const char *device = xendev_class->device;
+
+ if (!device) {
+ device = type;
+ }
+
+ return g_strdup_printf("/local/domain/%u/device/%s/%s",
+ xendev->frontend_id, device, xendev->name);
+}
+
+static void xen_device_unplug(XenDevice *xendev, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ const char *type = object_get_typename(OBJECT(xendev));
+ Error *local_err = NULL;
+ xs_transaction_t tid;
+
+ trace_xen_device_unplug(type, xendev->name);
+
+ /* Mimic the way the Xen toolstack does an unplug */
+again:
+ tid = xs_transaction_start(xenbus->xsh);
+ if (tid == XBT_NULL) {
+ error_setg_errno(errp, errno, "failed xs_transaction_start");
+ return;
+ }
+
+ xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online",
+ &local_err, "%u", 0);
+ if (local_err) {
+ goto abort;
+ }
+
+ xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state",
+ &local_err, "%u", XenbusStateClosing);
+ if (local_err) {
+ goto abort;
+ }
+
+ if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+ if (errno == EAGAIN) {
+ goto again;
+ }
+
+ error_setg_errno(errp, errno, "failed xs_transaction_end");
+ }
+
+ return;
+
+abort:
+ /*
+ * We only abort if there is already a failure so ignore any error
+ * from ending the transaction.
+ */
+ xs_transaction_end(xenbus->xsh, tid, true);
+ error_propagate(errp, local_err);
+}
+
+static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+
+ monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n",
+ indent, "", xendev->name, xendev->frontend_id);
+}
+
+static char *xen_bus_get_dev_path(DeviceState *dev)
+{
+ return xen_device_get_backend_path(XEN_DEVICE(dev));
+}
+
+struct XenWatch {
+ char *node, *key;
+ char *token;
+ XenWatchHandler handler;
+ void *opaque;
+ Notifier notifier;
+};
+
+static void watch_notify(Notifier *n, void *data)
+{
+ XenWatch *watch = container_of(n, XenWatch, notifier);
+ const char *token = data;
+
+ if (!strcmp(watch->token, token)) {
+ watch->handler(watch->opaque);
+ }
+}
+
+static XenWatch *new_watch(const char *node, const char *key,
+ XenWatchHandler handler, void *opaque)
+{
+ XenWatch *watch = g_new0(XenWatch, 1);
+ QemuUUID uuid;
+
+ qemu_uuid_generate(&uuid);
+
+ watch->token = qemu_uuid_unparse_strdup(&uuid);
+ watch->node = g_strdup(node);
+ watch->key = g_strdup(key);
+ watch->handler = handler;
+ watch->opaque = opaque;
+ watch->notifier.notify = watch_notify;
+
+ return watch;
+}
+
+static void free_watch(XenWatch *watch)
+{
+ g_free(watch->token);
+ g_free(watch->key);
+ g_free(watch->node);
+
+ g_free(watch);
+}
+
+static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node,
+ const char *key, XenWatchHandler handler,
+ void *opaque, Error **errp)
+{
+ XenWatch *watch = new_watch(node, key, handler, opaque);
+ Error *local_err = NULL;
+
+ trace_xen_bus_add_watch(watch->node, watch->key, watch->token);
+
+ notifier_list_add(&xenbus->watch_notifiers, &watch->notifier);
+
+ xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+
+ notifier_remove(&watch->notifier);
+ free_watch(watch);
+
+ return NULL;
+ }
+
+ return watch;
+}
+
+static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch,
+ Error **errp)
+{
+ trace_xen_bus_remove_watch(watch->node, watch->key, watch->token);
+
+ xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token,
+ errp);
+
+ notifier_remove(&watch->notifier);
+ free_watch(watch);
+}
+
+static void xen_bus_backend_create(XenBus *xenbus, const char *type,
+ const char *name, char *path,
+ Error **errp)
+{
+ xs_transaction_t tid;
+ char **key;
+ QDict *opts;
+ unsigned int i, n;
+ Error *local_err = NULL;
+
+ trace_xen_bus_backend_create(type, path);
+
+again:
+ tid = xs_transaction_start(xenbus->xsh);
+ if (tid == XBT_NULL) {
+ error_setg(errp, "failed xs_transaction_start");
+ return;
+ }
+
+ key = xs_directory(xenbus->xsh, tid, path, &n);
+ if (!key) {
+ if (!xs_transaction_end(xenbus->xsh, tid, true)) {
+ error_setg_errno(errp, errno, "failed xs_transaction_end");
+ }
+ return;
+ }
+
+ opts = qdict_new();
+ for (i = 0; i < n; i++) {
+ char *val;
+
+ /*
+ * Assume anything found in the xenstore backend area, other than
+ * the keys created for a generic XenDevice, are parameters
+ * to be used to configure the backend.
+ */
+ if (!strcmp(key[i], "state") ||
+ !strcmp(key[i], "online") ||
+ !strcmp(key[i], "frontend") ||
+ !strcmp(key[i], "frontend-id") ||
+ !strcmp(key[i], "hotplug-status"))
+ continue;
+
+ if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms",
+ &val) == 1) {
+ qdict_put_str(opts, key[i], val);
+ free(val);
+ }
+ }
+
+ free(key);
+
+ if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+ qobject_unref(opts);
+
+ if (errno == EAGAIN) {
+ goto again;
+ }
+
+ error_setg_errno(errp, errno, "failed xs_transaction_end");
+ return;
+ }
+
+ xen_backend_device_create(xenbus, type, name, opts, &local_err);
+ qobject_unref(opts);
+
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create '%s' device '%s': ",
+ type, name);
+ }
+}
+
+static void xen_bus_type_enumerate(XenBus *xenbus, const char *type)
+{
+ char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid);
+ char **backend;
+ unsigned int i, n;
+
+ trace_xen_bus_type_enumerate(type);
+
+ backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n);
+ if (!backend) {
+ goto out;
+ }
+
+ for (i = 0; i < n; i++) {
+ char *backend_path = g_strdup_printf("%s/%s", domain_path,
+ backend[i]);
+ enum xenbus_state backend_state;
+
+ if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state",
+ NULL, "%u", &backend_state) != 1)
+ backend_state = XenbusStateUnknown;
+
+ if (backend_state == XenbusStateInitialising) {
+ Error *local_err = NULL;
+
+ xen_bus_backend_create(xenbus, type, backend[i], backend_path,
+ &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+
+ g_free(backend_path);
+ }
+
+ free(backend);
+
+out:
+ g_free(domain_path);
+}
+
+static void xen_bus_enumerate(void *opaque)
+{
+ XenBus *xenbus = opaque;
+ char **type;
+ unsigned int i, n;
+
+ trace_xen_bus_enumerate();
+
+ type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n);
+ if (!type) {
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ xen_bus_type_enumerate(xenbus, type[i]);
+ }
+
+ free(type);
+}
+
+static void xen_bus_unrealize(BusState *bus, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(bus);
+
+ trace_xen_bus_unrealize();
+
+ if (xenbus->backend_watch) {
+ xen_bus_remove_watch(xenbus, xenbus->backend_watch, NULL);
+ xenbus->backend_watch = NULL;
+ }
+
+ if (!xenbus->xsh) {
+ return;
+ }
+
+ qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL);
+
+ xs_close(xenbus->xsh);
+}
+
+static void xen_bus_watch(void *opaque)
+{
+ XenBus *xenbus = opaque;
+ char **v;
+ const char *token;
+
+ g_assert(xenbus->xsh);
+
+ v = xs_check_watch(xenbus->xsh);
+ if (!v) {
+ return;
+ }
+
+ token = v[XS_WATCH_TOKEN];
+
+ trace_xen_bus_watch(token);
+
+ notifier_list_notify(&xenbus->watch_notifiers, (void *)token);
+
+ free(v);
+}
+
+static void xen_bus_realize(BusState *bus, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(bus);
+ unsigned int domid;
+ Error *local_err = NULL;
+
+ trace_xen_bus_realize();
+
+ xenbus->xsh = xs_open(0);
+ if (!xenbus->xsh) {
+ error_setg_errno(errp, errno, "failed xs_open");
+ goto fail;
+ }
+
+ if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */
+ "domid", NULL, "%u", &domid) == 1) {
+ xenbus->backend_id = domid;
+ } else {
+ xenbus->backend_id = 0; /* Assume lack of node means dom0 */
+ }
+
+ notifier_list_init(&xenbus->watch_notifiers);
+ qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL,
+ xenbus);
+
+ module_call_init(MODULE_INIT_XEN_BACKEND);
+
+ xenbus->backend_watch =
+ xen_bus_add_watch(xenbus, "", /* domain root node */
+ "backend", xen_bus_enumerate, xenbus, &local_err);
+ if (local_err) {
+ /* This need not be treated as a hard error so don't propagate */
+ error_reportf_err(local_err,
+ "failed to set up enumeration watch: ");
+ }
+
+ return;
+
+fail:
+ xen_bus_unrealize(bus, &error_abort);
+}
+
+static void xen_bus_unplug_request(HotplugHandler *hotplug,
+ DeviceState *dev,
+ Error **errp)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+
+ xen_device_unplug(xendev, errp);
+}
+
+static void xen_bus_class_init(ObjectClass *class, void *data)
+{
+ BusClass *bus_class = BUS_CLASS(class);
+ HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class);
+
+ bus_class->print_dev = xen_bus_print_dev;
+ bus_class->get_dev_path = xen_bus_get_dev_path;
+ bus_class->realize = xen_bus_realize;
+ bus_class->unrealize = xen_bus_unrealize;
+
+ hotplug_class->unplug_request = xen_bus_unplug_request;
+}
+
+static const TypeInfo xen_bus_type_info = {
+ .name = TYPE_XEN_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(XenBus),
+ .class_size = sizeof(XenBusClass),
+ .class_init = xen_bus_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ },
+};
+
+void xen_device_backend_printf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+ va_list ap;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key,
+ &local_err, fmt, ap);
+ va_end(ap);
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+static int xen_device_backend_scanf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ va_list ap;
+ int rc;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key,
+ NULL, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+void xen_device_backend_set_state(XenDevice *xendev,
+ enum xenbus_state state)
+{
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (xendev->backend_state == state) {
+ return;
+ }
+
+ trace_xen_device_backend_state(type, xendev->name,
+ xs_strstate(state));
+
+ xendev->backend_state = state;
+ xen_device_backend_printf(xendev, "state", "%u", state);
+}
+
+enum xenbus_state xen_device_backend_get_state(XenDevice *xendev)
+{
+ return xendev->backend_state;
+}
+
+static void xen_device_backend_set_online(XenDevice *xendev, bool online)
+{
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (xendev->backend_online == online) {
+ return;
+ }
+
+ trace_xen_device_backend_online(type, xendev->name, online);
+
+ xendev->backend_online = online;
+ xen_device_backend_printf(xendev, "online", "%u", online);
+}
+
+static void xen_device_backend_changed(void *opaque)
+{
+ XenDevice *xendev = opaque;
+ const char *type = object_get_typename(OBJECT(xendev));
+ enum xenbus_state state;
+ unsigned int online;
+
+ trace_xen_device_backend_changed(type, xendev->name);
+
+ if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) {
+ state = XenbusStateUnknown;
+ }
+
+ xen_device_backend_set_state(xendev, state);
+
+ if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) {
+ online = 0;
+ }
+
+ xen_device_backend_set_online(xendev, !!online);
+
+ /*
+ * If the toolstack (or unplug request callback) has set the backend
+ * state to Closing, but there is no active frontend (i.e. the
+ * state is not Connected) then set the backend state to Closed.
+ */
+ if (xendev->backend_state == XenbusStateClosing &&
+ xendev->frontend_state != XenbusStateConnected) {
+ xen_device_backend_set_state(xendev, XenbusStateClosed);
+ }
+
+ /*
+ * If a backend is still 'online' then its state should be cycled
+ * back round to InitWait in order for a new frontend instance to
+ * connect. This may happen when, for example, a frontend driver is
+ * re-installed or updated.
+ * If a backend is not 'online' then the device should be destroyed.
+ */
+ if (xendev->backend_online &&
+ xendev->backend_state == XenbusStateClosed) {
+ xen_device_backend_set_state(xendev, XenbusStateInitWait);
+ } else if (!xendev->backend_online &&
+ (xendev->backend_state == XenbusStateClosed ||
+ xendev->backend_state == XenbusStateInitialising ||
+ xendev->backend_state == XenbusStateInitWait ||
+ xendev->backend_state == XenbusStateUnknown)) {
+ Error *local_err = NULL;
+
+ if (!xen_backend_try_device_destroy(xendev, &local_err)) {
+ object_unparent(OBJECT(xendev));
+ }
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+}
+
+static void xen_device_backend_create(XenDevice *xendev, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ struct xs_permissions perms[2];
+ Error *local_err = NULL;
+
+ xendev->backend_path = xen_device_get_backend_path(xendev);
+
+ perms[0].id = xenbus->backend_id;
+ perms[0].perms = XS_PERM_NONE;
+ perms[1].id = xendev->frontend_id;
+ perms[1].perms = XS_PERM_READ;
+
+ g_assert(xenbus->xsh);
+
+ xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms,
+ ARRAY_SIZE(perms), &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create backend: ");
+ return;
+ }
+
+ xendev->backend_state_watch =
+ xen_bus_add_watch(xenbus, xendev->backend_path,
+ "state", xen_device_backend_changed,
+ xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to watch backend state: ");
+ return;
+ }
+
+ xendev->backend_online_watch =
+ xen_bus_add_watch(xenbus, xendev->backend_path,
+ "online", xen_device_backend_changed,
+ xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to watch backend online: ");
+ return;
+ }
+}
+
+static void xen_device_backend_destroy(XenDevice *xendev)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+
+ if (xendev->backend_online_watch) {
+ xen_bus_remove_watch(xenbus, xendev->backend_online_watch, NULL);
+ xendev->backend_online_watch = NULL;
+ }
+
+ if (xendev->backend_state_watch) {
+ xen_bus_remove_watch(xenbus, xendev->backend_state_watch, NULL);
+ xendev->backend_state_watch = NULL;
+ }
+
+ if (!xendev->backend_path) {
+ return;
+ }
+
+ g_assert(xenbus->xsh);
+
+ xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path,
+ &local_err);
+ g_free(xendev->backend_path);
+ xendev->backend_path = NULL;
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+void xen_device_frontend_printf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+ va_list ap;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key,
+ &local_err, fmt, ap);
+ va_end(ap);
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+int xen_device_frontend_scanf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ va_list ap;
+ int rc;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key,
+ NULL, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+static void xen_device_frontend_set_state(XenDevice *xendev,
+ enum xenbus_state state)
+{
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (xendev->frontend_state == state) {
+ return;
+ }
+
+ trace_xen_device_frontend_state(type, xendev->name,
+ xs_strstate(state));
+
+ xendev->frontend_state = state;
+ xen_device_frontend_printf(xendev, "state", "%u", state);
+}
+
+static void xen_device_frontend_changed(void *opaque)
+{
+ XenDevice *xendev = opaque;
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+ enum xenbus_state state;
+
+ trace_xen_device_frontend_changed(type, xendev->name);
+
+ if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) {
+ state = XenbusStateUnknown;
+ }
+
+ xen_device_frontend_set_state(xendev, state);
+
+ if (xendev_class->frontend_changed) {
+ Error *local_err = NULL;
+
+ xendev_class->frontend_changed(xendev, state, &local_err);
+
+ if (local_err) {
+ error_reportf_err(local_err, "frontend change error: ");
+ }
+ }
+}
+
+static void xen_device_frontend_create(XenDevice *xendev, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ struct xs_permissions perms[2];
+ Error *local_err = NULL;
+
+ xendev->frontend_path = xen_device_get_frontend_path(xendev);
+
+ perms[0].id = xendev->frontend_id;
+ perms[0].perms = XS_PERM_NONE;
+ perms[1].id = xenbus->backend_id;
+ perms[1].perms = XS_PERM_READ | XS_PERM_WRITE;
+
+ g_assert(xenbus->xsh);
+
+ xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms,
+ ARRAY_SIZE(perms), &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create frontend: ");
+ return;
+ }
+
+ xendev->frontend_state_watch =
+ xen_bus_add_watch(xenbus, xendev->frontend_path, "state",
+ xen_device_frontend_changed, xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to watch frontend state: ");
+ }
+}
+
+static void xen_device_frontend_destroy(XenDevice *xendev)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+
+ if (xendev->frontend_state_watch) {
+ xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL);
+ xendev->frontend_state_watch = NULL;
+ }
+
+ if (!xendev->frontend_path) {
+ return;
+ }
+
+ g_assert(xenbus->xsh);
+
+ xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path,
+ &local_err);
+ g_free(xendev->frontend_path);
+ xendev->frontend_path = NULL;
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
+ Error **errp)
+{
+ if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) {
+ error_setg_errno(errp, errno, "xengnttab_set_max_grants failed");
+ }
+}
+
+void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
+ unsigned int nr_refs, int prot,
+ Error **errp)
+{
+ void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs,
+ xendev->frontend_id, refs,
+ prot);
+
+ if (!map) {
+ error_setg_errno(errp, errno,
+ "xengnttab_map_domain_grant_refs failed");
+ }
+
+ return map;
+}
+
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+ unsigned int nr_refs, Error **errp)
+{
+ if (xengnttab_unmap(xendev->xgth, map, nr_refs)) {
+ error_setg_errno(errp, errno, "xengnttab_unmap failed");
+ }
+}
+
+static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain,
+ XenDeviceGrantCopySegment segs[],
+ unsigned int nr_segs, Error **errp)
+{
+ uint32_t *refs = g_new(uint32_t, nr_segs);
+ int prot = to_domain ? PROT_WRITE : PROT_READ;
+ void *map;
+ unsigned int i;
+
+ for (i = 0; i < nr_segs; i++) {
+ XenDeviceGrantCopySegment *seg = &segs[i];
+
+ refs[i] = to_domain ? seg->dest.foreign.ref :
+ seg->source.foreign.ref;
+ }
+
+ map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs,
+ xendev->frontend_id, refs,
+ prot);
+ if (!map) {
+ error_setg_errno(errp, errno,
+ "xengnttab_map_domain_grant_refs failed");
+ goto done;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ XenDeviceGrantCopySegment *seg = &segs[i];
+ void *page = map + (i * XC_PAGE_SIZE);
+
+ if (to_domain) {
+ memcpy(page + seg->dest.foreign.offset, seg->source.virt,
+ seg->len);
+ } else {
+ memcpy(seg->dest.virt, page + seg->source.foreign.offset,
+ seg->len);
+ }
+ }
+
+ if (xengnttab_unmap(xendev->xgth, map, nr_segs)) {
+ error_setg_errno(errp, errno, "xengnttab_unmap failed");
+ }
+
+done:
+ g_free(refs);
+}
+
+void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
+ XenDeviceGrantCopySegment segs[],
+ unsigned int nr_segs, Error **errp)
+{
+ xengnttab_grant_copy_segment_t *xengnttab_segs;
+ unsigned int i;
+
+ if (!xendev->feature_grant_copy) {
+ compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp);
+ return;
+ }
+
+ xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);
+
+ for (i = 0; i < nr_segs; i++) {
+ XenDeviceGrantCopySegment *seg = &segs[i];
+ xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+ if (to_domain) {
+ xengnttab_seg->flags = GNTCOPY_dest_gref;
+ xengnttab_seg->dest.foreign.domid = xendev->frontend_id;
+ xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
+ xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
+ xengnttab_seg->source.virt = seg->source.virt;
+ } else {
+ xengnttab_seg->flags = GNTCOPY_source_gref;
+ xengnttab_seg->source.foreign.domid = xendev->frontend_id;
+ xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
+ xengnttab_seg->source.foreign.offset =
+ seg->source.foreign.offset;
+ xengnttab_seg->dest.virt = seg->dest.virt;
+ }
+
+ xengnttab_seg->len = seg->len;
+ }
+
+ if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) {
+ error_setg_errno(errp, errno, "xengnttab_grant_copy failed");
+ goto done;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+ if (xengnttab_seg->status != GNTST_okay) {
+ error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i);
+ break;
+ }
+ }
+
+done:
+ g_free(xengnttab_segs);
+}
+
+struct XenEventChannel {
+ evtchn_port_t local_port;
+ XenEventHandler handler;
+ void *opaque;
+ Notifier notifier;
+};
+
+static void event_notify(Notifier *n, void *data)
+{
+ XenEventChannel *channel = container_of(n, XenEventChannel, notifier);
+ unsigned long port = (unsigned long)data;
+
+ if (port == channel->local_port) {
+ channel->handler(channel->opaque);
+ }
+}
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+ unsigned int port,
+ XenEventHandler handler,
+ void *opaque, Error **errp)
+{
+ XenEventChannel *channel = g_new0(XenEventChannel, 1);
+ xenevtchn_port_or_error_t local_port;
+
+ local_port = xenevtchn_bind_interdomain(xendev->xeh,
+ xendev->frontend_id,
+ port);
+ if (local_port < 0) {
+ error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed");
+
+ g_free(channel);
+ return NULL;
+ }
+
+ channel->local_port = local_port;
+ channel->handler = handler;
+ channel->opaque = opaque;
+ channel->notifier.notify = event_notify;
+
+ notifier_list_add(&xendev->event_notifiers, &channel->notifier);
+
+ return channel;
+}
+
+void xen_device_notify_event_channel(XenDevice *xendev,
+ XenEventChannel *channel,
+ Error **errp)
+{
+ if (!channel) {
+ error_setg(errp, "bad channel");
+ return;
+ }
+
+ if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) {
+ error_setg_errno(errp, errno, "xenevtchn_notify failed");
+ }
+}
+
+void xen_device_unbind_event_channel(XenDevice *xendev,
+ XenEventChannel *channel,
+ Error **errp)
+{
+ if (!channel) {
+ error_setg(errp, "bad channel");
+ return;
+ }
+
+ notifier_remove(&channel->notifier);
+
+ if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) {
+ error_setg_errno(errp, errno, "xenevtchn_unbind failed");
+ }
+
+ g_free(channel);
+}
+
+static void xen_device_unrealize(DeviceState *dev, Error **errp)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (!xendev->name) {
+ return;
+ }
+
+ trace_xen_device_unrealize(type, xendev->name);
+
+ if (xendev->exit.notify) {
+ qemu_remove_exit_notifier(&xendev->exit);
+ xendev->exit.notify = NULL;
+ }
+
+ if (xendev_class->unrealize) {
+ xendev_class->unrealize(xendev, errp);
+ }
+
+ xen_device_frontend_destroy(xendev);
+ xen_device_backend_destroy(xendev);
+
+ if (xendev->xeh) {
+ qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL);
+ xenevtchn_close(xendev->xeh);
+ xendev->xeh = NULL;
+ }
+
+ if (xendev->xgth) {
+ xengnttab_close(xendev->xgth);
+ xendev->xgth = NULL;
+ }
+
+ g_free(xendev->name);
+ xendev->name = NULL;
+}
+
+static void xen_device_exit(Notifier *n, void *data)
+{
+ XenDevice *xendev = container_of(n, XenDevice, exit);
+
+ xen_device_unrealize(DEVICE(xendev), &error_abort);
+}
+
+static void xen_device_event(void *opaque)
+{
+ XenDevice *xendev = opaque;
+ unsigned long port = xenevtchn_pending(xendev->xeh);
+
+ notifier_list_notify(&xendev->event_notifiers, (void *)port);
+
+ xenevtchn_unmask(xendev->xeh, port);
+}
+
+static void xen_device_realize(DeviceState *dev, Error **errp)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ const char *type = object_get_typename(OBJECT(xendev));
+ Error *local_err = NULL;
+
+ if (xendev->frontend_id == DOMID_INVALID) {
+ xendev->frontend_id = xen_domid;
+ }
+
+ if (xendev->frontend_id >= DOMID_FIRST_RESERVED) {
+ error_setg(errp, "invalid frontend-id");
+ goto unrealize;
+ }
+
+ if (!xendev_class->get_name) {
+ error_setg(errp, "get_name method not implemented");
+ goto unrealize;
+ }
+
+ xendev->name = xendev_class->get_name(xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to get device name: ");
+ goto unrealize;
+ }
+
+ trace_xen_device_realize(type, xendev->name);
+
+ xendev->xgth = xengnttab_open(NULL, 0);
+ if (!xendev->xgth) {
+ error_setg_errno(errp, errno, "failed xengnttab_open");
+ goto unrealize;
+ }
+
+ xendev->feature_grant_copy =
+ (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0);
+
+ xendev->xeh = xenevtchn_open(NULL, 0);
+ if (!xendev->xeh) {
+ error_setg_errno(errp, errno, "failed xenevtchn_open");
+ goto unrealize;
+ }
+
+ notifier_list_init(&xendev->event_notifiers);
+ qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL,
+ xendev);
+
+ xen_device_backend_create(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
+
+ xen_device_frontend_create(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
+
+ if (xendev_class->realize) {
+ xendev_class->realize(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
+ }
+
+ xen_device_backend_printf(xendev, "frontend", "%s",
+ xendev->frontend_path);
+ xen_device_backend_printf(xendev, "frontend-id", "%u",
+ xendev->frontend_id);
+ xen_device_backend_printf(xendev, "hotplug-status", "connected");
+
+ xen_device_backend_set_online(xendev, true);
+ xen_device_backend_set_state(xendev, XenbusStateInitWait);
+
+ xen_device_frontend_printf(xendev, "backend", "%s",
+ xendev->backend_path);
+ xen_device_frontend_printf(xendev, "backend-id", "%u",
+ xenbus->backend_id);
+
+ xen_device_frontend_set_state(xendev, XenbusStateInitialising);
+
+ xendev->exit.notify = xen_device_exit;
+ qemu_add_exit_notifier(&xendev->exit);
+ return;
+
+unrealize:
+ xen_device_unrealize(dev, &error_abort);
+}
+
+static Property xen_device_props[] = {
+ DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id,
+ DOMID_INVALID),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void xen_device_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dev_class = DEVICE_CLASS(class);
+
+ dev_class->realize = xen_device_realize;
+ dev_class->unrealize = xen_device_unrealize;
+ dev_class->props = xen_device_props;
+ dev_class->bus_type = TYPE_XEN_BUS;
+}
+
+static const TypeInfo xen_device_type_info = {
+ .name = TYPE_XEN_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(XenDevice),
+ .abstract = true,
+ .class_size = sizeof(XenDeviceClass),
+ .class_init = xen_device_class_init,
+};
+
+typedef struct XenBridge {
+ SysBusDevice busdev;
+} XenBridge;
+
+#define TYPE_XEN_BRIDGE "xen-bridge"
+
+static const TypeInfo xen_bridge_type_info = {
+ .name = TYPE_XEN_BRIDGE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XenBridge),
+};
+
+static void xen_register_types(void)
+{
+ type_register_static(&xen_bridge_type_info);
+ type_register_static(&xen_bus_type_info);
+ type_register_static(&xen_device_type_info);
+}
+
+type_init(xen_register_types)
+
+void xen_bus_init(void)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_XEN_BRIDGE);
+ BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL);
+
+ qdev_init_nofail(dev);
+ qbus_set_bus_hotplug_handler(bus, &error_abort);
+}
diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c
index 18a9045556..0e9e58f04d 100644
--- a/hw/xen/xen-common.c
+++ b/hw/xen/xen-common.c
@@ -10,7 +10,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "chardev/char.h"
#include "sysemu/accel.h"
#include "migration/misc.h"
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen-legacy-backend.c
index 0bc6b1de60..36fd1e9b09 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen-legacy-backend.c
@@ -30,7 +30,7 @@
#include "hw/boards.h"
#include "qemu/log.h"
#include "qapi/error.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "hw/xen/xen_pvdev.h"
#include "monitor/qdev.h"
@@ -42,49 +42,54 @@ BusState *xen_sysbus;
/* ------------------------------------------------------------- */
/* public */
-struct xs_handle *xenstore = NULL;
+struct xs_handle *xenstore;
const char *xen_protocol;
/* private */
static bool xen_feature_grant_copy;
static int debug;
-int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val)
+int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
+ const char *val)
{
return xenstore_write_str(xendev->be, node, val);
}
-int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival)
+int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node,
+ int ival)
{
return xenstore_write_int(xendev->be, node, ival);
}
-int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival)
+int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node,
+ int64_t ival)
{
return xenstore_write_int64(xendev->be, node, ival);
}
-char *xenstore_read_be_str(struct XenDevice *xendev, const char *node)
+char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node)
{
return xenstore_read_str(xendev->be, node);
}
-int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival)
+int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node,
+ int *ival)
{
return xenstore_read_int(xendev->be, node, ival);
}
-char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
+char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node)
{
return xenstore_read_str(xendev->fe, node);
}
-int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival)
+int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node,
+ int *ival)
{
return xenstore_read_int(xendev->fe, node, ival);
}
-int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
+int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node,
uint64_t *uval)
{
return xenstore_read_uint64(xendev->fe, node, uval);
@@ -92,7 +97,7 @@ int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
/* ------------------------------------------------------------- */
-int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
+int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state)
{
int rc;
@@ -106,7 +111,7 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
return 0;
}
-void xen_be_set_max_grant_refs(struct XenDevice *xendev,
+void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
unsigned int nr_refs)
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
@@ -117,7 +122,7 @@ void xen_be_set_max_grant_refs(struct XenDevice *xendev,
}
}
-void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
+void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,
unsigned int nr_refs, int prot)
{
void *ptr;
@@ -135,7 +140,7 @@ void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
return ptr;
}
-void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
+void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
unsigned int nr_refs)
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
@@ -146,7 +151,7 @@ void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
}
}
-static int compat_copy_grant_refs(struct XenDevice *xendev,
+static int compat_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
@@ -195,7 +200,7 @@ static int compat_copy_grant_refs(struct XenDevice *xendev,
return 0;
}
-int xen_be_copy_grant_refs(struct XenDevice *xendev,
+int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
@@ -259,10 +264,11 @@ int xen_be_copy_grant_refs(struct XenDevice *xendev,
/*
* get xen backend device, allocate a new one if it doesn't exist.
*/
-static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
- struct XenDevOps *ops)
+static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom,
+ int dev,
+ struct XenDevOps *ops)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
xendev = xen_pv_find_xendev(type, dom, dev);
if (xendev) {
@@ -314,7 +320,8 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
* Node specifies the changed field. node = NULL means
* update all fields (used for initialization).
*/
-static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
+static void xen_be_backend_changed(struct XenLegacyDevice *xendev,
+ const char *node)
{
if (node == NULL || strcmp(node, "online") == 0) {
if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) {
@@ -330,7 +337,8 @@ static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
}
}
-static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
+static void xen_be_frontend_changed(struct XenLegacyDevice *xendev,
+ const char *node)
{
int fe_state;
@@ -373,7 +381,7 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
* only affects the xendev->be_state variable as xenbus should
* already be put into that state by xend.
*/
-static int xen_be_try_setup(struct XenDevice *xendev)
+static int xen_be_try_setup(struct XenLegacyDevice *xendev)
{
char token[XEN_BUFSIZE];
int be_state;
@@ -417,7 +425,7 @@ static int xen_be_try_setup(struct XenDevice *xendev)
*
* Goes to InitWait on success.
*/
-static int xen_be_try_init(struct XenDevice *xendev)
+static int xen_be_try_init(struct XenLegacyDevice *xendev)
{
int rc = 0;
@@ -446,7 +454,7 @@ static int xen_be_try_init(struct XenDevice *xendev)
*
* Goes to Connected on success.
*/
-static int xen_be_try_initialise(struct XenDevice *xendev)
+static int xen_be_try_initialise(struct XenLegacyDevice *xendev)
{
int rc = 0;
@@ -487,7 +495,7 @@ static int xen_be_try_initialise(struct XenDevice *xendev)
* frontend being Connected. Note that this may be called more
* than once since the backend state is not modified.
*/
-static void xen_be_try_connected(struct XenDevice *xendev)
+static void xen_be_try_connected(struct XenLegacyDevice *xendev)
{
if (!xendev->ops->connected) {
return;
@@ -510,7 +518,8 @@ static void xen_be_try_connected(struct XenDevice *xendev)
*
* Goes to Closed when done.
*/
-static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
+static void xen_be_disconnect(struct XenLegacyDevice *xendev,
+ enum xenbus_state state)
{
if (xendev->be_state != XenbusStateClosing &&
xendev->be_state != XenbusStateClosed &&
@@ -529,7 +538,7 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
/*
* Try to reset xendev, for reconnection by another frontend instance.
*/
-static int xen_be_try_reset(struct XenDevice *xendev)
+static int xen_be_try_reset(struct XenLegacyDevice *xendev)
{
if (xendev->fe_state != XenbusStateInitialising) {
return -1;
@@ -543,7 +552,7 @@ static int xen_be_try_reset(struct XenDevice *xendev)
/*
* state change dispatcher function
*/
-void xen_be_check_state(struct XenDevice *xendev)
+void xen_be_check_state(struct XenLegacyDevice *xendev)
{
int rc = 0;
@@ -587,7 +596,7 @@ void xen_be_check_state(struct XenDevice *xendev)
static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
char **dev = NULL;
unsigned int cdev, j;
@@ -620,7 +629,7 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
void xenstore_update_be(char *watch, char *type, int dom,
struct XenDevOps *ops)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
char path[XEN_BUFSIZE], *bepath;
unsigned int len, dev;
@@ -628,9 +637,9 @@ void xenstore_update_be(char *watch, char *type, int dom,
if (strncmp(path, watch, len) != 0) {
return;
}
- if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) {
+ if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) {
strcpy(path, "");
- if (sscanf(watch+len, "/%u", &dev) != 1) {
+ if (sscanf(watch + len, "/%u", &dev) != 1) {
dev = -1;
}
}
@@ -651,7 +660,7 @@ void xenstore_update_be(char *watch, char *type, int dom,
}
}
-void xenstore_update_fe(char *watch, struct XenDevice *xendev)
+void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev)
{
char *node;
unsigned int len;
@@ -744,7 +753,6 @@ void xen_be_register_common(void)
xen_be_register("console", &xen_console_ops);
xen_be_register("vkbd", &xen_kbdmouse_ops);
- xen_be_register("qdisk", &xen_blkdev_ops);
#ifdef CONFIG_VIRTFS
xen_be_register("9pfs", &xen_9pfs_ops);
#endif
@@ -753,7 +761,7 @@ void xen_be_register_common(void)
#endif
}
-int xen_be_bind_evtchn(struct XenDevice *xendev)
+int xen_be_bind_evtchn(struct XenLegacyDevice *xendev)
{
if (xendev->local_port != -1) {
return 0;
@@ -789,7 +797,7 @@ static const TypeInfo xendev_type_info = {
.name = TYPE_XENBACKEND,
.parent = TYPE_XENSYSDEV,
.class_init = xendev_class_init,
- .instance_size = sizeof(struct XenDevice),
+ .instance_size = sizeof(struct XenLegacyDevice),
};
static void xen_sysbus_class_init(ObjectClass *klass, void *data)
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
index 3500d88a3e..315dbc9c51 100644
--- a/hw/xen/xen_devconfig.c
+++ b/hw/xen/xen_devconfig.c
@@ -1,5 +1,5 @@
#include "qemu/osdep.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "qemu/option.h"
#include "sysemu/blockdev.h"
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index f1f3a3727c..5539d56c3a 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -59,7 +59,7 @@
#include "hw/pci/pci.h"
#include "hw/xen/xen.h"
#include "hw/i386/pc.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
#include "qemu/range.h"
#include "exec/address-spaces.h"
@@ -847,6 +847,12 @@ static void xen_pt_realize(PCIDevice *d, Error **errp)
}
machine_irq = s->real_device.irq;
+ if (machine_irq == 0) {
+ XEN_PT_LOG(d, "machine irq is 0\n");
+ cmd |= PCI_COMMAND_INTX_DISABLE;
+ goto out;
+ }
+
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
if (rc < 0) {
error_setg_errno(errp, errno, "Mapping machine irq %u to"
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 47f9010c75..31ec5add1d 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -15,7 +15,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/timer.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
#define XEN_PT_MERGE_VALUE(value, data, val_mask) \
@@ -300,7 +300,9 @@ static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- *data = xen_pt_pci_read_intx(s);
+ if (s->real_device.irq) {
+ *data = xen_pt_pci_read_intx(s);
+ }
return 0;
}
diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c
index 135c8df1e7..b69732729b 100644
--- a/hw/xen/xen_pt_graphics.c
+++ b/hw/xen/xen_pt_graphics.c
@@ -5,7 +5,7 @@
#include "qapi/error.h"
#include "xen_pt.h"
#include "xen-host-pci-device.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
static unsigned long igd_guest_opregion;
static unsigned long igd_host_opregion;
@@ -185,8 +185,19 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
return;
}
+ if (bios_size < sizeof(struct rom_header)) {
+ error_setg(errp, "VGA: VBIOS image corrupt (too small)");
+ return;
+ }
+
/* Currently we fixed this address as a primary. */
rom = (struct rom_header *)bios;
+
+ if (rom->pcioffset + sizeof(struct pci_data) > bios_size) {
+ error_setg(errp, "VGA: VBIOS image corrupt (bad pcioffset field)");
+ return;
+ }
+
pd = (void *)(bios + (unsigned char)rom->pcioffset);
/* We may need to fixup Device Identification. */
@@ -194,6 +205,11 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
pd->device = s->real_device.device_id;
len = rom->size * 512;
+ if (len > bios_size) {
+ error_setg(errp, "VGA: VBIOS image corrupt (bad size field)");
+ return;
+ }
+
/* Then adjust the bios checksum */
for (c = (char *)bios; c < ((char *)bios + len); c++) {
checksum += *c;
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index cc514f9157..fb4b887b92 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -11,7 +11,7 @@
#include "qemu/osdep.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
#include "hw/i386/apic-msidef.h"
diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c
index f026556f62..6ef09cbf9d 100644
--- a/hw/xen/xen_pvdev.c
+++ b/hw/xen/xen_pvdev.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/qdev-core.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "hw/xen/xen_pvdev.h"
/* private */
@@ -34,7 +34,7 @@ struct xs_dirs {
static QTAILQ_HEAD(, xs_dirs) xs_cleanup =
QTAILQ_HEAD_INITIALIZER(xs_cleanup);
-static QTAILQ_HEAD(, XenDevice) xendevs =
+static QTAILQ_HEAD(, XenLegacyDevice) xendevs =
QTAILQ_HEAD_INITIALIZER(xendevs);
/* ------------------------------------------------------------- */
@@ -195,7 +195,7 @@ const char *xenbus_strstate(enum xenbus_state state)
* 2 == noisy debug messages (logfile only).
* 3 == will flood your log (logfile only).
*/
-void xen_pv_printf(struct XenDevice *xendev, int msg_level,
+void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level,
const char *fmt, ...)
{
va_list args;
@@ -230,7 +230,7 @@ void xen_pv_printf(struct XenDevice *xendev, int msg_level,
void xen_pv_evtchn_event(void *opaque)
{
- struct XenDevice *xendev = opaque;
+ struct XenLegacyDevice *xendev = opaque;
evtchn_port_t port;
port = xenevtchn_pending(xendev->evtchndev);
@@ -247,7 +247,7 @@ void xen_pv_evtchn_event(void *opaque)
}
}
-void xen_pv_unbind_evtchn(struct XenDevice *xendev)
+void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev)
{
if (xendev->local_port == -1) {
return;
@@ -258,16 +258,16 @@ void xen_pv_unbind_evtchn(struct XenDevice *xendev)
xendev->local_port = -1;
}
-int xen_pv_send_notify(struct XenDevice *xendev)
+int xen_pv_send_notify(struct XenLegacyDevice *xendev)
{
return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
}
/* ------------------------------------------------------------- */
-struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
+struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
QTAILQ_FOREACH(xendev, &xendevs, next) {
if (xendev->dom != dom) {
@@ -287,7 +287,7 @@ struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
/*
* release xen backend device.
*/
-void xen_pv_del_xendev(struct XenDevice *xendev)
+void xen_pv_del_xendev(struct XenLegacyDevice *xendev)
{
if (xendev->ops->free) {
xendev->ops->free(xendev);
@@ -312,7 +312,7 @@ void xen_pv_del_xendev(struct XenDevice *xendev)
qdev_unplug(&xendev->qdev, NULL);
}
-void xen_pv_insert_xendev(struct XenDevice *xendev)
+void xen_pv_insert_xendev(struct XenLegacyDevice *xendev)
{
QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
}