summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/interface/xen/xenbus.c
diff options
context:
space:
mode:
authorYang Zhang <yang.z.zhang@intel.com>2015-08-28 09:58:54 +0800
committerYang Zhang <yang.z.zhang@intel.com>2015-09-01 12:44:00 +0800
commite44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch)
tree66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/interface/xen/xenbus.c
parent9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff)
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/interface/xen/xenbus.c')
-rw-r--r--qemu/roms/ipxe/src/interface/xen/xenbus.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/interface/xen/xenbus.c b/qemu/roms/ipxe/src/interface/xen/xenbus.c
new file mode 100644
index 000000000..ffc8aba3e
--- /dev/null
+++ b/qemu/roms/ipxe/src/interface/xen/xenbus.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/malloc.h>
+#include <ipxe/device.h>
+#include <ipxe/timer.h>
+#include <ipxe/nap.h>
+#include <ipxe/xen.h>
+#include <ipxe/xenstore.h>
+#include <ipxe/xenbus.h>
+
+/** @file
+ *
+ * Xen device bus
+ *
+ */
+
+/* Disambiguate the various error causes */
+#define ETIMEDOUT_UNKNOWN \
+ __einfo_error ( EINFO_ETIMEDOUT_UNKNOWN )
+#define EINFO_ETIMEDOUT_UNKNOWN \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateUnknown, \
+ "Unknown" )
+#define ETIMEDOUT_INITIALISING \
+ __einfo_error ( EINFO_ETIMEDOUT_INITIALISING )
+#define EINFO_ETIMEDOUT_INITIALISING \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialising, \
+ "Initialising" )
+#define ETIMEDOUT_INITWAIT \
+ __einfo_error ( EINFO_ETIMEDOUT_INITWAIT )
+#define EINFO_ETIMEDOUT_INITWAIT \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitWait, \
+ "InitWait" )
+#define ETIMEDOUT_INITIALISED \
+ __einfo_error ( EINFO_ETIMEDOUT_INITIALISED )
+#define EINFO_ETIMEDOUT_INITIALISED \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialised, \
+ "Initialised" )
+#define ETIMEDOUT_CONNECTED \
+ __einfo_error ( EINFO_ETIMEDOUT_CONNECTED )
+#define EINFO_ETIMEDOUT_CONNECTED \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateConnected, \
+ "Connected" )
+#define ETIMEDOUT_CLOSING \
+ __einfo_error ( EINFO_ETIMEDOUT_CLOSING )
+#define EINFO_ETIMEDOUT_CLOSING \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosing, \
+ "Closing" )
+#define ETIMEDOUT_CLOSED \
+ __einfo_error ( EINFO_ETIMEDOUT_CLOSED )
+#define EINFO_ETIMEDOUT_CLOSED \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosed, \
+ "Closed" )
+#define ETIMEDOUT_RECONFIGURING \
+ __einfo_error ( EINFO_ETIMEDOUT_RECONFIGURING )
+#define EINFO_ETIMEDOUT_RECONFIGURING \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfiguring, \
+ "Reconfiguring" )
+#define ETIMEDOUT_RECONFIGURED \
+ __einfo_error ( EINFO_ETIMEDOUT_RECONFIGURED )
+#define EINFO_ETIMEDOUT_RECONFIGURED \
+ __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfigured, \
+ "Reconfigured" )
+#define ETIMEDOUT_STATE( state ) \
+ EUNIQ ( EINFO_ETIMEDOUT, (state), ETIMEDOUT_UNKNOWN, \
+ ETIMEDOUT_INITIALISING, ETIMEDOUT_INITWAIT, \
+ ETIMEDOUT_INITIALISED, ETIMEDOUT_CONNECTED, \
+ ETIMEDOUT_CLOSING, ETIMEDOUT_CLOSED, \
+ ETIMEDOUT_RECONFIGURING, ETIMEDOUT_RECONFIGURED )
+
+/** Maximum time to wait for backend to reach a given state, in ticks */
+#define XENBUS_BACKEND_TIMEOUT ( 5 * TICKS_PER_SEC )
+
+/**
+ * Set device state
+ *
+ * @v xendev Xen device
+ * @v state New state
+ * @ret rc Return status code
+ */
+int xenbus_set_state ( struct xen_device *xendev, int state ) {
+ int rc;
+
+ /* Attempt to set state */
+ if ( ( rc = xenstore_write_num ( xendev->xen, state, xendev->key,
+ "state", NULL ) ) != 0 ) {
+ DBGC ( xendev, "XENBUS %s could not set state=\"%d\": %s\n",
+ xendev->key, state, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Get backend state
+ *
+ * @v xendev Xen device
+ * @ret state Backend state, or negative error
+ */
+int xenbus_backend_state ( struct xen_device *xendev ) {
+ unsigned long state;
+ int rc;
+
+ /* Attempt to get backend state */
+ if ( ( rc = xenstore_read_num ( xendev->xen, &state, xendev->backend,
+ "state", NULL ) ) != 0 ) {
+ DBGC ( xendev, "XENBUS %s could not read %s/state: %s\n",
+ xendev->key, xendev->backend, strerror ( rc ) );
+ return rc;
+ }
+
+ return state;
+}
+
+/**
+ * Wait for backend to reach a given state
+ *
+ * @v xendev Xen device
+ * @v state Desired backend state
+ * @ret rc Return status code
+ */
+int xenbus_backend_wait ( struct xen_device *xendev, int state ) {
+ unsigned long started = currticks();
+ unsigned long elapsed;
+ unsigned int attempts = 0;
+ int current_state;
+ int rc;
+
+ /* Wait for backend to reach this state */
+ do {
+
+ /* Get current backend state */
+ current_state = xenbus_backend_state ( xendev );
+ if ( current_state < 0 ) {
+ rc = current_state;
+ return rc;
+ }
+ if ( current_state == state )
+ return 0;
+
+ /* Allow time for backend to react */
+ cpu_nap();
+
+ /* XenStore is a very slow interface; any fixed delay
+ * time would be dwarfed by the XenStore access time.
+ * We therefore use wall clock to time out this
+ * operation.
+ */
+ elapsed = ( currticks() - started );
+ attempts++;
+
+ } while ( elapsed < XENBUS_BACKEND_TIMEOUT );
+
+ /* Construct status code from current backend state */
+ rc = -ETIMEDOUT_STATE ( current_state );
+ DBGC ( xendev, "XENBUS %s timed out after %d attempts waiting for "
+ "%s/state=\"%d\": %s\n", xendev->key, attempts, xendev->backend,
+ state, strerror ( rc ) );
+
+ return rc;
+}
+
+/**
+ * Find driver for Xen device
+ *
+ * @v type Device type
+ * @ret driver Driver, or NULL
+ */
+static struct xen_driver * xenbus_find_driver ( const char *type ) {
+ struct xen_driver *xendrv;
+
+ for_each_table_entry ( xendrv, XEN_DRIVERS ) {
+ if ( strcmp ( xendrv->type, type ) == 0 )
+ return xendrv;
+ }
+ return NULL;
+}
+
+/**
+ * Probe Xen device
+ *
+ * @v xen Xen hypervisor
+ * @v parent Parent device
+ * @v type Device type
+ * @v instance Device instance
+ * @ret rc Return status code
+ */
+static int xenbus_probe_device ( struct xen_hypervisor *xen,
+ struct device *parent, const char *type,
+ const char *instance ) {
+ struct xen_device *xendev;
+ size_t key_len;
+ int rc;
+
+ /* Allocate and initialise structure */
+ key_len = ( 7 /* "device/" */ + strlen ( type ) + 1 /* "/" */ +
+ strlen ( instance ) + 1 /* NUL */ );
+ xendev = zalloc ( sizeof ( *xendev ) + key_len );
+ if ( ! xendev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ snprintf ( xendev->dev.name, sizeof ( xendev->dev.name ), "%s/%s",
+ type, instance );
+ xendev->dev.desc.bus_type = BUS_TYPE_XEN;
+ INIT_LIST_HEAD ( &xendev->dev.children );
+ list_add_tail ( &xendev->dev.siblings, &parent->children );
+ xendev->dev.parent = parent;
+ xendev->xen = xen;
+ xendev->key = ( ( void * ) ( xendev + 1 ) );
+ snprintf ( xendev->key, key_len, "device/%s/%s", type, instance );
+
+ /* Read backend key */
+ if ( ( rc = xenstore_read ( xen, &xendev->backend, xendev->key,
+ "backend", NULL ) ) != 0 ) {
+ DBGC ( xendev, "XENBUS %s could not read backend: %s\n",
+ xendev->key, strerror ( rc ) );
+ goto err_read_backend;
+ }
+
+ /* Read backend domain ID */
+ if ( ( rc = xenstore_read_num ( xen, &xendev->backend_id, xendev->key,
+ "backend-id", NULL ) ) != 0 ) {
+ DBGC ( xendev, "XENBUS %s could not read backend-id: %s\n",
+ xendev->key, strerror ( rc ) );
+ goto err_read_backend_id;
+ }
+ DBGC ( xendev, "XENBUS %s backend=\"%s\" in domain %ld\n",
+ xendev->key, xendev->backend, xendev->backend_id );
+
+ /* Look for a driver */
+ xendev->driver = xenbus_find_driver ( type );
+ if ( ! xendev->driver ) {
+ DBGC ( xendev, "XENBUS %s has no driver\n", xendev->key );
+ /* Not a fatal error */
+ rc = 0;
+ goto err_no_driver;
+ }
+ xendev->dev.driver_name = xendev->driver->name;
+ DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key,
+ xendev->driver->name );
+
+ /* Probe driver */
+ if ( ( rc = xendev->driver->probe ( xendev ) ) != 0 ) {
+ DBGC ( xendev, "XENBUS could not probe %s: %s\n",
+ xendev->key, strerror ( rc ) );
+ goto err_probe;
+ }
+
+ return 0;
+
+ xendev->driver->remove ( xendev );
+ err_probe:
+ err_no_driver:
+ err_read_backend_id:
+ free ( xendev->backend );
+ err_read_backend:
+ list_del ( &xendev->dev.siblings );
+ free ( xendev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove Xen device
+ *
+ * @v xendev Xen device
+ */
+static void xenbus_remove_device ( struct xen_device *xendev ) {
+
+ /* Remove device */
+ xendev->driver->remove ( xendev );
+ free ( xendev->backend );
+ list_del ( &xendev->dev.siblings );
+ free ( xendev );
+}
+
+/**
+ * Probe Xen devices of a given type
+ *
+ * @v xen Xen hypervisor
+ * @v parent Parent device
+ * @v type Device type
+ * @ret rc Return status code
+ */
+static int xenbus_probe_type ( struct xen_hypervisor *xen,
+ struct device *parent, const char *type ) {
+ char *children;
+ char *child;
+ size_t len;
+ int rc;
+
+ /* Get children of this key */
+ if ( ( rc = xenstore_directory ( xen, &children, &len, "device",
+ type, NULL ) ) != 0 ) {
+ DBGC ( xen, "XENBUS could not list \"%s\" devices: %s\n",
+ type, strerror ( rc ) );
+ goto err_directory;
+ }
+
+ /* Probe each child */
+ for ( child = children ; child < ( children + len ) ;
+ child += ( strlen ( child ) + 1 /* NUL */ ) ) {
+ if ( ( rc = xenbus_probe_device ( xen, parent, type,
+ child ) ) != 0 )
+ goto err_probe_device;
+ }
+
+ free ( children );
+ return 0;
+
+ err_probe_device:
+ free ( children );
+ err_directory:
+ return rc;
+}
+
+/**
+ * Probe Xen bus
+ *
+ * @v xen Xen hypervisor
+ * @v parent Parent device
+ * @ret rc Return status code
+ */
+int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent ) {
+ char *types;
+ char *type;
+ size_t len;
+ int rc;
+
+ /* Get children of "device" key */
+ if ( ( rc = xenstore_directory ( xen, &types, &len, "device",
+ NULL ) ) != 0 ) {
+ DBGC ( xen, "XENBUS could not list device types: %s\n",
+ strerror ( rc ) );
+ goto err_directory;
+ }
+
+ /* Probe each child type */
+ for ( type = types ; type < ( types + len ) ;
+ type += ( strlen ( type ) + 1 /* NUL */ ) ) {
+ if ( ( rc = xenbus_probe_type ( xen, parent, type ) ) != 0 )
+ goto err_probe_type;
+ }
+
+ free ( types );
+ return 0;
+
+ xenbus_remove ( xen, parent );
+ err_probe_type:
+ free ( types );
+ err_directory:
+ return rc;
+}
+
+/**
+ * Remove Xen bus
+ *
+ * @v xen Xen hypervisor
+ * @v parent Parent device
+ */
+void xenbus_remove ( struct xen_hypervisor *xen __unused,
+ struct device *parent ) {
+ struct xen_device *xendev;
+ struct xen_device *tmp;
+
+ /* Remove devices */
+ list_for_each_entry_safe ( xendev, tmp, &parent->children,
+ dev.siblings ) {
+ xenbus_remove_device ( xendev );
+ }
+}