mirror of
https://github.com/ipxe/ipxe
synced 2025-12-22 21:11:03 +03:00
[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>
This commit is contained in:
393
src/interface/xen/xenbus.c
Normal file
393
src/interface/xen/xenbus.c
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
125
src/interface/xen/xengrant.c
Normal file
125
src/interface/xen/xengrant.c
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
547
src/interface/xen/xenstore.c
Normal file
547
src/interface/xen/xenstore.c
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user