blob: caeb4e934316a097f520746d7f6d3868eba4a5d7 [file] [log] [blame]
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#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;
size_t fill;
DBGCP ( intf, "XENSTORE raw request:\n" );
DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len );
/* Write one byte at a time */
for ( offset = 0 ; offset < len ; offset++ ) {
/* 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;
size_t fill;
DBGCP ( intf, "XENSTORE raw response:\n" );
/* Read one byte at a time */
for ( offset = 0 ; offset < len ; offset++ ) {
/* 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;
/* Wait for response to become available */
while ( ! xenevent_pending ( xen, xen->store.port ) )
cpu_nap();
/* 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 */
if ( asprintf ( &child_key, "%s/%s", key, child ) < 0 ){
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 );
}
}