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) {
255 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
256 hv_vmbus_g_connection.interrupt_page = NULL;
259 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
260 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
263 sema_destroy(&msg_info->wait_sema);
264 free(msg_info, M_DEVBUF);
267 free(hv_vmbus_g_connection.channels, M_DEVBUF);
272 * Send a disconnect request on the partition service connection
275 hv_vmbus_disconnect(void) {
277 hv_vmbus_channel_unload msg;
279 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
281 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
283 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF);
285 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
287 free(hv_vmbus_g_connection.channels, M_DEVBUF);
288 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
297 hv_vmbus_on_events(int cpu)
302 uint32_t* recv_interrupt_page = NULL;
305 hv_vmbus_synic_event_flags *event;
306 /* int maxdword = PAGE_SIZE >> 3; */
308 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
309 "cpu out of range!"));
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 recv_interrupt_page =
318 hv_vmbus_g_connection.recv_interrupt_page;
321 * On Host with Win8 or above, the event page can be
322 * checked directly to get the id of the channel
323 * that has the pending interrupt.
325 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
326 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
327 event = (hv_vmbus_synic_event_flags *)
328 page_addr + HV_VMBUS_MESSAGE_SINT;
329 recv_interrupt_page = event->flags32;
335 if (recv_interrupt_page != NULL) {
336 for (dword = 0; dword < maxdword; dword++) {
337 if (recv_interrupt_page[dword]) {
338 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
339 if (synch_test_and_clear_bit(bit,
340 (uint32_t *) &recv_interrupt_page[dword])) {
341 rel_id = (dword << 5) + bit;
345 * vmbus channel protocol msg.
349 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
350 /* if channel is closed or closing */
351 if (channel == NULL || channel->rxq == NULL)
354 if (channel->batched_reading)
355 hv_ring_buffer_read_begin(&channel->inbound);
356 taskqueue_enqueue_fast(channel->rxq, &channel->channel_task);
368 * Send a msg on the vmbus's message connection
370 int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
372 hv_vmbus_connection_id connId;
373 unsigned retries = 0;
375 /* NetScaler delays from previous code were consolidated here */
376 static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
378 /* for(each entry in delayAmount) try to post message,
379 * delay a little bit before retrying
382 retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
383 connId.as_uint32_t = 0;
384 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
385 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
386 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
388 /* TODO: KYS We should use a blocking wait call */
389 DELAY(delayAmount[retries]);
392 KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
398 * Send an event notification to the parent
401 hv_vmbus_set_event(hv_vmbus_channel *channel) {
403 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
405 /* Each uint32_t represents 32 channels */
407 synch_set_bit(child_rel_id & 31,
408 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
409 + (child_rel_id >> 5))));
410 ret = hv_vmbus_signal_event(channel->signal_event_param);