summaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
authorMichael Brown2014-07-29 00:38:30 +0200
committerMichael Brown2014-07-29 16:57:44 +0200
commit036af27a4523c4e15e28d30a1513a3f6d9671774 (patch)
treebf284637f91e2258906d736f07a54b1f98dfd68d /src/interface
parent[xen] Import selected public headers (diff)
downloadipxe-036af27a4523c4e15e28d30a1513a3f6d9671774.tar.gz
ipxe-036af27a4523c4e15e28d30a1513a3f6d9671774.tar.xz
ipxe-036af27a4523c4e15e28d30a1513a3f6d9671774.zip
[xen] Add basic support for PV-HVM domains
Add basic support for Xen PV-HVM domains (detected via the Xen platform PCI device with IDs 5853:0001), including support for accessing configuration via XenStore and enumerating devices via XenBus. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/xen/xenbus.c393
-rw-r--r--src/interface/xen/xengrant.c125
-rw-r--r--src/interface/xen/xenstore.c547
3 files changed, 1065 insertions, 0 deletions
diff --git a/src/interface/xen/xenbus.c b/src/interface/xen/xenbus.c
new file mode 100644
index 00000000..7ac41899
--- /dev/null
+++ b/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
+ */
+static 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 );
+ }
+}
diff --git a/src/interface/xen/xengrant.c b/src/interface/xen/xengrant.c
new file mode 100644
index 00000000..55731bfa
--- /dev/null
+++ b/src/interface/xen/xengrant.c
@@ -0,0 +1,125 @@
+/*
+ * 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 <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/io.h>
+#include <ipxe/xen.h>
+#include <ipxe/xengrant.h>
+
+/** @file
+ *
+ * Xen grant tables
+ *
+ */
+
+/**
+ * Allocate grant references
+ *
+ * @v xen Xen hypervisor
+ * @v refs Grant references to fill in
+ * @v count Number of references
+ * @ret rc Return status code
+ */
+int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
+ unsigned int count ) {
+ union grant_entry_v2 *entry;
+ unsigned int mask = ( xen->grant.count - 1 );
+ unsigned int check = 0;
+ unsigned int avail;
+ unsigned int ref;
+
+ /* Fail unless we have enough references available */
+ avail = ( xen->grant.count - xen->grant.used -
+ GNTTAB_NR_RESERVED_ENTRIES );
+ if ( avail < count ) {
+ DBGC ( xen, "XENGRANT cannot allocate %d references (only %d "
+ "of %d available)\n", count, avail, xen->grant.count );
+ return -ENOBUFS;
+ }
+ DBGC ( xen, "XENGRANT allocating %d references (from %d of %d "
+ "available)\n", count, avail, xen->grant.count );
+
+ /* Update number of references used */
+ xen->grant.used += count;
+
+ /* Find unused references */
+ for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) {
+
+ /* Sanity check */
+ assert ( check++ < xen->grant.count );
+
+ /* Skip reserved references */
+ if ( ref < GNTTAB_NR_RESERVED_ENTRIES )
+ continue;
+
+ /* Skip in-use references */
+ entry = &xen->grant.table[ref];
+ if ( readw ( &entry->hdr.flags ) & GTF_type_mask )
+ continue;
+ if ( readw ( &entry->hdr.domid ) == DOMID_SELF )
+ continue;
+
+ /* Mark reference as in-use. We leave the flags as
+ * empty (to avoid creating a valid grant table entry)
+ * and set the domid to DOMID_SELF.
+ */
+ writew ( DOMID_SELF, &entry->hdr.domid );
+ DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref );
+
+ /* Record reference */
+ refs[--count] = ref;
+ }
+
+ /* Update cursor */
+ xen->grant.ref = ref;
+
+ return 0;
+}
+
+/**
+ * Free grant references
+ *
+ * @v xen Xen hypervisor
+ * @v refs Grant references
+ * @v count Number of references
+ */
+void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
+ unsigned int count ) {
+ union grant_entry_v2 *entry;
+ unsigned int ref;
+ unsigned int i;
+
+ /* Free references */
+ for ( i = 0 ; i < count ; i++ ) {
+
+ /* Sanity check */
+ ref = refs[i];
+ assert ( ref < xen->grant.count );
+
+ /* Mark reference as unused */
+ entry = &xen->grant.table[ref];
+ writew ( 0, &entry->hdr.flags );
+ writew ( 0, &entry->hdr.domid );
+ DBGC2 ( xen, "XENGRANT freed ref %d\n", ref );
+ }
+}
diff --git a/src/interface/xen/xenstore.c b/src/interface/xen/xenstore.c
new file mode 100644
index 00000000..b9698292
--- /dev/null
+++ b/src/interface/xen/xenstore.c
@@ -0,0 +1,547 @@
+/*
+ * 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 <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ipxe/io.h>
+#include <ipxe/nap.h>
+#include <ipxe/malloc.h>
+#include <ipxe/xen.h>
+#include <ipxe/xenevent.h>
+#include <ipxe/xenstore.h>
+
+/*
+ * xs_wire.h attempts to define a static error table xsd_errors, which
+ * interacts badly with the dynamically generated error numbers used
+ * by iPXE. Prevent this table from being constructed by including
+ * errno.h only after including xs_wire.h.
+ *
+ */
+#include <xen/io/xs_wire.h>
+#include <errno.h>
+
+/** @file
+ *
+ * XenStore interface
+ *
+ */
+
+/** Request identifier */
+static uint32_t xenstore_req_id;
+
+/**
+ * Send XenStore request raw data
+ *
+ * @v xen Xen hypervisor
+ * @v data Data buffer
+ * @v len Length of data
+ */
+static void xenstore_send ( struct xen_hypervisor *xen, const void *data,
+ size_t len ) {
+ struct xenstore_domain_interface *intf = xen->store.intf;
+ XENSTORE_RING_IDX prod = readl ( &intf->req_prod );
+ XENSTORE_RING_IDX cons;
+ XENSTORE_RING_IDX idx;
+ const char *bytes = data;
+ size_t offset = 0;
+ size_t fill;
+
+ DBGCP ( intf, "XENSTORE raw request:\n" );
+ DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len );
+
+ /* Write one byte at a time */
+ while ( offset < len ) {
+
+ /* Wait for space to become available */
+ while ( 1 ) {
+ cons = readl ( &intf->req_cons );
+ fill = ( prod - cons );
+ if ( fill < XENSTORE_RING_SIZE )
+ break;
+ DBGC2 ( xen, "." );
+ cpu_nap();
+ rmb();
+ }
+
+ /* Write byte */
+ idx = MASK_XENSTORE_IDX ( prod++ );
+ writeb ( bytes[offset++], &intf->req[idx] );
+ }
+
+ /* Update producer counter */
+ wmb();
+ writel ( prod, &intf->req_prod );
+ wmb();
+}
+
+/**
+ * Send XenStore request string (excluding terminating NUL)
+ *
+ * @v xen Xen hypervisor
+ * @v string String
+ */
+static void xenstore_send_string ( struct xen_hypervisor *xen,
+ const char *string ) {
+
+ xenstore_send ( xen, string, strlen ( string ) );
+}
+
+/**
+ * Receive XenStore response raw data
+ *
+ * @v xen Xen hypervisor
+ * @v data Data buffer, or NULL to discard data
+ * @v len Length of data
+ */
+static void xenstore_recv ( struct xen_hypervisor *xen, void *data,
+ size_t len ) {
+ struct xenstore_domain_interface *intf = xen->store.intf;
+ XENSTORE_RING_IDX cons = readl ( &intf->rsp_cons );
+ XENSTORE_RING_IDX prod;
+ XENSTORE_RING_IDX idx;
+ char *bytes = data;
+ size_t offset = 0;
+ size_t fill;
+
+ DBGCP ( intf, "XENSTORE raw response:\n" );
+
+ /* Read one byte at a time */
+ while ( offset < len ) {
+
+ /* Wait for data to be ready */
+ while ( 1 ) {
+ prod = readl ( &intf->rsp_prod );
+ fill = ( prod - cons );
+ if ( fill > 0 )
+ break;
+ DBGC2 ( xen, "." );
+ cpu_nap();
+ rmb();
+ }
+
+ /* Read byte */
+ idx = MASK_XENSTORE_IDX ( cons++ );
+ if ( data )
+ bytes[offset++] = readb ( &intf->rsp[idx] );
+ }
+ if ( data )
+ DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( cons - len ), data, len );
+
+ /* Update consumer counter */
+ writel ( cons, &intf->rsp_cons );
+ wmb();
+}
+
+/**
+ * Send XenStore request
+ *
+ * @v xen Xen hypervisor
+ * @v type Message type
+ * @v req_id Request ID
+ * @v value Value, or NULL to omit
+ * @v key Key path components
+ * @ret rc Return status code
+ */
+static int xenstore_request ( struct xen_hypervisor *xen,
+ enum xsd_sockmsg_type type, uint32_t req_id,
+ const char *value, va_list key ) {
+ struct xsd_sockmsg msg;
+ struct evtchn_send event;
+ const char *string;
+ va_list tmp;
+ int xenrc;
+ int rc;
+
+ /* Construct message header */
+ msg.type = type;
+ msg.req_id = req_id;
+ msg.tx_id = 0;
+ msg.len = 0;
+ DBGC2 ( xen, "XENSTORE request ID %d type %d ", req_id, type );
+
+ /* Calculate total length */
+ va_copy ( tmp, key );
+ while ( ( string = va_arg ( tmp, const char * ) ) != NULL ) {
+ DBGC2 ( xen, "%s%s", ( msg.len ? "/" : "" ), string );
+ msg.len += ( strlen ( string ) + 1 /* '/' or NUL */ );
+ }
+ va_end ( tmp );
+ if ( value ) {
+ DBGC2 ( xen, " = \"%s\"", value );
+ msg.len += strlen ( value );
+ }
+ DBGC2 ( xen, "\n" );
+
+ /* Send message */
+ xenstore_send ( xen, &msg, sizeof ( msg ) );
+ string = va_arg ( key, const char * );
+ assert ( string != NULL );
+ xenstore_send_string ( xen, string );
+ while ( ( string = va_arg ( key, const char * ) ) != NULL ) {
+ xenstore_send_string ( xen, "/" );
+ xenstore_send_string ( xen, string );
+ }
+ xenstore_send ( xen, "", 1 ); /* Separating NUL */
+ if ( value )
+ xenstore_send_string ( xen, value );
+
+ /* Notify the back end */
+ event.port = xen->store.port;
+ if ( ( xenrc = xenevent_send ( xen, &event ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( xen, "XENSTORE could not notify back end: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Receive XenStore response
+ *
+ * @v xen Xen hypervisor
+ * @v req_id Request ID
+ * @v value Value to fill in
+ * @v len Length to fill in
+ * @ret rc Return status code
+ *
+ * The caller is responsible for eventually calling free() on the
+ * returned value. Note that the value may comprise multiple
+ * NUL-terminated strings concatenated together. A terminating NUL
+ * will always be appended to the returned value.
+ */
+static int xenstore_response ( struct xen_hypervisor *xen, uint32_t req_id,
+ char **value, size_t *len ) {
+ struct xsd_sockmsg msg;
+ char *string;
+ int rc;
+
+ /* Receive message header */
+ xenstore_recv ( xen, &msg, sizeof ( msg ) );
+ *len = msg.len;
+
+ /* Allocate space for response */
+ *value = zalloc ( msg.len + 1 /* terminating NUL */ );
+
+ /* Receive data. Do this even if allocation failed, or if the
+ * request ID was incorrect, to avoid leaving data in the
+ * ring.
+ */
+ xenstore_recv ( xen, *value, msg.len );
+
+ /* Validate request ID */
+ if ( msg.req_id != req_id ) {
+ DBGC ( xen, "XENSTORE response ID mismatch (got %d, expected "
+ "%d)\n", msg.req_id, req_id );
+ rc = -EPROTO;
+ goto err_req_id;
+ }
+
+ /* Check for allocation failure */
+ if ( ! *value ) {
+ DBGC ( xen, "XENSTORE could not allocate %d bytes for "
+ "response\n", msg.len );
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Check for explicit errors */
+ if ( msg.type == XS_ERROR ) {
+ DBGC ( xen, "XENSTORE response error \"%s\"\n", *value );
+ rc = -EIO;
+ goto err_explicit;
+ }
+
+ DBGC2 ( xen, "XENSTORE response ID %d\n", req_id );
+ if ( DBG_EXTRA ) {
+ for ( string = *value ; string < ( *value + msg.len ) ;
+ string += ( strlen ( string ) + 1 /* NUL */ ) ) {
+ DBGC2 ( xen, " - \"%s\"\n", string );
+ }
+ }
+ return 0;
+
+ err_explicit:
+ err_alloc:
+ err_req_id:
+ free ( *value );
+ *value = NULL;
+ return rc;
+}
+
+/**
+ * Issue a XenStore message
+ *
+ * @v xen Xen hypervisor
+ * @v type Message type
+ * @v response Response value to fill in, or NULL to discard
+ * @v len Response length to fill in, or NULL to ignore
+ * @v request Request value, or NULL to omit
+ * @v key Key path components
+ * @ret rc Return status code
+ */
+static int xenstore_message ( struct xen_hypervisor *xen,
+ enum xsd_sockmsg_type type, char **response,
+ size_t *len, const char *request, va_list key ) {
+ char *response_value;
+ size_t response_len;
+ int rc;
+
+ /* Send request */
+ if ( ( rc = xenstore_request ( xen, type, ++xenstore_req_id,
+ request, key ) ) != 0 )
+ return rc;
+
+ /* Receive response */
+ if ( ( rc = xenstore_response ( xen, xenstore_req_id, &response_value,
+ &response_len ) ) != 0 )
+ return rc;
+
+ /* Return response, if applicable */
+ if ( response ) {
+ *response = response_value;
+ } else {
+ free ( response_value );
+ }
+ if ( len )
+ *len = response_len;
+
+ return 0;
+}
+
+/**
+ * Read XenStore value
+ *
+ * @v xen Xen hypervisor
+ * @v value Value to fill in
+ * @v key Key path components
+ * @ret rc Return status code
+ *
+ * On a successful return, the caller is responsible for calling
+ * free() on the returned value.
+ */
+static int xenstore_vread ( struct xen_hypervisor *xen, char **value,
+ va_list key ) {
+
+ return xenstore_message ( xen, XS_READ, value, NULL, NULL, key );
+}
+
+/**
+ * Read XenStore value
+ *
+ * @v xen Xen hypervisor
+ * @v value Value to fill in
+ * @v ... Key path components
+ * @ret rc Return status code
+ *
+ * On a successful return, the caller is responsible for calling
+ * free() on the returned value.
+ */
+__attribute__ (( sentinel )) int
+xenstore_read ( struct xen_hypervisor *xen, char **value, ... ) {
+ va_list key;
+ int rc;
+
+ va_start ( key, value );
+ rc = xenstore_vread ( xen, value, key );
+ va_end ( key );
+ return rc;
+}
+
+/**
+ * Read XenStore numeric value
+ *
+ * @v xen Xen hypervisor
+ * @v num Numeric value to fill in
+ * @v ... Key path components
+ * @ret rc Return status code
+ */
+__attribute__ (( sentinel )) int
+xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... ) {
+ va_list key;
+ char *value;
+ char *endp;
+ int rc;
+
+ /* Try to read text value */
+ va_start ( key, num );
+ rc = xenstore_vread ( xen, &value, key );
+ va_end ( key );
+ if ( rc != 0 )
+ goto err_read;
+
+ /* Try to parse as numeric value */
+ *num = strtoul ( value, &endp, 10 );
+ if ( ( *value == '\0' ) || ( *endp != '\0' ) ) {
+ DBGC ( xen, "XENSTORE found invalid numeric value \"%s\"\n",
+ value );
+ rc = -EINVAL;
+ goto err_strtoul;
+ }
+
+ err_strtoul:
+ free ( value );
+ err_read:
+ return rc;
+}
+
+/**
+ * Write XenStore value
+ *
+ * @v xen Xen hypervisor
+ * @v value Value
+ * @v key Key path components
+ * @ret rc Return status code
+ */
+static int xenstore_vwrite ( struct xen_hypervisor *xen, const char *value,
+ va_list key ) {
+
+ return xenstore_message ( xen, XS_WRITE, NULL, NULL, value, key );
+}
+
+/**
+ * Write XenStore value
+ *
+ * @v xen Xen hypervisor
+ * @v value Value
+ * @v ... Key path components
+ * @ret rc Return status code
+ */
+__attribute__ (( sentinel )) int
+xenstore_write ( struct xen_hypervisor *xen, const char *value, ... ) {
+ va_list key;
+ int rc;
+
+ va_start ( key, value );
+ rc = xenstore_vwrite ( xen, value, key );
+ va_end ( key );
+ return rc;
+}
+
+/**
+ * Write XenStore numeric value
+ *
+ * @v xen Xen hypervisor
+ * @v num Numeric value
+ * @v ... Key path components
+ * @ret rc Return status code
+ */
+__attribute__ (( sentinel )) int
+xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... ) {
+ char value[ 21 /* "18446744073709551615" + NUL */ ];
+ va_list key;
+ int rc;
+
+ /* Construct value */
+ snprintf ( value, sizeof ( value ), "%ld", num );
+
+ /* Write value */
+ va_start ( key, num );
+ rc = xenstore_vwrite ( xen, value, key );
+ va_end ( key );
+ return rc;
+}
+
+/**
+ * Delete XenStore value
+ *
+ * @v xen Xen hypervisor
+ * @v ... Key path components
+ * @ret rc Return status code
+ */
+__attribute__ (( sentinel )) int
+xenstore_rm ( struct xen_hypervisor *xen, ... ) {
+ va_list key;
+ int rc;
+
+ va_start ( key, xen );
+ rc = xenstore_message ( xen, XS_RM, NULL, NULL, NULL, key );
+ va_end ( key );
+ return rc;
+}
+
+/**
+ * Read XenStore directory
+ *
+ * @v xen Xen hypervisor
+ * @v children Child key names to fill in
+ * @v len Length of child key names to fill in
+ * @v ... Key path components
+ * @ret rc Return status code
+ */
+__attribute__ (( sentinel )) int
+xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len,
+ ... ) {
+ va_list key;
+ int rc;
+
+ va_start ( key, len );
+ rc = xenstore_message ( xen, XS_DIRECTORY, children, len, NULL, key );
+ va_end ( key );
+ return rc;
+}
+
+/**
+ * Dump XenStore directory contents (for debugging)
+ *
+ * @v xen Xen hypervisor
+ * @v key Key
+ */
+void xenstore_dump ( struct xen_hypervisor *xen, const char *key ) {
+ char *value;
+ char *children;
+ char *child;
+ char *child_key;
+ size_t len;
+ int rc;
+
+ /* Try to dump current key as a value */
+ if ( ( rc = xenstore_read ( xen, &value, key, NULL ) ) == 0 ) {
+ DBGC ( xen, "%s = \"%s\"\n", key, value );
+ free ( value );
+ }
+
+ /* Try to recurse into each child in turn */
+ if ( ( rc = xenstore_directory ( xen, &children, &len, key,
+ NULL ) ) == 0 ) {
+ for ( child = children ; child < ( children + len ) ;
+ child += ( strlen ( child ) + 1 /* NUL */ ) ) {
+
+ /* Construct child key */
+ asprintf ( &child_key, "%s/%s", key, child );
+ if ( ! child_key ) {
+ DBGC ( xen, "XENSTORE could not allocate child "
+ "key \"%s/%s\"\n", key, child );
+ rc = -ENOMEM;
+ break;
+ }
+
+ /* Recurse into child key, continuing on error */
+ xenstore_dump ( xen, child_key );
+ free ( child_key );
+ }
+ free ( children );
+ }
+}