iPXE
Data Structures | Defines | Enumerations | Functions | Variables
vmbus.h File Reference

Hyper-V virtual machine bus. More...

#include <byteswap.h>
#include <ipxe/uuid.h>
#include <ipxe/device.h>
#include <ipxe/tables.h>
#include <ipxe/uaccess.h>
#include <ipxe/iobuf.h>
#include <ipxe/hyperv.h>

Go to the source code of this file.

Data Structures

union  vmbus_version
 VMBus version number. More...
struct  vmbus_gpa_range
 Guest physical address range descriptor. More...
struct  vmbus_message_header
 VMBus message header. More...
struct  vmbus_offer_channel
 VMBus "offer channel" message. More...
struct  vmbus_open_channel
 VMBus "open channel" message. More...
struct  vmbus_open_channel_result
 VMBus "open channel result" message. More...
struct  vmbus_close_channel
 VMBus "close channel" message. More...
struct  vmbus_gpadl_header
 VMBus "GPADL header" message. More...
struct  vmbus_gpadl_created
 VMBus "GPADL created" message. More...
struct  vmbus_gpadl_teardown
 VMBus "GPADL teardown" message. More...
struct  vmbus_gpadl_torndown
 VMBus "GPADL torndown" message. More...
struct  vmbus_initiate_contact
 VMBus "initiate contact" message. More...
struct  vmbus_version_response
 VMBus "version response" message. More...
union  vmbus_message
 VMBus message. More...
struct  vmbus_packet_header
 VMBus packet header. More...
struct  vmbus_gpa_direct_header
 VMBus GPA direct header. More...
struct  vmbus_xfer_page_range
 VMBus transfer page range. More...
struct  vmbus_xfer_page_header
 VMBus transfer page header. More...
union  vmbus_packet_header_max
 VMBus maximum-sized packet header. More...
struct  vmbus_packet_footer
 VMBus packet footer. More...
struct  vmbus_ring
 VMBus ring buffer. More...
struct  vmbus_interrupt
 VMBus interrupt page. More...
struct  vmbus
 A virtual machine bus. More...
struct  vmbus_channel_operations
 VMBus channel operations. More...
struct  vmbus_xfer_pages_operations
 VMBus transfer page set operations. More...
struct  vmbus_xfer_pages
 VMBus transfer page set. More...
struct  vmbus_device
 A VMBus device. More...
struct  vmbus_driver
 A VMBus device driver. More...

Defines

#define VMBUS_MESSAGE_ID   1
 VMBus message connection ID.
#define VMBUS_EVENT_ID   2
 VMBus event connection ID.
#define VMBUS_MESSAGE_TYPE   1
 VMBus message type.
#define VMBUS_MESSAGE_SINT   2
 VMBus message synthetic interrupt.
#define VMBUS_PACKET_MAX_HEADER_LEN   64
 Maximum expected size of VMBus packet header.
#define VMBUS_DRIVERS   __table ( struct vmbus_driver, "vmbus_drivers" )
 VMBus device driver table.
#define __vmbus_driver   __table_entry ( VMBUS_DRIVERS, 01 )
 Declare a VMBus device driver.
#define VMBUS_TYPE(a, b, c, d, e0, e1, e2, e3, e4, e5)
 Construct VMBus type.

Enumerations

enum  vmbus_raw_version { VMBUS_VERSION_WS2008 = ( ( 0 << 16 ) | ( 13 << 0 ) ), VMBUS_VERSION_WIN7 = ( ( 1 << 16 ) | ( 1 << 0 ) ), VMBUS_VERSION_WIN8 = ( ( 2 << 16 ) | ( 4 << 0 ) ), VMBUS_VERSION_WIN8_1 = ( ( 3 << 16 ) | ( 0 << 0 ) ) }
 Known VMBus protocol versions. More...
