kernel-ark/drivers/hv/hyperv_vmbus.h
Jason Wang 2608fb6531 drivers: hv: allocate synic structures before hv_synic_init()
We currently allocate synic structures in hv_sync_init(), but there's no way for
the driver to know about the allocation failure and it may continue to use the
uninitialized pointers. Solve this by introducing helpers for allocating and
freeing and doing the allocation before the on_each_cpu() call in
vmbus_bus_init().

Cc: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-24 16:24:17 -07:00

667 lines
16 KiB
C

/*
*
* 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 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];
};
/*
* 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
/*
* The guest OS needs to register the guest ID with the hypervisor.
* The guest ID is a 64 bit entity and the structure of this ID is
* specified in the Hyper-V specification:
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
*
* While the current guideline does not specify how Linux guest ID(s)
* need to be generated, our plan is to publish the guidelines for
* Linux and other guest operating systems that currently are hosted
* on Hyper-V. The implementation here conforms to this yet
* unpublished guidelines.
*
*
* Bit(s)
* 63 - Indicates if the OS is Open Source or not; 1 is Open Source
* 62:56 - Os Type; Linux is 0x100
* 55:48 - Distro specific identification
* 47:16 - Linux kernel version number
* 15:0 - Distro specific identification
*
*
*/
#define HV_LINUX_VENDOR_ID 0x8100
/*
* Generate the guest ID based on the guideline described above.
*/
static inline __u64 generate_guest_id(__u8 d_info1, __u32 kernel_version,
__u16 d_info2)
{
__u64 guest_id = 0;
guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48);
guest_id |= (((__u64)(d_info1)) << 48);
guest_id |= (((__u64)(kernel_version)) << 16);
guest_id |= ((__u64)(d_info2));
return guest_id;
}
#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
},
};
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;
void *synic_message_page[NR_CPUS];
void *synic_event_page[NR_CPUS];
/*
* Hypervisor's notion of virtual processor ID is different from
* Linux' notion of CPU ID. This information can only be retrieved
* in the context of the calling CPU. Setup a map for easy access
* to this information:
*
* vp_index[a] is the Hyper-V's processor ID corresponding to
* Linux cpuid 'a'.
*/
u32 vp_index[NR_CPUS];
/*
* Starting with win8, we can take channel interrupts on any CPU;
* we will manage the tasklet that handles events on a per CPU
* basis.
*/
struct tasklet_struct *event_dpc[NR_CPUS];
};
extern struct hv_context hv_context;
/* Hv Interface */
extern int hv_init(void);
extern void hv_cleanup(void);
extern int 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 *con_id);
extern int hv_synic_alloc(void);
extern void hv_synic_free(void);
extern void hv_synic_init(void *irqarg);
extern void hv_synic_cleanup(void *arg);
/*
* Host version information.
*/
extern unsigned int host_info_eax;
extern unsigned int host_info_ebx;
extern unsigned int host_info_ecx;
extern unsigned int host_info_edx;
/* 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, bool *signal);
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, bool *signal);
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info);
void hv_begin_read(struct hv_ring_buffer_info *rbi);
u32 hv_end_read(struct hv_ring_buffer_info *rbi);
/*
* 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);
void vmbus_free_channels(void);
/* Connection interface */
int vmbus_connect(void);
int vmbus_post_msg(void *buffer, size_t buflen);
int vmbus_set_event(struct vmbus_channel *channel);
void vmbus_on_event(unsigned long data);
#endif /* _HYPERV_VMBUS_H */