summaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/Makefile1
-rw-r--r--src/arch/x86/Makefile1
-rw-r--r--src/arch/x86/drivers/xen/hvm.c521
-rw-r--r--src/arch/x86/drivers/xen/hvm.h75
-rw-r--r--src/arch/x86/include/bits/errfile.h1
-rw-r--r--src/arch/x86/include/bits/xen.h164
-rw-r--r--src/include/ipxe/device.h3
-rw-r--r--src/include/ipxe/errfile.h3
-rw-r--r--src/include/ipxe/xen.h73
-rw-r--r--src/include/ipxe/xenbus.h85
-rw-r--r--src/include/ipxe/xenevent.h59
-rw-r--r--src/include/ipxe/xengrant.h102
-rw-r--r--src/include/ipxe/xenmem.h46
-rw-r--r--src/include/ipxe/xenstore.h29
-rw-r--r--src/include/ipxe/xenver.h44
-rw-r--r--src/interface/xen/xenbus.c393
-rw-r--r--src/interface/xen/xengrant.c125
-rw-r--r--src/interface/xen/xenstore.c547
18 files changed, 2272 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
index c6760ee9..aeeabcda 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -84,6 +84,7 @@ SRCDIRS += drivers/bitbash
SRCDIRS += drivers/infiniband
SRCDIRS += interface/pxe interface/efi interface/smbios
SRCDIRS += interface/bofm
+SRCDIRS += interface/xen
SRCDIRS += tests
SRCDIRS += crypto crypto/axtls crypto/matrixssl
SRCDIRS += hci hci/commands hci/tui
diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile
index cdd397d4..e555587d 100644
--- a/src/arch/x86/Makefile
+++ b/src/arch/x86/Makefile
@@ -8,6 +8,7 @@ SRCDIRS += arch/x86/core
SRCDIRS += arch/x86/interface/efi
SRCDIRS += arch/x86/prefix
SRCDIRS += arch/x86/hci/commands
+SRCDIRS += arch/x86/drivers/xen
# breaks building some of the linux-related objects
CFLAGS += -Ulinux
diff --git a/src/arch/x86/drivers/xen/hvm.c b/src/arch/x86/drivers/xen/hvm.c
new file mode 100644
index 00000000..fbbcdf02
--- /dev/null
+++ b/src/arch/x86/drivers/xen/hvm.c
@@ -0,0 +1,521 @@
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+#include <ipxe/malloc.h>
+#include <ipxe/pci.h>
+#include <ipxe/cpuid.h>
+#include <ipxe/msr.h>
+#include <ipxe/xen.h>
+#include <ipxe/xenver.h>
+#include <ipxe/xenmem.h>
+#include <ipxe/xenstore.h>
+#include <ipxe/xenbus.h>
+#include <ipxe/xengrant.h>
+#include "hvm.h"
+
+/** @file
+ *
+ * Xen HVM driver
+ *
+ */
+
+/**
+ * Get CPUID base
+ *
+ * @v hvm HVM device
+ * @ret rc Return status code
+ */
+static int hvm_cpuid_base ( struct hvm_device *hvm ) {
+ struct {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ } __attribute__ (( packed )) signature;
+ uint32_t base;
+ uint32_t version;
+ uint32_t discard_eax;
+ uint32_t discard_ebx;
+ uint32_t discard_ecx;
+ uint32_t discard_edx;
+
+ /* Scan for magic signature */
+ for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
+ base += HVM_CPUID_STEP ) {
+ cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx,
+ &signature.edx );
+ if ( memcmp ( &signature, HVM_CPUID_MAGIC,
+ sizeof ( signature ) ) == 0 ) {
+ hvm->cpuid_base = base;
+ cpuid ( ( base + HVM_CPUID_VERSION ), &version,
+ &discard_ebx, &discard_ecx, &discard_edx );
+ DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
+ base, ( version >> 16 ), ( version & 0xffff ) );
+ return 0;
+ }
+ }
+
+ DBGC ( hvm, "HVM could not find hypervisor\n" );
+ return -ENODEV;
+}
+
+/**
+ * Map hypercall page(s)
+ *
+ * @v hvm HVM device
+ * @ret rc Return status code
+ */
+static int hvm_map_hypercall ( struct hvm_device *hvm ) {
+ uint32_t pages;
+ uint32_t msr;
+ uint32_t discard_ecx;
+ uint32_t discard_edx;
+ physaddr_t hypercall_phys;
+ uint32_t version;
+ static xen_extraversion_t extraversion;
+ int xenrc;
+ int rc;
+
+ /* Get number of hypercall pages and MSR to use */
+ cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr,
+ &discard_ecx, &discard_edx );
+
+ /* Allocate pages */
+ hvm->hypercall_len = ( pages * PAGE_SIZE );
+ hvm->xen.hypercall = malloc_dma ( hvm->hypercall_len, PAGE_SIZE );
+ if ( ! hvm->xen.hypercall ) {
+ DBGC ( hvm, "HVM could not allocate %d hypercall page(s)\n",
+ pages );
+ return -ENOMEM;
+ }
+ hypercall_phys = virt_to_phys ( hvm->xen.hypercall );
+ DBGC2 ( hvm, "HVM hypercall page(s) at [%#08lx,%#08lx) via MSR %#08x\n",
+ hypercall_phys, ( hypercall_phys + hvm->hypercall_len ), msr );
+
+ /* Write to MSR */
+ wrmsr ( msr, hypercall_phys );
+
+ /* Check that hypercall mechanism is working */
+ version = xenver_version ( &hvm->xen );
+ if ( ( xenrc = xenver_extraversion ( &hvm->xen, &extraversion ) ) != 0){
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not get extraversion: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ DBGC2 ( hvm, "HVM found Xen version %d.%d%s\n",
+ ( version >> 16 ), ( version & 0xffff ) , extraversion );
+
+ return 0;
+}
+
+/**
+ * Unmap hypercall page(s)
+ *
+ * @v hvm HVM device
+ */
+static void hvm_unmap_hypercall ( struct hvm_device *hvm ) {
+
+ /* Free pages */
+ free_dma ( hvm->xen.hypercall, hvm->hypercall_len );
+}
+
+/**
+ * Allocate and map MMIO space
+ *
+ * @v hvm HVM device
+ * @v space Source mapping space
+ * @v pages Number of pages
+ * @ret mmio MMIO space address, or NULL on error
+ */
+static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space,
+ unsigned int pages ) {
+ struct xen_add_to_physmap add;
+ struct xen_remove_from_physmap remove;
+ physaddr_t mmio_phys;
+ unsigned int i;
+ size_t len;
+ void *mmio;
+ int xenrc;
+ int rc;
+
+ /* Check for available space */
+ len = ( pages * PAGE_SIZE );
+ if ( ( hvm->mmio_offset + len ) > hvm->mmio_len ) {
+ DBGC ( hvm, "HVM could not allocate %zd bytes of MMIO space "
+ "(%zd of %zd remaining)\n", len,
+ ( hvm->mmio_len - hvm->mmio_offset ), hvm->mmio_len );
+ goto err_no_space;
+ }
+
+ /* Map this space */
+ mmio = ioremap ( ( hvm->mmio + hvm->mmio_offset ), len );
+ if ( ! mmio ) {
+ DBGC ( hvm, "HVM could not map MMIO space [%08lx,%08lx)\n",
+ ( hvm->mmio + hvm->mmio_offset ),
+ ( hvm->mmio + hvm->mmio_offset + len ) );
+ goto err_ioremap;
+ }
+ mmio_phys = virt_to_phys ( mmio );
+
+ /* Add to physical address space */
+ for ( i = 0 ; i < pages ; i++ ) {
+ add.domid = DOMID_SELF;
+ add.idx = i;
+ add.space = space;
+ add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
+ if ( ( xenrc = xenmem_add_to_physmap ( &hvm->xen, &add ) ) !=0){
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not add space %d idx %d at "
+ "[%08lx,%08lx): %s\n", space, i,
+ ( mmio_phys + ( i * PAGE_SIZE ) ),
+ ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
+ strerror ( rc ) );
+ goto err_add_to_physmap;
+ }
+ }
+
+ /* Update offset */
+ hvm->mmio_offset += len;
+
+ return mmio;
+
+ i = pages;
+ err_add_to_physmap:
+ for ( i-- ; ( signed int ) i >= 0 ; i-- ) {
+ remove.domid = DOMID_SELF;
+ add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
+ xenmem_remove_from_physmap ( &hvm->xen, &remove );
+ }
+ iounmap ( mmio );
+ err_ioremap:
+ err_no_space:
+ return NULL;
+}
+
+/**
+ * Unmap MMIO space
+ *
+ * @v hvm HVM device
+ * @v mmio MMIO space address
+ * @v pages Number of pages
+ */
+static void hvm_iounmap ( struct hvm_device *hvm, void *mmio,
+ unsigned int pages ) {
+ struct xen_remove_from_physmap remove;
+ physaddr_t mmio_phys = virt_to_phys ( mmio );
+ unsigned int i;
+ int xenrc;
+ int rc;
+
+ /* Unmap this space */
+ iounmap ( mmio );
+
+ /* Remove from physical address space */
+ for ( i = 0 ; i < pages ; i++ ) {
+ remove.domid = DOMID_SELF;
+ remove.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
+ if ( ( xenrc = xenmem_remove_from_physmap ( &hvm->xen,
+ &remove ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not remove space [%08lx,%08lx): "
+ "%s\n", ( mmio_phys + ( i * PAGE_SIZE ) ),
+ ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
+ strerror ( rc ) );
+ /* Nothing we can do about this */
+ }
+ }
+}
+
+/**
+ * Map shared info page
+ *
+ * @v hvm HVM device
+ * @ret rc Return status code
+ */
+static int hvm_map_shared_info ( struct hvm_device *hvm ) {
+ physaddr_t shared_info_phys;
+ int rc;
+
+ /* Map shared info page */
+ hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info, 1 );
+ if ( ! hvm->xen.shared ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ shared_info_phys = virt_to_phys ( hvm->xen.shared );
+ DBGC2 ( hvm, "HVM shared info page at [%#08lx,%#08lx)\n",
+ shared_info_phys, ( shared_info_phys + PAGE_SIZE ) );
+
+ /* Sanity check */
+ DBGC2 ( hvm, "HVM wallclock time is %d\n",
+ readl ( &hvm->xen.shared->wc_sec ) );
+
+ return 0;
+
+ hvm_iounmap ( hvm, hvm->xen.shared, 1 );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Unmap shared info page
+ *
+ * @v hvm HVM device
+ */
+static void hvm_unmap_shared_info ( struct hvm_device *hvm ) {
+
+ /* Unmap shared info page */
+ hvm_iounmap ( hvm, hvm->xen.shared, 1 );
+}
+
+/**
+ * Map grant table
+ *
+ * @v hvm HVM device
+ * @ret rc Return status code
+ */
+static int hvm_map_grant ( struct hvm_device *hvm ) {
+ struct gnttab_query_size size;
+ struct gnttab_set_version version;
+ physaddr_t grant_phys;
+ size_t len;
+ int xenrc;
+ int rc;
+
+ /* Get grant table size */
+ size.dom = DOMID_SELF;
+ if ( ( xenrc = xengrant_query_size ( &hvm->xen, &size ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not get grant table size: %s\n",
+ strerror ( rc ) );
+ goto err_query_size;
+ }
+ len = ( size.nr_frames * PAGE_SIZE );
+
+ /* Configure to use version 2 tables */
+ version.version = 2;
+ if ( ( xenrc = xengrant_set_version ( &hvm->xen, &version ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not set version 2 grant table: %s\n",
+ strerror ( rc ) );
+ goto err_set_version;
+ }
+ if ( version.version != 2 ) {
+ DBGC ( hvm, "HVM could not set version 2 grant table\n" );
+ rc = -ENOTTY;
+ goto err_set_version;
+ }
+
+ /* Map grant table */
+ hvm->xen.grant.table = hvm_ioremap ( hvm, XENMAPSPACE_grant_table,
+ size.nr_frames );
+ if ( ! hvm->xen.grant.table ) {
+ rc = -ENODEV;
+ goto err_ioremap;
+ }
+ grant_phys = virt_to_phys ( hvm->xen.grant.table );
+ DBGC2 ( hvm, "HVM mapped grant table at [%08lx,%08lx)\n",
+ grant_phys, ( grant_phys + len ) );
+ hvm->xen.grant.count = ( len / sizeof ( hvm->xen.grant.table[0] ) );
+
+ return 0;
+
+ hvm_iounmap ( hvm, hvm->xen.grant.table, size.nr_frames );
+ err_ioremap:
+ err_set_version:
+ err_query_size:
+ return rc;
+}
+
+/**
+ * Unmap grant table
+ *
+ * @v hvm HVM device
+ */
+static void hvm_unmap_grant ( struct hvm_device *hvm ) {
+ size_t len;
+
+ /* Unmap grant table */
+ len = ( hvm->xen.grant.count * sizeof ( hvm->xen.grant.table[0] ) );
+ hvm_iounmap ( hvm, hvm->xen.grant.table, ( len / PAGE_SIZE ) );
+}
+
+/**
+ * Map XenStore
+ *
+ * @v hvm HVM device
+ * @ret rc Return status code
+ */
+static int hvm_map_xenstore ( struct hvm_device *hvm ) {
+ uint64_t xenstore_evtchn;
+ uint64_t xenstore_pfn;
+ physaddr_t xenstore_phys;
+ char *name;
+ int xenrc;
+ int rc;
+
+ /* Get XenStore event channel */
+ if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_EVTCHN,
+ &xenstore_evtchn ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not get XenStore event channel: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ hvm->xen.store.port = xenstore_evtchn;
+
+ /* Get XenStore PFN */
+ if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_PFN,
+ &xenstore_pfn ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( hvm, "HVM could not get XenStore PFN: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ xenstore_phys = ( xenstore_pfn * PAGE_SIZE );
+
+ /* Map XenStore */
+ hvm->xen.store.intf = ioremap ( xenstore_phys, PAGE_SIZE );
+ if ( ! hvm->xen.store.intf ) {
+ DBGC ( hvm, "HVM could not map XenStore at [%08lx,%08lx)\n",
+ xenstore_phys, ( xenstore_phys + PAGE_SIZE ) );
+ return -ENODEV;
+ }
+ DBGC2 ( hvm, "HVM mapped XenStore at [%08lx,%08lx) with event port "
+ "%d\n", xenstore_phys, ( xenstore_phys + PAGE_SIZE ),
+ hvm->xen.store.port );
+
+ /* Check that XenStore is working */
+ if ( ( rc = xenstore_read ( &hvm->xen, &name, "name", NULL ) ) != 0 ) {
+ DBGC ( hvm, "HVM could not read domain name: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ DBGC2 ( hvm, "HVM running in domain \"%s\"\n", name );
+ free ( name );
+
+ return 0;
+}
+
+/**
+ * Unmap XenStore
+ *
+ * @v hvm HVM device
+ */
+static void hvm_unmap_xenstore ( struct hvm_device *hvm ) {
+
+ /* Unmap XenStore */
+ iounmap ( hvm->xen.store.intf );
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+static int hvm_probe ( struct pci_device *pci ) {
+ struct hvm_device *hvm;
+ int rc;
+
+ /* Allocate and initialise structure */
+ hvm = zalloc ( sizeof ( *hvm ) );
+ if ( ! hvm ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ hvm->mmio = pci_bar_start ( pci, HVM_MMIO_BAR );
+ hvm->mmio_len = pci_bar_size ( pci, HVM_MMIO_BAR );
+ DBGC2 ( hvm, "HVM has MMIO space [%08lx,%08lx)\n",
+ hvm->mmio, ( hvm->mmio + hvm->mmio_len ) );
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Attach to hypervisor */
+ if ( ( rc = hvm_cpuid_base ( hvm ) ) != 0 )
+ goto err_cpuid_base;
+ if ( ( rc = hvm_map_hypercall ( hvm ) ) != 0 )
+ goto err_map_hypercall;
+ if ( ( rc = hvm_map_shared_info ( hvm ) ) != 0 )
+ goto err_map_shared_info;
+ if ( ( rc = hvm_map_grant ( hvm ) ) != 0 )
+ goto err_map_grant;
+ if ( ( rc = hvm_map_xenstore ( hvm ) ) != 0 )
+ goto err_map_xenstore;
+
+ /* Probe Xen devices */
+ if ( ( rc = xenbus_probe ( &hvm->xen, &pci->dev ) ) != 0 ) {
+ DBGC ( hvm, "HVM could not probe Xen bus: %s\n",
+ strerror ( rc ) );
+ goto err_xenbus_probe;
+ }
+
+ pci_set_drvdata ( pci, hvm );
+ return 0;
+
+ xenbus_remove ( &hvm->xen, &pci->dev );
+ err_xenbus_probe:
+ hvm_unmap_xenstore ( hvm );
+ err_map_xenstore:
+ hvm_unmap_grant ( hvm );
+ err_map_grant:
+ hvm_unmap_shared_info ( hvm );
+ err_map_shared_info:
+ hvm_unmap_hypercall ( hvm );
+ err_map_hypercall:
+ err_cpuid_base:
+ free ( hvm );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void hvm_remove ( struct pci_device *pci ) {
+ struct hvm_device *hvm = pci_get_drvdata ( pci );
+
+ xenbus_remove ( &hvm->xen, &pci->dev );
+ hvm_unmap_xenstore ( hvm );
+ hvm_unmap_grant ( hvm );
+ hvm_unmap_shared_info ( hvm );
+ hvm_unmap_hypercall ( hvm );
+ free ( hvm );
+}
+
+/** PCI device IDs */
+static struct pci_device_id hvm_ids[] = {
+ PCI_ROM ( 0x5853, 0x0001, "hvm", "hvm", 0 ),
+};
+
+/** PCI driver */
+struct pci_driver hvm_driver __pci_driver = {
+ .ids = hvm_ids,
+ .id_count = ( sizeof ( hvm_ids ) / sizeof ( hvm_ids[0] ) ),
+ .probe = hvm_probe,
+ .remove = hvm_remove,
+};
diff --git a/src/arch/x86/drivers/xen/hvm.h b/src/arch/x86/drivers/xen/hvm.h
new file mode 100644
index 00000000..325d20d6
--- /dev/null
+++ b/src/arch/x86/drivers/xen/hvm.h
@@ -0,0 +1,75 @@
+#ifndef _HVM_H
+#define _HVM_H
+
+/** @file
+ *
+ * Xen HVM driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <ipxe/xen.h>
+#include <xen/hvm/hvm_op.h>
+#include <xen/hvm/params.h>
+
+/** Minimum CPUID base */
+#define HVM_CPUID_MIN 0x40000000UL
+
+/** Maximum CPUID base */
+#define HVM_CPUID_MAX 0x4000ff00UL
+
+/** Increment between CPUID bases */
+#define HVM_CPUID_STEP 0x00000100UL
+
+/** Magic signature */
+#define HVM_CPUID_MAGIC "XenVMMXenVMM"
+
+/** Get Xen version */
+#define HVM_CPUID_VERSION 1
+
+/** Get number of hypercall pages */
+#define HVM_CPUID_PAGES 2
+
+/** PCI MMIO BAR */
+#define HVM_MMIO_BAR PCI_BASE_ADDRESS_1
+
+/** A Xen HVM device */
+struct hvm_device {
+ /** Xen hypervisor */
+ struct xen_hypervisor xen;
+ /** CPUID base */
+ uint32_t cpuid_base;
+ /** Length of hypercall table */
+ size_t hypercall_len;
+ /** MMIO base address */
+ unsigned long mmio;
+ /** Current offset within MMIO address space */
+ size_t mmio_offset;
+ /** Length of MMIO address space */
+ size_t mmio_len;
+};
+
+/**
+ * Get HVM parameter value
+ *
+ * @v xen Xen hypervisor
+ * @v index Parameter index
+ * @v value Value to fill in
+ * @ret xenrc Xen status code
+ */
+static inline int xen_hvm_get_param ( struct xen_hypervisor *xen,
+ unsigned int index, uint64_t *value ) {
+ struct xen_hvm_param param;
+ int xenrc;
+
+ param.domid = DOMID_SELF;
+ param.index = index;
+ xenrc = xen_hypercall_2 ( xen, __HYPERVISOR_hvm_op, HVMOP_get_param,
+ virt_to_phys ( &param ) );
+ *value = param.value;
+ return xenrc;
+}
+
+#endif /* _HVM_H */
diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h
index acf8c3e3..62457562 100644
--- a/src/arch/x86/include/bits/errfile.h
+++ b/src/arch/x86/include/bits/errfile.h
@@ -45,6 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 )
+#define ERRFILE_hvm ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00020000 )
#define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 )
#define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 )
diff --git a/src/arch/x86/include/bits/xen.h b/src/arch/x86/include/bits/xen.h
new file mode 100644
index 00000000..dbccf1b7
--- /dev/null
+++ b/src/arch/x86/include/bits/xen.h
@@ -0,0 +1,164 @@
+#ifndef _BITS_XEN_H
+#define _BITS_XEN_H
+
+/** @file
+ *
+ * Xen interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/* Hypercall registers */
+#ifdef __x86_64__
+#define XEN_REG1 "rdi"
+#define XEN_REG2 "rsi"
+#define XEN_REG3 "rdx"
+#define XEN_REG4 "r10"
+#define XEN_REG5 "r8"
+#else
+#define XEN_REG1 "ebx"
+#define XEN_REG2 "ecx"
+#define XEN_REG3 "edx"
+#define XEN_REG4 "esi"
+#define XEN_REG5 "edi"
+#endif
+
+/** A hypercall entry point */
+struct xen_hypercall {
+ /** Code generated by hypervisor */
+ uint8_t code[32];
+} __attribute__ (( packed ));
+
+/**
+ * Issue hypercall with one argument
+ *
+ * @v xen Xen hypervisor
+ * @v hypercall Hypercall number
+ * @v arg1 First argument
+ * @ret retval Return value
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+xen_hypercall_1 ( struct xen_hypervisor *xen, unsigned int hypercall,
+ unsigned long arg1 ) {
+ register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
+ unsigned long retval;
+
+ __asm__ __volatile__ ( "call *%2"
+ : "=a" ( retval ), "+r" ( reg1 )
+ : "r" ( &xen->hypercall[hypercall] )
+ : XEN_REG2, XEN_REG3, XEN_REG4, XEN_REG5,
+ "memory" );
+ return retval;
+}
+
+/**
+ * Issue hypercall with two arguments
+ *
+ * @v xen Xen hypervisor
+ * @v hypercall Hypercall number
+ * @v arg1 First argument
+ * @v arg2 Second argument
+ * @ret retval Return value
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+xen_hypercall_2 ( struct xen_hypervisor *xen, unsigned int hypercall,
+ unsigned long arg1, unsigned long arg2 ) {
+ register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
+ register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
+ unsigned long retval;
+
+ __asm__ __volatile__ ( "call *%3"
+ : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 )
+ : "r" ( &xen->hypercall[hypercall] )
+ : XEN_REG3, XEN_REG4, XEN_REG5, "memory" );
+ return retval;
+}
+
+/**
+ * Issue hypercall with three arguments
+ *
+ * @v xen Xen hypervisor
+ * @v hypercall Hypercall number
+ * @v arg1 First argument
+ * @v arg2 Second argument
+ * @v arg3 Third argument
+ * @ret retval Return value
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+xen_hypercall_3 ( struct xen_hypervisor *xen, unsigned int hypercall,
+ unsigned long arg1, unsigned long arg2, unsigned long arg3 ) {
+ register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
+ register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
+ register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
+ unsigned long retval;
+
+ __asm__ __volatile__ ( "call *%4"
+ : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
+ "+r" ( reg3 )
+ : "r" ( &xen->hypercall[hypercall] )
+ : XEN_REG4, XEN_REG5, "memory" );
+ return retval;
+}
+
+/**
+ * Issue hypercall with four arguments
+ *
+ * @v xen Xen hypervisor
+ * @v hypercall Hypercall number
+ * @v arg1 First argument
+ * @v arg2 Second argument
+ * @v arg3 Third argument
+ * @v arg4 Fourth argument
+ * @ret retval Return value
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+xen_hypercall_4 ( struct xen_hypervisor *xen, unsigned int hypercall,
+ unsigned long arg1, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4 ) {
+ register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
+ register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
+ register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
+ register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
+ unsigned long retval;
+
+ __asm__ __volatile__ ( "call *%5"
+ : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
+ "+r" ( reg3 ), "+r" ( reg4 )
+ : "r" ( &xen->hypercall[hypercall] )
+ : XEN_REG5, "memory" );
+ return retval;
+}
+
+/**
+ * Issue hypercall with five arguments
+ *
+ * @v xen Xen hypervisor
+ * @v hypercall Hypercall number
+ * @v arg1 First argument
+ * @v arg2 Second argument
+ * @v arg3 Third argument
+ * @v arg4 Fourth argument
+ * @v arg5 Fifth argument
+ * @ret retval Return value
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall,
+ unsigned long arg1, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5 ) {
+ register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
+ register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
+ register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
+ register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
+ register unsigned long reg5 asm ( XEN_REG5 ) = arg5;
+ unsigned long retval;
+
+ __asm__ __volatile__ ( "call *%6"
+ : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
+ "+r" ( reg3 ), "+r" ( reg4 ), "+r" ( reg5 )
+ : "r" ( &xen->hypercall[hypercall] )
+ : "memory" );
+ return retval;
+}
+
+#endif /* _BITS_XEN_H */
diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h
index 89509734..7202a696 100644
--- a/src/include/ipxe/device.h
+++ b/src/include/ipxe/device.h
@@ -60,6 +60,9 @@ struct device_description {
/** EFI bus type */
#define BUS_TYPE_EFI 7
+/** Xen bus type */
+#define BUS_TYPE_XEN 8
+
/** A hardware device */
struct device {
/** Name */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index a8b3a8e4..3fa499dd 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -301,6 +301,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 )
#define ERRFILE_param_cmd ( ERRFILE_OTHER | 0x00400000 )
#define ERRFILE_deflate ( ERRFILE_OTHER | 0x00410000 )
+#define ERRFILE_xenstore ( ERRFILE_OTHER | 0x00420000 )
+#define ERRFILE_xenbus ( ERRFILE_OTHER | 0x00430000 )
+#define ERRFILE_xengrant ( ERRFILE_OTHER | 0x00440000 )
/** @} */
diff --git a/src/include/ipxe/xen.h b/src/include/ipxe/xen.h
new file mode 100644
index 00000000..546b0c34
--- /dev/null
+++ b/src/include/ipxe/xen.h
@@ -0,0 +1,73 @@
+#ifndef _IPXE_XEN_H
+#define _IPXE_XEN_H
+
+/** @file
+ *
+ * Xen interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/* Define Xen interface version before including any Xen header files */
+#define __XEN_INTERFACE_VERSION__ 0x00040400
+
+#include <stdint.h>
+#include <ipxe/uaccess.h>
+#include <xen/xen.h>
+#include <xen/event_channel.h>
+
+/* Memory barrier macros used by ring.h */
+#define xen_mb() mb()
+#define xen_rmb() rmb()
+#define xen_wmb() wmb()
+
+struct xen_hypercall;
+
+/** A Xen grant table */
+struct xen_grant {
+ /** Grant table entries */
+ union grant_entry_v2 *table;
+ /** Number of grant table entries (must be a power of two) */
+ unsigned int count;
+ /** Number of grant table entries in use */
+ unsigned int used;
+ /** Most recently used grant reference */
+ unsigned int ref;
+};
+
+/** A XenStore */
+struct xen_store {
+ /** XenStore domain interface */
+ struct xenstore_domain_interface *intf;
+ /** Event channel */
+ evtchn_port_t port;
+};
+
+/** A Xen hypervisor */
+struct xen_hypervisor {
+ /** Hypercall table */
+ struct xen_hypercall *hypercall;
+ /** Shared info page */
+ struct shared_info *shared;
+ /** Grant table */
+ struct xen_grant grant;
+ /** XenStore */
+ struct xen_store store;
+};
+
+#include <bits/xen.h>
+
+/**
+ * Convert a Xen status code to an iPXE status code
+ *
+ * @v xenrc Xen status code (negated)
+ * @ret rc iPXE status code (before negation)
+ *
+ * Xen status codes are defined in the file include/xen/errno.h in the
+ * Xen repository. They happen to match the Linux error codes, some
+ * of which can be found in our include/ipxe/errno/linux.h.
+ */
+#define EXEN( xenrc ) EPLATFORM ( EINFO_EPLATFORM, -(xenrc) )
+
+#endif /* _IPXE_XEN_H */
diff --git a/src/include/ipxe/xenbus.h b/src/include/ipxe/xenbus.h
new file mode 100644
index 00000000..2777226f
--- /dev/null
+++ b/src/include/ipxe/xenbus.h
@@ -0,0 +1,85 @@
+#ifndef _IPXE_XENBUS_H
+#define _IPXE_XENBUS_H
+
+/** @file
+ *
+ * Xen device bus
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/device.h>
+#include <ipxe/tables.h>
+#include <ipxe/xen.h>
+#include <xen/io/xenbus.h>
+
+/** A Xen device */
+struct xen_device {
+ /** Generic iPXE device */
+ struct device dev;
+ /** Xen hypervisor */
+ struct xen_hypervisor *xen;
+ /** XenStore key */
+ char *key;
+ /** Backend XenStore key */
+ char *backend;
+ /** Backend domain ID */
+ unsigned long backend_id;
+ /** Driver */
+ struct xen_driver *driver;
+ /** Driver-private data */
+ void *priv;
+};
+
+/** A Xen device driver */
+struct xen_driver {
+ /** Name */
+ const char *name;
+ /** Device type */
+ const char *type;
+ /** Probe device
+ *
+ * @v xendev Xen device
+ * @ret rc Return status code
+ */
+ int ( * probe ) ( struct xen_device *xendev );
+ /** Remove device
+ *
+ * @v xendev Xen device
+ */
+ void ( * remove ) ( struct xen_device *xendev );
+};
+
+/** Xen device driver table */
+#define XEN_DRIVERS __table ( struct xen_driver, "xen_drivers" )
+
+/** Declare a Xen device driver */
+#define __xen_driver __table_entry ( XEN_DRIVERS, 01 )
+
+/**
+ * Set Xen device driver-private data
+ *
+ * @v xendev Xen device
+ * @v priv Private data
+ */
+static inline void xen_set_drvdata ( struct xen_device *xendev, void *priv ) {
+ xendev->priv = priv;
+}
+
+/**
+ * Get Xen device driver-private data
+ *
+ * @v xendev Xen device
+ * @ret priv Private data
+ */
+static inline void * xen_get_drvdata ( struct xen_device *xendev ) {
+ return xendev->priv;
+}
+
+extern int xenbus_set_state ( struct xen_device *xendev, int state );
+extern int xenbus_backend_wait ( struct xen_device *xendev, int state );
+extern int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent );
+extern void xenbus_remove ( struct xen_hypervisor *xen, struct device *parent );
+
+#endif /* _IPXE_XENBUS_H */
diff --git a/src/include/ipxe/xenevent.h b/src/include/ipxe/xenevent.h
new file mode 100644
index 00000000..1dd6a0c0
--- /dev/null
+++ b/src/include/ipxe/xenevent.h
@@ -0,0 +1,59 @@
+#ifndef _IPXE_XENEVENT_H
+#define _IPXE_XENEVENT_H
+
+/** @file
+ *
+ * Xen events
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/xen.h>
+#include <xen/event_channel.h>
+
+/**
+ * Close event channel
+ *
+ * @v xen Xen hypervisor
+ * @v close Event descriptor
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xenevent_close ( struct xen_hypervisor *xen, struct evtchn_close *close ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op,
+ EVTCHNOP_close, virt_to_phys ( close ) );
+}
+
+/**
+ * Send event
+ *
+ * @v xen Xen hypervisor
+ * @v send Event descriptor
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xenevent_send ( struct xen_hypervisor *xen, struct evtchn_send *send ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op,
+ EVTCHNOP_send, virt_to_phys ( send ) );
+}
+
+/**
+ * Allocate an unbound event channel
+ *
+ * @v xen Xen hypervisor
+ * @v alloc_unbound Event descriptor
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xenevent_alloc_unbound ( struct xen_hypervisor *xen,
+ struct evtchn_alloc_unbound *alloc_unbound ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op,
+ EVTCHNOP_alloc_unbound,
+ virt_to_phys ( alloc_unbound ) );
+}
+
+#endif /* _IPXE_XENEVENT_H */
diff --git a/src/include/ipxe/xengrant.h b/src/include/ipxe/xengrant.h
new file mode 100644
index 00000000..776eb927
--- /dev/null
+++ b/src/include/ipxe/xengrant.h
@@ -0,0 +1,102 @@
+#ifndef _IPXE_XENGRANT_H
+#define _IPXE_XENGRANT_H
+
+/** @file
+ *
+ * Xen grant tables
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <ipxe/io.h>
+#include <ipxe/xen.h>
+#include <xen/grant_table.h>
+
+/**
+ * Query grant table size
+ *
+ * @v xen Xen hypervisor
+ * @v size Table size
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xengrant_query_size ( struct xen_hypervisor *xen,
+ struct gnttab_query_size *size ) {
+
+ return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op,
+ GNTTABOP_query_size,
+ virt_to_phys ( size ), 1 );
+}
+
+/**
+ * Set grant table version
+ *
+ * @v xen Xen hypervisor
+ * @v version Version
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xengrant_set_version ( struct xen_hypervisor *xen,
+ struct gnttab_set_version *version ) {
+
+ return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op,
+ GNTTABOP_set_version,
+ virt_to_phys ( version ), 1 );
+}
+
+/**
+ * Invalidate access to a page
+ *
+ * @v xen Xen hypervisor
+ * @v ref Grant reference
+ */
+static inline __attribute__ (( always_inline )) void
+xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) {
+ union grant_entry_v2 *entry = &xen->grant.table[ref];
+
+ /* Sanity check */
+ assert ( ( readw ( &entry->hdr.flags ) &
+ ( GTF_reading | GTF_writing ) ) == 0 );
+
+ /* This should apparently be done using a cmpxchg instruction.
+ * We omit this: partly in the interests of simplicity, but
+ * mainly since our control flow generally does not permit
+ * failure paths to themselves fail.
+ */
+ writew ( 0, &entry->hdr.flags );
+}
+
+/**
+ * Permit access to a page
+ *
+ * @v xen Xen hypervisor
+ * @v ref Grant reference
+ * @v domid Domain ID
+ * @v subflags Additional flags
+ * @v page Page start
+ */
+static inline __attribute__ (( always_inline )) void
+xengrant_permit_access ( struct xen_hypervisor *xen, grant_ref_t ref,
+ domid_t domid, unsigned int subflags, void *page ) {
+ union grant_entry_v2 *entry = &xen->grant.table[ref];
+ unsigned long frame = ( virt_to_phys ( page ) / PAGE_SIZE );
+
+ writew ( domid, &entry->full_page.hdr.domid );
+ if ( sizeof ( physaddr_t ) == sizeof ( uint64_t ) ) {
+ writeq ( frame, &entry->full_page.frame );
+ } else {
+ writel ( frame, &entry->full_page.frame );
+ }
+ wmb();
+ writew ( ( GTF_permit_access | subflags ), &entry->full_page.hdr.flags);
+ wmb();
+}
+
+extern int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
+ unsigned int count );
+extern void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
+ unsigned int count );
+
+#endif /* _IPXE_XENGRANT_H */
diff --git a/src/include/ipxe/xenmem.h b/src/include/ipxe/xenmem.h
new file mode 100644
index 00000000..9b9aeda9
--- /dev/null
+++ b/src/include/ipxe/xenmem.h
@@ -0,0 +1,46 @@
+#ifndef _IPXE_XENMEM_H
+#define _IPXE_XENMEM_H
+
+/** @file
+ *
+ * Xen memory operations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/xen.h>
+#include <xen/memory.h>
+
+/**
+ * Add page to physical address space
+ *
+ * @v xen Xen hypervisor
+ * @v add Page mapping descriptor
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xenmem_add_to_physmap ( struct xen_hypervisor *xen,
+ struct xen_add_to_physmap *add ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_memory_op,
+ XENMEM_add_to_physmap, virt_to_phys ( add ) );
+}
+
+/**
+ * Remove page from physical address space
+ *
+ * @v xen Xen hypervisor
+ * @v remove Page mapping descriptor
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xenmem_remove_from_physmap ( struct xen_hypervisor *xen,
+ struct xen_remove_from_physmap *remove ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_memory_op,
+ XENMEM_remove_from_physmap,
+ virt_to_phys ( remove ) );
+}
+
+#endif /* _IPXE_XENMEM_H */
diff --git a/src/include/ipxe/xenstore.h b/src/include/ipxe/xenstore.h
new file mode 100644
index 00000000..f25f1570
--- /dev/null
+++ b/src/include/ipxe/xenstore.h
@@ -0,0 +1,29 @@
+#ifndef _IPXE_XENSTORE_H
+#define _IPXE_XENSTORE_H
+
+/** @file
+ *
+ * XenStore interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/xen.h>
+
+extern __attribute__ (( sentinel )) int
+xenstore_read ( struct xen_hypervisor *xen, char **value, ... );
+extern __attribute__ (( sentinel )) int
+xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... );
+extern __attribute__ (( sentinel )) int
+xenstore_write ( struct xen_hypervisor *xen, const char *value, ... );
+extern __attribute__ (( sentinel )) int
+xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... );
+extern __attribute__ (( sentinel )) int
+xenstore_rm ( struct xen_hypervisor *xen, ... );
+extern __attribute__ (( sentinel )) int
+xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len,
+ ... );
+extern void xenstore_dump ( struct xen_hypervisor *xen, const char *key );
+
+#endif /* _IPXE_XENSTORE_H */
diff --git a/src/include/ipxe/xenver.h b/src/include/ipxe/xenver.h
new file mode 100644
index 00000000..5d678c5a
--- /dev/null
+++ b/src/include/ipxe/xenver.h
@@ -0,0 +1,44 @@
+#ifndef _IPXE_XENVER_H
+#define _IPXE_VENVER_H
+
+/** @file
+ *
+ * Xen version
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/xen.h>
+#include <xen/version.h>
+
+/**
+ * Get Xen version
+ *
+ * @v xen Xen hypervisor
+ * @ret version Version (major.minor: 16 bits each)
+ */
+static inline __attribute__ (( always_inline )) uint32
+xenver_version ( struct xen_hypervisor *xen ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_xen_version,
+ XENVER_version, 0 );
+}
+
+/**
+ * Get Xen extra version string
+ *
+ * @v xen Xen hypervisor
+ * @v extraversion Extra version string to fill in
+ * @ret xenrc Xen status code
+ */
+static inline __attribute__ (( always_inline )) int
+xenver_extraversion ( struct xen_hypervisor *xen,
+ xen_extraversion_t *extraversion ) {
+
+ return xen_hypercall_2 ( xen, __HYPERVISOR_xen_version,
+ XENVER_extraversion,
+ virt_to_phys ( extraversion ) );
+}
+
+#endif /* _IPXE_XENVER_H */
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 );
+ }
+}