enum  vmbus_message_type {
  VMBUS_OFFER_CHANNEL = 1, VMBUS_REQUEST_OFFERS = 3, VMBUS_ALL_OFFERS_DELIVERED = 4, VMBUS_OPEN_CHANNEL = 5,
  VMBUS_OPEN_CHANNEL_RESULT = 6, VMBUS_CLOSE_CHANNEL = 7, VMBUS_GPADL_HEADER = 8, VMBUS_GPADL_CREATED = 10,
  VMBUS_GPADL_TEARDOWN = 11, VMBUS_GPADL_TORNDOWN = 12, VMBUS_INITIATE_CONTACT = 14, VMBUS_VERSION_RESPONSE = 15,
  VMBUS_UNLOAD = 16, VMBUS_UNLOAD_RESPONSE = 17
}
 VMBus message types. More...
enum  vmbus_packet_type {
  VMBUS_DATA_INBAND = 6, VMBUS_DATA_XFER_PAGES = 7, VMBUS_DATA_GPA_DIRECT = 9, VMBUS_CANCELLATION = 10,
  VMBUS_COMPLETION = 11
}
 VMBus packet types. More...
enum  vmbus_packet_flags { VMBUS_COMPLETION_REQUESTED = 0x0001 }
 VMBus packet flags. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void vmbus_set_drvdata (struct vmbus_device *vmdev, void *priv)
 Set VMBus device driver-private data.
static void * vmbus_get_drvdata (struct vmbus_device *vmdev)
 Get VMBus device driver-private data.
static int vmbus_has_data (struct vmbus_device *vmdev)
 Check if data is present in ring buffer.
static int vmbus_register_pages (struct vmbus_device *vmdev, struct vmbus_xfer_pages *pages)
 Register transfer page set.
static void vmbus_unregister_pages (struct vmbus_device *vmdev, struct vmbus_xfer_pages *pages)
 Unregister transfer page set.
static int vmbus_gpadl_is_obsolete (unsigned int gpadl)
 Check if GPADL is obsolete.
int vmbus_establish_gpadl (struct vmbus_device *vmdev, userptr_t data, size_t len)
 Establish GPA descriptor list.
int vmbus_gpadl_teardown (struct vmbus_device *vmdev, unsigned int gpadl)
 Tear down GPA descriptor list.
int vmbus_open (struct vmbus_device *vmdev, struct vmbus_channel_operations *op, size_t out_len, size_t in_len, size_t mtu)
 Open VMBus channel.
void vmbus_close (struct vmbus_device *vmdev)
 Close VMBus channel.
