vmbus.c revision 300567
1250199Sgrehan/*- 2298446Ssephe * Copyright (c) 2009-2012,2016 Microsoft Corp. 3250199Sgrehan * Copyright (c) 2012 NetApp Inc. 4250199Sgrehan * Copyright (c) 2012 Citrix Inc. 5250199Sgrehan * All rights reserved. 6250199Sgrehan * 7250199Sgrehan * Redistribution and use in source and binary forms, with or without 8250199Sgrehan * modification, are permitted provided that the following conditions 9250199Sgrehan * are met: 10250199Sgrehan * 1. Redistributions of source code must retain the above copyright 11250199Sgrehan * notice unmodified, this list of conditions, and the following 12250199Sgrehan * disclaimer. 13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14250199Sgrehan * notice, this list of conditions and the following disclaimer in the 15250199Sgrehan * documentation and/or other materials provided with the distribution. 16250199Sgrehan * 17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27250199Sgrehan */ 28250199Sgrehan 29250199Sgrehan/* 30250199Sgrehan * VM Bus Driver Implementation 31250199Sgrehan */ 32256276Sdim#include <sys/cdefs.h> 33256276Sdim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 300567 2016-05-24 05:18:26Z sephe $"); 34250199Sgrehan 35250199Sgrehan#include <sys/param.h> 36250199Sgrehan#include <sys/bus.h> 37250199Sgrehan#include <sys/kernel.h> 38250199Sgrehan#include <sys/lock.h> 39250199Sgrehan#include <sys/malloc.h> 40250199Sgrehan#include <sys/module.h> 41293873Ssephe#include <sys/proc.h> 42250199Sgrehan#include <sys/sysctl.h> 43250199Sgrehan#include <sys/syslog.h> 44250199Sgrehan#include <sys/systm.h> 45250199Sgrehan#include <sys/rtprio.h> 46250199Sgrehan#include <sys/interrupt.h> 47250199Sgrehan#include <sys/sx.h> 48250199Sgrehan#include <sys/taskqueue.h> 49250199Sgrehan#include <sys/mutex.h> 50250199Sgrehan#include <sys/smp.h> 51250199Sgrehan 52250199Sgrehan#include <machine/resource.h> 53250199Sgrehan#include <sys/rman.h> 54250199Sgrehan 55250199Sgrehan#include <machine/stdarg.h> 56250199Sgrehan#include <machine/intr_machdep.h> 57282212Swhu#include <machine/md_var.h> 58282212Swhu#include <machine/segments.h> 59250199Sgrehan#include <sys/pcpu.h> 60282212Swhu#include <x86/apicvar.h> 61250199Sgrehan 62297142Ssephe#include <dev/hyperv/include/hyperv.h> 63300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h> 64300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h> 65250199Sgrehan 66293870Ssephe#include <contrib/dev/acpica/include/acpi.h> 67293870Ssephe#include "acpi_if.h" 68250199Sgrehan 69300102Ssephestruct vmbus_softc *vmbus_sc; 70300102Ssephe 71250199Sgrehanstatic int vmbus_inited; 72255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */ 73250199Sgrehan 74293870Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL }; 75293870Ssephe 76250199Sgrehanstatic void 77300112Ssephevmbus_msg_task(void *arg __unused, int pending __unused) 78250199Sgrehan{ 79300108Ssephe hv_vmbus_message *msg; 80250199Sgrehan 81300481Ssephe msg = hv_vmbus_g_context.syn_ic_msg_page[curcpu] + 82300108Ssephe HV_VMBUS_MESSAGE_SINT; 83300481Ssephe 84300108Ssephe for (;;) { 85300108Ssephe const hv_vmbus_channel_msg_table_entry *entry; 86300108Ssephe hv_vmbus_channel_msg_header *hdr; 87300108Ssephe hv_vmbus_channel_msg_type msg_type; 88282212Swhu 89292861Sdelphij if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 90250199Sgrehan break; /* no message */ 91292861Sdelphij 92292861Sdelphij hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 93292861Sdelphij msg_type = hdr->message_type; 94292861Sdelphij 95295307Ssephe if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { 96292861Sdelphij printf("VMBUS: unknown message type = %d\n", msg_type); 97292861Sdelphij goto handled; 98292861Sdelphij } 99292861Sdelphij 100292861Sdelphij entry = &g_channel_message_table[msg_type]; 101295307Ssephe if (entry->messageHandler) 102292861Sdelphij entry->messageHandler(hdr); 103292861Sdelphijhandled: 104300108Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 105300108Ssephe /* 106300108Ssephe * Make sure the write to message_type (ie set to 107300108Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 108300108Ssephe * message_pending and EOMing. Otherwise, the EOMing will 109300108Ssephe * not deliver any more messages 110300108Ssephe * since there is no empty slot 111300108Ssephe * 112300108Ssephe * NOTE: 113300108Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 114300108Ssephe * will become compiler fence on UP kernel. 115300108Ssephe */ 116300108Ssephe mb(); 117300108Ssephe if (msg->header.message_flags.u.message_pending) { 118250199Sgrehan /* 119250199Sgrehan * This will cause message queue rescan to possibly 120250199Sgrehan * deliver another msg from the hypervisor 121250199Sgrehan */ 122255414Sgrehan wrmsr(HV_X64_MSR_EOM, 0); 123300108Ssephe } 124250199Sgrehan } 125250199Sgrehan} 126250199Sgrehan 127250199Sgrehan/** 128250199Sgrehan * @brief Interrupt filter routine for VMBUS. 129250199Sgrehan * 130250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol 131250199Sgrehan * message to process - an event or a channel message. 132250199Sgrehan */ 133282212Swhustatic inline int 134300567Ssephehv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu) 135250199Sgrehan{ 136300481Ssephe hv_vmbus_message *msg, *msg_base; 137250199Sgrehan 138250199Sgrehan /* 139250199Sgrehan * The Windows team has advised that we check for events 140250199Sgrehan * before checking for messages. This is the way they do it 141250199Sgrehan * in Windows when running as a guest in Hyper-V 142250199Sgrehan */ 143300107Ssephe sc->vmbus_event_proc(sc, cpu); 144250199Sgrehan 145250199Sgrehan /* Check if there are actual msgs to be process */ 146300481Ssephe msg_base = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 147300481Ssephe msg = msg_base + HV_VMBUS_TIMER_SINT; 148250199Sgrehan 149293873Ssephe /* we call eventtimer process the message */ 150293873Ssephe if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { 151293873Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 152293873Ssephe 153297176Ssephe /* call intrrupt handler of event timer */ 154297176Ssephe hv_et_intr(frame); 155297176Ssephe 156293873Ssephe /* 157293873Ssephe * Make sure the write to message_type (ie set to 158293873Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 159293873Ssephe * message_pending and EOMing. Otherwise, the EOMing will 160293873Ssephe * not deliver any more messages 161293873Ssephe * since there is no empty slot 162297634Ssephe * 163297634Ssephe * NOTE: 164297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 165297636Ssephe * will become compiler fence on UP kernel. 166293873Ssephe */ 167297634Ssephe mb(); 168293873Ssephe 169293873Ssephe if (msg->header.message_flags.u.message_pending) { 170293873Ssephe /* 171293873Ssephe * This will cause message queue rescan to possibly 172293873Ssephe * deliver another msg from the hypervisor 173293873Ssephe */ 174293873Ssephe wrmsr(HV_X64_MSR_EOM, 0); 175293873Ssephe } 176293873Ssephe } 177293873Ssephe 178300481Ssephe msg = msg_base + HV_VMBUS_MESSAGE_SINT; 179250199Sgrehan if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 180297221Ssephe taskqueue_enqueue(hv_vmbus_g_context.hv_msg_tq[cpu], 181297221Ssephe &hv_vmbus_g_context.hv_msg_task[cpu]); 182250199Sgrehan } 183250199Sgrehan 184293873Ssephe return (FILTER_HANDLED); 185250199Sgrehan} 186250199Sgrehan 187282212Swhuvoid 188282212Swhuhv_vector_handler(struct trapframe *trap_frame) 189282212Swhu{ 190300565Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 191300565Ssephe int cpu = curcpu; 192282212Swhu 193282212Swhu /* 194282212Swhu * Disable preemption. 195282212Swhu */ 196282212Swhu critical_enter(); 197282212Swhu 198282212Swhu /* 199282212Swhu * Do a little interrupt counting. 200282212Swhu */ 201300565Ssephe (*VMBUS_SC_PCPU_GET(sc, intr_cnt, cpu))++; 202282212Swhu 203300567Ssephe hv_vmbus_isr(sc, trap_frame, cpu); 204282212Swhu 205282212Swhu /* 206282212Swhu * Enable preemption. 207282212Swhu */ 208282212Swhu critical_exit(); 209282212Swhu} 210282212Swhu 211250199Sgrehanstatic int 212250199Sgrehanvmbus_read_ivar( 213250199Sgrehan device_t dev, 214250199Sgrehan device_t child, 215250199Sgrehan int index, 216250199Sgrehan uintptr_t* result) 217250199Sgrehan{ 218250199Sgrehan struct hv_device *child_dev_ctx = device_get_ivars(child); 219250199Sgrehan 220250199Sgrehan switch (index) { 221250199Sgrehan 222250199Sgrehan case HV_VMBUS_IVAR_TYPE: 223250199Sgrehan *result = (uintptr_t) &child_dev_ctx->class_id; 224250199Sgrehan return (0); 225250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 226250199Sgrehan *result = (uintptr_t) &child_dev_ctx->device_id; 227250199Sgrehan return (0); 228250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 229250199Sgrehan *result = (uintptr_t) child_dev_ctx; 230250199Sgrehan return (0); 231250199Sgrehan case HV_VMBUS_IVAR_NODE: 232250199Sgrehan *result = (uintptr_t) child_dev_ctx->device; 233250199Sgrehan return (0); 234250199Sgrehan } 235250199Sgrehan return (ENOENT); 236250199Sgrehan} 237250199Sgrehan 238250199Sgrehanstatic int 239250199Sgrehanvmbus_write_ivar( 240250199Sgrehan device_t dev, 241250199Sgrehan device_t child, 242250199Sgrehan int index, 243250199Sgrehan uintptr_t value) 244250199Sgrehan{ 245250199Sgrehan switch (index) { 246250199Sgrehan 247250199Sgrehan case HV_VMBUS_IVAR_TYPE: 248250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 249250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 250250199Sgrehan case HV_VMBUS_IVAR_NODE: 251250199Sgrehan /* read-only */ 252250199Sgrehan return (EINVAL); 253250199Sgrehan } 254250199Sgrehan return (ENOENT); 255250199Sgrehan} 256250199Sgrehan 257297143Ssephestatic int 258297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 259297143Ssephe{ 260297143Ssephe char guidbuf[40]; 261297143Ssephe struct hv_device *dev_ctx = device_get_ivars(child); 262297143Ssephe 263298449Ssephe if (dev_ctx == NULL) 264298449Ssephe return (0); 265298449Ssephe 266297143Ssephe strlcat(buf, "classid=", buflen); 267297143Ssephe snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id); 268297143Ssephe strlcat(buf, guidbuf, buflen); 269297143Ssephe 270297143Ssephe strlcat(buf, " deviceid=", buflen); 271297143Ssephe snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id); 272297143Ssephe strlcat(buf, guidbuf, buflen); 273297143Ssephe 274297143Ssephe return (0); 275297143Ssephe} 276297143Ssephe 277250199Sgrehanstruct hv_device* 278250199Sgrehanhv_vmbus_child_device_create( 279250199Sgrehan hv_guid type, 280250199Sgrehan hv_guid instance, 281250199Sgrehan hv_vmbus_channel* channel) 282250199Sgrehan{ 283250199Sgrehan hv_device* child_dev; 284250199Sgrehan 285250199Sgrehan /* 286250199Sgrehan * Allocate the new child device 287250199Sgrehan */ 288250199Sgrehan child_dev = malloc(sizeof(hv_device), M_DEVBUF, 289295308Ssephe M_WAITOK | M_ZERO); 290250199Sgrehan 291250199Sgrehan child_dev->channel = channel; 292250199Sgrehan memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 293250199Sgrehan memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 294250199Sgrehan 295250199Sgrehan return (child_dev); 296250199Sgrehan} 297250199Sgrehan 298297142Ssepheint 299297142Ssephesnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid) 300250199Sgrehan{ 301297142Ssephe int cnt; 302297142Ssephe const unsigned char *d = guid->data; 303297142Ssephe 304297142Ssephe cnt = snprintf(buf, sz, 305297142Ssephe "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 306297142Ssephe d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], 307297142Ssephe d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); 308297142Ssephe return (cnt); 309250199Sgrehan} 310250199Sgrehan 311250199Sgrehanint 312250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev) 313250199Sgrehan{ 314250199Sgrehan device_t child; 315250199Sgrehan 316297142Ssephe if (bootverbose) { 317297142Ssephe char name[40]; 318297142Ssephe snprintf_hv_guid(name, sizeof(name), &child_dev->class_id); 319297142Ssephe printf("VMBUS: Class ID: %s\n", name); 320297142Ssephe } 321250199Sgrehan 322300486Ssephe child = device_add_child(vmbus_get_device(), NULL, -1); 323250199Sgrehan child_dev->device = child; 324250199Sgrehan device_set_ivars(child, child_dev); 325250199Sgrehan 326250199Sgrehan return (0); 327250199Sgrehan} 328250199Sgrehan 329250199Sgrehanint 330250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev) 331250199Sgrehan{ 332250199Sgrehan int ret = 0; 333250199Sgrehan /* 334250199Sgrehan * XXXKYS: Ensure that this is the opposite of 335250199Sgrehan * device_add_child() 336250199Sgrehan */ 337250199Sgrehan mtx_lock(&Giant); 338300486Ssephe ret = device_delete_child(vmbus_get_device(), child_dev->device); 339250199Sgrehan mtx_unlock(&Giant); 340250199Sgrehan return(ret); 341250199Sgrehan} 342250199Sgrehan 343250199Sgrehanstatic int 344300127Ssephevmbus_probe(device_t dev) 345300127Ssephe{ 346293870Ssephe if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || 347300480Ssephe device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV) 348293870Ssephe return (ENXIO); 349250199Sgrehan 350300129Ssephe device_set_desc(dev, "Hyper-V Vmbus"); 351250199Sgrehan 352293870Ssephe return (BUS_PROBE_DEFAULT); 353250199Sgrehan} 354250199Sgrehan 355297641Ssepheextern inthand_t IDTVEC(hv_vmbus_callback); 356282212Swhu 357282212Swhu/** 358250199Sgrehan * @brief Main vmbus driver initialization routine. 359250199Sgrehan * 360250199Sgrehan * Here, we 361250199Sgrehan * - initialize the vmbus driver context 362250199Sgrehan * - setup various driver entry points 363250199Sgrehan * - invoke the vmbus hv main init routine 364250199Sgrehan * - get the irq resource 365250199Sgrehan * - invoke the vmbus to add the vmbus root device 366250199Sgrehan * - setup the vmbus root device 367250199Sgrehan * - retrieve the channel offers 368250199Sgrehan */ 369250199Sgrehanstatic int 370250199Sgrehanvmbus_bus_init(void) 371250199Sgrehan{ 372300107Ssephe struct vmbus_softc *sc; 373282212Swhu int i, j, n, ret; 374293874Ssephe char buf[MAXCOMLEN + 1]; 375294886Ssephe cpuset_t cpu_mask; 376250199Sgrehan 377250199Sgrehan if (vmbus_inited) 378250199Sgrehan return (0); 379250199Sgrehan 380250199Sgrehan vmbus_inited = 1; 381300107Ssephe sc = vmbus_get_softc(); 382250199Sgrehan 383250199Sgrehan /* 384300487Ssephe * Find a free IDT vector for vmbus messages/events. 385250199Sgrehan */ 386300487Ssephe sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback)); 387300487Ssephe if (sc->vmbus_idtvec < 0) { 388300487Ssephe device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); 389300480Ssephe ret = ENXIO; 390282212Swhu goto cleanup; 391282212Swhu } 392300487Ssephe if(bootverbose) { 393300487Ssephe device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", 394300487Ssephe sc->vmbus_idtvec); 395300487Ssephe } 396250199Sgrehan 397282212Swhu CPU_FOREACH(j) { 398293874Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); 399300565Ssephe intrcnt_add(buf, VMBUS_SC_PCPU_PTR(sc, intr_cnt, j)); 400293874Ssephe 401282212Swhu for (i = 0; i < 2; i++) 402282212Swhu setup_args.page_buffers[2 * j + i] = NULL; 403250199Sgrehan } 404250199Sgrehan 405250199Sgrehan /* 406282212Swhu * Per cpu setup. 407250199Sgrehan */ 408282212Swhu CPU_FOREACH(j) { 409282212Swhu /* 410294886Ssephe * Setup taskqueue to handle events 411294886Ssephe */ 412294886Ssephe hv_vmbus_g_context.hv_event_queue[j] = taskqueue_create_fast("hyperv event", M_WAITOK, 413294886Ssephe taskqueue_thread_enqueue, &hv_vmbus_g_context.hv_event_queue[j]); 414294886Ssephe CPU_SETOF(j, &cpu_mask); 415294886Ssephe taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_event_queue[j], 1, PI_NET, &cpu_mask, 416294886Ssephe "hvevent%d", j); 417294886Ssephe 418294886Ssephe /* 419297221Ssephe * Setup per-cpu tasks and taskqueues to handle msg. 420282212Swhu */ 421297221Ssephe hv_vmbus_g_context.hv_msg_tq[j] = taskqueue_create_fast( 422297221Ssephe "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 423297221Ssephe &hv_vmbus_g_context.hv_msg_tq[j]); 424297221Ssephe CPU_SETOF(j, &cpu_mask); 425297221Ssephe taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_msg_tq[j], 426297221Ssephe 1, PI_NET, &cpu_mask, "hvmsg%d", j); 427297221Ssephe TASK_INIT(&hv_vmbus_g_context.hv_msg_task[j], 0, 428300112Ssephe vmbus_msg_task, NULL); 429297221Ssephe 430282212Swhu /* 431282212Swhu * Prepare the per cpu msg and event pages to be called on each cpu. 432282212Swhu */ 433282212Swhu for(i = 0; i < 2; i++) { 434282212Swhu setup_args.page_buffers[2 * j + i] = 435295308Ssephe malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 436255414Sgrehan } 437255414Sgrehan } 438250199Sgrehan 439282212Swhu if (bootverbose) 440282212Swhu printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n", 441282212Swhu smp_started); 442282212Swhu 443255414Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); 444250199Sgrehan 445255414Sgrehan /* 446250199Sgrehan * Connect to VMBus in the root partition 447250199Sgrehan */ 448250199Sgrehan ret = hv_vmbus_connect(); 449250199Sgrehan 450255414Sgrehan if (ret != 0) 451282212Swhu goto cleanup1; 452250199Sgrehan 453300107Ssephe if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || 454300107Ssephe hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) 455300107Ssephe sc->vmbus_event_proc = vmbus_event_proc_compat; 456300107Ssephe else 457300107Ssephe sc->vmbus_event_proc = vmbus_event_proc; 458300107Ssephe 459250199Sgrehan hv_vmbus_request_channel_offers(); 460298260Ssephe 461298260Ssephe vmbus_scan(); 462300486Ssephe bus_generic_attach(sc->vmbus_dev); 463300486Ssephe device_printf(sc->vmbus_dev, "device scan, probe and attach done\n"); 464298260Ssephe 465250199Sgrehan return (ret); 466250199Sgrehan 467282212Swhu cleanup1: 468282212Swhu /* 469282212Swhu * Free pages alloc'ed 470282212Swhu */ 471282212Swhu for (n = 0; n < 2 * MAXCPU; n++) 472282212Swhu if (setup_args.page_buffers[n] != NULL) 473282212Swhu free(setup_args.page_buffers[n], M_DEVBUF); 474250199Sgrehan 475250199Sgrehan /* 476282212Swhu * remove swi and vmbus callback vector; 477250199Sgrehan */ 478282212Swhu CPU_FOREACH(j) { 479297177Ssephe if (hv_vmbus_g_context.hv_event_queue[j] != NULL) { 480294886Ssephe taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]); 481297177Ssephe hv_vmbus_g_context.hv_event_queue[j] = NULL; 482297177Ssephe } 483282212Swhu } 484250199Sgrehan 485300487Ssephe lapic_ipi_free(sc->vmbus_idtvec); 486250199Sgrehan 487250199Sgrehan cleanup: 488250199Sgrehan return (ret); 489250199Sgrehan} 490250199Sgrehan 491300107Ssephestatic void 492300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 493300107Ssephe{ 494300107Ssephe} 495300107Ssephe 496250199Sgrehanstatic int 497250199Sgrehanvmbus_attach(device_t dev) 498250199Sgrehan{ 499300102Ssephe vmbus_sc = device_get_softc(dev); 500300486Ssephe vmbus_sc->vmbus_dev = dev; 501250199Sgrehan 502300107Ssephe /* 503300107Ssephe * Event processing logic will be configured: 504300107Ssephe * - After the vmbus protocol version negotiation. 505300107Ssephe * - Before we request channel offers. 506300107Ssephe */ 507300107Ssephe vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 508300107Ssephe 509299746Sjhb#ifndef EARLY_AP_STARTUP 510250199Sgrehan /* 511250199Sgrehan * If the system has already booted and thread 512250199Sgrehan * scheduling is possible indicated by the global 513250199Sgrehan * cold set to zero, we just call the driver 514250199Sgrehan * initialization directly. 515250199Sgrehan */ 516250199Sgrehan if (!cold) 517299746Sjhb#endif 518250199Sgrehan vmbus_bus_init(); 519250199Sgrehan 520298449Ssephe bus_generic_probe(dev); 521250199Sgrehan return (0); 522250199Sgrehan} 523250199Sgrehan 524250199Sgrehanstatic void 525300126Ssephevmbus_sysinit(void *arg __unused) 526250199Sgrehan{ 527300102Ssephe if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL) 528256425Sgibbs return; 529256425Sgibbs 530299746Sjhb#ifndef EARLY_AP_STARTUP 531250199Sgrehan /* 532250199Sgrehan * If the system has already booted and thread 533256425Sgibbs * scheduling is possible, as indicated by the 534256425Sgibbs * global cold set to zero, we just call the driver 535250199Sgrehan * initialization directly. 536250199Sgrehan */ 537250199Sgrehan if (!cold) 538299746Sjhb#endif 539250199Sgrehan vmbus_bus_init(); 540250199Sgrehan} 541250199Sgrehan 542300121Ssephestatic int 543300121Ssephevmbus_detach(device_t dev) 544250199Sgrehan{ 545300487Ssephe struct vmbus_softc *sc = device_get_softc(dev); 546255414Sgrehan int i; 547255414Sgrehan 548250199Sgrehan hv_vmbus_release_unattached_channels(); 549250199Sgrehan hv_vmbus_disconnect(); 550250199Sgrehan 551250199Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 552250199Sgrehan 553282212Swhu for(i = 0; i < 2 * MAXCPU; i++) { 554297841Ssephe if (setup_args.page_buffers[i] != NULL) 555255414Sgrehan free(setup_args.page_buffers[i], M_DEVBUF); 556255414Sgrehan } 557255414Sgrehan 558282212Swhu /* remove swi */ 559282212Swhu CPU_FOREACH(i) { 560297177Ssephe if (hv_vmbus_g_context.hv_event_queue[i] != NULL) { 561294886Ssephe taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]); 562297177Ssephe hv_vmbus_g_context.hv_event_queue[i] = NULL; 563297177Ssephe } 564282212Swhu } 565250199Sgrehan 566300487Ssephe lapic_ipi_free(sc->vmbus_idtvec); 567250199Sgrehan 568250199Sgrehan return (0); 569250199Sgrehan} 570250199Sgrehan 571250199Sgrehanstatic device_method_t vmbus_methods[] = { 572300124Ssephe /* Device interface */ 573300124Ssephe DEVMETHOD(device_probe, vmbus_probe), 574300124Ssephe DEVMETHOD(device_attach, vmbus_attach), 575300124Ssephe DEVMETHOD(device_detach, vmbus_detach), 576300124Ssephe DEVMETHOD(device_shutdown, bus_generic_shutdown), 577300124Ssephe DEVMETHOD(device_suspend, bus_generic_suspend), 578300124Ssephe DEVMETHOD(device_resume, bus_generic_resume), 579250199Sgrehan 580300124Ssephe /* Bus interface */ 581300124Ssephe DEVMETHOD(bus_add_child, bus_generic_add_child), 582300124Ssephe DEVMETHOD(bus_print_child, bus_generic_print_child), 583300124Ssephe DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 584300124Ssephe DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 585300124Ssephe DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 586250199Sgrehan 587300124Ssephe DEVMETHOD_END 588300124Ssephe}; 589250199Sgrehan 590300102Ssephestatic driver_t vmbus_driver = { 591300102Ssephe "vmbus", 592300102Ssephe vmbus_methods, 593300102Ssephe sizeof(struct vmbus_softc) 594300102Ssephe}; 595250199Sgrehan 596300123Ssephestatic devclass_t vmbus_devclass; 597250199Sgrehan 598300120SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL); 599293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 600293870SsepheMODULE_VERSION(vmbus, 1); 601250199Sgrehan 602299746Sjhb#ifndef EARLY_AP_STARTUP 603300126Ssephe/* 604300126Ssephe * NOTE: 605300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is 606300126Ssephe * initialized. 607300126Ssephe */ 608300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL); 609299746Sjhb#endif 610