david/ipxe
david
/
ipxe
Archived
1
0
Fork 0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/include/ipxe/vmbus.h

661 lines
16 KiB
C
Raw Normal View History

#ifndef _IPXE_VMBUS_H
#define _IPXE_VMBUS_H
/** @file
*
* Hyper-V virtual machine bus
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#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>
/** VMBus message connection ID */
#define VMBUS_MESSAGE_ID 1
/** VMBus event connection ID */
#define VMBUS_EVENT_ID 2
/** VMBus message type */
#define VMBUS_MESSAGE_TYPE 1
/** VMBus message synthetic interrupt */
#define VMBUS_MESSAGE_SINT 2
/** VMBus version number */
union vmbus_version {
/** Raw version */
uint32_t raw;
/** Major/minor version */
struct {
/** Minor version */
uint16_t minor;
/** Major version */
uint16_t major;
};
} __attribute__ (( packed ));
/** Known VMBus protocol versions */
enum vmbus_raw_version {
/** 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 ) ),
};
/** Guest physical address range descriptor */
struct vmbus_gpa_range {
/** Byte count */
uint32_t len;
/** Starting byte offset */
uint32_t offset;
/** Page frame numbers
*
* The length of this array is implied by the byte count and
* starting offset.
*/
uint64_t pfn[0];
} __attribute__ (( packed ));
/** VMBus message header */
struct vmbus_message_header {
/** Message type */
uint32_t type;
/** Reserved */
uint32_t reserved;
} __attribute__ (( packed ));
/** VMBus message types */
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 "offer channel" message */
struct vmbus_offer_channel {
/** Message header */
struct vmbus_message_header header;
/** Channel type */
union uuid type;
/** Channel instance */
union uuid instance;
/** Reserved */
uint8_t reserved_a[16];
/** Flags */
uint16_t flags;
/** Reserved */
uint8_t reserved_b[2];
/** User data */
uint8_t data[120];
/** Reserved */
uint8_t reserved_c[4];
/** Channel ID */
uint32_t channel;
/** Monitor ID */
uint8_t monitor;
/** Monitor exists */
uint8_t monitored;
/** Reserved */
uint8_t reserved[2];
/** Connection ID */
uint32_t connection;
} __attribute__ (( packed ));
/** VMBus "open channel" message */
struct vmbus_open_channel {
/** Message header */
struct vmbus_message_header header;
/** Channel ID */
uint32_t channel;
/** Open ID */
uint32_t id;
/** Ring buffer GPADL ID */
uint32_t gpadl;
/** Reserved */
uint32_t reserved;
/** Outbound ring buffer size (in pages) */
uint32_t out_pages;
/** User-specific data */
uint8_t data[120];
} __attribute__ (( packed ));
/** VMBus "open channel result" message */
struct vmbus_open_channel_result {
/** Message header */
struct vmbus_message_header header;
/** Channel ID */
uint32_t channel;
/** Open ID */
uint32_t id;
/** Status */
uint32_t status;
} __attribute__ (( packed ));
/** VMBus "close channel" message */
struct vmbus_close_channel {
/** Message header */
struct vmbus_message_header header;
/** Channel ID */
uint32_t channel;
} __attribute__ (( packed ));
/** VMBus "GPADL header" message */
struct vmbus_gpadl_header {
/** Message header */
struct vmbus_message_header header;
/** Channel ID */
uint32_t channel;
/** GPADL ID */
uint32_t gpadl;
/** Length of range descriptors */
uint16_t range_len;
/** Number of range descriptors */
uint16_t range_count;
/** Range descriptors */
struct vmbus_gpa_range range[0];
} __attribute__ (( packed ));
/** VMBus "GPADL created" message */
struct vmbus_gpadl_created {
/** Message header */
struct vmbus_message_header header;
/** Channel ID */
uint32_t channel;
/** GPADL ID */
uint32_t gpadl;
/** Creation status */
uint32_t status;
} __attribute__ (( packed ));
/** VMBus "GPADL teardown" message */
struct vmbus_gpadl_teardown {
/** Message header */
struct vmbus_message_header header;
/** Channel ID */
uint32_t channel;
/** GPADL ID */
uint32_t gpadl;
} __attribute__ (( packed ));
/** VMBus "GPADL torndown" message */
struct vmbus_gpadl_torndown {
/** Message header */
struct vmbus_message_header header;
/** GPADL ID */
uint32_t gpadl;
} __attribute__ (( packed ));
/** VMBus "initiate contact" message */
struct vmbus_initiate_contact {
/** Message header */
struct vmbus_message_header header;
/** Requested version */
union vmbus_version version;
/** Target virtual CPU */
uint32_t vcpu;
/** Interrupt page base address */
uint64_t intr;
/** Parent to child monitor page base address */
uint64_t monitor_in;
/** Child to parent monitor page base address */
uint64_t monitor_out;
} __attribute__ (( packed ));
/** VMBus "version response" message */
struct vmbus_version_response {
/** Message header */
struct vmbus_message_header header;
/** Version is supported */
uint8_t supported;
/** Reserved */
uint8_t reserved[3];
/** Version */
union vmbus_version version;
} __attribute__ (( packed ));
/** VMBus message */
union vmbus_message {
/** Common message header */
struct vmbus_message_header header;
/** "Offer channel" message */
struct vmbus_offer_channel offer;
/** "Open channel" message */
struct vmbus_open_channel open;
/** "Open channel result" message */
struct vmbus_open_channel_result opened;
/** "Close channel" message */
struct vmbus_close_channel close;
/** "GPADL header" message */
struct vmbus_gpadl_header gpadlhdr;
/** "GPADL created" message */
struct vmbus_gpadl_created created;
/** "GPADL teardown" message */
struct vmbus_gpadl_teardown teardown;
/** "GPADL torndown" message */
struct vmbus_gpadl_torndown torndown;
/** "Initiate contact" message */
struct vmbus_initiate_contact initiate;
/** "Version response" message */
struct vmbus_version_response version;
};
/** VMBus packet header */
struct vmbus_packet_header {
/** Type */
uint16_t type;
/** Length of packet header (in quadwords) */
uint16_t hdr_qlen;
/** Length of packet (in quadwords) */
uint16_t qlen;
/** Flags */
uint16_t flags;
/** Transaction ID
*
* This is an opaque token: we therefore treat it as
* native-endian and don't worry about byte-swapping.
*/
uint64_t xid;
} __attribute__ (( packed ));
/** VMBus packet types */
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 flags */
enum vmbus_packet_flags {
VMBUS_COMPLETION_REQUESTED = 0x0001,
};
/** VMBus GPA direct header */
struct vmbus_gpa_direct_header {
/** Packet header */
struct vmbus_packet_header header;
/** Reserved */
uint32_t reserved;
/** Number of range descriptors */
uint32_t range_count;
/** Range descriptors */
struct vmbus_gpa_range range[0];
} __attribute__ (( packed ));
/** VMBus transfer page range */
struct vmbus_xfer_page_range {
/** Length */
uint32_t len;
/** Offset */
uint32_t offset;
} __attribute__ (( packed ));
/** VMBus transfer page header */
struct vmbus_xfer_page_header {
/** Packet header */
struct vmbus_packet_header header;
/** Page set ID */
uint16_t pageset;
/** Sender owns page set */
uint8_t owner;
/** Reserved */
uint8_t reserved;
/** Number of range descriptors */
uint32_t range_count;
/** Range descriptors */
struct vmbus_xfer_page_range range[0];
} __attribute__ (( packed ));
/** Maximum expected size of VMBus packet header */
#define VMBUS_PACKET_MAX_HEADER_LEN 64
/** VMBus maximum-sized packet header */
union vmbus_packet_header_max {
/** Common header */
struct vmbus_packet_header header;
/** GPA direct header */
struct vmbus_gpa_direct_header gpa;
/** Transfer page header */
struct vmbus_xfer_page_header xfer;
/** Padding to maximum supported size */
uint8_t padding[VMBUS_PACKET_MAX_HEADER_LEN];
} __attribute__ (( packed ));
/** VMBus packet footer */
struct vmbus_packet_footer {
/** Reserved */
uint32_t reserved;
/** Producer index of the first byte of the packet */
uint32_t prod;
} __attribute__ (( packed ));
/** VMBus ring buffer
*
* This is the structure of the each of the ring buffers created when
* a VMBus channel is opened.
*/
struct vmbus_ring {
/** Producer index (modulo ring length) */
uint32_t prod;
/** Consumer index (modulo ring length) */
uint32_t cons;
/** Interrupt mask */
uint32_t intr_mask;
/** Reserved */
uint8_t reserved[4084];
/** Ring buffer contents */
uint8_t data[0];
} __attribute__ (( packed ));
/** VMBus interrupt page */
struct vmbus_interrupt {
/** Inbound interrupts */
uint8_t in[ PAGE_SIZE / 2 ];
/** Outbound interrupts */
uint8_t out[ PAGE_SIZE / 2 ];
} __attribute__ (( packed ));
/** A virtual machine bus */
struct vmbus {
/** Interrupt page */
struct vmbus_interrupt *intr;
/** Inbound notifications */
struct hv_monitor *monitor_in;
/** Outbound notifications */
struct hv_monitor *monitor_out;
/** Received message buffer */
const union vmbus_message *message;
};
struct vmbus_device;
/** VMBus channel operations */
struct vmbus_channel_operations {
/**
* Handle received control packet
*
* @v vmdev VMBus device
* @v xid Transaction ID
* @v data Data
* @v len Length of data
* @ret rc Return status code
*/
int ( * recv_control ) ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len );
/**
* Handle received data packet
*
* @v vmdev VMBus device
* @v xid Transaction ID
* @v data Data
* @v len Length of data
[hyperv] Assume that VMBus xfer page ranges correspond to RNDIS messages The (undocumented) VMBus protocol seems to allow for transfer page-based packets where the data payload is split into an arbitrary set of ranges within the transfer page set. The RNDIS protocol includes a length field within the header of each message, and it is known from observation that multiple RNDIS messages can be concatenated into a single VMBus message. iPXE currently assumes that the transfer page range boundaries are entirely arbitrary, and uses the RNDIS header length to determine the RNDIS message boundaries. Windows Server 2012 R2 generates an RNDIS_INDICATE_STATUS_MSG for an undocumented and unknown status code (0x40020006) with a malformed RNDIS header length: the length does not cover the StatusBuffer portion of the message. This causes iPXE to report a malformed RNDIS message and to discard any further RNDIS messages within the same VMBus message. The Linux Hyper-V driver assumes that the transfer page range boundaries correspond to RNDIS message boundaries, and so does not notice the malformed length field in the RNDIS header. Match the behaviour of the Linux Hyper-V driver: assume that the transfer page range boundaries correspond to the RNDIS message boundaries and ignore the RNDIS header length. This avoids triggering the "malformed packet" error and also avoids unnecessary data copying: since we now have one I/O buffer per RNDIS message, there is no longer any need to use iob_split(). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2014-12-20 22:01:27 +01:00
* @v list List of I/O buffers
* @ret rc Return status code
*
* This function takes ownership of the I/O buffer. It should
* eventually call vmbus_send_completion() to indicate to the
* host that the buffer can be reused.
*/
int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len,
[hyperv] Assume that VMBus xfer page ranges correspond to RNDIS messages The (undocumented) VMBus protocol seems to allow for transfer page-based packets where the data payload is split into an arbitrary set of ranges within the transfer page set. The RNDIS protocol includes a length field within the header of each message, and it is known from observation that multiple RNDIS messages can be concatenated into a single VMBus message. iPXE currently assumes that the transfer page range boundaries are entirely arbitrary, and uses the RNDIS header length to determine the RNDIS message boundaries. Windows Server 2012 R2 generates an RNDIS_INDICATE_STATUS_MSG for an undocumented and unknown status code (0x40020006) with a malformed RNDIS header length: the length does not cover the StatusBuffer portion of the message. This causes iPXE to report a malformed RNDIS message and to discard any further RNDIS messages within the same VMBus message. The Linux Hyper-V driver assumes that the transfer page range boundaries correspond to RNDIS message boundaries, and so does not notice the malformed length field in the RNDIS header. Match the behaviour of the Linux Hyper-V driver: assume that the transfer page range boundaries correspond to the RNDIS message boundaries and ignore the RNDIS header length. This avoids triggering the "malformed packet" error and also avoids unnecessary data copying: since we now have one I/O buffer per RNDIS message, there is no longer any need to use iob_split(). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2014-12-20 22:01:27 +01:00
struct list_head *list );
/**
* Handle received completion packet
*
* @v vmdev VMBus device
* @v xid Transaction ID
* @v data Data
* @v len Length of data
* @ret rc Return status code
*/
int ( * recv_completion ) ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len );
/**
* Handle received cancellation packet
*
* @v vmdev VMBus device
* @v xid Transaction ID
* @ret rc Return status code
*/
int ( * recv_cancellation ) ( struct vmbus_device *vmdev,
uint64_t xid );
};
struct vmbus_xfer_pages;
/** VMBus transfer page set operations */
struct vmbus_xfer_pages_operations {
/**
* Copy data from transfer page
*
* @v pages Transfer page set
* @v data Data buffer
* @v offset Offset within page set
* @v len Length within page set
* @ret rc Return status code
*/
int ( * copy ) ( struct vmbus_xfer_pages *pages, void *data,
size_t offset, size_t len );
};
/** VMBus transfer page set */
struct vmbus_xfer_pages {
/** List of all transfer page sets */
struct list_head list;
/** Page set ID (in protocol byte order) */
uint16_t pageset;
/** Page set operations */
struct vmbus_xfer_pages_operations *op;
};
/** A VMBus device */
struct vmbus_device {
/** Generic iPXE device */
struct device dev;
/** Hyper-V hypervisor */
struct hv_hypervisor *hv;
/** Channel instance */
union uuid instance;
/** Channel ID */
unsigned int channel;
/** Monitor ID */
unsigned int monitor;
/** Signal channel
*
* @v vmdev VMBus device
*/
void ( * signal ) ( struct vmbus_device *vmdev );
/** Outbound ring buffer length */
uint32_t out_len;
/** Inbound ring buffer length */
uint32_t in_len;
/** Outbound ring buffer */
struct vmbus_ring *out;
/** Inbound ring buffer */
struct vmbus_ring *in;
/** Ring buffer GPADL ID */
unsigned int gpadl;
/** Channel operations */
struct vmbus_channel_operations *op;
/** Maximum expected data packet length */
size_t mtu;
/** Packet buffer */
void *packet;
/** List of transfer page sets */
struct list_head pages;
/** Driver */
struct vmbus_driver *driver;
/** Driver-private data */
void *priv;
};
/** A VMBus device driver */
struct vmbus_driver {
/** Name */
const char *name;
/** Device type */
union uuid type;
/** Probe device
*
* @v vmdev VMBus device
* @ret rc Return status code
*/
int ( * probe ) ( struct vmbus_device *vmdev );
/** Reset device
*
* @v vmdev VMBus device
* @ret rc Return status code
*/
int ( * reset ) ( struct vmbus_device *vmdev );
/** Remove device
*
* @v vmdev VMBus device
*/
void ( * remove ) ( struct vmbus_device *vmdev );
};
/** VMBus device driver table */
#define VMBUS_DRIVERS __table ( struct vmbus_driver, "vmbus_drivers" )
/** Declare a VMBus device driver */
#define __vmbus_driver __table_entry ( VMBUS_DRIVERS, 01 )
/**
* Set VMBus device driver-private data
*
* @v vmdev VMBus device
* @v priv Private data
*/
static inline void vmbus_set_drvdata ( struct vmbus_device *vmdev, void *priv ){
vmdev->priv = priv;
}
/**
* Get VMBus device driver-private data
*
* @v vmdev VMBus device
* @ret priv Private data
*/
static inline void * vmbus_get_drvdata ( struct vmbus_device *vmdev ) {
return vmdev->priv;
}
/** Construct VMBus type */
#define VMBUS_TYPE( a, b, c, d, e0, e1, e2, e3, e4, e5 ) { \
.canonical = { \
cpu_to_le32 ( a ), cpu_to_le16 ( b ), \
cpu_to_le16 ( c ), cpu_to_be16 ( d ), \
{ e0, e1, e2, e3, e4, e5 } \
} }
/**
* Check if data is present in ring buffer
*
* @v vmdev VMBus device
* @v has_data Data is present
*/
static inline __attribute__ (( always_inline )) int
vmbus_has_data ( struct vmbus_device *vmdev ) {
return ( vmdev->in->prod != vmdev->in->cons );
}
/**
* Register transfer page set
*
* @v vmdev VMBus device
* @v pages Transfer page set
* @ret rc Return status code
*/
static inline __attribute__ (( always_inline )) int
vmbus_register_pages ( struct vmbus_device *vmdev,
struct vmbus_xfer_pages *pages ) {
list_add ( &pages->list, &vmdev->pages );
return 0;
}
/**
* Unregister transfer page set
*
* @v vmdev VMBus device
* @v pages Transfer page set
*/
static inline __attribute__ (( always_inline )) void
vmbus_unregister_pages ( struct vmbus_device *vmdev,
struct vmbus_xfer_pages *pages ) {
list_check_contains_entry ( pages, &vmdev->pages, list );
list_del ( &pages->list );
}
extern unsigned int vmbus_obsolete_gpadl;
/**
* Check if GPADL is obsolete
*
* @v gpadl GPADL ID
* @v is_obsolete GPADL ID is obsolete
*
* Check if GPADL is obsolete (i.e. was created before the most recent
* Hyper-V reset).
*/
static inline __attribute__ (( always_inline )) int
vmbus_gpadl_is_obsolete ( unsigned int gpadl ) {
return ( gpadl <= vmbus_obsolete_gpadl );
}
extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
size_t len );
extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev,
unsigned int gpadl );
extern int vmbus_open ( struct vmbus_device *vmdev,
struct vmbus_channel_operations *op,
size_t out_len, size_t in_len, size_t mtu );
extern void vmbus_close ( struct vmbus_device *vmdev );
extern int vmbus_send_control ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len );
extern int vmbus_send_data ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len,
struct io_buffer *iobuf );
extern int vmbus_send_completion ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len );
extern int vmbus_send_cancellation ( struct vmbus_device *vmdev, uint64_t xid );
extern int vmbus_poll ( struct vmbus_device *vmdev );
extern void vmbus_dump_channel ( struct vmbus_device *vmdev );
extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent );
extern int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent );
extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent );
#endif /* _IPXE_VMBUS_H */