diff options
author | Michael Brown | 2014-07-29 00:38:30 +0200 |
---|---|---|
committer | Michael Brown | 2014-07-29 16:57:44 +0200 |
commit | 036af27a4523c4e15e28d30a1513a3f6d9671774 (patch) | |
tree | bf284637f91e2258906d736f07a54b1f98dfd68d /src | |
parent | [xen] Import selected public headers (diff) | |
download | ipxe-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/Makefile | 1 | ||||
-rw-r--r-- | src/arch/x86/Makefile | 1 | ||||
-rw-r--r-- | src/arch/x86/drivers/xen/hvm.c | 521 | ||||
-rw-r--r-- | src/arch/x86/drivers/xen/hvm.h | 75 | ||||
-rw-r--r-- | src/arch/x86/include/bits/errfile.h | 1 | ||||
-rw-r--r-- | src/arch/x86/include/bits/xen.h | 164 | ||||
-rw-r--r-- | src/include/ipxe/device.h | 3 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 3 | ||||
-rw-r--r-- | src/include/ipxe/xen.h | 73 | ||||
-rw-r--r-- | src/include/ipxe/xenbus.h | 85 | ||||
-rw-r--r-- | src/include/ipxe/xenevent.h | 59 | ||||
-rw-r--r-- | src/include/ipxe/xengrant.h | 102 | ||||
-rw-r--r-- | src/include/ipxe/xenmem.h | 46 | ||||
-rw-r--r-- | src/include/ipxe/xenstore.h | 29 | ||||
-rw-r--r-- | src/include/ipxe/xenver.h | 44 | ||||
-rw-r--r-- | src/interface/xen/xenbus.c | 393 | ||||
-rw-r--r-- | src/interface/xen/xengrant.c | 125 | ||||
-rw-r--r-- | src/interface/xen/xenstore.c | 547 |
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 ( ¶m ) ); + *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 ); + } +} |