diff options
Diffstat (limited to 'drivers/hv/hyperv_vmbus.h')
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h new file mode 100644 index 00000000000..0aee1122734 --- /dev/null +++ b/drivers/hv/hyperv_vmbus.h @@ -0,0 +1,626 @@ +/* + * + * Copyright (c) 2011, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang <haiyangz@microsoft.com> + * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> + * + */ + +#ifndef _HYPERV_VMBUS_H +#define _HYPERV_VMBUS_H + +#include <linux/list.h> +#include <asm/sync_bitops.h> +#include <linux/atomic.h> +#include <linux/hyperv.h> + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HVCPUID_VERSION_FEATURES). + */ +enum hv_cpuid_function { + HVCPUID_VERSION_FEATURES = 0x00000001, + HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, + HVCPUID_INTERFACE = 0x40000001, + + /* + * The remaining functions depend on the value of + * HVCPUID_INTERFACE + */ + HVCPUID_VERSION = 0x40000002, + HVCPUID_FEATURES = 0x40000003, + HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, + HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, +}; + +/* Define version of the synthetic interrupt controller. */ +#define HV_SYNIC_VERSION (1) + +/* Define the expected SynIC version. */ +#define HV_SYNIC_VERSION_1 (0x1) + +/* Define synthetic interrupt controller message constants. */ +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) +#define HV_ANY_VP (0xFFFFFFFF) + +/* Define synthetic interrupt controller flag constants. */ +#define HV_EVENT_FLAGS_COUNT (256 * 8) +#define HV_EVENT_FLAGS_BYTE_COUNT (256) +#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) + +/* Define hypervisor message types. */ +enum hv_message_type { + HVMSG_NONE = 0x00000000, + + /* Memory access messages. */ + HVMSG_UNMAPPED_GPA = 0x80000000, + HVMSG_GPA_INTERCEPT = 0x80000001, + + /* Timer notification messages. */ + HVMSG_TIMER_EXPIRED = 0x80000010, + + /* Error messages. */ + HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, + HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, + HVMSG_UNSUPPORTED_FEATURE = 0x80000022, + + /* Trace buffer complete messages. */ + HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, + + /* Platform-specific processor intercept messages. */ + HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, + HVMSG_X64_MSR_INTERCEPT = 0x80010001, + HVMSG_X64_CPUID_INTERCEPT = 0x80010002, + HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, + HVMSG_X64_APIC_EOI = 0x80010004, + HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 +}; + +/* Define the number of synthetic interrupt sources. */ +#define HV_SYNIC_SINT_COUNT (16) +#define HV_SYNIC_STIMER_COUNT (4) + +/* Define invalid partition identifier. */ +#define HV_PARTITION_ID_INVALID ((u64)0x0) + +/* Define connection identifier type. */ +union hv_connection_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u; +}; + +/* Define port identifier type. */ +union hv_port_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u ; +}; + +/* Define port type. */ +enum hv_port_type { + HVPORT_MSG = 1, + HVPORT_EVENT = 2, + HVPORT_MONITOR = 3 +}; + +/* Define port information structure. */ +struct hv_port_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u32 target_sint; + u32 target_vp; + u64 rsvdz; + } message_port_info; + struct { + u32 target_sint; + u32 target_vp; + u16 base_flag_bumber; + u16 flag_count; + u32 rsvdz; + } event_port_info; + struct { + u64 monitor_address; + u64 rsvdz; + } monitor_port_info; + }; +}; + +struct hv_connection_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u64 rsvdz; + } message_connection_info; + struct { + u64 rsvdz; + } event_connection_info; + struct { + u64 monitor_address; + } monitor_connection_info; + }; +}; + +/* Define synthetic interrupt controller message flags. */ +union hv_message_flags { + u8 asu8; + struct { + u8 msg_pending:1; + u8 reserved:7; + }; +}; + +/* Define synthetic interrupt controller message header. */ +struct hv_message_header { + enum hv_message_type message_type; + u8 payload_size; + union hv_message_flags message_flags; + u8 reserved[2]; + union { + u64 sender; + union hv_port_id port; + }; +}; + +/* Define timer message payload structure. */ +struct hv_timer_message_payload { + u32 timer_index; + u32 reserved; + u64 expiration_time; /* When the timer expired */ + u64 delivery_time; /* When the message was delivered */ +}; + +/* Define synthetic interrupt controller message format. */ +struct hv_message { + struct hv_message_header header; + union { + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u ; +}; + +/* Define the number of message buffers associated with each port. */ +#define HV_PORT_MESSAGE_BUFFER_COUNT (16) + +/* Define the synthetic interrupt message page layout. */ +struct hv_message_page { + struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; +}; + +/* Define the synthetic interrupt controller event flags format. */ +union hv_synic_event_flags { + u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; + u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; +}; + +/* Define the synthetic interrupt flags page layout. */ +struct hv_synic_event_flags_page { + union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; +}; + +/* Define SynIC control register. */ +union hv_synic_scontrol { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:63; + }; +}; + +/* Define synthetic interrupt source. */ +union hv_synic_sint { + u64 as_uint64; + struct { + u64 vector:8; + u64 reserved1:8; + u64 masked:1; + u64 auto_eoi:1; + u64 reserved2:46; + }; +}; + +/* Define the format of the SIMP register */ +union hv_synic_simp { + u64 as_uint64; + struct { + u64 simp_enabled:1; + u64 preserved:11; + u64 base_simp_gpa:52; + }; +}; + +/* Define the format of the SIEFP register */ +union hv_synic_siefp { + u64 as_uint64; + struct { + u64 siefp_enabled:1; + u64 preserved:11; + u64 base_siefp_gpa:52; + }; +}; + +/* Definitions for the monitored notification facility */ +union hv_monitor_trigger_group { + u64 as_uint64; + struct { + u32 pending; + u32 armed; + }; +}; + +struct hv_monitor_parameter { + union hv_connection_id connectionid; + u16 flagnumber; + u16 rsvdz; +}; + +union hv_monitor_trigger_state { + u32 asu32; + + struct { + u32 group_enable:4; + u32 rsvdz:28; + }; +}; + +/* struct hv_monitor_page Layout */ +/* ------------------------------------------------------ */ +/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ +/* | 8 | TriggerGroup[0] | */ +/* | 10 | TriggerGroup[1] | */ +/* | 18 | TriggerGroup[2] | */ +/* | 20 | TriggerGroup[3] | */ +/* | 28 | Rsvd2[0] | */ +/* | 30 | Rsvd2[1] | */ +/* | 38 | Rsvd2[2] | */ +/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ +/* | ... | */ +/* | 240 | Latency[0][0..3] | */ +/* | 340 | Rsvz3[0] | */ +/* | 440 | Parameter[0][0] | */ +/* | 448 | Parameter[0][1] | */ +/* | ... | */ +/* | 840 | Rsvd4[0] | */ +/* ------------------------------------------------------ */ +struct hv_monitor_page { + union hv_monitor_trigger_state trigger_state; + u32 rsvdz1; + + union hv_monitor_trigger_group trigger_group[4]; + u64 rsvdz2[3]; + + s32 next_checktime[4][32]; + + u16 latency[4][32]; + u64 rsvdz3[32]; + + struct hv_monitor_parameter parameter[4][32]; + + u8 rsvdz4[1984]; +}; + +/* Declare the various hypercall operations. */ +enum hv_call_code { + HVCALL_POST_MESSAGE = 0x005c, + HVCALL_SIGNAL_EVENT = 0x005d, +}; + +/* Definition of the hv_post_message hypercall input structure. */ +struct hv_input_post_message { + union hv_connection_id connectionid; + u32 reserved; + enum hv_message_type message_type; + u32 payload_size; + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; +}; + +/* Definition of the hv_signal_event hypercall input structure. */ +struct hv_input_signal_event { + union hv_connection_id connectionid; + u16 flag_number; + u16 rsvdz; +}; + +/* + * Versioning definitions used for guests reporting themselves to the + * hypervisor, and visa versa. + */ + +/* Version info reported by guest OS's */ +enum hv_guest_os_vendor { + HVGUESTOS_VENDOR_MICROSOFT = 0x0001 +}; + +enum hv_guest_os_microsoft_ids { + HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, + HVGUESTOS_MICROSOFT_MSDOS = 0x01, + HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, + HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, + HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, + HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 +}; + +/* + * Declare the MSR used to identify the guest OS. + */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +union hv_x64_msr_guest_os_id_contents { + u64 as_uint64; + struct { + u64 build_number:16; + u64 service_version:8; /* Service Pack, etc. */ + u64 minor_version:8; + u64 major_version:8; + u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ + u64 vendor_id:16; /* enum hv_guest_os_vendor */ + }; +}; + +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor. + */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +union hv_x64_msr_hypercall_contents { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:11; + u64 guest_physical_address:52; + }; +}; + + +enum { + VMBUS_MESSAGE_CONNECTION_ID = 1, + VMBUS_MESSAGE_PORT_ID = 1, + VMBUS_EVENT_CONNECTION_ID = 2, + VMBUS_EVENT_PORT_ID = 2, + VMBUS_MONITOR_CONNECTION_ID = 3, + VMBUS_MONITOR_PORT_ID = 3, + VMBUS_MESSAGE_SINT = 2, +}; + +/* #defines */ + +#define HV_PRESENT_BIT 0x80000000 + +#define HV_LINUX_GUEST_ID_LO 0x00000000 +#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 +#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ + HV_LINUX_GUEST_ID_LO) + +#define HV_CPU_POWER_MANAGEMENT (1 << 0) +#define HV_RECOMMENDATIONS_MAX 4 + +#define HV_X64_MAX 5 +#define HV_CAPS_MAX 8 + + +#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) + + +/* Service definitions */ + +#define HV_SERVICE_PARENT_PORT (0) +#define HV_SERVICE_PARENT_CONNECTION (0) + +#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) +#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) +#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) +#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) + +#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) +#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) +#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) +#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) +#define HV_SERVICE_MAX_MESSAGE_ID (4) + +#define HV_SERVICE_PROTOCOL_VERSION (0x0010) +#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 + +/* #define VMBUS_REVISION_NUMBER 6 */ + +/* Our local vmbus's port and connection id. Anything >0 is fine */ +/* #define VMBUS_PORT_ID 11 */ + +/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ +static const uuid_le VMBUS_SERVICE_ID = { + .b = { + 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, + 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 + }, +}; + +#define MAX_NUM_CPUS 32 + + +struct hv_input_signal_event_buffer { + u64 align8; + struct hv_input_signal_event event; +}; + +struct hv_context { + /* We only support running on top of Hyper-V + * So at this point this really can only contain the Hyper-V ID + */ + u64 guestid; + + void *hypercall_page; + + bool synic_initialized; + + /* + * This is used as an input param to HvCallSignalEvent hypercall. The + * input param is immutable in our usage and must be dynamic mem (vs + * stack or global). */ + struct hv_input_signal_event_buffer *signal_event_buffer; + /* 8-bytes aligned of the buffer above */ + struct hv_input_signal_event *signal_event_param; + + void *synic_message_page[MAX_NUM_CPUS]; + void *synic_event_page[MAX_NUM_CPUS]; +}; + +extern struct hv_context hv_context; + + +/* Hv Interface */ + +extern int hv_init(void); + +extern void hv_cleanup(void); + +extern u16 hv_post_message(union hv_connection_id connection_id, + enum hv_message_type message_type, + void *payload, size_t payload_size); + +extern u16 hv_signal_event(void); + +extern void hv_synic_init(void *irqarg); + +extern void hv_synic_cleanup(void *arg); + + +/* Interface */ + + +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); + +int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, + struct scatterlist *sglist, + u32 sgcount); + +int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, + void *buffer, + u32 buflen, + u32 offset); + +u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); + +void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, + struct hv_ring_buffer_debug_info *debug_info); + +/* + * Maximum channels is determined by the size of the interrupt page + * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt + * and the other is receive endpoint interrupt + */ +#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ + +/* The value here must be in multiple of 32 */ +/* TODO: Need to make this configurable */ +#define MAX_NUM_CHANNELS_SUPPORTED 256 + + +enum vmbus_connect_state { + DISCONNECTED, + CONNECTING, + CONNECTED, + DISCONNECTING +}; + +#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT + +struct vmbus_connection { + enum vmbus_connect_state conn_state; + + atomic_t next_gpadl_handle; + + /* + * Represents channel interrupts. Each bit position represents a + * channel. When a channel sends an interrupt via VMBUS, it finds its + * bit in the sendInterruptPage, set it and calls Hv to generate a port + * event. The other end receives the port event and parse the + * recvInterruptPage to see which bit is set + */ + void *int_page; + void *send_int_page; + void *recv_int_page; + + /* + * 2 pages - 1st page for parent->child notification and 2nd + * is child->parent notification + */ + void *monitor_pages; + struct list_head chn_msg_list; + spinlock_t channelmsg_lock; + + /* List of channels */ + struct list_head chn_list; + spinlock_t channel_lock; + + struct workqueue_struct *work_queue; +}; + + +struct vmbus_msginfo { + /* Bookkeeping stuff */ + struct list_head msglist_entry; + + /* The message itself */ + unsigned char msg[0]; +}; + + +extern struct vmbus_connection vmbus_connection; + +/* General vmbus interface */ + +struct hv_device *vmbus_device_create(uuid_le *type, + uuid_le *instance, + struct vmbus_channel *channel); + +int vmbus_device_register(struct hv_device *child_device_obj); +void vmbus_device_unregister(struct hv_device *device_obj); + +/* static void */ +/* VmbusChildDeviceDestroy( */ +/* struct hv_device *); */ + +struct vmbus_channel *relid2channel(u32 relid); + + +/* Connection interface */ + +int vmbus_connect(void); + +int vmbus_post_msg(void *buffer, size_t buflen); + +int vmbus_set_event(u32 child_relid); + +void vmbus_on_event(unsigned long data); + + +#endif /* _HYPERV_VMBUS_H */ |