summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/interface/hyperv/vmbus.c
diff options
context:
space:
mode:
authorRajithaY <rajithax.yerrumsetty@intel.com>2017-04-25 03:31:15 -0700
committerRajitha Yerrumchetty <rajithax.yerrumsetty@intel.com>2017-05-22 06:48:08 +0000
commitbb756eebdac6fd24e8919e2c43f7d2c8c4091f59 (patch)
treeca11e03542edf2d8f631efeca5e1626d211107e3 /qemu/roms/ipxe/src/interface/hyperv/vmbus.c
parenta14b48d18a9ed03ec191cf16b162206998a895ce (diff)
Adding qemu as a submodule of KVMFORNFV
This Patch includes the changes to add qemu as a submodule to kvmfornfv repo and make use of the updated latest qemu for the execution of all testcase Change-Id: I1280af507a857675c7f81d30c95255635667bdd7 Signed-off-by:RajithaY<rajithax.yerrumsetty@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/interface/hyperv/vmbus.c')
-rw-r--r--qemu/roms/ipxe/src/interface/hyperv/vmbus.c1333
1 files changed, 0 insertions, 1333 deletions
diff --git a/qemu/roms/ipxe/src/interface/hyperv/vmbus.c b/qemu/roms/ipxe/src/interface/hyperv/vmbus.c
deleted file mode 100644
index 795929eae..000000000
--- a/qemu/roms/ipxe/src/interface/hyperv/vmbus.c
+++ /dev/null
@@ -1,1333 +0,0 @@
-/*
- * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-/** @file
- *
- * Hyper-V virtual machine bus
- *
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <byteswap.h>
-#include <ipxe/nap.h>
-#include <ipxe/malloc.h>
-#include <ipxe/iobuf.h>
-#include <ipxe/hyperv.h>
-#include <ipxe/vmbus.h>
-
-/** VMBus initial GPADL ID
- *
- * This is an opaque value with no meaning. The Linux kernel uses
- * 0xe1e10.
- */
-#define VMBUS_GPADL_MAGIC 0x18ae0000
-
-/**
- * Post message
- *
- * @v hv Hyper-V hypervisor
- * @v header Message header
- * @v len Length of message (including header)
- * @ret rc Return status code
- */
-static int vmbus_post_message ( struct hv_hypervisor *hv,
- const struct vmbus_message_header *header,
- size_t len ) {
- struct vmbus *vmbus = hv->vmbus;
- int rc;
-
- /* Post message */
- if ( ( rc = hv_post_message ( hv, VMBUS_MESSAGE_ID, VMBUS_MESSAGE_TYPE,
- header, len ) ) != 0 ) {
- DBGC ( vmbus, "VMBUS %p could not post message: %s\n",
- vmbus, strerror ( rc ) );
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Post empty message
- *
- * @v hv Hyper-V hypervisor
- * @v type Message type
- * @ret rc Return status code
- */
-static int vmbus_post_empty_message ( struct hv_hypervisor *hv,
- unsigned int type ) {
- struct vmbus_message_header header = { .type = cpu_to_le32 ( type ) };
-
- return vmbus_post_message ( hv, &header, sizeof ( header ) );
-}
-
-/**
- * Wait for received message
- *
- * @v hv Hyper-V hypervisor
- * @ret rc Return status code
- */
-static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) {
- struct vmbus *vmbus = hv->vmbus;
- int rc;
-
- /* Wait for message */
- if ( ( rc = hv_wait_for_message ( hv, VMBUS_MESSAGE_SINT ) ) != 0 ) {
- DBGC ( vmbus, "VMBUS %p failed waiting for message: %s\n",
- vmbus, strerror ( rc ) );
- return rc;
- }
-
- /* Sanity check */
- if ( hv->message->received.type != cpu_to_le32 ( VMBUS_MESSAGE_TYPE ) ){
- DBGC ( vmbus, "VMBUS %p invalid message type %d\n",
- vmbus, le32_to_cpu ( hv->message->received.type ) );
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * Initiate contact
- *
- * @v hv Hyper-V hypervisor
- * @v raw VMBus protocol (raw) version
- * @ret rc Return status code
- */
-static int vmbus_initiate_contact ( struct hv_hypervisor *hv,
- unsigned int raw ) {
- struct vmbus *vmbus = hv->vmbus;
- const struct vmbus_version_response *version = &vmbus->message->version;
- struct vmbus_initiate_contact initiate;
- int rc;
-
- /* Construct message */
- memset ( &initiate, 0, sizeof ( initiate ) );
- initiate.header.type = cpu_to_le32 ( VMBUS_INITIATE_CONTACT );
- initiate.version.raw = cpu_to_le32 ( raw );
- initiate.intr = virt_to_phys ( vmbus->intr );
- initiate.monitor_in = virt_to_phys ( vmbus->monitor_in );
- initiate.monitor_out = virt_to_phys ( vmbus->monitor_out );
-
- /* Post message */
- if ( ( rc = vmbus_post_message ( hv, &initiate.header,
- sizeof ( initiate ) ) ) != 0 )
- return rc;
-
- /* Wait for response */
- if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
- return rc;
-
- /* Check response */
- if ( version->header.type != cpu_to_le32 ( VMBUS_VERSION_RESPONSE ) ) {
- DBGC ( vmbus, "VMBUS %p unexpected version response type %d\n",
- vmbus, le32_to_cpu ( version->header.type ) );
- return -EPROTO;
- }
- if ( ! version->supported ) {
- DBGC ( vmbus, "VMBUS %p requested version not supported\n",
- vmbus );
- return -ENOTSUP;
- }
- if ( version->version.raw != cpu_to_le32 ( raw ) ) {
- DBGC ( vmbus, "VMBUS %p unexpected version %d.%d\n",
- vmbus, le16_to_cpu ( version->version.major ),
- le16_to_cpu ( version->version.minor ) );
- return -EPROTO;
- }
-
- DBGC ( vmbus, "VMBUS %p initiated contact using version %d.%d\n",
- vmbus, le16_to_cpu ( version->version.major ),
- le16_to_cpu ( version->version.minor ) );
- return 0;
-}
-
-/**
- * Terminate contact
- *
- * @v hv Hyper-V hypervisor
- * @ret rc Return status code
- */
-static int vmbus_unload ( struct hv_hypervisor *hv ) {
- struct vmbus *vmbus = hv->vmbus;
- const struct vmbus_message_header *header = &vmbus->message->header;
- int rc;
-
- /* Post message */
- if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_UNLOAD ) ) != 0 )
- return rc;
-
- /* Wait for response */
- if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
- return rc;
-
- /* Check response */
- if ( header->type != cpu_to_le32 ( VMBUS_UNLOAD_RESPONSE ) ) {
- DBGC ( vmbus, "VMBUS %p unexpected unload response type %d\n",
- vmbus, le32_to_cpu ( header->type ) );
- return -EPROTO;
- }
-
- return 0;
-}
-
-/**
- * Negotiate protocol version
- *
- * @v hv Hyper-V hypervisor
- * @ret rc Return status code
- */
-static int vmbus_negotiate_version ( struct hv_hypervisor *hv ) {
- int rc;
-
- /* We require the ability to disconnect from and reconnect to
- * VMBus; if we don't have this then there is no (viable) way
- * for a loaded operating system to continue to use any VMBus
- * devices. (There is also a small but non-zero risk that the
- * host will continue to write to our interrupt and monitor
- * pages, since the VMBUS_UNLOAD message in earlier versions
- * is essentially a no-op.)
- *
- * This requires us to ensure that the host supports protocol
- * version 3.0 (VMBUS_VERSION_WIN8_1). However, we can't
- * actually _use_ protocol version 3.0, since doing so causes
- * an iSCSI-booted Windows Server 2012 R2 VM to crash due to a
- * NULL pointer dereference in vmbus.sys.
- *
- * To work around this problem, we first ensure that we can
- * connect using protocol v3.0, then disconnect and reconnect
- * using the oldest known protocol.
- */
-
- /* Initiate contact to check for required protocol support */
- if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WIN8_1 ) ) != 0 )
- return rc;
-
- /* Terminate contact */
- if ( ( rc = vmbus_unload ( hv ) ) != 0 )
- return rc;
-
- /* Reinitiate contact using the oldest known protocol version */
- if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WS2008 ) ) != 0 )
- return rc;
-
- return 0;
-}
-
-/**
- * Establish GPA descriptor list
- *
- * @v vmdev VMBus device
- * @v data Data buffer
- * @v len Length of data buffer
- * @ret gpadl GPADL ID, or negative error
- */
-int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
- size_t len ) {
- struct hv_hypervisor *hv = vmdev->hv;
- struct vmbus *vmbus = hv->vmbus;
- physaddr_t addr = user_to_phys ( data, 0 );
- unsigned int pfn_count = hv_pfn_count ( addr, len );
- struct {
- struct vmbus_gpadl_header gpadlhdr;
- struct vmbus_gpa_range range;
- uint64_t pfn[pfn_count];
- } __attribute__ (( packed )) gpadlhdr;
- const struct vmbus_gpadl_created *created = &vmbus->message->created;
- static unsigned int gpadl = VMBUS_GPADL_MAGIC;
- unsigned int i;
- int rc;
-
- /* Allocate GPADL ID */
- gpadl++;
-
- /* Construct message */
- memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) );
- gpadlhdr.gpadlhdr.header.type = cpu_to_le32 ( VMBUS_GPADL_HEADER );
- gpadlhdr.gpadlhdr.channel = cpu_to_le32 ( vmdev->channel );
- gpadlhdr.gpadlhdr.gpadl = cpu_to_le32 ( gpadl );
- gpadlhdr.gpadlhdr.range_len =
- cpu_to_le16 ( ( sizeof ( gpadlhdr.range ) +
- sizeof ( gpadlhdr.pfn ) ) );
- gpadlhdr.gpadlhdr.range_count = cpu_to_le16 ( 1 );
- gpadlhdr.range.len = cpu_to_le32 ( len );
- gpadlhdr.range.offset = cpu_to_le32 ( addr & ( PAGE_SIZE - 1 ) );
- for ( i = 0 ; i < pfn_count ; i++ )
- gpadlhdr.pfn[i] = ( ( addr / PAGE_SIZE ) + i );
-
- /* Post message */
- if ( ( rc = vmbus_post_message ( hv, &gpadlhdr.gpadlhdr.header,
- sizeof ( gpadlhdr ) ) ) != 0 )
- return rc;
-
- /* Wait for response */
- if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
- return rc;
-
- /* Check response */
- if ( created->header.type != cpu_to_le32 ( VMBUS_GPADL_CREATED ) ) {
- DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n",
- vmdev->dev.name, le32_to_cpu ( created->header.type ) );
- return -EPROTO;
- }
- if ( created->channel != cpu_to_le32 ( vmdev->channel ) ) {
- DBGC ( vmdev, "VMBUS %s unexpected GPADL channel %d\n",
- vmdev->dev.name, le32_to_cpu ( created->channel ) );
- return -EPROTO;
- }
- if ( created->gpadl != cpu_to_le32 ( gpadl ) ) {
- DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n",
- vmdev->dev.name, le32_to_cpu ( created->gpadl ) );
- return -EPROTO;
- }
- if ( created->status != 0 ) {
- DBGC ( vmdev, "VMBUS %s GPADL creation failed: %#08x\n",
- vmdev->dev.name, le32_to_cpu ( created->status ) );
- return -EPROTO;
- }
-
- DBGC ( vmdev, "VMBUS %s GPADL %#08x is [%08lx,%08lx)\n",
- vmdev->dev.name, gpadl, addr, ( addr + len ) );
- return gpadl;
-}
-
-/**
- * Tear down GPA descriptor list
- *
- * @v vmdev VMBus device
- * @v gpadl GPADL ID
- * @ret rc Return status code
- */
-int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) {
- struct hv_hypervisor *hv = vmdev->hv;
- struct vmbus *vmbus = hv->vmbus;
- struct vmbus_gpadl_teardown teardown;
- const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown;
- int rc;
-
- /* Construct message */
- memset ( &teardown, 0, sizeof ( teardown ) );
- teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN );
- teardown.channel = cpu_to_le32 ( vmdev->channel );
- teardown.gpadl = cpu_to_le32 ( gpadl );
-
- /* Post message */
- if ( ( rc = vmbus_post_message ( hv, &teardown.header,
- sizeof ( teardown ) ) ) != 0 )
- return rc;
-
- /* Wait for response */
- if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
- return rc;
-
- /* Check response */
- if ( torndown->header.type != cpu_to_le32 ( VMBUS_GPADL_TORNDOWN ) ) {
- DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n",
- vmdev->dev.name, le32_to_cpu ( torndown->header.type ) );
- return -EPROTO;
- }
- if ( torndown->gpadl != cpu_to_le32 ( gpadl ) ) {
- DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n",
- vmdev->dev.name, le32_to_cpu ( torndown->gpadl ) );
- return -EPROTO;
- }
-
- return 0;
-}
-
-/**
- * Open VMBus channel
- *
- * @v vmdev VMBus device
- * @v op Channel operations
- * @v out_len Outbound ring buffer length
- * @v in_len Inbound ring buffer length
- * @v mtu Maximum expected data packet length (including headers)
- * @ret rc Return status code
- *
- * Both outbound and inbound ring buffer lengths must be a power of
- * two and a multiple of PAGE_SIZE. The requirement to be a power of
- * two is a policy decision taken to simplify the ring buffer indexing
- * logic.
- */
-int vmbus_open ( struct vmbus_device *vmdev,
- struct vmbus_channel_operations *op,
- size_t out_len, size_t in_len, size_t mtu ) {
- struct hv_hypervisor *hv = vmdev->hv;
- struct vmbus *vmbus = hv->vmbus;
- struct vmbus_open_channel open;
- const struct vmbus_open_channel_result *opened =
- &vmbus->message->opened;
- size_t len;
- void *ring;
- void *packet;
- int gpadl;
- uint32_t open_id;
- int rc;
-
- /* Sanity checks */
- assert ( ( out_len % PAGE_SIZE ) == 0 );
- assert ( ( out_len & ( out_len - 1 ) ) == 0 );
- assert ( ( in_len % PAGE_SIZE ) == 0 );
- assert ( ( in_len & ( in_len - 1 ) ) == 0 );
- assert ( mtu >= ( sizeof ( struct vmbus_packet_header ) +
- sizeof ( struct vmbus_packet_footer ) ) );
-
- /* Allocate packet buffer */
- packet = malloc ( mtu );
- if ( ! packet ) {
- rc = -ENOMEM;
- goto err_alloc_packet;
- }
-
- /* Allocate ring buffer */
- len = ( sizeof ( *vmdev->out ) + out_len +
- sizeof ( *vmdev->in ) + in_len );
- assert ( ( len % PAGE_SIZE ) == 0 );
- ring = malloc_dma ( len, PAGE_SIZE );
- if ( ! ring ) {
- rc = -ENOMEM;
- goto err_alloc_ring;
- }
- memset ( ring, 0, len );
-
- /* Establish GPADL for ring buffer */
- gpadl = vmbus_establish_gpadl ( vmdev, virt_to_user ( ring ), len );
- if ( gpadl < 0 ) {
- rc = gpadl;
- goto err_establish;
- }
-
- /* Construct message */
- memset ( &open, 0, sizeof ( open ) );
- open.header.type = cpu_to_le32 ( VMBUS_OPEN_CHANNEL );
- open.channel = cpu_to_le32 ( vmdev->channel );
- open_id = random();
- open.id = open_id; /* Opaque random value: endianness irrelevant */
- open.gpadl = cpu_to_le32 ( gpadl );
- open.out_pages = ( ( sizeof ( *vmdev->out ) / PAGE_SIZE ) +
- ( out_len / PAGE_SIZE ) );
-
- /* Post message */
- if ( ( rc = vmbus_post_message ( hv, &open.header,
- sizeof ( open ) ) ) != 0 )
- return rc;
-
- /* Wait for response */
- if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
- return rc;
-
- /* Check response */
- if ( opened->header.type != cpu_to_le32 ( VMBUS_OPEN_CHANNEL_RESULT ) ){
- DBGC ( vmdev, "VMBUS %s unexpected open response type %d\n",
- vmdev->dev.name, le32_to_cpu ( opened->header.type ) );
- return -EPROTO;
- }
- if ( opened->channel != cpu_to_le32 ( vmdev->channel ) ) {
- DBGC ( vmdev, "VMBUS %s unexpected opened channel %#08x\n",
- vmdev->dev.name, le32_to_cpu ( opened->channel ) );
- return -EPROTO;
- }
- if ( opened->id != open_id /* Non-endian */ ) {
- DBGC ( vmdev, "VMBUS %s unexpected open ID %#08x\n",
- vmdev->dev.name, le32_to_cpu ( opened->id ) );
- return -EPROTO;
- }
- if ( opened->status != 0 ) {
- DBGC ( vmdev, "VMBUS %s open failed: %#08x\n",
- vmdev->dev.name, le32_to_cpu ( opened->status ) );
- return -EPROTO;
- }
-
- /* Store channel parameters */
- vmdev->out_len = out_len;
- vmdev->in_len = in_len;
- vmdev->out = ring;
- vmdev->in = ( ring + sizeof ( *vmdev->out ) + out_len );
- vmdev->gpadl = gpadl;
- vmdev->op = op;
- vmdev->mtu = mtu;
- vmdev->packet = packet;
-
- DBGC ( vmdev, "VMBUS %s channel GPADL %#08x ring "
- "[%#08lx,%#08lx,%#08lx)\n", vmdev->dev.name, vmdev->gpadl,
- virt_to_phys ( vmdev->out ), virt_to_phys ( vmdev->in ),
- ( virt_to_phys ( vmdev->out ) + len ) );
- return 0;
-
- vmbus_gpadl_teardown ( vmdev, vmdev->gpadl );
- err_establish:
- free_dma ( ring, len );
- err_alloc_ring:
- free ( packet );
- err_alloc_packet:
- return rc;
-}
-
-/**
- * Close VMBus channel
- *
- * @v vmdev VMBus device
- */
-void vmbus_close ( struct vmbus_device *vmdev ) {
- struct hv_hypervisor *hv = vmdev->hv;
- struct vmbus_close_channel close;
- size_t len;
- int rc;
-
- /* Construct message */
- memset ( &close, 0, sizeof ( close ) );
- close.header.type = cpu_to_le32 ( VMBUS_CLOSE_CHANNEL );
- close.channel = cpu_to_le32 ( vmdev->channel );
-
- /* Post message */
- if ( ( rc = vmbus_post_message ( hv, &close.header,
- sizeof ( close ) ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s failed to close: %s\n",
- vmdev->dev.name, strerror ( rc ) );
- /* Continue to attempt to tear down GPADL, so that our
- * memory is no longer accessible by the remote VM.
- */
- }
-
- /* Tear down GPADL */
- if ( ( rc = vmbus_gpadl_teardown ( vmdev,
- vmdev->gpadl ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: "
- "%s\n", vmdev->dev.name, strerror ( rc ) );
- /* We can't prevent the remote VM from continuing to
- * access this memory, so leak it.
- */
- return;
- }
-
- /* Free ring buffer */
- len = ( sizeof ( *vmdev->out ) + vmdev->out_len +
- sizeof ( *vmdev->in ) + vmdev->in_len );
- free_dma ( vmdev->out, len );
- vmdev->out = NULL;
- vmdev->in = NULL;
-
- /* Free packet buffer */
- free ( vmdev->packet );
- vmdev->packet = NULL;
-
- DBGC ( vmdev, "VMBUS %s closed\n", vmdev->dev.name );
-}
-
-/**
- * Signal channel via monitor page
- *
- * @v vmdev VMBus device
- */
-static void vmbus_signal_monitor ( struct vmbus_device *vmdev ) {
- struct hv_hypervisor *hv = vmdev->hv;
- struct vmbus *vmbus = hv->vmbus;
- struct hv_monitor_trigger *trigger;
- unsigned int group;
- unsigned int bit;
-
- /* Set bit in monitor trigger group */
- group = ( vmdev->monitor / ( 8 * sizeof ( trigger->pending ) ));
- bit = ( vmdev->monitor % ( 8 * sizeof ( trigger->pending ) ) );
- trigger = &vmbus->monitor_out->trigger[group];
- hv_set_bit ( trigger, bit );
-}
-
-/**
- * Signal channel via hypervisor event
- *
- * @v vmdev VMBus device
- */
-static void vmbus_signal_event ( struct vmbus_device *vmdev ) {
- struct hv_hypervisor *hv = vmdev->hv;
- int rc;
-
- /* Signal hypervisor event */
- if ( ( rc = hv_signal_event ( hv, VMBUS_EVENT_ID, 0 ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s could not signal event: %s\n",
- vmdev->dev.name, strerror ( rc ) );
- return;
- }
-}
-
-/**
- * Fill outbound ring buffer
- *
- * @v vmdev VMBus device
- * @v prod Producer index
- * @v data Data
- * @v len Length
- * @ret prod New producer index
- *
- * The caller must ensure that there is sufficient space in the ring
- * buffer.
- */
-static size_t vmbus_produce ( struct vmbus_device *vmdev, size_t prod,
- const void *data, size_t len ) {
- size_t first;
- size_t second;
-
- /* Determine fragment lengths */
- first = ( vmdev->out_len - prod );
- if ( first > len )
- first = len;
- second = ( len - first );
-
- /* Copy fragment(s) */
- memcpy ( &vmdev->out->data[prod], data, first );
- if ( second )
- memcpy ( &vmdev->out->data[0], ( data + first ), second );
-
- return ( ( prod + len ) & ( vmdev->out_len - 1 ) );
-}
-
-/**
- * Consume inbound ring buffer
- *
- * @v vmdev VMBus device
- * @v cons Consumer index
- * @v data Data buffer, or NULL
- * @v len Length to consume
- * @ret cons New consumer index
- */
-static size_t vmbus_consume ( struct vmbus_device *vmdev, size_t cons,
- void *data, size_t len ) {
- size_t first;
- size_t second;
-
- /* Determine fragment lengths */
- first = ( vmdev->in_len - cons );
- if ( first > len )
- first = len;
- second = ( len - first );
-
- /* Copy fragment(s) */
- memcpy ( data, &vmdev->in->data[cons], first );
- if ( second )
- memcpy ( ( data + first ), &vmdev->in->data[0], second );
-
- return ( ( cons + len ) & ( vmdev->in_len - 1 ) );
-}
-
-/**
- * Send packet via ring buffer
- *
- * @v vmdev VMBus device
- * @v header Packet header
- * @v data Data
- * @v len Length of data
- * @ret rc Return status code
- *
- * Send a packet via the outbound ring buffer. All fields in the
- * packet header must be filled in, with the exception of the total
- * packet length.
- */
-static int vmbus_send ( struct vmbus_device *vmdev,
- struct vmbus_packet_header *header,
- const void *data, size_t len ) {
- struct hv_hypervisor *hv = vmdev->hv;
- struct vmbus *vmbus = hv->vmbus;
- static uint8_t padding[ 8 - 1 ];
- struct vmbus_packet_footer footer;
- size_t header_len;
- size_t pad_len;
- size_t footer_len;
- size_t ring_len;
- size_t cons;
- size_t prod;
- size_t old_prod;
- size_t fill;
-
- /* Sanity check */
- assert ( vmdev->out != NULL );
-
- /* Calculate lengths */
- header_len = ( le16_to_cpu ( header->hdr_qlen ) * 8 );
- pad_len = ( ( -len ) & ( 8 - 1 ) );
- footer_len = sizeof ( footer );
- ring_len = ( header_len + len + pad_len + footer_len );
-
- /* Check that we have enough room in the outbound ring buffer */
- cons = le32_to_cpu ( vmdev->out->cons );
- prod = le32_to_cpu ( vmdev->out->prod );
- old_prod = prod;
- fill = ( ( prod - cons ) & ( vmdev->out_len - 1 ) );
- if ( ( fill + ring_len ) >= vmdev->out_len ) {
- DBGC ( vmdev, "VMBUS %s ring buffer full\n", vmdev->dev.name );
- return -ENOBUFS;
- }
-
- /* Complete header */
- header->qlen = cpu_to_le16 ( ( ring_len - footer_len ) / 8 );
-
- /* Construct footer */
- footer.reserved = 0;
- footer.prod = vmdev->out->prod;
-
- /* Copy packet to buffer */
- DBGC2 ( vmdev, "VMBUS %s sending:\n", vmdev->dev.name );
- DBGC2_HDA ( vmdev, prod, header, header_len );
- prod = vmbus_produce ( vmdev, prod, header, header_len );
- DBGC2_HDA ( vmdev, prod, data, len );
- prod = vmbus_produce ( vmdev, prod, data, len );
- prod = vmbus_produce ( vmdev, prod, padding, pad_len );
- DBGC2_HDA ( vmdev, prod, &footer, sizeof ( footer ) );
- prod = vmbus_produce ( vmdev, prod, &footer, sizeof ( footer ) );
- assert ( ( ( prod - old_prod ) & ( vmdev->out_len - 1 ) ) == ring_len );
-
- /* Update producer index */
- wmb();
- vmdev->out->prod = cpu_to_le32 ( prod );
-
- /* Return if we do not need to signal the host. This follows
- * the logic of hv_need_to_signal() in the Linux driver.
- */
- mb();
- if ( vmdev->out->intr_mask )
- return 0;
- rmb();
- cons = le32_to_cpu ( vmdev->out->cons );
- if ( cons != old_prod )
- return 0;
-
- /* Set channel bit in interrupt page */
- hv_set_bit ( vmbus->intr->out, vmdev->channel );
-
- /* Signal the host */
- vmdev->signal ( vmdev );
-
- return 0;
-}
-
-/**
- * Send control packet via ring buffer
- *
- * @v vmdev VMBus device
- * @v xid Transaction ID (or zero to not request completion)
- * @v data Data
- * @v len Length of data
- * @ret rc Return status code
- *
- * Send data using a VMBUS_DATA_INBAND packet.
- */
-int vmbus_send_control ( struct vmbus_device *vmdev, uint64_t xid,
- const void *data, size_t len ) {
- struct vmbus_packet_header *header = vmdev->packet;
-
- /* Construct header in packet buffer */
- assert ( header != NULL );
- header->type = cpu_to_le16 ( VMBUS_DATA_INBAND );
- header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 );
- header->flags = ( xid ?
- cpu_to_le16 ( VMBUS_COMPLETION_REQUESTED ) : 0 );
- header->xid = xid; /* Non-endian */
-
- return vmbus_send ( vmdev, header, data, len );
-}
-
-/**
- * Send data packet via ring buffer
- *
- * @v vmdev VMBus device
- * @v xid Transaction ID
- * @v data Data
- * @v len Length of data
- * @v iobuf I/O buffer
- * @ret rc Return status code
- *
- * Send data using a VMBUS_DATA_GPA_DIRECT packet. The caller is
- * responsible for ensuring that the I/O buffer remains untouched
- * until the corresponding completion has been received.
- */
-int vmbus_send_data ( struct vmbus_device *vmdev, uint64_t xid,
- const void *data, size_t len, struct io_buffer *iobuf ) {
- physaddr_t addr = virt_to_phys ( iobuf->data );
- unsigned int pfn_count = hv_pfn_count ( addr, iob_len ( iobuf ) );
- struct {
- struct vmbus_gpa_direct_header gpa;
- struct vmbus_gpa_range range;
- uint64_t pfn[pfn_count];
- } __attribute__ (( packed )) *header = vmdev->packet;
- unsigned int i;
-
- /* Sanity check */
- assert ( header != NULL );
- assert ( sizeof ( *header ) <= vmdev->mtu );
-
- /* Construct header in packet buffer */
- header->gpa.header.type = cpu_to_le16 ( VMBUS_DATA_GPA_DIRECT );
- header->gpa.header.hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 );
- header->gpa.header.flags = cpu_to_le16 ( VMBUS_COMPLETION_REQUESTED );
- header->gpa.header.xid = xid; /* Non-endian */
- header->gpa.range_count = 1;
- header->range.len = cpu_to_le32 ( iob_len ( iobuf ) );
- header->range.offset = cpu_to_le32 ( addr & ( PAGE_SIZE - 1 ) );
- for ( i = 0 ; i < pfn_count ; i++ )
- header->pfn[i] = ( ( addr / PAGE_SIZE ) + i );
-
- return vmbus_send ( vmdev, &header->gpa.header, data, len );
-}
-
-/**
- * Send completion packet via ring buffer
- *
- * @v vmdev VMBus device
- * @v xid Transaction ID
- * @v data Data
- * @v len Length of data
- * @ret rc Return status code
- *
- * Send data using a VMBUS_COMPLETION packet.
- */
-int vmbus_send_completion ( struct vmbus_device *vmdev, uint64_t xid,
- const void *data, size_t len ) {
- struct vmbus_packet_header *header = vmdev->packet;
-
- /* Construct header in packet buffer */
- assert ( header != NULL );
- header->type = cpu_to_le16 ( VMBUS_COMPLETION );
- header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 );
- header->flags = 0;
- header->xid = xid; /* Non-endian */
-
- return vmbus_send ( vmdev, header, data, len );
-}
-
-/**
- * Send cancellation packet via ring buffer
- *
- * @v vmdev VMBus device
- * @v xid Transaction ID
- * @ret rc Return status code
- *
- * Send data using a VMBUS_CANCELLATION packet.
- */
-int vmbus_send_cancellation ( struct vmbus_device *vmdev, uint64_t xid ) {
- struct vmbus_packet_header *header = vmdev->packet;
-
- /* Construct header in packet buffer */
- assert ( header != NULL );
- header->type = cpu_to_le16 ( VMBUS_CANCELLATION );
- header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 );
- header->flags = 0;
- header->xid = xid; /* Non-endian */
-
- return vmbus_send ( vmdev, header, NULL, 0 );
-}
-
-/**
- * Get transfer page set from pageset ID
- *
- * @v vmdev VMBus device
- * @v pageset Page set ID (in protocol byte order)
- * @ret pages Page set, or NULL if not found
- */
-static struct vmbus_xfer_pages * vmbus_xfer_pages ( struct vmbus_device *vmdev,
- uint16_t pageset ) {
- struct vmbus_xfer_pages *pages;
-
- /* Locate page set */
- list_for_each_entry ( pages, &vmdev->pages, list ) {
- if ( pages->pageset == pageset )
- return pages;
- }
-
- DBGC ( vmdev, "VMBUS %s unrecognised page set ID %#04x\n",
- vmdev->dev.name, le16_to_cpu ( pageset ) );
- return NULL;
-}
-
-/**
- * Construct I/O buffer list from transfer pages
- *
- * @v vmdev VMBus device
- * @v header Transfer page header
- * @v list I/O buffer list to populate
- * @ret rc Return status code
- */
-static int vmbus_xfer_page_iobufs ( struct vmbus_device *vmdev,
- struct vmbus_packet_header *header,
- struct list_head *list ) {
- struct vmbus_xfer_page_header *page_header =
- container_of ( header, struct vmbus_xfer_page_header, header );
- struct vmbus_xfer_pages *pages;
- struct io_buffer *iobuf;
- struct io_buffer *tmp;
- size_t len;
- size_t offset;
- unsigned int range_count;
- unsigned int i;
- int rc;
-
- /* Sanity check */
- assert ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) );
-
- /* Locate page set */
- pages = vmbus_xfer_pages ( vmdev, page_header->pageset );
- if ( ! pages ) {
- rc = -ENOENT;
- goto err_pages;
- }
-
- /* Allocate and populate I/O buffers */
- range_count = le32_to_cpu ( page_header->range_count );
- for ( i = 0 ; i < range_count ; i++ ) {
-
- /* Parse header */
- len = le32_to_cpu ( page_header->range[i].len );
- offset = le32_to_cpu ( page_header->range[i].offset );
-
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( len );
- if ( ! iobuf ) {
- DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte "
- "I/O buffer\n", vmdev->dev.name, len );
- rc = -ENOMEM;
- goto err_alloc;
- }
-
- /* Add I/O buffer to list */
- list_add ( &iobuf->list, list );
-
- /* Populate I/O buffer */
- if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ),
- offset, len ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s could not populate I/O buffer "
- "range [%zd,%zd): %s\n",
- vmdev->dev.name, offset, len, strerror ( rc ) );
- goto err_copy;
- }
- }
-
- return 0;
-
- err_copy:
- err_alloc:
- list_for_each_entry_safe ( iobuf, tmp, list, list ) {
- list_del ( &iobuf->list );
- free_iob ( iobuf );
- }
- err_pages:
- return rc;
-}
-
-/**
- * Poll ring buffer
- *
- * @v vmdev VMBus device
- * @ret rc Return status code
- */
-int vmbus_poll ( struct vmbus_device *vmdev ) {
- struct vmbus_packet_header *header = vmdev->packet;
- struct list_head list;
- void *data;
- size_t header_len;
- size_t len;
- size_t footer_len;
- size_t ring_len;
- size_t cons;
- size_t old_cons;
- uint64_t xid;
- int rc;
-
- /* Sanity checks */
- assert ( vmdev->packet != NULL );
- assert ( vmdev->in != NULL );
-
- /* Return immediately if buffer is empty */
- if ( ! vmbus_has_data ( vmdev ) )
- return 0;
- cons = le32_to_cpu ( vmdev->in->cons );
- old_cons = cons;
-
- /* Consume (start of) header */
- cons = vmbus_consume ( vmdev, cons, header, sizeof ( *header ) );
-
- /* Parse and sanity check header */
- header_len = ( le16_to_cpu ( header->hdr_qlen ) * 8 );
- if ( header_len < sizeof ( *header ) ) {
- DBGC ( vmdev, "VMBUS %s received underlength header (%zd "
- "bytes)\n", vmdev->dev.name, header_len );
- return -EINVAL;
- }
- len = ( ( le16_to_cpu ( header->qlen ) * 8 ) - header_len );
- footer_len = sizeof ( struct vmbus_packet_footer );
- ring_len = ( header_len + len + footer_len );
- if ( ring_len > vmdev->mtu ) {
- DBGC ( vmdev, "VMBUS %s received overlength packet (%zd "
- "bytes)\n", vmdev->dev.name, ring_len );
- return -ERANGE;
- }
- xid = le64_to_cpu ( header->xid );
-
- /* Consume remainder of packet */
- cons = vmbus_consume ( vmdev, cons,
- ( ( ( void * ) header ) + sizeof ( *header ) ),
- ( ring_len - sizeof ( *header ) ) );
- DBGC2 ( vmdev, "VMBUS %s received:\n", vmdev->dev.name );
- DBGC2_HDA ( vmdev, old_cons, header, ring_len );
- assert ( ( ( cons - old_cons ) & ( vmdev->in_len - 1 ) ) == ring_len );
-
- /* Allocate I/O buffers, if applicable */
- INIT_LIST_HEAD ( &list );
- if ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ) {
- if ( ( rc = vmbus_xfer_page_iobufs ( vmdev, header,
- &list ) ) != 0 )
- return rc;
- }
-
- /* Update producer index */
- rmb();
- vmdev->in->cons = cpu_to_le32 ( cons );
-
- /* Handle packet */
- data = ( ( ( void * ) header ) + header_len );
- switch ( header->type ) {
-
- case cpu_to_le16 ( VMBUS_DATA_INBAND ) :
- if ( ( rc = vmdev->op->recv_control ( vmdev, xid, data,
- len ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s could not handle control "
- "packet: %s\n",
- vmdev->dev.name, strerror ( rc ) );
- return rc;
- }
- break;
-
- case cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) :
- if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len,
- &list ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s could not handle data packet: "
- "%s\n", vmdev->dev.name, strerror ( rc ) );
- return rc;
- }
- break;
-
- case cpu_to_le16 ( VMBUS_COMPLETION ) :
- if ( ( rc = vmdev->op->recv_completion ( vmdev, xid, data,
- len ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s could not handle completion: "
- "%s\n", vmdev->dev.name, strerror ( rc ) );
- return rc;
- }
- break;
-
- case cpu_to_le16 ( VMBUS_CANCELLATION ) :
- if ( ( rc = vmdev->op->recv_cancellation ( vmdev, xid ) ) != 0){
- DBGC ( vmdev, "VMBUS %s could not handle cancellation: "
- "%s\n", vmdev->dev.name, strerror ( rc ) );
- return rc;
- }
- break;
-
- default:
- DBGC ( vmdev, "VMBUS %s unknown packet type %d\n",
- vmdev->dev.name, le16_to_cpu ( header->type ) );
- return -ENOTSUP;
- }
-
- return 0;
-}
-
-/**
- * Dump channel status (for debugging)
- *
- * @v vmdev VMBus device
- */
-void vmbus_dump_channel ( struct vmbus_device *vmdev ) {
- size_t out_prod = le32_to_cpu ( vmdev->out->prod );
- size_t out_cons = le32_to_cpu ( vmdev->out->cons );
- size_t in_prod = le32_to_cpu ( vmdev->in->prod );
- size_t in_cons = le32_to_cpu ( vmdev->in->cons );
- size_t in_len;
- size_t first;
- size_t second;
-
- /* Dump ring status */
- DBGC ( vmdev, "VMBUS %s out %03zx:%03zx%s in %03zx:%03zx%s\n",
- vmdev->dev.name, out_prod, out_cons,
- ( vmdev->out->intr_mask ? "(m)" : "" ), in_prod, in_cons,
- ( vmdev->in->intr_mask ? "(m)" : "" ) );
-
- /* Dump inbound ring contents, if any */
- if ( in_prod != in_cons ) {
- in_len = ( ( in_prod - in_cons ) &
- ( vmdev->in_len - 1 ) );
- first = ( vmdev->in_len - in_cons );
- if ( first > in_len )
- first = in_len;
- second = ( in_len - first );
- DBGC_HDA ( vmdev, in_cons, &vmdev->in->data[in_cons], first );
- DBGC_HDA ( vmdev, 0, &vmdev->in->data[0], second );
- }
-}
-
-/**
- * Find driver for VMBus device
- *
- * @v vmdev VMBus device
- * @ret driver Driver, or NULL
- */
-static struct vmbus_driver * vmbus_find_driver ( const union uuid *type ) {
- struct vmbus_driver *vmdrv;
-
- for_each_table_entry ( vmdrv, VMBUS_DRIVERS ) {
- if ( memcmp ( &vmdrv->type, type, sizeof ( *type ) ) == 0 )
- return vmdrv;
- }
- return NULL;
-}
-
-/**
- * Probe channels
- *
- * @v hv Hyper-V hypervisor
- * @v parent Parent device
- * @ret rc Return status code
- */
-static int vmbus_probe_channels ( struct hv_hypervisor *hv,
- struct device *parent ) {
- struct vmbus *vmbus = hv->vmbus;
- const struct vmbus_message_header *header = &vmbus->message->header;
- const struct vmbus_offer_channel *offer = &vmbus->message->offer;
- const union uuid *type;
- struct vmbus_driver *driver;
- struct vmbus_device *vmdev;
- struct vmbus_device *tmp;
- unsigned int channel;
- int rc;
-
- /* Post message */
- if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0)
- goto err_post_message;
-
- /* Collect responses */
- while ( 1 ) {
-
- /* Wait for response */
- if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
- goto err_wait_for_message;
-
- /* Handle response */
- if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) {
-
- /* Parse offer */
- type = &offer->type;
- channel = le32_to_cpu ( offer->channel );
- DBGC2 ( vmbus, "VMBUS %p offer %d type %s",
- vmbus, channel, uuid_ntoa ( type ) );
- if ( offer->monitored )
- DBGC2 ( vmbus, " monitor %d", offer->monitor );
- DBGC2 ( vmbus, "\n" );
-
- /* Look for a driver */
- driver = vmbus_find_driver ( type );
- if ( ! driver ) {
- DBGC2 ( vmbus, "VMBUS %p has no driver for "
- "type %s\n", vmbus, uuid_ntoa ( type ));
- /* Not a fatal error */
- continue;
- }
-
- /* Allocate and initialise device */
- vmdev = zalloc ( sizeof ( *vmdev ) );
- if ( ! vmdev ) {
- rc = -ENOMEM;
- goto err_alloc_vmdev;
- }
- snprintf ( vmdev->dev.name, sizeof ( vmdev->dev.name ),
- "vmbus:%02x", channel );
- vmdev->dev.desc.bus_type = BUS_TYPE_HV;
- INIT_LIST_HEAD ( &vmdev->dev.children );
- list_add_tail ( &vmdev->dev.siblings,
- &parent->children );
- vmdev->dev.parent = parent;
- vmdev->hv = hv;
- vmdev->channel = channel;
- vmdev->monitor = offer->monitor;
- vmdev->signal = ( offer->monitored ?
- vmbus_signal_monitor :
- vmbus_signal_event );
- INIT_LIST_HEAD ( &vmdev->pages );
- vmdev->driver = driver;
- vmdev->dev.driver_name = driver->name;
- DBGC ( vmdev, "VMBUS %s has driver \"%s\"\n",
- vmdev->dev.name, vmdev->driver->name );
-
- } else if ( header->type ==
- cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
-
- break;
-
- } else {
- DBGC ( vmbus, "VMBUS %p unexpected offer response type "
- "%d\n", vmbus, le32_to_cpu ( header->type ) );
- rc = -EPROTO;
- goto err_unexpected_offer;
- }
- }
-
- /* Probe all devices. We do this only after completing
- * enumeration since devices will need to send and receive
- * VMBus messages.
- */
- list_for_each_entry ( vmdev, &parent->children, dev.siblings ) {
- if ( ( rc = vmdev->driver->probe ( vmdev ) ) != 0 ) {
- DBGC ( vmdev, "VMBUS %s could not probe: %s\n",
- vmdev->dev.name, strerror ( rc ) );
- goto err_probe;
- }
- }
-
- return 0;
-
- err_probe:
- /* Remove driver from each device that was already probed */
- list_for_each_entry_continue_reverse ( vmdev, &parent->children,
- dev.siblings ) {
- vmdev->driver->remove ( vmdev );
- }
- err_unexpected_offer:
- err_alloc_vmdev:
- err_wait_for_message:
- /* Free any devices allocated (but potentially not yet probed) */
- list_for_each_entry_safe ( vmdev, tmp, &parent->children,
- dev.siblings ) {
- list_del ( &vmdev->dev.siblings );
- free ( vmdev );
- }
- err_post_message:
- return rc;
-}
-
-/**
- * Remove channels
- *
- * @v hv Hyper-V hypervisor
- * @v parent Parent device
- */
-static void vmbus_remove_channels ( struct hv_hypervisor *hv __unused,
- struct device *parent ) {
- struct vmbus_device *vmdev;
- struct vmbus_device *tmp;
-
- /* Remove devices */
- list_for_each_entry_safe ( vmdev, tmp, &parent->children,
- dev.siblings ) {
- vmdev->driver->remove ( vmdev );
- assert ( list_empty ( &vmdev->dev.children ) );
- assert ( vmdev->out == NULL );
- assert ( vmdev->in == NULL );
- assert ( vmdev->packet == NULL );
- assert ( list_empty ( &vmdev->pages ) );
- list_del ( &vmdev->dev.siblings );
- free ( vmdev );
- }
-}
-
-/**
- * Probe Hyper-V virtual machine bus
- *
- * @v hv Hyper-V hypervisor
- * @v parent Parent device
- * @ret rc Return status code
- */
-int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) {
- struct vmbus *vmbus;
- int rc;
-
- /* Allocate and initialise structure */
- vmbus = zalloc ( sizeof ( *vmbus ) );
- if ( ! vmbus ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- hv->vmbus = vmbus;
-
- /* Initialise message buffer pointer
- *
- * We use a pointer to the fixed-size Hyper-V received message
- * buffer. This allows us to access fields within received
- * messages without first checking the message size: any
- * fields beyond the end of the message will read as zero.
- */
- vmbus->message = ( ( void * ) hv->message->received.data );
- assert ( sizeof ( *vmbus->message ) <=
- sizeof ( hv->message->received.data ) );
-
- /* Allocate interrupt and monitor pages */
- if ( ( rc = hv_alloc_pages ( hv, &vmbus->intr, &vmbus->monitor_in,
- &vmbus->monitor_out, NULL ) ) != 0 )
- goto err_alloc_pages;
-
- /* Enable message interrupt */
- hv_enable_sint ( hv, VMBUS_MESSAGE_SINT );
-
- /* Negotiate protocol version */
- if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 )
- goto err_negotiate_version;
-
- /* Enumerate channels */
- if ( ( rc = vmbus_probe_channels ( hv, parent ) ) != 0 )
- goto err_probe_channels;
-
- return 0;
-
- vmbus_remove_channels ( hv, parent );
- err_probe_channels:
- vmbus_unload ( hv );
- err_negotiate_version:
- hv_disable_sint ( hv, VMBUS_MESSAGE_SINT );
- hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out,
- NULL );
- err_alloc_pages:
- free ( vmbus );
- err_alloc:
- return rc;
-}
-
-/**
- * Remove Hyper-V virtual machine bus
- *
- * @v hv Hyper-V hypervisor
- * @v parent Parent device
- */
-void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ) {
- struct vmbus *vmbus = hv->vmbus;
-
- vmbus_remove_channels ( hv, parent );
- vmbus_unload ( hv );
- hv_disable_sint ( hv, VMBUS_MESSAGE_SINT );
- hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out,
- NULL );
- free ( vmbus );
-}