int vmbus_send_control (struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
 Send control packet via ring buffer.
int vmbus_send_data (struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len, struct io_buffer *iobuf)
 Send data packet via ring buffer.
int vmbus_send_completion (struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
 Send completion packet via ring buffer.
int vmbus_send_cancellation (struct vmbus_device *vmdev, uint64_t xid)
 Send cancellation packet via ring buffer.
int vmbus_poll (struct vmbus_device *vmdev)
 Poll ring buffer.
void vmbus_dump_channel (struct vmbus_device *vmdev)
 Dump channel status (for debugging)
int vmbus_probe (struct hv_hypervisor *hv, struct device *parent)
 Probe Hyper-V virtual machine bus.
int vmbus_reset (struct hv_hypervisor *hv, struct device *parent)
 Reset Hyper-V virtual machine bus.
void vmbus_remove (struct hv_hypervisor *hv, struct device *parent)
 Remove Hyper-V virtual machine bus.

Variables

union vmbus_version __attribute__
unsigned int vmbus_obsolete_gpadl
 Obsolete GPADL ID threshold.

Detailed Description

Hyper-V virtual machine bus.

Definition in file vmbus.h.


Define Documentation

#define VMBUS_MESSAGE_ID   1

VMBus message connection ID.

Definition at line 21 of file vmbus.h.

Referenced by vmbus_post_message().

#define VMBUS_EVENT_ID   2

VMBus event connection ID.

Definition at line 24 of file vmbus.h.

Referenced by vmbus_signal_event().

#define VMBUS_MESSAGE_TYPE   1

VMBus message type.

Definition at line 27 of file vmbus.h.

Referenced by vmbus_post_message(), and vmbus_wait_for_any_message().

#define VMBUS_MESSAGE_SINT   2

VMBus message synthetic interrupt.

Definition at line 30 of file vmbus.h.

Referenced by vmbus_probe(), vmbus_remove(), vmbus_reset(), and vmbus_wait_for_any_message().

#define VMBUS_PACKET_MAX_HEADER_LEN   64

Maximum expected size of VMBus packet header.

Definition at line 334 of file vmbus.h.

#define VMBUS_DRIVERS   __table ( struct vmbus_driver, "vmbus_drivers" )

VMBus device driver table.

Definition at line 546 of file vmbus.h.

Referenced by vmbus_find_driver().

Declare a VMBus device driver.

Definition at line 549 of file vmbus.h.

#define VMBUS_TYPE (   a,
  b,
  c,
  d,
  e0,
  e1,
  e2,
  e3,
  e4,
  e5 
)
Value:
{               \
        .canonical = {                                                  \
                cpu_to_le32 ( a ), cpu_to_le16 ( b ),                   \
                cpu_to_le16 ( c ), cpu_to_be16 ( d ),                   \
                { e0, e1, e2, e3, e4, e5 }                              \
         } }

Construct VMBus type.

Definition at line 572 of file vmbus.h.


Enumeration Type Documentation

Known VMBus protocol versions.

Enumerator:
VMBUS_VERSION_WS2008 

Windows Server 2008.

VMBUS_VERSION_WIN7 

Windows 7.

VMBUS_VERSION_WIN8 

Windows 8.

VMBUS_VERSION_WIN8_1 

Windows 8.1.

Definition at line 46 of file vmbus.h.

                       {
        /** Windows Server 2008 */
        VMBUS_VERSION_WS2008 = ( ( 0 << 16 ) | ( 13 << 0 ) ),
        /** Windows 7 */
        VMBUS_VERSION_WIN7 = ( ( 1 << 16 ) | ( 1 << 0 ) ),
        /** Windows 8 */
        VMBUS_VERSION_WIN8 = ( ( 2 << 16 ) | ( 4 << 0 ) ),
        /** Windows 8.1 */
        VMBUS_VERSION_WIN8_1 = ( ( 3 << 16 ) | ( 0 << 0 ) ),
};

VMBus message types.

Enumerator:
VMBUS_OFFER_CHANNEL 
VMBUS_REQUEST_OFFERS 
VMBUS_ALL_OFFERS_DELIVERED 
VMBUS_OPEN_CHANNEL 
VMBUS_OPEN_CHANNEL_RESULT 
VMBUS_CLOSE_CHANNEL 
VMBUS_GPADL_HEADER 
VMBUS_GPADL_CREATED 
VMBUS_GPADL_TEARDOWN 
VMBUS_GPADL_TORNDOWN 
VMBUS_INITIATE_CONTACT 
VMBUS_VERSION_RESPONSE 
VMBUS_UNLOAD 
VMBUS_UNLOAD_RESPONSE 

Definition at line 80 of file vmbus.h.

VMBus packet types.

Enumerator:
VMBUS_DATA_INBAND 
VMBUS_DATA_XFER_PAGES 
VMBUS_DATA_GPA_DIRECT 
VMBUS_CANCELLATION 
VMBUS_COMPLETION 

Definition at line 284 of file vmbus.h.

VMBus packet flags.

Enumerator:
VMBUS_COMPLETION_REQUESTED 

Definition at line 293 of file vmbus.h.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void vmbus_set_drvdata ( struct vmbus_device vmdev,
void *  priv 
) [inline, static]

Set VMBus device driver-private data.

Parameters:
vmdevVMBus device
privPrivate data

Definition at line 557 of file vmbus.h.

References priv, and vmbus_device::priv.

Referenced by netvsc_probe().

                                                                               {
        vmdev->priv = priv;
}
static void* vmbus_get_drvdata ( struct vmbus_device vmdev) [inline, static]

Get VMBus device driver-private data.

Parameters:
vmdevVMBus device
Return values:
privPrivate data

Definition at line 567 of file vmbus.h.

References vmbus_device::priv.

Referenced by netvsc_recv_cancellation(), netvsc_recv_completion(), netvsc_recv_control(), netvsc_recv_data(), netvsc_remove(), and netvsc_reset().

                                                                      {
        return vmdev->priv;
}
static int vmbus_has_data ( struct vmbus_device vmdev) [inline, static]

Check if data is present in ring buffer.

Parameters:
vmdevVMBus device
has_dataData is present

Definition at line 586 of file vmbus.h.

Referenced by netvsc_poll(), and vmbus_poll().

                                              {

        return ( vmdev->in->prod != vmdev->in->cons );
}
static int vmbus_register_pages ( struct vmbus_device vmdev,
struct vmbus_xfer_pages pages 
) [inline, static]

Register transfer page set.

Parameters:
vmdevVMBus device
pagesTransfer page set
Return values:
rcReturn status code

Definition at line 599 of file vmbus.h.

References list_add.

Referenced by netvsc_create_buffer().

                                                        {

        list_add ( &pages->list, &vmdev->pages );
        return 0;
}
static void vmbus_unregister_pages ( struct vmbus_device vmdev,
struct vmbus_xfer_pages pages 
) [inline, static]

Unregister transfer page set.

Parameters:
vmdevVMBus device
pagesTransfer page set

Definition at line 613 of file vmbus.h.

References list_check_contains_entry, and list_del.

Referenced by netvsc_create_buffer(), and netvsc_destroy_buffer().

                                                          {

        list_check_contains_entry ( pages, &vmdev->pages, list );
        list_del ( &pages->list );
}
static int vmbus_gpadl_is_obsolete ( unsigned int  gpadl) [inline, static]

Check if GPADL is obsolete.

Parameters:
gpadlGPADL ID
is_obsoleteGPADL ID is obsolete

Check if GPADL is obsolete (i.e. was created before the most recent Hyper-V reset).

Definition at line 632 of file vmbus.h.

Referenced by vmbus_gpadl_teardown().

                                               {

        return ( gpadl <= vmbus_obsolete_gpadl );
}
int vmbus_establish_gpadl ( struct vmbus_device vmdev,
userptr_t  data,
size_t  len 
)

Establish GPA descriptor list.

Parameters:
vmdevVMBus device
dataData buffer
lenLength of data buffer
Return values:
gpadlGPADL ID, or negative error

Definition at line 276 of file vmbus.c.

References __attribute__, addr, vmbus_gpadl_created::channel, vmbus_device::channel, cpu_to_le16, cpu_to_le32, vmbus_message::created, DBGC, vmbus_device::dev, EPROTO, gpadl, vmbus_gpadl_created::gpadl, vmbus_device::hv, hv_pfn_count(), le32_to_cpu, memset(), vmbus::message, device::name, PAGE_SIZE, vmbus_gpa_range::pfn, rc, vmbus_gpadl_created::status, user_to_phys(), hv_hypervisor::vmbus, vmbus_gpadl, VMBUS_GPADL_CREATED, VMBUS_GPADL_HEADER, vmbus_post_message(), and vmbus_wait_for_message().

Referenced by netvsc_create_buffer(), and vmbus_open().

                                         {
        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;
        unsigned int gpadl;
        unsigned int i;
        int rc;

        /* Allocate GPADL ID */
        gpadl = ++vmbus_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, VMBUS_GPADL_CREATED ) ) != 0 )
                return rc;

        /* Check response */
        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;
}
int vmbus_gpadl_teardown ( struct vmbus_device vmdev,
unsigned int  gpadl 
)

