vmbus.c revision 300647
1/*- 2 * Copyright (c) 2009-2012,2016 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/* 30 * VM Bus Driver Implementation 31 */ 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 300647 2016-05-25 05:06:15Z sephe $"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/kernel.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/module.h> 41#include <sys/proc.h> 42#include <sys/sysctl.h> 43#include <sys/syslog.h> 44#include <sys/systm.h> 45#include <sys/rtprio.h> 46#include <sys/interrupt.h> 47#include <sys/sx.h> 48#include <sys/taskqueue.h> 49#include <sys/mutex.h> 50#include <sys/smp.h> 51 52#include <machine/resource.h> 53#include <sys/rman.h> 54 55#include <machine/stdarg.h> 56#include <machine/intr_machdep.h> 57#include <machine/md_var.h> 58#include <machine/segments.h> 59#include <sys/pcpu.h> 60#include <x86/apicvar.h> 61 62#include <dev/hyperv/include/hyperv.h> 63#include <dev/hyperv/vmbus/hv_vmbus_priv.h> 64#include <dev/hyperv/vmbus/vmbus_var.h> 65 66#include <contrib/dev/acpica/include/acpi.h> 67#include "acpi_if.h" 68 69struct vmbus_softc *vmbus_sc; 70 71static int vmbus_inited; 72 73static char *vmbus_ids[] = { "VMBUS", NULL }; 74 75extern inthand_t IDTVEC(hv_vmbus_callback); 76 77static void 78vmbus_msg_task(void *xsc, int pending __unused) 79{ 80 struct vmbus_softc *sc = xsc; 81 hv_vmbus_message *msg; 82 83 msg = VMBUS_PCPU_GET(sc, message, curcpu) + HV_VMBUS_MESSAGE_SINT; 84 for (;;) { 85 const hv_vmbus_channel_msg_table_entry *entry; 86 hv_vmbus_channel_msg_header *hdr; 87 hv_vmbus_channel_msg_type msg_type; 88 89 if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 90 break; /* no message */ 91 92 hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 93 msg_type = hdr->message_type; 94 95 if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { 96 printf("VMBUS: unknown message type = %d\n", msg_type); 97 goto handled; 98 } 99 100 entry = &g_channel_message_table[msg_type]; 101 if (entry->messageHandler) 102 entry->messageHandler(hdr); 103handled: 104 msg->header.message_type = HV_MESSAGE_TYPE_NONE; 105 /* 106 * Make sure the write to message_type (ie set to 107 * HV_MESSAGE_TYPE_NONE) happens before we read the 108 * message_pending and EOMing. Otherwise, the EOMing will 109 * not deliver any more messages 110 * since there is no empty slot 111 * 112 * NOTE: 113 * mb() is used here, since atomic_thread_fence_seq_cst() 114 * will become compiler fence on UP kernel. 115 */ 116 mb(); 117 if (msg->header.message_flags.u.message_pending) { 118 /* 119 * This will cause message queue rescan to possibly 120 * deliver another msg from the hypervisor 121 */ 122 wrmsr(HV_X64_MSR_EOM, 0); 123 } 124 } 125} 126 127/** 128 * @brief Interrupt filter routine for VMBUS. 129 * 130 * The purpose of this routine is to determine the type of VMBUS protocol 131 * message to process - an event or a channel message. 132 */ 133static inline int 134hv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu) 135{ 136 hv_vmbus_message *msg, *msg_base; 137 138 /* 139 * The Windows team has advised that we check for events 140 * before checking for messages. This is the way they do it 141 * in Windows when running as a guest in Hyper-V 142 */ 143 sc->vmbus_event_proc(sc, cpu); 144 145 /* Check if there are actual msgs to be process */ 146 msg_base = VMBUS_PCPU_GET(sc, message, cpu); 147 msg = msg_base + HV_VMBUS_TIMER_SINT; 148 149 /* we call eventtimer process the message */ 150 if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { 151 msg->header.message_type = HV_MESSAGE_TYPE_NONE; 152 153 /* call intrrupt handler of event timer */ 154 hv_et_intr(frame); 155 156 /* 157 * Make sure the write to message_type (ie set to 158 * HV_MESSAGE_TYPE_NONE) happens before we read the 159 * message_pending and EOMing. Otherwise, the EOMing will 160 * not deliver any more messages 161 * since there is no empty slot 162 * 163 * NOTE: 164 * mb() is used here, since atomic_thread_fence_seq_cst() 165 * will become compiler fence on UP kernel. 166 */ 167 mb(); 168 169 if (msg->header.message_flags.u.message_pending) { 170 /* 171 * This will cause message queue rescan to possibly 172 * deliver another msg from the hypervisor 173 */ 174 wrmsr(HV_X64_MSR_EOM, 0); 175 } 176 } 177 178 msg = msg_base + HV_VMBUS_MESSAGE_SINT; 179 if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 180 taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), 181 VMBUS_PCPU_PTR(sc, message_task, cpu)); 182 } 183 184 return (FILTER_HANDLED); 185} 186 187void 188hv_vector_handler(struct trapframe *trap_frame) 189{ 190 struct vmbus_softc *sc = vmbus_get_softc(); 191 int cpu = curcpu; 192 193 /* 194 * Disable preemption. 195 */ 196 critical_enter(); 197 198 /* 199 * Do a little interrupt counting. 200 */ 201 (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++; 202 203 hv_vmbus_isr(sc, trap_frame, cpu); 204 205 /* 206 * Enable preemption. 207 */ 208 critical_exit(); 209} 210 211static void 212vmbus_synic_setup(void *arg __unused) 213{ 214 struct vmbus_softc *sc = vmbus_get_softc(); 215 int cpu; 216 hv_vmbus_synic_simp simp; 217 hv_vmbus_synic_siefp siefp; 218 hv_vmbus_synic_scontrol sctrl; 219 hv_vmbus_synic_sint shared_sint; 220 uint64_t version; 221 222 cpu = PCPU_GET(cpuid); 223 224 /* 225 * TODO: Check the version 226 */ 227 version = rdmsr(HV_X64_MSR_SVERSION); 228 229 /* 230 * Setup the Synic's message page 231 */ 232 simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); 233 simp.u.simp_enabled = 1; 234 simp.u.base_simp_gpa = 235 VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT; 236 237 wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); 238 239 /* 240 * Setup the Synic's event page 241 */ 242 siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); 243 siefp.u.siefp_enabled = 1; 244 siefp.u.base_siefp_gpa = 245 VMBUS_PCPU_GET(sc, event_flag_dma.hv_paddr, cpu) >> PAGE_SHIFT; 246 247 wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); 248 249 /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */ 250 shared_sint.as_uint64_t = 0; 251 shared_sint.u.vector = sc->vmbus_idtvec; 252 shared_sint.u.masked = FALSE; 253 shared_sint.u.auto_eoi = TRUE; 254 255 wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, 256 shared_sint.as_uint64_t); 257 258 wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT, 259 shared_sint.as_uint64_t); 260 261 /* Enable the global synic bit */ 262 sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL); 263 sctrl.u.enable = 1; 264 265 wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t); 266 267 hv_vmbus_g_context.syn_ic_initialized = TRUE; 268 269 /* 270 * Set up the cpuid mapping from Hyper-V to FreeBSD. 271 * The array is indexed using FreeBSD cpuid. 272 */ 273 VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(HV_X64_MSR_VP_INDEX); 274} 275 276static void 277vmbus_synic_teardown(void *arg) 278{ 279 hv_vmbus_synic_sint shared_sint; 280 hv_vmbus_synic_simp simp; 281 hv_vmbus_synic_siefp siefp; 282 283 if (!hv_vmbus_g_context.syn_ic_initialized) 284 return; 285 286 shared_sint.as_uint64_t = rdmsr( 287 HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT); 288 289 shared_sint.u.masked = 1; 290 291 /* 292 * Disable the interrupt 0 293 */ 294 wrmsr( 295 HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, 296 shared_sint.as_uint64_t); 297 298 shared_sint.as_uint64_t = rdmsr( 299 HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT); 300 301 shared_sint.u.masked = 1; 302 303 /* 304 * Disable the interrupt 1 305 */ 306 wrmsr( 307 HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT, 308 shared_sint.as_uint64_t); 309 simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); 310 simp.u.simp_enabled = 0; 311 simp.u.base_simp_gpa = 0; 312 313 wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); 314 315 siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); 316 siefp.u.siefp_enabled = 0; 317 siefp.u.base_siefp_gpa = 0; 318 319 wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); 320} 321 322static int 323vmbus_dma_alloc(struct vmbus_softc *sc) 324{ 325 int cpu; 326 327 CPU_FOREACH(cpu) { 328 void *ptr; 329 330 /* 331 * Per-cpu messages and event flags. 332 */ 333 ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 334 PAGE_SIZE, 0, PAGE_SIZE, 335 VMBUS_PCPU_PTR(sc, message_dma, cpu), 336 BUS_DMA_WAITOK | BUS_DMA_ZERO); 337 if (ptr == NULL) 338 return ENOMEM; 339 VMBUS_PCPU_GET(sc, message, cpu) = ptr; 340 341 ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 342 PAGE_SIZE, 0, PAGE_SIZE, 343 VMBUS_PCPU_PTR(sc, event_flag_dma, cpu), 344 BUS_DMA_WAITOK | BUS_DMA_ZERO); 345 if (ptr == NULL) 346 return ENOMEM; 347 VMBUS_PCPU_GET(sc, event_flag, cpu) = ptr; 348 } 349 return 0; 350} 351 352static void 353vmbus_dma_free(struct vmbus_softc *sc) 354{ 355 int cpu; 356 357 CPU_FOREACH(cpu) { 358 if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) { 359 hyperv_dmamem_free( 360 VMBUS_PCPU_PTR(sc, message_dma, cpu), 361 VMBUS_PCPU_GET(sc, message, cpu)); 362 VMBUS_PCPU_GET(sc, message, cpu) = NULL; 363 } 364 if (VMBUS_PCPU_GET(sc, event_flag, cpu) != NULL) { 365 hyperv_dmamem_free( 366 VMBUS_PCPU_PTR(sc, event_flag_dma, cpu), 367 VMBUS_PCPU_GET(sc, event_flag, cpu)); 368 VMBUS_PCPU_GET(sc, event_flag, cpu) = NULL; 369 } 370 } 371} 372 373static int 374vmbus_intr_setup(struct vmbus_softc *sc) 375{ 376 int cpu; 377 378 CPU_FOREACH(cpu) { 379 char buf[MAXCOMLEN + 1]; 380 cpuset_t cpu_mask; 381 382 /* Allocate an interrupt counter for Hyper-V interrupt */ 383 snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu); 384 intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu)); 385 386 /* 387 * Setup taskqueue to handle events. Task will be per- 388 * channel. 389 */ 390 VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast( 391 "hyperv event", M_WAITOK, taskqueue_thread_enqueue, 392 VMBUS_PCPU_PTR(sc, event_tq, cpu)); 393 CPU_SETOF(cpu, &cpu_mask); 394 taskqueue_start_threads_cpuset( 395 VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask, 396 "hvevent%d", cpu); 397 398 /* 399 * Setup tasks and taskqueues to handle messages. 400 */ 401 VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast( 402 "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 403 VMBUS_PCPU_PTR(sc, message_tq, cpu)); 404 CPU_SETOF(cpu, &cpu_mask); 405 taskqueue_start_threads_cpuset( 406 VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask, 407 "hvmsg%d", cpu); 408 TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0, 409 vmbus_msg_task, sc); 410 } 411 412 /* 413 * All Hyper-V ISR required resources are setup, now let's find a 414 * free IDT vector for Hyper-V ISR and set it up. 415 */ 416 sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback)); 417 if (sc->vmbus_idtvec < 0) { 418 device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); 419 return ENXIO; 420 } 421 if(bootverbose) { 422 device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", 423 sc->vmbus_idtvec); 424 } 425 return 0; 426} 427 428static void 429vmbus_intr_teardown(struct vmbus_softc *sc) 430{ 431 int cpu; 432 433 if (sc->vmbus_idtvec >= 0) { 434 lapic_ipi_free(sc->vmbus_idtvec); 435 sc->vmbus_idtvec = -1; 436 } 437 438 CPU_FOREACH(cpu) { 439 if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) { 440 taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu)); 441 VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL; 442 } 443 if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) { 444 taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu), 445 VMBUS_PCPU_PTR(sc, message_task, cpu)); 446 taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu)); 447 VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL; 448 } 449 } 450} 451 452static int 453vmbus_read_ivar( 454 device_t dev, 455 device_t child, 456 int index, 457 uintptr_t* result) 458{ 459 struct hv_device *child_dev_ctx = device_get_ivars(child); 460 461 switch (index) { 462 463 case HV_VMBUS_IVAR_TYPE: 464 *result = (uintptr_t) &child_dev_ctx->class_id; 465 return (0); 466 case HV_VMBUS_IVAR_INSTANCE: 467 *result = (uintptr_t) &child_dev_ctx->device_id; 468 return (0); 469 case HV_VMBUS_IVAR_DEVCTX: 470 *result = (uintptr_t) child_dev_ctx; 471 return (0); 472 case HV_VMBUS_IVAR_NODE: 473 *result = (uintptr_t) child_dev_ctx->device; 474 return (0); 475 } 476 return (ENOENT); 477} 478 479static int 480vmbus_write_ivar( 481 device_t dev, 482 device_t child, 483 int index, 484 uintptr_t value) 485{ 486 switch (index) { 487 488 case HV_VMBUS_IVAR_TYPE: 489 case HV_VMBUS_IVAR_INSTANCE: 490 case HV_VMBUS_IVAR_DEVCTX: 491 case HV_VMBUS_IVAR_NODE: 492 /* read-only */ 493 return (EINVAL); 494 } 495 return (ENOENT); 496} 497 498static int 499vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 500{ 501 char guidbuf[40]; 502 struct hv_device *dev_ctx = device_get_ivars(child); 503 504 if (dev_ctx == NULL) 505 return (0); 506 507 strlcat(buf, "classid=", buflen); 508 snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id); 509 strlcat(buf, guidbuf, buflen); 510 511 strlcat(buf, " deviceid=", buflen); 512 snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id); 513 strlcat(buf, guidbuf, buflen); 514 515 return (0); 516} 517 518struct hv_device* 519hv_vmbus_child_device_create( 520 hv_guid type, 521 hv_guid instance, 522 hv_vmbus_channel* channel) 523{ 524 hv_device* child_dev; 525 526 /* 527 * Allocate the new child device 528 */ 529 child_dev = malloc(sizeof(hv_device), M_DEVBUF, 530 M_WAITOK | M_ZERO); 531 532 child_dev->channel = channel; 533 memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 534 memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 535 536 return (child_dev); 537} 538 539int 540snprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid) 541{ 542 int cnt; 543 const unsigned char *d = guid->data; 544 545 cnt = snprintf(buf, sz, 546 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 547 d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], 548 d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); 549 return (cnt); 550} 551 552int 553hv_vmbus_child_device_register(struct hv_device *child_dev) 554{ 555 device_t child; 556 557 if (bootverbose) { 558 char name[40]; 559 snprintf_hv_guid(name, sizeof(name), &child_dev->class_id); 560 printf("VMBUS: Class ID: %s\n", name); 561 } 562 563 child = device_add_child(vmbus_get_device(), NULL, -1); 564 child_dev->device = child; 565 device_set_ivars(child, child_dev); 566 567 return (0); 568} 569 570int 571hv_vmbus_child_device_unregister(struct hv_device *child_dev) 572{ 573 int ret = 0; 574 /* 575 * XXXKYS: Ensure that this is the opposite of 576 * device_add_child() 577 */ 578 mtx_lock(&Giant); 579 ret = device_delete_child(vmbus_get_device(), child_dev->device); 580 mtx_unlock(&Giant); 581 return(ret); 582} 583 584static int 585vmbus_probe(device_t dev) 586{ 587 if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || 588 device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV) 589 return (ENXIO); 590 591 device_set_desc(dev, "Hyper-V Vmbus"); 592 593 return (BUS_PROBE_DEFAULT); 594} 595 596/** 597 * @brief Main vmbus driver initialization routine. 598 * 599 * Here, we 600 * - initialize the vmbus driver context 601 * - setup various driver entry points 602 * - invoke the vmbus hv main init routine 603 * - get the irq resource 604 * - invoke the vmbus to add the vmbus root device 605 * - setup the vmbus root device 606 * - retrieve the channel offers 607 */ 608static int 609vmbus_bus_init(void) 610{ 611 struct vmbus_softc *sc; 612 int ret; 613 614 if (vmbus_inited) 615 return (0); 616 617 vmbus_inited = 1; 618 sc = vmbus_get_softc(); 619 620 /* 621 * Allocate DMA stuffs. 622 */ 623 ret = vmbus_dma_alloc(sc); 624 if (ret != 0) 625 goto cleanup; 626 627 /* 628 * Setup interrupt. 629 */ 630 ret = vmbus_intr_setup(sc); 631 if (ret != 0) 632 goto cleanup; 633 634 if (bootverbose) 635 printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n", 636 smp_started); 637 smp_rendezvous(NULL, vmbus_synic_setup, NULL, NULL); 638 639 /* 640 * Connect to VMBus in the root partition 641 */ 642 ret = hv_vmbus_connect(); 643 644 if (ret != 0) 645 goto cleanup; 646 647 if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || 648 hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) 649 sc->vmbus_event_proc = vmbus_event_proc_compat; 650 else 651 sc->vmbus_event_proc = vmbus_event_proc; 652 653 hv_vmbus_request_channel_offers(); 654 655 vmbus_scan(); 656 bus_generic_attach(sc->vmbus_dev); 657 device_printf(sc->vmbus_dev, "device scan, probe and attach done\n"); 658 659 return (ret); 660 661cleanup: 662 vmbus_intr_teardown(sc); 663 vmbus_dma_free(sc); 664 665 return (ret); 666} 667 668static void 669vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 670{ 671} 672 673static int 674vmbus_attach(device_t dev) 675{ 676 vmbus_sc = device_get_softc(dev); 677 vmbus_sc->vmbus_dev = dev; 678 vmbus_sc->vmbus_idtvec = -1; 679 680 /* 681 * Event processing logic will be configured: 682 * - After the vmbus protocol version negotiation. 683 * - Before we request channel offers. 684 */ 685 vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 686 687#ifndef EARLY_AP_STARTUP 688 /* 689 * If the system has already booted and thread 690 * scheduling is possible indicated by the global 691 * cold set to zero, we just call the driver 692 * initialization directly. 693 */ 694 if (!cold) 695#endif 696 vmbus_bus_init(); 697 698 bus_generic_probe(dev); 699 return (0); 700} 701 702static void 703vmbus_sysinit(void *arg __unused) 704{ 705 if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL) 706 return; 707 708#ifndef EARLY_AP_STARTUP 709 /* 710 * If the system has already booted and thread 711 * scheduling is possible, as indicated by the 712 * global cold set to zero, we just call the driver 713 * initialization directly. 714 */ 715 if (!cold) 716#endif 717 vmbus_bus_init(); 718} 719 720static int 721vmbus_detach(device_t dev) 722{ 723 struct vmbus_softc *sc = device_get_softc(dev); 724 725 hv_vmbus_release_unattached_channels(); 726 hv_vmbus_disconnect(); 727 728 smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL); 729 730 vmbus_intr_teardown(sc); 731 vmbus_dma_free(sc); 732 733 return (0); 734} 735 736static device_method_t vmbus_methods[] = { 737 /* Device interface */ 738 DEVMETHOD(device_probe, vmbus_probe), 739 DEVMETHOD(device_attach, vmbus_attach), 740 DEVMETHOD(device_detach, vmbus_detach), 741 DEVMETHOD(device_shutdown, bus_generic_shutdown), 742 DEVMETHOD(device_suspend, bus_generic_suspend), 743 DEVMETHOD(device_resume, bus_generic_resume), 744 745 /* Bus interface */ 746 DEVMETHOD(bus_add_child, bus_generic_add_child), 747 DEVMETHOD(bus_print_child, bus_generic_print_child), 748 DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 749 DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 750 DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 751 752 DEVMETHOD_END 753}; 754 755static driver_t vmbus_driver = { 756 "vmbus", 757 vmbus_methods, 758 sizeof(struct vmbus_softc) 759}; 760 761static devclass_t vmbus_devclass; 762 763DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL); 764MODULE_DEPEND(vmbus, acpi, 1, 1, 1); 765MODULE_VERSION(vmbus, 1); 766 767#ifndef EARLY_AP_STARTUP 768/* 769 * NOTE: 770 * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is 771 * initialized. 772 */ 773SYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL); 774#endif 775