Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / interface / xen / xenstore.c
diff --git a/qemu/roms/ipxe/src/interface/xen/xenstore.c b/qemu/roms/ipxe/src/interface/xen/xenstore.c
new file mode 100644 (file)
index 0000000..b969829
--- /dev/null
@@ -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 );
+       }
+}