#ifndef _IPXE_VMBUS_H #define _IPXE_VMBUS_H /** @file * * Hyper-V virtual machine bus * */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include #include #include /** 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 * @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, 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 */