1/*- 2 * Copyright (c) 2009-2012 Microsoft Corp. 3 * Copyright (c) 2012 NetApp Inc. 4 * Copyright (c) 2012 Citrix Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 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. 16 * 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. 27 */ 28 29#include <sys/param.h> 30#include <sys/kernel.h> 31#include <sys/malloc.h> 32#include <sys/systm.h> 33#include <sys/lock.h> 34#include <sys/mutex.h> 35#include <machine/bus.h> 36#include <vm/vm.h> 37#include <vm/vm_param.h> 38#include <vm/pmap.h> 39 40#include "hv_vmbus_priv.h" 41 42/* 43 * Globals 44 */ 45hv_vmbus_connection hv_vmbus_g_connection = 46 { .connect_state = HV_DISCONNECTED, 47 .next_gpadl_handle = 0xE1E10, }; 48 49uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008; 50 51static uint32_t 52hv_vmbus_get_next_version(uint32_t current_ver) 53{ 54 switch (current_ver) { 55 case (HV_VMBUS_VERSION_WIN7): 56 return(HV_VMBUS_VERSION_WS2008); 57 58 case (HV_VMBUS_VERSION_WIN8): 59 return(HV_VMBUS_VERSION_WIN7); 60 61 case (HV_VMBUS_VERSION_WIN8_1): 62 return(HV_VMBUS_VERSION_WIN8); 63 64 case (HV_VMBUS_VERSION_WS2008): 65 default: 66 return(HV_VMBUS_VERSION_INVALID); 67 } 68} 69 70/** 71 * Negotiate the highest supported hypervisor version. 72 */ 73static int 74hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info, 75 uint32_t version) 76{ 77 int ret = 0; 78 hv_vmbus_channel_initiate_contact *msg; 79 80 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 81 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 82 83 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 84 msg->vmbus_version_requested = version; 85 86 msg->interrupt_page = hv_get_phys_addr( 87 hv_vmbus_g_connection.interrupt_page); 88 89 msg->monitor_page_1 = hv_get_phys_addr( 90 hv_vmbus_g_connection.monitor_page_1); 91 92 msg->monitor_page_2 = hv_get_phys_addr( 93 hv_vmbus_g_connection.monitor_page_2); 94 95 /** 96 * Add to list before we send the request since we may receive the 97 * response before returning from this routine 98 */ 99 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 100 101 TAILQ_INSERT_TAIL( 102 &hv_vmbus_g_connection.channel_msg_anchor, 103 msg_info, 104 msg_list_entry); 105 106 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 107 108 ret = hv_vmbus_post_message( 109 msg, 110 sizeof(hv_vmbus_channel_initiate_contact)); 111 112 if (ret != 0) { 113 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 114 TAILQ_REMOVE( 115 &hv_vmbus_g_connection.channel_msg_anchor, 116 msg_info, 117 msg_list_entry); 118 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 119 return (ret); 120 } 121 122 /** 123 * Wait for the connection response 124 */ 125 ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */ 126 127 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 128 TAILQ_REMOVE( 129 &hv_vmbus_g_connection.channel_msg_anchor, 130 msg_info, 131 msg_list_entry); 132 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 133 134 /** 135 * Check if successful 136 */ 137 if (msg_info->response.version_response.version_supported) { 138 hv_vmbus_g_connection.connect_state = HV_CONNECTED; 139 } else { 140 ret = ECONNREFUSED; 141 } 142 143 return (ret); 144} 145 146/** 147 * Send a connect request on the partition service connection 148 */ 149int 150hv_vmbus_connect(void) { 151 int ret = 0; 152 uint32_t version; 153 hv_vmbus_channel_msg_info* msg_info = NULL; 154 155 /** 156 * Make sure we are not connecting or connected 157 */ 158 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 159 return (-1); 160 } 161 162 /** 163 * Initialize the vmbus connection 164 */ 165 hv_vmbus_g_connection.connect_state = HV_CONNECTING; 166 167 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 168 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 169 NULL, MTX_DEF); 170 171 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 172 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 173 NULL, MTX_DEF); 174 175 /** 176 * Setup the vmbus event connection for channel interrupt abstraction 177 * stuff 178 */ 179 hv_vmbus_g_connection.interrupt_page = malloc( 180 PAGE_SIZE, M_DEVBUF, 181 M_WAITOK | M_ZERO); 182 183 hv_vmbus_g_connection.recv_interrupt_page = 184 hv_vmbus_g_connection.interrupt_page; 185 186 hv_vmbus_g_connection.send_interrupt_page = 187 ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 188 (PAGE_SIZE >> 1)); 189 190 /** 191 * Set up the monitor notification facility. The 1st page for 192 * parent->child and the 2nd page for child->parent 193 */ 194 hv_vmbus_g_connection.monitor_page_1 = malloc( 195 PAGE_SIZE, 196 M_DEVBUF, 197 M_WAITOK | M_ZERO); 198 hv_vmbus_g_connection.monitor_page_2 = malloc( 199 PAGE_SIZE, 200 M_DEVBUF, 201 M_WAITOK | M_ZERO); 202 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); 207 208 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) * 209 HV_CHANNEL_MAX_COUNT, 210 M_DEVBUF, M_WAITOK | M_ZERO); 211 /* 212 * Find the highest vmbus version number we can support. 213 */ 214 version = HV_VMBUS_VERSION_CURRENT; 215 216 do { 217 ret = hv_vmbus_negotiate_version(msg_info, version); 218 if (ret == EWOULDBLOCK) { 219 /* 220 * We timed out. 221 */ 222 goto cleanup; 223 } 224 225 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) 226 break; 227 228 version = hv_vmbus_get_next_version(version); 229 } while (version != HV_VMBUS_VERSION_INVALID); 230 231 hv_vmbus_protocal_version = version; 232 if (bootverbose) 233 printf("VMBUS: Protocol Version: %d.%d\n", 234 version >> 16, version & 0xFFFF); 235 236 sema_destroy(&msg_info->wait_sema); 237 free(msg_info, M_DEVBUF); 238 239 return (0); 240 241 /* 242 * Cleanup after failure! 243 */ 244 cleanup: 245 246 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 247 248 mtx_destroy(&hv_vmbus_g_connection.channel_lock); 249 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 250 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; 254 } 255 256 free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF); 257 free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF); 258 259 if (msg_info) { 260 sema_destroy(&msg_info->wait_sema); 261 free(msg_info, M_DEVBUF); 262 } 263 264 free(hv_vmbus_g_connection.channels, M_DEVBUF); 265 return (ret); 266} 267 268/** 269 * Send a disconnect request on the partition service connection 270 */ 271int 272hv_vmbus_disconnect(void) { 273 int ret = 0; 274 hv_vmbus_channel_unload msg; 275 276 msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD; 277 278 ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload)); 279 280 free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF); 281 282 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 283 284 free(hv_vmbus_g_connection.channels, M_DEVBUF); 285 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 286 287 return (ret); 288} 289 290/** 291 * Handler for events 292 */ 293void 294hv_vmbus_on_events(int cpu) 295{ 296 int bit; 297 int dword; 298 void *page_addr; 299 uint32_t* recv_interrupt_page = NULL; 300 int rel_id; 301 int maxdword; 302 hv_vmbus_synic_event_flags *event; 303 /* int maxdword = PAGE_SIZE >> 3; */ 304 305 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 306 "cpu out of range!")); 307
|
327 recv_interrupt_page = event->flags32; 328 } 329 330 /* 331 * Check events 332 */ 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; 340 if (rel_id == 0) { 341 /* 342 * Special case - 343 * vmbus channel protocol msg. 344 */ 345 continue; 346 } else { 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) 350 continue; 351 352 if (channel->batched_reading) 353 hv_ring_buffer_read_begin(&channel->inbound); 354 taskqueue_enqueue(channel->rxq, &channel->channel_task); 355 } 356 } 357 } 358 } 359 } 360 } 361 362 return; 363} 364 365/** 366 * Send a msg on the vmbus's message connection 367 */ 368int hv_vmbus_post_message(void *buffer, size_t bufferLen) 369{ 370 hv_vmbus_connection_id connId; 371 sbintime_t time = SBT_1MS; 372 int retries; 373 int ret; 374 375 connId.as_uint32_t = 0; 376 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 377 378 /* 379 * We retry to cope with transient failures caused by host side's 380 * insufficient resources. 20 times should suffice in practice. 381 */ 382 for (retries = 0; retries < 20; retries++) { 383 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, 384 bufferLen); 385 if (ret == HV_STATUS_SUCCESS) 386 return (0); 387 388 pause_sbt("pstmsg", time, 0, C_HARDCLOCK); 389 if (time < SBT_1S * 2) 390 time *= 2; 391 } 392 393 KASSERT(ret == HV_STATUS_SUCCESS, 394 ("Error VMBUS: Message Post Failed, ret=%d\n", ret)); 395 396 return (EAGAIN); 397} 398 399/** 400 * Send an event notification to the parent 401 */ 402int 403hv_vmbus_set_event(hv_vmbus_channel *channel) { 404 int ret = 0; 405 uint32_t child_rel_id = channel->offer_msg.child_rel_id; 406 407 /* Each uint32_t represents 32 channels */ 408 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); 413 414 return (ret); 415}
|