Tear down GPA descriptor list.

Parameters:
vmdevVMBus device
gpadlGPADL ID
Return values:
rcReturn status code

Definition at line 347 of file vmbus.c.

References vmbus_gpadl_teardown::channel, vmbus_device::channel, cpu_to_le32, DBGC, vmbus_device::dev, EPROTO, vmbus_gpadl_teardown::gpadl, vmbus_gpadl_torndown::gpadl, vmbus_gpadl_teardown::header, vmbus_device::hv, le32_to_cpu, memset(), vmbus::message, device::name, rc, vmbus_message::torndown, vmbus_message_header::type, hv_hypervisor::vmbus, vmbus_gpadl_is_obsolete(), VMBUS_GPADL_TEARDOWN, VMBUS_GPADL_TORNDOWN, vmbus_post_message(), and vmbus_wait_for_message().

Referenced by netvsc_create_buffer(), and vmbus_open().

                                                                            {
        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;

        /* If GPADL is obsolete (i.e. was created before the most
         * recent Hyper-V reset), then we will never receive a
         * response to the teardown message.  Since the GPADL is
         * already destroyed as far as the hypervisor is concerned, no
         * further action is required.
         */
        if ( vmbus_gpadl_is_obsolete ( gpadl ) )
                return 0;

        /* 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, VMBUS_GPADL_TORNDOWN ) ) != 0 )
                return rc;

        /* Check response */
        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;
}
int vmbus_open ( struct vmbus_device vmdev,
struct vmbus_channel_operations op,
size_t  out_len,
size_t  in_len,
size_t  mtu 
)

