2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
32 #include <sys/systm.h>
34 #include <sys/mutex.h>
35 #include <machine/bus.h>
37 #include <vm/vm_param.h>
40 #include "hv_vmbus_priv.h"
45 hv_vmbus_connection hv_vmbus_g_connection =
46 { .connect_state = HV_DISCONNECTED,
47 .next_gpadl_handle = 0xE1E10, };
49 uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
52 hv_vmbus_get_next_version(uint32_t current_ver)
54 switch (current_ver) {
55 case (HV_VMBUS_VERSION_WIN7):
56 return(HV_VMBUS_VERSION_WS2008);
58 case (HV_VMBUS_VERSION_WIN8):
59 return(HV_VMBUS_VERSION_WIN7);
61 case (HV_VMBUS_VERSION_WIN8_1):
62 return(HV_VMBUS_VERSION_WIN8);
64 case (HV_VMBUS_VERSION_WS2008):
66 return(HV_VMBUS_VERSION_INVALID);
71 * Negotiate the highest supported hypervisor version.
74 hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
78 hv_vmbus_channel_initiate_contact *msg;
80 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
81 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
83 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
84 msg->vmbus_version_requested = version;
86 msg->interrupt_page = hv_get_phys_addr(
87 hv_vmbus_g_connection.interrupt_page);
89 msg->monitor_page_1 = hv_get_phys_addr(
90 hv_vmbus_g_connection.monitor_page_1);
92 msg->monitor_page_2 = hv_get_phys_addr(
93 hv_vmbus_g_connection.monitor_page_2);
96 * Add to list before we send the request since we may receive the
97 * response before returning from this routine
99 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
102 &hv_vmbus_g_connection.channel_msg_anchor,
106 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
108 ret = hv_vmbus_post_message(
110 sizeof(hv_vmbus_channel_initiate_contact));
113 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
115 &hv_vmbus_g_connection.channel_msg_anchor,
118 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
123 * Wait for the connection response
125 ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
127 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
129 &hv_vmbus_g_connection.channel_msg_anchor,
132 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
135 * Check if successful
137 if (msg_info->response.version_response.version_supported) {
138 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
147 * Send a connect request on the partition service connection
150 hv_vmbus_connect(void) {
153 hv_vmbus_channel_msg_info* msg_info = NULL;
156 * Make sure we are not connecting or connected
158 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
163 * Initialize the vmbus connection
165 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
167 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
168 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
171 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
172 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
176 * Setup the vmbus event connection for channel interrupt abstraction
179 hv_vmbus_g_connection.interrupt_page = malloc(
183 hv_vmbus_g_connection.recv_interrupt_page =
184 hv_vmbus_g_connection.interrupt_page;
186 hv_vmbus_g_connection.send_interrupt_page =
187 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
191 * Set up the monitor notification facility. The 1st page for
192 * parent->child and the 2nd page for child->parent
194 hv_vmbus_g_connection.monitor_page_1 = malloc(
198 hv_vmbus_g_connection.monitor_page_2 = malloc(
203 msg_info = (hv_vmbus_channel_msg_info*)
204 malloc(sizeof(hv_vmbus_channel_msg_info) +
205 sizeof(hv_vmbus_channel_initiate_contact),
206 M_DEVBUF, M_WAITOK | M_ZERO);
208 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
209 HV_CHANNEL_MAX_COUNT,
210 M_DEVBUF, M_WAITOK | M_ZERO);
212 * Find the highest vmbus version number we can support.
214 version = HV_VMBUS_VERSION_CURRENT;
217 ret = hv_vmbus_negotiate_version(msg_info, version);
218 if (ret == EWOULDBLOCK) {
225 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
228 version = hv_vmbus_get_next_version(version);
229 } while (version != HV_VMBUS_VERSION_INVALID);
231 hv_vmbus_protocal_version = version;
233 printf("VMBUS: Protocol Version: %d.%d\n",
234 version >> 16, version & 0xFFFF);
236 sema_destroy(&msg_info->wait_sema);
237 free(msg_info, M_DEVBUF);
242 * Cleanup after failure!
246 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
248 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
249 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
251 if (hv_vmbus_g_connection.interrupt_page != NULL) {
252 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
253 hv_vmbus_g_connection.interrupt_page = NULL;
256 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
257 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
260 sema_destroy(&msg_info->wait_sema);
261 free(msg_info, M_DEVBUF);
264 free(hv_vmbus_g_connection.channels, M_DEVBUF);
269 * Send a disconnect request on the partition service connection
272 hv_vmbus_disconnect(void) {
274 hv_vmbus_channel_unload msg;
276 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
278 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
280 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
282 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
284 free(hv_vmbus_g_connection.channels, M_DEVBUF);
285 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
294 hv_vmbus_on_events(int cpu)
299 uint32_t* recv_interrupt_page = NULL;
302 hv_vmbus_synic_event_flags *event;
303 /* int maxdword = PAGE_SIZE >> 3; */
305 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
306 "cpu out of range!"));
308 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
309 event = (hv_vmbus_synic_event_flags *)
310 page_addr + HV_VMBUS_MESSAGE_SINT;
311 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
312 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
313 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
315 * receive size is 1/2 page and divide that by 4 bytes
317 if (synch_test_and_clear_bit(0, &event->flags32[0]))
318 recv_interrupt_page =
319 hv_vmbus_g_connection.recv_interrupt_page;
322 * On Host with Win8 or above, the event page can be
323 * checked directly to get the id of the channel
324 * that has the pending interrupt.
326 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
327 recv_interrupt_page = event->flags32;
333 if (recv_interrupt_page != NULL) {
334 for (dword = 0; dword < maxdword; dword++) {
335 if (recv_interrupt_page[dword]) {
336 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
337 if (synch_test_and_clear_bit(bit,
338 (uint32_t *) &recv_interrupt_page[dword])) {
339 rel_id = (dword << 5) + bit;
343 * vmbus channel protocol msg.
347 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
348 /* if channel is closed or closing */
349 if (channel == NULL || channel->rxq == NULL)
352 if (channel->batched_reading)
353 hv_ring_buffer_read_begin(&channel->inbound);
354 taskqueue_enqueue(channel->rxq, &channel->channel_task);
366 * Send a msg on the vmbus's message connection
368 int hv_vmbus_post_message(void *buffer, size_t bufferLen)
370 hv_vmbus_connection_id connId;
371 sbintime_t time = SBT_1MS;
375 connId.as_uint32_t = 0;
376 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
379 * We retry to cope with transient failures caused by host side's
380 * insufficient resources. 20 times should suffice in practice.
382 for (retries = 0; retries < 20; retries++) {
383 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer,
385 if (ret == HV_STATUS_SUCCESS)
388 pause_sbt("pstmsg", time, 0, C_HARDCLOCK);
389 if (time < SBT_1S * 2)
393 KASSERT(ret == HV_STATUS_SUCCESS,
394 ("Error VMBUS: Message Post Failed, ret=%d\n", ret));
400 * Send an event notification to the parent
403 hv_vmbus_set_event(hv_vmbus_channel *channel) {
405 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
407 /* Each uint32_t represents 32 channels */
409 synch_set_bit(child_rel_id & 31,
410 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
411 + (child_rel_id >> 5))));
412 ret = hv_vmbus_signal_event(channel->signal_event_param);