2 * Copyright (c) 2009-2012 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/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/systm.h>
37 #include <sys/mutex.h>
38 #include <machine/bus.h>
40 #include <vm/vm_param.h>
43 #include "hv_vmbus_priv.h"
48 hv_vmbus_connection hv_vmbus_g_connection =
49 { .connect_state = HV_DISCONNECTED,
50 .next_gpadl_handle = 0xE1E10, };
52 uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
55 hv_vmbus_get_next_version(uint32_t current_ver)
57 switch (current_ver) {
58 case (HV_VMBUS_VERSION_WIN7):
59 return(HV_VMBUS_VERSION_WS2008);
61 case (HV_VMBUS_VERSION_WIN8):
62 return(HV_VMBUS_VERSION_WIN7);
64 case (HV_VMBUS_VERSION_WIN8_1):
65 return(HV_VMBUS_VERSION_WIN8);
67 case (HV_VMBUS_VERSION_WS2008):
69 return(HV_VMBUS_VERSION_INVALID);
74 * Negotiate the highest supported hypervisor version.
77 hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
81 hv_vmbus_channel_initiate_contact *msg;
83 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
84 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
86 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
87 msg->vmbus_version_requested = version;
89 msg->interrupt_page = hv_get_phys_addr(
90 hv_vmbus_g_connection.interrupt_page);
92 msg->monitor_page_1 = hv_get_phys_addr(
93 hv_vmbus_g_connection.monitor_page_1);
95 msg->monitor_page_2 = hv_get_phys_addr(
96 hv_vmbus_g_connection.monitor_page_2);
99 * Add to list before we send the request since we may receive the
100 * response before returning from this routine
102 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
105 &hv_vmbus_g_connection.channel_msg_anchor,
109 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
111 ret = hv_vmbus_post_message(
113 sizeof(hv_vmbus_channel_initiate_contact));
116 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
118 &hv_vmbus_g_connection.channel_msg_anchor,
121 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
126 * Wait for the connection response
128 ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
130 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
132 &hv_vmbus_g_connection.channel_msg_anchor,
135 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
138 * Check if successful
140 if (msg_info->response.version_response.version_supported) {
141 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
150 * Send a connect request on the partition service connection
153 hv_vmbus_connect(void) {
156 hv_vmbus_channel_msg_info* msg_info = NULL;
159 * Make sure we are not connecting or connected
161 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
166 * Initialize the vmbus connection
168 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
170 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
171 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
174 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
175 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
179 * Setup the vmbus event connection for channel interrupt abstraction
182 hv_vmbus_g_connection.interrupt_page = malloc(
186 hv_vmbus_g_connection.recv_interrupt_page =
187 hv_vmbus_g_connection.interrupt_page;
189 hv_vmbus_g_connection.send_interrupt_page =
190 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
194 * Set up the monitor notification facility. The 1st page for
195 * parent->child and the 2nd page for child->parent
197 hv_vmbus_g_connection.monitor_page_1 = malloc(
201 hv_vmbus_g_connection.monitor_page_2 = malloc(
206 msg_info = (hv_vmbus_channel_msg_info*)
207 malloc(sizeof(hv_vmbus_channel_msg_info) +
208 sizeof(hv_vmbus_channel_initiate_contact),
209 M_DEVBUF, M_WAITOK | M_ZERO);
211 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
212 HV_CHANNEL_MAX_COUNT,
213 M_DEVBUF, M_WAITOK | M_ZERO);
215 * Find the highest vmbus version number we can support.
217 version = HV_VMBUS_VERSION_CURRENT;
220 ret = hv_vmbus_negotiate_version(msg_info, version);
221 if (ret == EWOULDBLOCK) {
228 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
231 version = hv_vmbus_get_next_version(version);
232 } while (version != HV_VMBUS_VERSION_INVALID);
234 hv_vmbus_protocal_version = version;
236 printf("VMBUS: Protocol Version: %d.%d\n",
237 version >> 16, version & 0xFFFF);
239 sema_destroy(&msg_info->wait_sema);
240 free(msg_info, M_DEVBUF);
245 * Cleanup after failure!
249 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
251 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
252 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
254 if (hv_vmbus_g_connection.interrupt_page != NULL) {
256 hv_vmbus_g_connection.interrupt_page,
259 hv_vmbus_g_connection.interrupt_page = NULL;
262 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
263 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
266 sema_destroy(&msg_info->wait_sema);
267 free(msg_info, M_DEVBUF);
270 free(hv_vmbus_g_connection.channels, M_DEVBUF);
275 * Send a disconnect request on the partition service connection
278 hv_vmbus_disconnect(void) {
280 hv_vmbus_channel_unload msg;
282 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
284 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
286 contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
288 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
290 free(hv_vmbus_g_connection.channels, M_DEVBUF);
291 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
300 hv_vmbus_on_events(int cpu)
305 uint32_t* recv_interrupt_page = NULL;
308 hv_vmbus_synic_event_flags *event;
309 /* int maxdword = PAGE_SIZE >> 3; */
311 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
312 "cpu out of range!"));
314 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
315 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
316 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
318 * receive size is 1/2 page and divide that by 4 bytes
320 recv_interrupt_page =
321 hv_vmbus_g_connection.recv_interrupt_page;
324 * On Host with Win8 or above, the event page can be
325 * checked directly to get the id of the channel
326 * that has the pending interrupt.
328 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
329 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
330 event = (hv_vmbus_synic_event_flags *)
331 page_addr + HV_VMBUS_MESSAGE_SINT;
332 recv_interrupt_page = event->flags32;
338 if (recv_interrupt_page != NULL) {
339 for (dword = 0; dword < maxdword; dword++) {
340 if (recv_interrupt_page[dword]) {
341 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
342 if (synch_test_and_clear_bit(bit,
343 (uint32_t *) &recv_interrupt_page[dword])) {
344 rel_id = (dword << 5) + bit;
348 * vmbus channel protocol msg.
352 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
353 /* if channel is closed or closing */
354 if (channel == NULL || channel->rxq == NULL)
357 if (channel->batched_reading)
358 hv_ring_buffer_read_begin(&channel->inbound);
359 taskqueue_enqueue_fast(channel->rxq, &channel->channel_task);
371 * Send a msg on the vmbus's message connection
373 int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
375 hv_vmbus_connection_id connId;
376 unsigned retries = 0;
378 /* NetScaler delays from previous code were consolidated here */
379 static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
381 /* for(each entry in delayAmount) try to post message,
382 * delay a little bit before retrying
385 retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
386 connId.as_uint32_t = 0;
387 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
388 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
389 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
391 /* TODO: KYS We should use a blocking wait call */
392 DELAY(delayAmount[retries]);
395 KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
401 * Send an event notification to the parent
404 hv_vmbus_set_event(hv_vmbus_channel *channel) {
406 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
408 /* Each uint32_t represents 32 channels */
410 synch_set_bit(child_rel_id & 31,
411 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
412 + (child_rel_id >> 5))));
413 ret = hv_vmbus_signal_event(channel->signal_event_param);