Open VMBus channel.

Parameters:
vmdevVMBus device
opChannel operations
out_lenOutbound ring buffer length
in_lenInbound ring buffer length
mtuMaximum expected data packet length (including headers)
Return values:
rcReturn 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.

Definition at line 403 of file vmbus.c.

References assert, vmbus_open_channel::channel, vmbus_open_channel_result::channel, vmbus_device::channel, cpu_to_le32, DBGC, vmbus_device::dev, ENOMEM, EPROTO, free, free_dma(), vmbus_open_channel::gpadl, gpadl, vmbus_device::gpadl, vmbus_open_channel::header, vmbus_device::hv, vmbus_open_channel::id, vmbus_open_channel_result::id, vmbus_device::in, vmbus_device::in_len, le32_to_cpu, len, malloc(), malloc_dma(), memset(), vmbus::message, mtu, vmbus_device::mtu, device::name, op, vmbus_device::op, vmbus_message::opened, vmbus_device::out, vmbus_device::out_len, vmbus_open_channel::out_pages, vmbus_device::packet, PAGE_SIZE, random(), rc, vmbus_open_channel_result::status, vmbus_message_header::type, virt_to_phys(), virt_to_user(), hv_hypervisor::vmbus, vmbus_establish_gpadl(), vmbus_gpadl_teardown(), VMBUS_OPEN_CHANNEL, VMBUS_OPEN_CHANNEL_RESULT, vmbus_post_message(), and vmbus_wait_for_message().

