mirror of
https://github.com/ipxe/ipxe
synced 2025-12-22 21:11:03 +03:00
[xen] Add basic support for PV-HVM domains
Add basic support for Xen PV-HVM domains (detected via the Xen platform PCI device with IDs 5853:0001), including support for accessing configuration via XenStore and enumerating devices via XenBus. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -84,6 +84,7 @@ SRCDIRS += drivers/bitbash
|
|||||||
SRCDIRS += drivers/infiniband
|
SRCDIRS += drivers/infiniband
|
||||||
SRCDIRS += interface/pxe interface/efi interface/smbios
|
SRCDIRS += interface/pxe interface/efi interface/smbios
|
||||||
SRCDIRS += interface/bofm
|
SRCDIRS += interface/bofm
|
||||||
|
SRCDIRS += interface/xen
|
||||||
SRCDIRS += tests
|
SRCDIRS += tests
|
||||||
SRCDIRS += crypto crypto/axtls crypto/matrixssl
|
SRCDIRS += crypto crypto/axtls crypto/matrixssl
|
||||||
SRCDIRS += hci hci/commands hci/tui
|
SRCDIRS += hci hci/commands hci/tui
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ SRCDIRS += arch/x86/core
|
|||||||
SRCDIRS += arch/x86/interface/efi
|
SRCDIRS += arch/x86/interface/efi
|
||||||
SRCDIRS += arch/x86/prefix
|
SRCDIRS += arch/x86/prefix
|
||||||
SRCDIRS += arch/x86/hci/commands
|
SRCDIRS += arch/x86/hci/commands
|
||||||
|
SRCDIRS += arch/x86/drivers/xen
|
||||||
|
|
||||||
# breaks building some of the linux-related objects
|
# breaks building some of the linux-related objects
|
||||||
CFLAGS += -Ulinux
|
CFLAGS += -Ulinux
|
||||||
|
|||||||
521
src/arch/x86/drivers/xen/hvm.c
Normal file
521
src/arch/x86/drivers/xen/hvm.c
Normal file
@@ -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,
|
||||||
|
};
|
||||||
75
src/arch/x86/drivers/xen/hvm.h
Normal file
75
src/arch/x86/drivers/xen/hvm.h
Normal file
@@ -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 */
|
||||||
@@ -45,6 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
|
|
||||||
#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 )
|
#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 )
|
||||||
#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 )
|
#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_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 )
|
||||||
#define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 )
|
#define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 )
|
||||||
|
|||||||
164
src/arch/x86/include/bits/xen.h
Normal file
164
src/arch/x86/include/bits/xen.h
Normal file
@@ -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 */
|
||||||
@@ -60,6 +60,9 @@ struct device_description {
|
|||||||
/** EFI bus type */
|
/** EFI bus type */
|
||||||
#define BUS_TYPE_EFI 7
|
#define BUS_TYPE_EFI 7
|
||||||
|
|
||||||
|
/** Xen bus type */
|
||||||
|
#define BUS_TYPE_XEN 8
|
||||||
|
|
||||||
/** A hardware device */
|
/** A hardware device */
|
||||||
struct device {
|
struct device {
|
||||||
/** Name */
|
/** Name */
|
||||||
|
|||||||
@@ -301,6 +301,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
#define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 )
|
#define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 )
|
||||||
#define ERRFILE_param_cmd ( ERRFILE_OTHER | 0x00400000 )
|
#define ERRFILE_param_cmd ( ERRFILE_OTHER | 0x00400000 )
|
||||||
#define ERRFILE_deflate ( ERRFILE_OTHER | 0x00410000 )
|
#define ERRFILE_deflate ( ERRFILE_OTHER | 0x00410000 )
|
||||||
|
#define ERRFILE_xenstore ( ERRFILE_OTHER | 0x00420000 )
|
||||||
|
#define ERRFILE_xenbus ( ERRFILE_OTHER | 0x00430000 )
|
||||||
|
#define ERRFILE_xengrant ( ERRFILE_OTHER | 0x00440000 )
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|||||||
73
src/include/ipxe/xen.h
Normal file
73
src/include/ipxe/xen.h
Normal file
@@ -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 */
|
||||||
85
src/include/ipxe/xenbus.h
Normal file
85
src/include/ipxe/xenbus.h
Normal file
@@ -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 */
|
||||||
59
src/include/ipxe/xenevent.h
Normal file
59
src/include/ipxe/xenevent.h
Normal file
@@ -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 */
|
||||||
102
src/include/ipxe/xengrant.h
Normal file
102
src/include/ipxe/xengrant.h
Normal file
@@ -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 */
|
||||||
46
src/include/ipxe/xenmem.h
Normal file
46
src/include/ipxe/xenmem.h
Normal file
@@ -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 */
|
||||||
29
src/include/ipxe/xenstore.h
Normal file
29
src/include/ipxe/xenstore.h
Normal file
@@ -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 */
|
||||||
44
src/include/ipxe/xenver.h
Normal file
44
src/include/ipxe/xenver.h
Normal file
@@ -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 */
|
||||||
393
src/interface/xen/xenbus.c
Normal file
393
src/interface/xen/xenbus.c
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ipxe/malloc.h>
|
||||||
|
#include <ipxe/device.h>
|
||||||
|
#include <ipxe/timer.h>
|
||||||
|
#include <ipxe/nap.h>
|
||||||
|
#include <ipxe/xen.h>
|
||||||
|
#include <ipxe/xenstore.h>
|
||||||
|
#include <ipxe/xenbus.h>
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* Xen device bus
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Disambiguate the various error causes */
|
||||||
|
#define ETIMEDOUT_UNKNOWN \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_UNKNOWN )
|
||||||
|
#define EINFO_ETIMEDOUT_UNKNOWN \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateUnknown, \
|
||||||
|
"Unknown" )
|
||||||
|
#define ETIMEDOUT_INITIALISING \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_INITIALISING )
|
||||||
|
#define EINFO_ETIMEDOUT_INITIALISING \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialising, \
|
||||||
|
"Initialising" )
|
||||||
|
#define ETIMEDOUT_INITWAIT \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_INITWAIT )
|
||||||
|
#define EINFO_ETIMEDOUT_INITWAIT \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitWait, \
|
||||||
|
"InitWait" )
|
||||||
|
#define ETIMEDOUT_INITIALISED \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_INITIALISED )
|
||||||
|
#define EINFO_ETIMEDOUT_INITIALISED \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialised, \
|
||||||
|
"Initialised" )
|
||||||
|
#define ETIMEDOUT_CONNECTED \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_CONNECTED )
|
||||||
|
#define EINFO_ETIMEDOUT_CONNECTED \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateConnected, \
|
||||||
|
"Connected" )
|
||||||
|
#define ETIMEDOUT_CLOSING \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_CLOSING )
|
||||||
|
#define EINFO_ETIMEDOUT_CLOSING \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosing, \
|
||||||
|
"Closing" )
|
||||||
|
#define ETIMEDOUT_CLOSED \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_CLOSED )
|
||||||
|
#define EINFO_ETIMEDOUT_CLOSED \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosed, \
|
||||||
|
"Closed" )
|
||||||
|
#define ETIMEDOUT_RECONFIGURING \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_RECONFIGURING )
|
||||||
|
#define EINFO_ETIMEDOUT_RECONFIGURING \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfiguring, \
|
||||||
|
"Reconfiguring" )
|
||||||
|
#define ETIMEDOUT_RECONFIGURED \
|
||||||
|
__einfo_error ( EINFO_ETIMEDOUT_RECONFIGURED )
|
||||||
|
#define EINFO_ETIMEDOUT_RECONFIGURED \
|
||||||
|
__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfigured, \
|
||||||
|
"Reconfigured" )
|
||||||
|
#define ETIMEDOUT_STATE( state ) \
|
||||||
|
EUNIQ ( EINFO_ETIMEDOUT, (state), ETIMEDOUT_UNKNOWN, \
|
||||||
|
ETIMEDOUT_INITIALISING, ETIMEDOUT_INITWAIT, \
|
||||||
|
ETIMEDOUT_INITIALISED, ETIMEDOUT_CONNECTED, \
|
||||||
|
ETIMEDOUT_CLOSING, ETIMEDOUT_CLOSED, \
|
||||||
|
ETIMEDOUT_RECONFIGURING, ETIMEDOUT_RECONFIGURED )
|
||||||
|
|
||||||
|
/** Maximum time to wait for backend to reach a given state, in ticks */
|
||||||
|
#define XENBUS_BACKEND_TIMEOUT ( 5 * TICKS_PER_SEC )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set device state
|
||||||
|
*
|
||||||
|
* @v xendev Xen device
|
||||||
|
* @v state New state
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
int xenbus_set_state ( struct xen_device *xendev, int state ) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Attempt to set state */
|
||||||
|
if ( ( rc = xenstore_write_num ( xendev->xen, state, xendev->key,
|
||||||
|
"state", NULL ) ) != 0 ) {
|
||||||
|
DBGC ( xendev, "XENBUS %s could not set state=\"%d\": %s\n",
|
||||||
|
xendev->key, state, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get backend state
|
||||||
|
*
|
||||||
|
* @v xendev Xen device
|
||||||
|
* @ret state Backend state, or negative error
|
||||||
|
*/
|
||||||
|
static int xenbus_backend_state ( struct xen_device *xendev ) {
|
||||||
|
unsigned long state;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Attempt to get backend state */
|
||||||
|
if ( ( rc = xenstore_read_num ( xendev->xen, &state, xendev->backend,
|
||||||
|
"state", NULL ) ) != 0 ) {
|
||||||
|
DBGC ( xendev, "XENBUS %s could not read %s/state: %s\n",
|
||||||
|
xendev->key, xendev->backend, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for backend to reach a given state
|
||||||
|
*
|
||||||
|
* @v xendev Xen device
|
||||||
|
* @v state Desired backend state
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
int xenbus_backend_wait ( struct xen_device *xendev, int state ) {
|
||||||
|
unsigned long started = currticks();
|
||||||
|
unsigned long elapsed;
|
||||||
|
unsigned int attempts = 0;
|
||||||
|
int current_state;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Wait for backend to reach this state */
|
||||||
|
do {
|
||||||
|
|
||||||
|
/* Get current backend state */
|
||||||
|
current_state = xenbus_backend_state ( xendev );
|
||||||
|
if ( current_state < 0 ) {
|
||||||
|
rc = current_state;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if ( current_state == state )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Allow time for backend to react */
|
||||||
|
cpu_nap();
|
||||||
|
|
||||||
|
/* XenStore is a very slow interface; any fixed delay
|
||||||
|
* time would be dwarfed by the XenStore access time.
|
||||||
|
* We therefore use wall clock to time out this
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
elapsed = ( currticks() - started );
|
||||||
|
attempts++;
|
||||||
|
|
||||||
|
} while ( elapsed < XENBUS_BACKEND_TIMEOUT );
|
||||||
|
|
||||||
|
/* Construct status code from current backend state */
|
||||||
|
rc = -ETIMEDOUT_STATE ( current_state );
|
||||||
|
DBGC ( xendev, "XENBUS %s timed out after %d attempts waiting for "
|
||||||
|
"%s/state=\"%d\": %s\n", xendev->key, attempts, xendev->backend,
|
||||||
|
state, strerror ( rc ) );
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find driver for Xen device
|
||||||
|
*
|
||||||
|
* @v type Device type
|
||||||
|
* @ret driver Driver, or NULL
|
||||||
|
*/
|
||||||
|
static struct xen_driver * xenbus_find_driver ( const char *type ) {
|
||||||
|
struct xen_driver *xendrv;
|
||||||
|
|
||||||
|
for_each_table_entry ( xendrv, XEN_DRIVERS ) {
|
||||||
|
if ( strcmp ( xendrv->type, type ) == 0 )
|
||||||
|
return xendrv;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Probe Xen device
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v parent Parent device
|
||||||
|
* @v type Device type
|
||||||
|
* @v instance Device instance
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int xenbus_probe_device ( struct xen_hypervisor *xen,
|
||||||
|
struct device *parent, const char *type,
|
||||||
|
const char *instance ) {
|
||||||
|
struct xen_device *xendev;
|
||||||
|
size_t key_len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Allocate and initialise structure */
|
||||||
|
key_len = ( 7 /* "device/" */ + strlen ( type ) + 1 /* "/" */ +
|
||||||
|
strlen ( instance ) + 1 /* NUL */ );
|
||||||
|
xendev = zalloc ( sizeof ( *xendev ) + key_len );
|
||||||
|
if ( ! xendev ) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_alloc;
|
||||||
|
}
|
||||||
|
snprintf ( xendev->dev.name, sizeof ( xendev->dev.name ), "%s/%s",
|
||||||
|
type, instance );
|
||||||
|
xendev->dev.desc.bus_type = BUS_TYPE_XEN;
|
||||||
|
INIT_LIST_HEAD ( &xendev->dev.children );
|
||||||
|
list_add_tail ( &xendev->dev.siblings, &parent->children );
|
||||||
|
xendev->dev.parent = parent;
|
||||||
|
xendev->xen = xen;
|
||||||
|
xendev->key = ( ( void * ) ( xendev + 1 ) );
|
||||||
|
snprintf ( xendev->key, key_len, "device/%s/%s", type, instance );
|
||||||
|
|
||||||
|
/* Read backend key */
|
||||||
|
if ( ( rc = xenstore_read ( xen, &xendev->backend, xendev->key,
|
||||||
|
"backend", NULL ) ) != 0 ) {
|
||||||
|
DBGC ( xendev, "XENBUS %s could not read backend: %s\n",
|
||||||
|
xendev->key, strerror ( rc ) );
|
||||||
|
goto err_read_backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read backend domain ID */
|
||||||
|
if ( ( rc = xenstore_read_num ( xen, &xendev->backend_id, xendev->key,
|
||||||
|
"backend-id", NULL ) ) != 0 ) {
|
||||||
|
DBGC ( xendev, "XENBUS %s could not read backend-id: %s\n",
|
||||||
|
xendev->key, strerror ( rc ) );
|
||||||
|
goto err_read_backend_id;
|
||||||
|
}
|
||||||
|
DBGC ( xendev, "XENBUS %s backend=\"%s\" in domain %ld\n",
|
||||||
|
xendev->key, xendev->backend, xendev->backend_id );
|
||||||
|
|
||||||
|
/* Look for a driver */
|
||||||
|
xendev->driver = xenbus_find_driver ( type );
|
||||||
|
if ( ! xendev->driver ) {
|
||||||
|
DBGC ( xendev, "XENBUS %s has no driver\n", xendev->key );
|
||||||
|
/* Not a fatal error */
|
||||||
|
rc = 0;
|
||||||
|
goto err_no_driver;
|
||||||
|
}
|
||||||
|
xendev->dev.driver_name = xendev->driver->name;
|
||||||
|
DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key,
|
||||||
|
xendev->driver->name );
|
||||||
|
|
||||||
|
/* Probe driver */
|
||||||
|
if ( ( rc = xendev->driver->probe ( xendev ) ) != 0 ) {
|
||||||
|
DBGC ( xendev, "XENBUS could not probe %s: %s\n",
|
||||||
|
xendev->key, strerror ( rc ) );
|
||||||
|
goto err_probe;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
xendev->driver->remove ( xendev );
|
||||||
|
err_probe:
|
||||||
|
err_no_driver:
|
||||||
|
err_read_backend_id:
|
||||||
|
free ( xendev->backend );
|
||||||
|
err_read_backend:
|
||||||
|
list_del ( &xendev->dev.siblings );
|
||||||
|
free ( xendev );
|
||||||
|
err_alloc:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove Xen device
|
||||||
|
*
|
||||||
|
* @v xendev Xen device
|
||||||
|
*/
|
||||||
|
static void xenbus_remove_device ( struct xen_device *xendev ) {
|
||||||
|
|
||||||
|
/* Remove device */
|
||||||
|
xendev->driver->remove ( xendev );
|
||||||
|
free ( xendev->backend );
|
||||||
|
list_del ( &xendev->dev.siblings );
|
||||||
|
free ( xendev );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Probe Xen devices of a given type
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v parent Parent device
|
||||||
|
* @v type Device type
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int xenbus_probe_type ( struct xen_hypervisor *xen,
|
||||||
|
struct device *parent, const char *type ) {
|
||||||
|
char *children;
|
||||||
|
char *child;
|
||||||
|
size_t len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Get children of this key */
|
||||||
|
if ( ( rc = xenstore_directory ( xen, &children, &len, "device",
|
||||||
|
type, NULL ) ) != 0 ) {
|
||||||
|
DBGC ( xen, "XENBUS could not list \"%s\" devices: %s\n",
|
||||||
|
type, strerror ( rc ) );
|
||||||
|
goto err_directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe each child */
|
||||||
|
for ( child = children ; child < ( children + len ) ;
|
||||||
|
child += ( strlen ( child ) + 1 /* NUL */ ) ) {
|
||||||
|
if ( ( rc = xenbus_probe_device ( xen, parent, type,
|
||||||
|
child ) ) != 0 )
|
||||||
|
goto err_probe_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
free ( children );
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_probe_device:
|
||||||
|
free ( children );
|
||||||
|
err_directory:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Probe Xen bus
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v parent Parent device
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent ) {
|
||||||
|
char *types;
|
||||||
|
char *type;
|
||||||
|
size_t len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Get children of "device" key */
|
||||||
|
if ( ( rc = xenstore_directory ( xen, &types, &len, "device",
|
||||||
|
NULL ) ) != 0 ) {
|
||||||
|
DBGC ( xen, "XENBUS could not list device types: %s\n",
|
||||||
|
strerror ( rc ) );
|
||||||
|
goto err_directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe each child type */
|
||||||
|
for ( type = types ; type < ( types + len ) ;
|
||||||
|
type += ( strlen ( type ) + 1 /* NUL */ ) ) {
|
||||||
|
if ( ( rc = xenbus_probe_type ( xen, parent, type ) ) != 0 )
|
||||||
|
goto err_probe_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
free ( types );
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
xenbus_remove ( xen, parent );
|
||||||
|
err_probe_type:
|
||||||
|
free ( types );
|
||||||
|
err_directory:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove Xen bus
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v parent Parent device
|
||||||
|
*/
|
||||||
|
void xenbus_remove ( struct xen_hypervisor *xen __unused,
|
||||||
|
struct device *parent ) {
|
||||||
|
struct xen_device *xendev;
|
||||||
|
struct xen_device *tmp;
|
||||||
|
|
||||||
|
/* Remove devices */
|
||||||
|
list_for_each_entry_safe ( xendev, tmp, &parent->children,
|
||||||
|
dev.siblings ) {
|
||||||
|
xenbus_remove_device ( xendev );
|
||||||
|
}
|
||||||
|
}
|
||||||
125
src/interface/xen/xengrant.c
Normal file
125
src/interface/xen/xengrant.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ipxe/io.h>
|
||||||
|
#include <ipxe/xen.h>
|
||||||
|
#include <ipxe/xengrant.h>
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* Xen grant tables
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate grant references
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v refs Grant references to fill in
|
||||||
|
* @v count Number of references
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
|
||||||
|
unsigned int count ) {
|
||||||
|
union grant_entry_v2 *entry;
|
||||||
|
unsigned int mask = ( xen->grant.count - 1 );
|
||||||
|
unsigned int check = 0;
|
||||||
|
unsigned int avail;
|
||||||
|
unsigned int ref;
|
||||||
|
|
||||||
|
/* Fail unless we have enough references available */
|
||||||
|
avail = ( xen->grant.count - xen->grant.used -
|
||||||
|
GNTTAB_NR_RESERVED_ENTRIES );
|
||||||
|
if ( avail < count ) {
|
||||||
|
DBGC ( xen, "XENGRANT cannot allocate %d references (only %d "
|
||||||
|
"of %d available)\n", count, avail, xen->grant.count );
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
DBGC ( xen, "XENGRANT allocating %d references (from %d of %d "
|
||||||
|
"available)\n", count, avail, xen->grant.count );
|
||||||
|
|
||||||
|
/* Update number of references used */
|
||||||
|
xen->grant.used += count;
|
||||||
|
|
||||||
|
/* Find unused references */
|
||||||
|
for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) {
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
assert ( check++ < xen->grant.count );
|
||||||
|
|
||||||
|
/* Skip reserved references */
|
||||||
|
if ( ref < GNTTAB_NR_RESERVED_ENTRIES )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Skip in-use references */
|
||||||
|
entry = &xen->grant.table[ref];
|
||||||
|
if ( readw ( &entry->hdr.flags ) & GTF_type_mask )
|
||||||
|
continue;
|
||||||
|
if ( readw ( &entry->hdr.domid ) == DOMID_SELF )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Mark reference as in-use. We leave the flags as
|
||||||
|
* empty (to avoid creating a valid grant table entry)
|
||||||
|
* and set the domid to DOMID_SELF.
|
||||||
|
*/
|
||||||
|
writew ( DOMID_SELF, &entry->hdr.domid );
|
||||||
|
DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref );
|
||||||
|
|
||||||
|
/* Record reference */
|
||||||
|
refs[--count] = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update cursor */
|
||||||
|
xen->grant.ref = ref;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free grant references
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v refs Grant references
|
||||||
|
* @v count Number of references
|
||||||
|
*/
|
||||||
|
void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
|
||||||
|
unsigned int count ) {
|
||||||
|
union grant_entry_v2 *entry;
|
||||||
|
unsigned int ref;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Free references */
|
||||||
|
for ( i = 0 ; i < count ; i++ ) {
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
ref = refs[i];
|
||||||
|
assert ( ref < xen->grant.count );
|
||||||
|
|
||||||
|
/* Mark reference as unused */
|
||||||
|
entry = &xen->grant.table[ref];
|
||||||
|
writew ( 0, &entry->hdr.flags );
|
||||||
|
writew ( 0, &entry->hdr.domid );
|
||||||
|
DBGC2 ( xen, "XENGRANT freed ref %d\n", ref );
|
||||||
|
}
|
||||||
|
}
|
||||||
547
src/interface/xen/xenstore.c
Normal file
547
src/interface/xen/xenstore.c
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ipxe/io.h>
|
||||||
|
#include <ipxe/nap.h>
|
||||||
|
#include <ipxe/malloc.h>
|
||||||
|
#include <ipxe/xen.h>
|
||||||
|
#include <ipxe/xenevent.h>
|
||||||
|
#include <ipxe/xenstore.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xs_wire.h attempts to define a static error table xsd_errors, which
|
||||||
|
* interacts badly with the dynamically generated error numbers used
|
||||||
|
* by iPXE. Prevent this table from being constructed by including
|
||||||
|
* errno.h only after including xs_wire.h.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <xen/io/xs_wire.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* XenStore interface
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Request identifier */
|
||||||
|
static uint32_t xenstore_req_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send XenStore request raw data
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v data Data buffer
|
||||||
|
* @v len Length of data
|
||||||
|
*/
|
||||||
|
static void xenstore_send ( struct xen_hypervisor *xen, const void *data,
|
||||||
|
size_t len ) {
|
||||||
|
struct xenstore_domain_interface *intf = xen->store.intf;
|
||||||
|
XENSTORE_RING_IDX prod = readl ( &intf->req_prod );
|
||||||
|
XENSTORE_RING_IDX cons;
|
||||||
|
XENSTORE_RING_IDX idx;
|
||||||
|
const char *bytes = data;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t fill;
|
||||||
|
|
||||||
|
DBGCP ( intf, "XENSTORE raw request:\n" );
|
||||||
|
DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len );
|
||||||
|
|
||||||
|
/* Write one byte at a time */
|
||||||
|
while ( offset < len ) {
|
||||||
|
|
||||||
|
/* Wait for space to become available */
|
||||||
|
while ( 1 ) {
|
||||||
|
cons = readl ( &intf->req_cons );
|
||||||
|
fill = ( prod - cons );
|
||||||
|
if ( fill < XENSTORE_RING_SIZE )
|
||||||
|
break;
|
||||||
|
DBGC2 ( xen, "." );
|
||||||
|
cpu_nap();
|
||||||
|
rmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write byte */
|
||||||
|
idx = MASK_XENSTORE_IDX ( prod++ );
|
||||||
|
writeb ( bytes[offset++], &intf->req[idx] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update producer counter */
|
||||||
|
wmb();
|
||||||
|
writel ( prod, &intf->req_prod );
|
||||||
|
wmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send XenStore request string (excluding terminating NUL)
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v string String
|
||||||
|
*/
|
||||||
|
static void xenstore_send_string ( struct xen_hypervisor *xen,
|
||||||
|
const char *string ) {
|
||||||
|
|
||||||
|
xenstore_send ( xen, string, strlen ( string ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive XenStore response raw data
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v data Data buffer, or NULL to discard data
|
||||||
|
* @v len Length of data
|
||||||
|
*/
|
||||||
|
static void xenstore_recv ( struct xen_hypervisor *xen, void *data,
|
||||||
|
size_t len ) {
|
||||||
|
struct xenstore_domain_interface *intf = xen->store.intf;
|
||||||
|
XENSTORE_RING_IDX cons = readl ( &intf->rsp_cons );
|
||||||
|
XENSTORE_RING_IDX prod;
|
||||||
|
XENSTORE_RING_IDX idx;
|
||||||
|
char *bytes = data;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t fill;
|
||||||
|
|
||||||
|
DBGCP ( intf, "XENSTORE raw response:\n" );
|
||||||
|
|
||||||
|
/* Read one byte at a time */
|
||||||
|
while ( offset < len ) {
|
||||||
|
|
||||||
|
/* Wait for data to be ready */
|
||||||
|
while ( 1 ) {
|
||||||
|
prod = readl ( &intf->rsp_prod );
|
||||||
|
fill = ( prod - cons );
|
||||||
|
if ( fill > 0 )
|
||||||
|
break;
|
||||||
|
DBGC2 ( xen, "." );
|
||||||
|
cpu_nap();
|
||||||
|
rmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read byte */
|
||||||
|
idx = MASK_XENSTORE_IDX ( cons++ );
|
||||||
|
if ( data )
|
||||||
|
bytes[offset++] = readb ( &intf->rsp[idx] );
|
||||||
|
}
|
||||||
|
if ( data )
|
||||||
|
DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( cons - len ), data, len );
|
||||||
|
|
||||||
|
/* Update consumer counter */
|
||||||
|
writel ( cons, &intf->rsp_cons );
|
||||||
|
wmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send XenStore request
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v type Message type
|
||||||
|
* @v req_id Request ID
|
||||||
|
* @v value Value, or NULL to omit
|
||||||
|
* @v key Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int xenstore_request ( struct xen_hypervisor *xen,
|
||||||
|
enum xsd_sockmsg_type type, uint32_t req_id,
|
||||||
|
const char *value, va_list key ) {
|
||||||
|
struct xsd_sockmsg msg;
|
||||||
|
struct evtchn_send event;
|
||||||
|
const char *string;
|
||||||
|
va_list tmp;
|
||||||
|
int xenrc;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Construct message header */
|
||||||
|
msg.type = type;
|
||||||
|
msg.req_id = req_id;
|
||||||
|
msg.tx_id = 0;
|
||||||
|
msg.len = 0;
|
||||||
|
DBGC2 ( xen, "XENSTORE request ID %d type %d ", req_id, type );
|
||||||
|
|
||||||
|
/* Calculate total length */
|
||||||
|
va_copy ( tmp, key );
|
||||||
|
while ( ( string = va_arg ( tmp, const char * ) ) != NULL ) {
|
||||||
|
DBGC2 ( xen, "%s%s", ( msg.len ? "/" : "" ), string );
|
||||||
|
msg.len += ( strlen ( string ) + 1 /* '/' or NUL */ );
|
||||||
|
}
|
||||||
|
va_end ( tmp );
|
||||||
|
if ( value ) {
|
||||||
|
DBGC2 ( xen, " = \"%s\"", value );
|
||||||
|
msg.len += strlen ( value );
|
||||||
|
}
|
||||||
|
DBGC2 ( xen, "\n" );
|
||||||
|
|
||||||
|
/* Send message */
|
||||||
|
xenstore_send ( xen, &msg, sizeof ( msg ) );
|
||||||
|
string = va_arg ( key, const char * );
|
||||||
|
assert ( string != NULL );
|
||||||
|
xenstore_send_string ( xen, string );
|
||||||
|
while ( ( string = va_arg ( key, const char * ) ) != NULL ) {
|
||||||
|
xenstore_send_string ( xen, "/" );
|
||||||
|
xenstore_send_string ( xen, string );
|
||||||
|
}
|
||||||
|
xenstore_send ( xen, "", 1 ); /* Separating NUL */
|
||||||
|
if ( value )
|
||||||
|
xenstore_send_string ( xen, value );
|
||||||
|
|
||||||
|
/* Notify the back end */
|
||||||
|
event.port = xen->store.port;
|
||||||
|
if ( ( xenrc = xenevent_send ( xen, &event ) ) != 0 ) {
|
||||||
|
rc = -EXEN ( xenrc );
|
||||||
|
DBGC ( xen, "XENSTORE could not notify back end: %s\n",
|
||||||
|
strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive XenStore response
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v req_id Request ID
|
||||||
|
* @v value Value to fill in
|
||||||
|
* @v len Length to fill in
|
||||||
|
* @ret rc Return status code
|
||||||
|
*
|
||||||
|
* The caller is responsible for eventually calling free() on the
|
||||||
|
* returned value. Note that the value may comprise multiple
|
||||||
|
* NUL-terminated strings concatenated together. A terminating NUL
|
||||||
|
* will always be appended to the returned value.
|
||||||
|
*/
|
||||||
|
static int xenstore_response ( struct xen_hypervisor *xen, uint32_t req_id,
|
||||||
|
char **value, size_t *len ) {
|
||||||
|
struct xsd_sockmsg msg;
|
||||||
|
char *string;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Receive message header */
|
||||||
|
xenstore_recv ( xen, &msg, sizeof ( msg ) );
|
||||||
|
*len = msg.len;
|
||||||
|
|
||||||
|
/* Allocate space for response */
|
||||||
|
*value = zalloc ( msg.len + 1 /* terminating NUL */ );
|
||||||
|
|
||||||
|
/* Receive data. Do this even if allocation failed, or if the
|
||||||
|
* request ID was incorrect, to avoid leaving data in the
|
||||||
|
* ring.
|
||||||
|
*/
|
||||||
|
xenstore_recv ( xen, *value, msg.len );
|
||||||
|
|
||||||
|
/* Validate request ID */
|
||||||
|
if ( msg.req_id != req_id ) {
|
||||||
|
DBGC ( xen, "XENSTORE response ID mismatch (got %d, expected "
|
||||||
|
"%d)\n", msg.req_id, req_id );
|
||||||
|
rc = -EPROTO;
|
||||||
|
goto err_req_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for allocation failure */
|
||||||
|
if ( ! *value ) {
|
||||||
|
DBGC ( xen, "XENSTORE could not allocate %d bytes for "
|
||||||
|
"response\n", msg.len );
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for explicit errors */
|
||||||
|
if ( msg.type == XS_ERROR ) {
|
||||||
|
DBGC ( xen, "XENSTORE response error \"%s\"\n", *value );
|
||||||
|
rc = -EIO;
|
||||||
|
goto err_explicit;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGC2 ( xen, "XENSTORE response ID %d\n", req_id );
|
||||||
|
if ( DBG_EXTRA ) {
|
||||||
|
for ( string = *value ; string < ( *value + msg.len ) ;
|
||||||
|
string += ( strlen ( string ) + 1 /* NUL */ ) ) {
|
||||||
|
DBGC2 ( xen, " - \"%s\"\n", string );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_explicit:
|
||||||
|
err_alloc:
|
||||||
|
err_req_id:
|
||||||
|
free ( *value );
|
||||||
|
*value = NULL;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issue a XenStore message
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v type Message type
|
||||||
|
* @v response Response value to fill in, or NULL to discard
|
||||||
|
* @v len Response length to fill in, or NULL to ignore
|
||||||
|
* @v request Request value, or NULL to omit
|
||||||
|
* @v key Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int xenstore_message ( struct xen_hypervisor *xen,
|
||||||
|
enum xsd_sockmsg_type type, char **response,
|
||||||
|
size_t *len, const char *request, va_list key ) {
|
||||||
|
char *response_value;
|
||||||
|
size_t response_len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Send request */
|
||||||
|
if ( ( rc = xenstore_request ( xen, type, ++xenstore_req_id,
|
||||||
|
request, key ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Receive response */
|
||||||
|
if ( ( rc = xenstore_response ( xen, xenstore_req_id, &response_value,
|
||||||
|
&response_len ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Return response, if applicable */
|
||||||
|
if ( response ) {
|
||||||
|
*response = response_value;
|
||||||
|
} else {
|
||||||
|
free ( response_value );
|
||||||
|
}
|
||||||
|
if ( len )
|
||||||
|
*len = response_len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read XenStore value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v value Value to fill in
|
||||||
|
* @v key Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*
|
||||||
|
* On a successful return, the caller is responsible for calling
|
||||||
|
* free() on the returned value.
|
||||||
|
*/
|
||||||
|
static int xenstore_vread ( struct xen_hypervisor *xen, char **value,
|
||||||
|
va_list key ) {
|
||||||
|
|
||||||
|
return xenstore_message ( xen, XS_READ, value, NULL, NULL, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read XenStore value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v value Value to fill in
|
||||||
|
* @v ... Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*
|
||||||
|
* On a successful return, the caller is responsible for calling
|
||||||
|
* free() on the returned value.
|
||||||
|
*/
|
||||||
|
__attribute__ (( sentinel )) int
|
||||||
|
xenstore_read ( struct xen_hypervisor *xen, char **value, ... ) {
|
||||||
|
va_list key;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
va_start ( key, value );
|
||||||
|
rc = xenstore_vread ( xen, value, key );
|
||||||
|
va_end ( key );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read XenStore numeric value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v num Numeric value to fill in
|
||||||
|
* @v ... Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
__attribute__ (( sentinel )) int
|
||||||
|
xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... ) {
|
||||||
|
va_list key;
|
||||||
|
char *value;
|
||||||
|
char *endp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Try to read text value */
|
||||||
|
va_start ( key, num );
|
||||||
|
rc = xenstore_vread ( xen, &value, key );
|
||||||
|
va_end ( key );
|
||||||
|
if ( rc != 0 )
|
||||||
|
goto err_read;
|
||||||
|
|
||||||
|
/* Try to parse as numeric value */
|
||||||
|
*num = strtoul ( value, &endp, 10 );
|
||||||
|
if ( ( *value == '\0' ) || ( *endp != '\0' ) ) {
|
||||||
|
DBGC ( xen, "XENSTORE found invalid numeric value \"%s\"\n",
|
||||||
|
value );
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err_strtoul;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_strtoul:
|
||||||
|
free ( value );
|
||||||
|
err_read:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write XenStore value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v value Value
|
||||||
|
* @v key Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int xenstore_vwrite ( struct xen_hypervisor *xen, const char *value,
|
||||||
|
va_list key ) {
|
||||||
|
|
||||||
|
return xenstore_message ( xen, XS_WRITE, NULL, NULL, value, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write XenStore value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v value Value
|
||||||
|
* @v ... Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
__attribute__ (( sentinel )) int
|
||||||
|
xenstore_write ( struct xen_hypervisor *xen, const char *value, ... ) {
|
||||||
|
va_list key;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
va_start ( key, value );
|
||||||
|
rc = xenstore_vwrite ( xen, value, key );
|
||||||
|
va_end ( key );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write XenStore numeric value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v num Numeric value
|
||||||
|
* @v ... Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
__attribute__ (( sentinel )) int
|
||||||
|
xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... ) {
|
||||||
|
char value[ 21 /* "18446744073709551615" + NUL */ ];
|
||||||
|
va_list key;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Construct value */
|
||||||
|
snprintf ( value, sizeof ( value ), "%ld", num );
|
||||||
|
|
||||||
|
/* Write value */
|
||||||
|
va_start ( key, num );
|
||||||
|
rc = xenstore_vwrite ( xen, value, key );
|
||||||
|
va_end ( key );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete XenStore value
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v ... Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
__attribute__ (( sentinel )) int
|
||||||
|
xenstore_rm ( struct xen_hypervisor *xen, ... ) {
|
||||||
|
va_list key;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
va_start ( key, xen );
|
||||||
|
rc = xenstore_message ( xen, XS_RM, NULL, NULL, NULL, key );
|
||||||
|
va_end ( key );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read XenStore directory
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v children Child key names to fill in
|
||||||
|
* @v len Length of child key names to fill in
|
||||||
|
* @v ... Key path components
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
__attribute__ (( sentinel )) int
|
||||||
|
xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len,
|
||||||
|
... ) {
|
||||||
|
va_list key;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
va_start ( key, len );
|
||||||
|
rc = xenstore_message ( xen, XS_DIRECTORY, children, len, NULL, key );
|
||||||
|
va_end ( key );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump XenStore directory contents (for debugging)
|
||||||
|
*
|
||||||
|
* @v xen Xen hypervisor
|
||||||
|
* @v key Key
|
||||||
|
*/
|
||||||
|
void xenstore_dump ( struct xen_hypervisor *xen, const char *key ) {
|
||||||
|
char *value;
|
||||||
|
char *children;
|
||||||
|
char *child;
|
||||||
|
char *child_key;
|
||||||
|
size_t len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Try to dump current key as a value */
|
||||||
|
if ( ( rc = xenstore_read ( xen, &value, key, NULL ) ) == 0 ) {
|
||||||
|
DBGC ( xen, "%s = \"%s\"\n", key, value );
|
||||||
|
free ( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to recurse into each child in turn */
|
||||||
|
if ( ( rc = xenstore_directory ( xen, &children, &len, key,
|
||||||
|
NULL ) ) == 0 ) {
|
||||||
|
for ( child = children ; child < ( children + len ) ;
|
||||||
|
child += ( strlen ( child ) + 1 /* NUL */ ) ) {
|
||||||
|
|
||||||
|
/* Construct child key */
|
||||||
|
asprintf ( &child_key, "%s/%s", key, child );
|
||||||
|
if ( ! child_key ) {
|
||||||
|
DBGC ( xen, "XENSTORE could not allocate child "
|
||||||
|
"key \"%s/%s\"\n", key, child );
|
||||||
|
rc = -ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recurse into child key, continuing on error */
|
||||||
|
xenstore_dump ( xen, child_key );
|
||||||
|
free ( child_key );
|
||||||
|
}
|
||||||
|
free ( children );
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user