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/cdefs.h> 30__FBSDID("$FreeBSD: releng/10.3/sys/dev/hyperv/vmbus/hv_connection.c 303984 2016-08-12 04:01:16Z glebius $"); 31 32#include <sys/param.h> 33#include <sys/kernel.h> 34#include <sys/malloc.h> 35#include <sys/systm.h> 36#include <sys/lock.h> 37#include <sys/mutex.h> 38#include <machine/bus.h> 39#include <vm/vm.h> 40#include <vm/vm_param.h> 41#include <vm/pmap.h> 42 43#include "hv_vmbus_priv.h" 44 45/* 46 * Globals 47 */ 48hv_vmbus_connection hv_vmbus_g_connection = 49 { .connect_state = HV_DISCONNECTED, 50 .next_gpadl_handle = 0xE1E10, }; 51 52uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008; 53 54static uint32_t 55hv_vmbus_get_next_version(uint32_t current_ver) 56{ 57 switch (current_ver) { 58 case (HV_VMBUS_VERSION_WIN7): 59 return(HV_VMBUS_VERSION_WS2008); 60 61 case (HV_VMBUS_VERSION_WIN8): 62 return(HV_VMBUS_VERSION_WIN7); 63 64 case (HV_VMBUS_VERSION_WIN8_1): 65 return(HV_VMBUS_VERSION_WIN8); 66 67 case (HV_VMBUS_VERSION_WS2008): 68 default: 69 return(HV_VMBUS_VERSION_INVALID); 70 } 71} 72 73/** 74 * Negotiate the highest supported hypervisor version. 75 */ 76static int 77hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info, 78 uint32_t version) 79{ 80 int ret = 0; 81 hv_vmbus_channel_initiate_contact *msg; 82 83 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 84 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 85 86 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 87 msg->vmbus_version_requested = version; 88 89 msg->interrupt_page = hv_get_phys_addr( 90 hv_vmbus_g_connection.interrupt_page); 91 92 msg->monitor_page_1 = hv_get_phys_addr( 93 hv_vmbus_g_connection.monitor_pages); 94 95 msg->monitor_page_2 = 96 hv_get_phys_addr( 97 ((uint8_t *) hv_vmbus_g_connection.monitor_pages 98 + PAGE_SIZE)); 99 100 /** 101 * Add to list before we send the request since we may receive the 102 * response before returning from this routine 103 */ 104 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 105 106 TAILQ_INSERT_TAIL( 107 &hv_vmbus_g_connection.channel_msg_anchor, 108 msg_info, 109 msg_list_entry); 110 111 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 112 113 ret = hv_vmbus_post_message( 114 msg, 115 sizeof(hv_vmbus_channel_initiate_contact)); 116 117 if (ret != 0) { 118 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 119 TAILQ_REMOVE( 120 &hv_vmbus_g_connection.channel_msg_anchor, 121 msg_info, 122 msg_list_entry); 123 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 124 return (ret); 125 } 126 127 /** 128 * Wait for the connection response 129 */ 130 ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */ 131 132 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 133 TAILQ_REMOVE( 134 &hv_vmbus_g_connection.channel_msg_anchor, 135 msg_info, 136 msg_list_entry); 137 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 138 139 /** 140 * Check if successful 141 */ 142 if (msg_info->response.version_response.version_supported) { 143 hv_vmbus_g_connection.connect_state = HV_CONNECTED; 144 } else { 145 ret = ECONNREFUSED; 146 } 147 148 return (ret); 149} 150 151/** 152 * Send a connect request on the partition service connection 153 */ 154int 155hv_vmbus_connect(void) { 156 int ret = 0; 157 uint32_t version; 158 hv_vmbus_channel_msg_info* msg_info = NULL; 159 160 /** 161 * Make sure we are not connecting or connected 162 */ 163 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 164 return (-1); 165 } 166 167 /** 168 * Initialize the vmbus connection 169 */ 170 hv_vmbus_g_connection.connect_state = HV_CONNECTING; 171 hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ"); 172 sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); 173 174 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 175 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 176 NULL, MTX_DEF); 177 178 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 179 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 180 NULL, MTX_DEF); 181 182 /** 183 * Setup the vmbus event connection for channel interrupt abstraction 184 * stuff 185 */ 186 hv_vmbus_g_connection.interrupt_page = contigmalloc( 187 PAGE_SIZE, M_DEVBUF, 188 M_NOWAIT | M_ZERO, 0UL, 189 BUS_SPACE_MAXADDR, 190 PAGE_SIZE, 0); 191 KASSERT(hv_vmbus_g_connection.interrupt_page != NULL, 192 ("Error VMBUS: malloc failed to allocate Channel" 193 " Request Event message!")); 194 if (hv_vmbus_g_connection.interrupt_page == NULL) { 195 ret = ENOMEM; 196 goto cleanup; 197 } 198 199 hv_vmbus_g_connection.recv_interrupt_page = 200 hv_vmbus_g_connection.interrupt_page; 201 202 hv_vmbus_g_connection.send_interrupt_page = 203 ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 204 (PAGE_SIZE >> 1)); 205 206 /** 207 * Set up the monitor notification facility. The 1st page for 208 * parent->child and the 2nd page for child->parent 209 */ 210 hv_vmbus_g_connection.monitor_pages = contigmalloc( 211 2 * PAGE_SIZE, 212 M_DEVBUF, 213 M_NOWAIT | M_ZERO, 214 0UL, 215 BUS_SPACE_MAXADDR, 216 PAGE_SIZE, 217 0); 218 KASSERT(hv_vmbus_g_connection.monitor_pages != NULL, 219 ("Error VMBUS: malloc failed to allocate Monitor Pages!")); 220 if (hv_vmbus_g_connection.monitor_pages == NULL) { 221 ret = ENOMEM; 222 goto cleanup; 223 } 224 225 msg_info = (hv_vmbus_channel_msg_info*) 226 malloc(sizeof(hv_vmbus_channel_msg_info) + 227 sizeof(hv_vmbus_channel_initiate_contact), 228 M_DEVBUF, M_NOWAIT | M_ZERO); 229 KASSERT(msg_info != NULL, 230 ("Error VMBUS: malloc failed for Initiate Contact message!")); 231 if (msg_info == NULL) { 232 ret = ENOMEM; 233 goto cleanup; 234 } 235 236 hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) * 237 HV_CHANNEL_MAX_COUNT, 238 M_DEVBUF, M_WAITOK | M_ZERO); 239 /* 240 * Find the highest vmbus version number we can support. 241 */ 242 version = HV_VMBUS_VERSION_CURRENT; 243 244 do { 245 ret = hv_vmbus_negotiate_version(msg_info, version); 246 if (ret == EWOULDBLOCK) { 247 /* 248 * We timed out. 249 */ 250 goto cleanup; 251 } 252 253 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) 254 break; 255 256 version = hv_vmbus_get_next_version(version); 257 } while (version != HV_VMBUS_VERSION_INVALID); 258 259 hv_vmbus_protocal_version = version; 260 if (bootverbose) 261 printf("VMBUS: Protocol Version: %d.%d\n", 262 version >> 16, version & 0xFFFF); 263 264 sema_destroy(&msg_info->wait_sema); 265 free(msg_info, M_DEVBUF); 266 267 return (0); 268 269 /* 270 * Cleanup after failure! 271 */ 272 cleanup: 273 274 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 275 276 hv_work_queue_close(hv_vmbus_g_connection.work_queue); 277 sema_destroy(&hv_vmbus_g_connection.control_sema); 278 mtx_destroy(&hv_vmbus_g_connection.channel_lock); 279 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 280 281 if (hv_vmbus_g_connection.interrupt_page != NULL) { 282 contigfree( 283 hv_vmbus_g_connection.interrupt_page, 284 PAGE_SIZE, 285 M_DEVBUF); 286 hv_vmbus_g_connection.interrupt_page = NULL; 287 } 288 289 if (hv_vmbus_g_connection.monitor_pages != NULL) { 290 contigfree( 291 hv_vmbus_g_connection.monitor_pages, 292 2 * PAGE_SIZE, 293 M_DEVBUF); 294 hv_vmbus_g_connection.monitor_pages = NULL; 295 } 296 297 if (msg_info) { 298 sema_destroy(&msg_info->wait_sema); 299 free(msg_info, M_DEVBUF); 300 } 301 302 free(hv_vmbus_g_connection.channels, M_DEVBUF); 303 return (ret); 304} 305 306/** 307 * Send a disconnect request on the partition service connection 308 */ 309int 310hv_vmbus_disconnect(void) { 311 int ret = 0; 312 hv_vmbus_channel_unload* msg; 313 314 msg = malloc(sizeof(hv_vmbus_channel_unload), 315 M_DEVBUF, M_NOWAIT | M_ZERO); 316 KASSERT(msg != NULL, 317 ("Error VMBUS: malloc failed to allocate Channel Unload Msg!")); 318 if (msg == NULL) 319 return (ENOMEM); 320 321 msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD; 322 323 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload)); 324 325 326 contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF); 327 328 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 329 330 hv_work_queue_close(hv_vmbus_g_connection.work_queue); 331 sema_destroy(&hv_vmbus_g_connection.control_sema); 332 333 free(hv_vmbus_g_connection.channels, M_DEVBUF); 334 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 335 336 free(msg, M_DEVBUF); 337 338 return (ret); 339} 340 341/** 342 * Process a channel event notification 343 */ 344static void 345VmbusProcessChannelEvent(uint32_t relid) 346{ 347 void* arg; 348 uint32_t bytes_to_read; 349 hv_vmbus_channel* channel; 350 boolean_t is_batched_reading; 351 352 /** 353 * Find the channel based on this relid and invokes 354 * the channel callback to process the event 355 */ 356 357 channel = hv_vmbus_g_connection.channels[relid]; 358 359 if (channel == NULL) { 360 return; 361 } 362 /** 363 * To deal with the race condition where we might 364 * receive a packet while the relevant driver is 365 * being unloaded, dispatch the callback while 366 * holding the channel lock. The unloading driver 367 * will acquire the same channel lock to set the 368 * callback to NULL. This closes the window. 369 */ 370 371 /* 372 * Disable the lock due to newly added WITNESS check in r277723. 373 * Will seek other way to avoid race condition. 374 * -- whu 375 */ 376 // mtx_lock(&channel->inbound_lock); 377 if (channel->on_channel_callback != NULL) { 378 arg = channel->channel_callback_context; 379 is_batched_reading = channel->batched_reading; 380 /* 381 * Optimize host to guest signaling by ensuring: 382 * 1. While reading the channel, we disable interrupts from 383 * host. 384 * 2. Ensure that we process all posted messages from the host 385 * before returning from this callback. 386 * 3. Once we return, enable signaling from the host. Once this 387 * state is set we check to see if additional packets are 388 * available to read. In this case we repeat the process. 389 */ 390 do { 391 if (is_batched_reading) 392 hv_ring_buffer_read_begin(&channel->inbound); 393 394 channel->on_channel_callback(arg); 395 396 if (is_batched_reading) 397 bytes_to_read = 398 hv_ring_buffer_read_end(&channel->inbound); 399 else 400 bytes_to_read = 0; 401 } while (is_batched_reading && (bytes_to_read != 0)); 402 } 403 // mtx_unlock(&channel->inbound_lock); 404} 405 406/** 407 * Handler for events 408 */ 409void 410hv_vmbus_on_events(void *arg) 411{ 412 int bit; 413 int cpu; 414 int dword; 415 void *page_addr; 416 uint32_t* recv_interrupt_page = NULL; 417 int rel_id; 418 int maxdword; 419 hv_vmbus_synic_event_flags *event; 420 /* int maxdword = PAGE_SIZE >> 3; */ 421 422 cpu = (int)(long)arg; 423 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 424 "cpu out of range!")); 425 426 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 427 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 428 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 429 /* 430 * receive size is 1/2 page and divide that by 4 bytes 431 */ 432 recv_interrupt_page = 433 hv_vmbus_g_connection.recv_interrupt_page; 434 } else { 435 /* 436 * On Host with Win8 or above, the event page can be 437 * checked directly to get the id of the channel 438 * that has the pending interrupt. 439 */ 440 maxdword = HV_EVENT_FLAGS_DWORD_COUNT; 441 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 442 event = (hv_vmbus_synic_event_flags *) 443 page_addr + HV_VMBUS_MESSAGE_SINT; 444 recv_interrupt_page = event->flags32; 445 } 446 447 /* 448 * Check events 449 */ 450 if (recv_interrupt_page != NULL) { 451 for (dword = 0; dword < maxdword; dword++) { 452 if (recv_interrupt_page[dword]) { 453 for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) { 454 if (synch_test_and_clear_bit(bit, 455 (uint32_t *) &recv_interrupt_page[dword])) { 456 rel_id = (dword << 5) + bit; 457 if (rel_id == 0) { 458 /* 459 * Special case - 460 * vmbus channel protocol msg. 461 */ 462 continue; 463 } else { 464 VmbusProcessChannelEvent(rel_id); 465 466 } 467 } 468 } 469 } 470 } 471 } 472 473 return; 474} 475 476/** 477 * Send a msg on the vmbus's message connection 478 */ 479int hv_vmbus_post_message(void *buffer, size_t bufferLen) 480{ 481 hv_vmbus_connection_id connId; 482 sbintime_t time = SBT_1MS; 483 int retries; 484 int ret; 485 486 connId.as_uint32_t = 0; 487 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 488 489 /* 490 * We retry to cope with transient failures caused by host side's 491 * insufficient resources. 20 times should suffice in practice. 492 */ 493 for (retries = 0; retries < 20; retries++) { 494 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, 495 bufferLen); 496 if (ret == HV_STATUS_SUCCESS) 497 return (0); 498 499 pause_sbt("pstmsg", time, 0, C_HARDCLOCK); 500 if (time < SBT_1S * 2) 501 time *= 2; 502 } 503 504 KASSERT(ret == HV_STATUS_SUCCESS, 505 ("Error VMBUS: Message Post Failed, ret=%d\n", ret)); 506 507 return (EAGAIN); 508} 509 510/** 511 * Send an event notification to the parent 512 */ 513int 514hv_vmbus_set_event(hv_vmbus_channel *channel) { 515 int ret = 0; 516 uint32_t child_rel_id = channel->offer_msg.child_rel_id; 517 518 /* Each uint32_t represents 32 channels */ 519 520 synch_set_bit(child_rel_id & 31, 521 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 522 + (child_rel_id >> 5)))); 523 ret = hv_vmbus_signal_event(channel->signal_event_param); 524 525 return (ret); 526} 527