Referenced by netvsc_open().

                                                             {
        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 )
                goto err_post_message;

        /* Wait for response */
        if ( ( rc = vmbus_wait_for_message ( hv,
                                             VMBUS_OPEN_CHANNEL_RESULT ) ) != 0)
                goto err_wait_for_message;

        /* Check response */
        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 ) );
                rc = -EPROTO;
                goto err_check_response;
        }
        if ( opened->id != open_id /* Non-endian */ ) {
                DBGC ( vmdev, "VMBUS %s unexpected open ID %#08x\n",
                       vmdev->dev.name, le32_to_cpu ( opened->id ) );
                rc = -EPROTO;
                goto err_check_response;
        }
        if ( opened->status != 0 ) {
                DBGC ( vmdev, "VMBUS %s open failed: %#08x\n",
                       vmdev->dev.name, le32_to_cpu ( opened->status ) );
                rc = -EPROTO;
                goto err_check_response;
        }

        /* 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;

 err_check_response:
 err_wait_for_message:
 err_post_message:
        vmbus_gpadl_teardown ( vmdev, vmdev->gpadl );
 err_establish:
        free_dma ( ring, len );
 err_alloc_ring:
        free ( packet );
 err_alloc_packet:
        return rc;
}
void vmbus_close ( struct vmbus_device vmdev)

Close VMBus channel.

Parameters:
vmdevVMBus device

Definition at line 524 of file vmbus.c.

References vmbus_close_channel::channel, vmbus_device::channel, cpu_to_le32, DBGC, vmbus_device::dev, free, free_dma(), vmbus_device::gpadl, vmbus_close_channel::header, vmbus_device::hv, vmbus_device::in, vmbus_device::in_len, len, memset(), device::name, NULL, vmbus_device::out, vmbus_device::out_len, vmbus_device::packet, rc, strerror(), vmbus_message_header::type, VMBUS_CLOSE_CHANNEL, and vmbus_post_message().

Referenced by netvsc_close(), and netvsc_open().

                                                {
        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 );
}
int vmbus_send_control ( struct vmbus_device vmdev,
uint64_t  xid,
const void *  data,
size_t  len 
)

Send control packet via ring buffer.

Parameters:
vmdevVMBus device
xidTransaction ID (or zero to not request completion)
dataData
lenLength of data
Return values:
rcReturn status code

Send data using a VMBUS_DATA_INBAND packet.

Definition at line 765 of file vmbus.c.

References assert, cpu_to_le16, vmbus_packet_header::flags, vmbus_packet_header::hdr_qlen, header, NULL, vmbus_device::packet, vmbus_packet_header::type, VMBUS_COMPLETION_REQUESTED, VMBUS_DATA_INBAND, vmbus_send(), and vmbus_packet_header::xid.

Referenced by netvsc_control().

                                                        {
        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 );
}
int vmbus_send_data ( struct vmbus_device vmdev,
uint64_t  xid,
const void *  data,
size_t  len,
struct io_buffer iobuf 
)

Send data packet via ring buffer.

Parameters:
vmdevVMBus device
xidTransaction ID
dataData
lenLength of data
iobufI/O buffer
Return values:
rcReturn 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.

Definition at line 794 of file vmbus.c.

References __attribute__, addr, assert, cpu_to_le16, cpu_to_le32, io_buffer::data, ena_aq_header::flags, header, hv_pfn_count(), iob_len(), vmbus_device::mtu, NULL, vmbus_device::packet, PAGE_SIZE, vmbus_gpa_range::pfn, virt_to_phys(), VMBUS_COMPLETION_REQUESTED, VMBUS_DATA_GPA_DIRECT, and vmbus_send().

Referenced by netvsc_transmit().

                                                                              {
        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 );
}
int vmbus_send_completion ( struct vmbus_device vmdev,
uint64_t  xid,
const void *  data,
size_t  len 
)

Send completion packet via ring buffer.

Parameters:
vmdevVMBus device
xidTransaction ID
dataData
lenLength of data
Return values:
rcReturn status code

Send data using a VMBUS_COMPLETION packet.

Definition at line 834 of file vmbus.c.

References assert, cpu_to_le16, vmbus_packet_header::flags, vmbus_packet_header::hdr_qlen, header, NULL, vmbus_device::packet, vmbus_packet_header::type, VMBUS_COMPLETION, vmbus_send(), and vmbus_packet_header::xid.

Referenced by netvsc_recv_data().

                                                           {
        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 );
}
int vmbus_send_cancellation ( struct vmbus_device vmdev,
uint64_t  xid 
)

Send cancellation packet via ring buffer.

Parameters:
vmdevVMBus device
xidTransaction ID
Return values:
rcReturn status code

Send data using a VMBUS_CANCELLATION packet.

Definition at line 857 of file vmbus.c.

References assert, cpu_to_le16, vmbus_packet_header::flags, vmbus_packet_header::hdr_qlen, header, NULL, vmbus_device::packet, vmbus_packet_header::type, VMBUS_CANCELLATION, vmbus_send(), and vmbus_packet_header::xid.

Referenced by netvsc_cancel_transmit().

                                                                         {
        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 );
}
int vmbus_poll ( struct vmbus_device vmdev)

Poll ring buffer.

Parameters:
vmdevVMBus device
Return values:
rcReturn status code

Definition at line 972 of file vmbus.c.

References assert, cons, vmbus_ring::cons, cpu_to_le16, cpu_to_le32, data, DBGC, DBGC2, DBGC2_HDA, vmbus_device::dev, EINVAL, ENOTSUP, ERANGE, vmbus_packet_header::hdr_qlen, header, vmbus_device::in, vmbus_device::in_len, INIT_LIST_HEAD, le16_to_cpu, le32_to_cpu, le64_to_cpu, len, vmbus_device::mtu, device::name, NULL, vmbus_device::op, vmbus_device::packet, vmbus_packet_header::qlen, rc, vmbus_channel_operations::recv_cancellation, vmbus_channel_operations::recv_completion, vmbus_channel_operations::recv_control, vmbus_channel_operations::recv_data, rmb, strerror(), VMBUS_CANCELLATION, VMBUS_COMPLETION, vmbus_consume(), VMBUS_DATA_INBAND, VMBUS_DATA_XFER_PAGES, vmbus_has_data(), vmbus_xfer_page_iobufs(), and vmbus_packet_header::xid.

Referenced by netvsc_control(), and netvsc_poll().

                                              {
        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;
}
void vmbus_dump_channel ( struct vmbus_device vmdev)

Dump channel status (for debugging)

Parameters:
vmdevVMBus device

Definition at line 1089 of file vmbus.c.

References vmbus_ring::cons, vmbus_ring::data, DBGC, DBGC_HDA, vmbus_device::dev, first, vmbus_device::in, vmbus_device::in_len, vmbus_ring::intr_mask, le32_to_cpu, device::name, vmbus_device::out, and vmbus_ring::prod.

Referenced by netvsc_control().

                                                       {
        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 );
        }
}
int vmbus_probe ( struct hv_hypervisor hv,
struct device parent 
)

Probe Hyper-V virtual machine bus.

Parameters:
hvHyper-V hypervisor
parentParent device
Return values:
rcReturn status code

Definition at line 1365 of file vmbus.c.

References assert, hv_message::data, ENOMEM, free, hv_alloc_pages(), hv_disable_sint(), hv_enable_sint(), hv_free_pages(), vmbus::intr, hv_hypervisor::message, vmbus::message, vmbus::monitor_in, vmbus::monitor_out, NULL, rc, hv_message_buffer::received, hv_hypervisor::vmbus, VMBUS_MESSAGE_SINT, vmbus_negotiate_version(), vmbus_probe_channels(), vmbus_remove_channels(), vmbus_unload(), and zalloc().

Referenced by hv_probe().

                                                                    {
        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;
}
int vmbus_reset ( struct hv_hypervisor hv,
struct device parent 
)

Reset Hyper-V virtual machine bus.

Parameters:
hvHyper-V hypervisor
parentParent device
Return values:
rcReturn status code

Definition at line 1426 of file vmbus.c.

References hv_enable_sint(), vmbus::intr, memset(), vmbus::monitor_in, vmbus::monitor_out, PAGE_SIZE, rc, hv_hypervisor::vmbus, vmbus_gpadl, VMBUS_MESSAGE_SINT, vmbus_negotiate_version(), vmbus_obsolete_gpadl, and vmbus_reset_channels().

Referenced by hv_unquiesce().

                                                                    {
        struct vmbus *vmbus = hv->vmbus;
        int rc;

        /* Mark all existent GPADLs as obsolete */
        vmbus_obsolete_gpadl = vmbus_gpadl;

        /* Clear interrupt and monitor pages */
        memset ( vmbus->intr, 0, PAGE_SIZE );
        memset ( vmbus->monitor_in, 0, PAGE_SIZE );
        memset ( vmbus->monitor_out, 0, PAGE_SIZE );

        /* Enable message interrupt */
        hv_enable_sint ( hv, VMBUS_MESSAGE_SINT );

        /* Renegotiate protocol version */
        if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 )
                return rc;

        /* Reenumerate channels */
        if ( ( rc = vmbus_reset_channels ( hv, parent ) ) != 0 )
                return rc;

        return 0;
}
void vmbus_remove ( struct hv_hypervisor hv,
struct device parent 
)

Remove Hyper-V virtual machine bus.

Parameters:
hvHyper-V hypervisor
parentParent device

Definition at line 1458 of file vmbus.c.

References free, hv_disable_sint(), hv_free_pages(), vmbus::intr, vmbus::monitor_in, vmbus::monitor_out, NULL, hv_hypervisor::vmbus, VMBUS_MESSAGE_SINT, vmbus_remove_channels(), and vmbus_unload().

Referenced by hv_probe(), and hv_remove().

                                                                      {
        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 );
}

Variable Documentation

unsigned int vmbus_obsolete_gpadl

Obsolete GPADL ID threshold.

When the Hyper-V connection is reset, any previous GPADLs are automatically rendered obsolete.

Definition at line 61 of file vmbus.c.

Referenced by vmbus_reset().