diff options
Diffstat (limited to 'roms/ipxe/src/interface/xen/xenstore.c')
-rw-r--r-- | roms/ipxe/src/interface/xen/xenstore.c | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/roms/ipxe/src/interface/xen/xenstore.c b/roms/ipxe/src/interface/xen/xenstore.c new file mode 100644 index 00000000..b9698292 --- /dev/null +++ b/roms/ipxe/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 ); + } +} |