vmbus.c revision 300107
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 300107 2016-05-18 03:41:37Z 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 device_t vmbus_devp; 72250199Sgrehanstatic int vmbus_inited; 73255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */ 74250199Sgrehan 75293870Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL }; 76293870Ssephe 77250199Sgrehan/** 78250199Sgrehan * @brief Software interrupt thread routine to handle channel messages from 79250199Sgrehan * the hypervisor. 80250199Sgrehan */ 81250199Sgrehanstatic void 82297177Ssephevmbus_msg_swintr(void *arg, int pending __unused) 83250199Sgrehan{ 84250199Sgrehan int cpu; 85250199Sgrehan void* page_addr; 86292861Sdelphij hv_vmbus_channel_msg_header *hdr; 87292861Sdelphij hv_vmbus_channel_msg_table_entry *entry; 88292861Sdelphij hv_vmbus_channel_msg_type msg_type; 89250199Sgrehan hv_vmbus_message* msg; 90250199Sgrehan 91282212Swhu cpu = (int)(long)arg; 92282212Swhu KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: " 93282212Swhu "cpu out of range!")); 94282212Swhu 95250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 96250199Sgrehan msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 97250199Sgrehan 98250199Sgrehan for (;;) { 99292861Sdelphij if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 100250199Sgrehan break; /* no message */ 101292861Sdelphij 102292861Sdelphij hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 103292861Sdelphij msg_type = hdr->message_type; 104292861Sdelphij 105295307Ssephe if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { 106292861Sdelphij printf("VMBUS: unknown message type = %d\n", msg_type); 107292861Sdelphij goto handled; 108292861Sdelphij } 109292861Sdelphij 110292861Sdelphij entry = &g_channel_message_table[msg_type]; 111292861Sdelphij 112295307Ssephe if (entry->messageHandler) 113292861Sdelphij entry->messageHandler(hdr); 114292861Sdelphijhandled: 115250199Sgrehan msg->header.message_type = HV_MESSAGE_TYPE_NONE; 116250199Sgrehan 117250199Sgrehan /* 118250199Sgrehan * Make sure the write to message_type (ie set to 119250199Sgrehan * HV_MESSAGE_TYPE_NONE) happens before we read the 120250199Sgrehan * message_pending and EOMing. Otherwise, the EOMing will 121250199Sgrehan * not deliver any more messages 122250199Sgrehan * since there is no empty slot 123297634Ssephe * 124297634Ssephe * NOTE: 125297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 126297636Ssephe * will become compiler fence on UP kernel. 127250199Sgrehan */ 128297634Ssephe mb(); 129250199Sgrehan 130256276Sdim if (msg->header.message_flags.u.message_pending) { 131250199Sgrehan /* 132250199Sgrehan * This will cause message queue rescan to possibly 133250199Sgrehan * deliver another msg from the hypervisor 134250199Sgrehan */ 135255414Sgrehan wrmsr(HV_X64_MSR_EOM, 0); 136250199Sgrehan } 137250199Sgrehan } 138250199Sgrehan} 139250199Sgrehan 140250199Sgrehan/** 141250199Sgrehan * @brief Interrupt filter routine for VMBUS. 142250199Sgrehan * 143250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol 144250199Sgrehan * message to process - an event or a channel message. 145250199Sgrehan */ 146282212Swhustatic inline int 147293873Ssephehv_vmbus_isr(struct trapframe *frame) 148250199Sgrehan{ 149300107Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 150250199Sgrehan int cpu; 151250199Sgrehan hv_vmbus_message* msg; 152250199Sgrehan void* page_addr; 153250199Sgrehan 154250199Sgrehan cpu = PCPU_GET(cpuid); 155250199Sgrehan 156250199Sgrehan /* 157250199Sgrehan * The Windows team has advised that we check for events 158250199Sgrehan * before checking for messages. This is the way they do it 159250199Sgrehan * in Windows when running as a guest in Hyper-V 160250199Sgrehan */ 161300107Ssephe sc->vmbus_event_proc(sc, cpu); 162250199Sgrehan 163250199Sgrehan /* Check if there are actual msgs to be process */ 164250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 165297176Ssephe msg = (hv_vmbus_message*) page_addr + HV_VMBUS_TIMER_SINT; 166250199Sgrehan 167293873Ssephe /* we call eventtimer process the message */ 168293873Ssephe if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { 169293873Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 170293873Ssephe 171297176Ssephe /* call intrrupt handler of event timer */ 172297176Ssephe hv_et_intr(frame); 173297176Ssephe 174293873Ssephe /* 175293873Ssephe * Make sure the write to message_type (ie set to 176293873Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 177293873Ssephe * message_pending and EOMing. Otherwise, the EOMing will 178293873Ssephe * not deliver any more messages 179293873Ssephe * since there is no empty slot 180297634Ssephe * 181297634Ssephe * NOTE: 182297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 183297636Ssephe * will become compiler fence on UP kernel. 184293873Ssephe */ 185297634Ssephe mb(); 186293873Ssephe 187293873Ssephe if (msg->header.message_flags.u.message_pending) { 188293873Ssephe /* 189293873Ssephe * This will cause message queue rescan to possibly 190293873Ssephe * deliver another msg from the hypervisor 191293873Ssephe */ 192293873Ssephe wrmsr(HV_X64_MSR_EOM, 0); 193293873Ssephe } 194293873Ssephe } 195293873Ssephe 196297176Ssephe msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 197250199Sgrehan if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 198297221Ssephe taskqueue_enqueue(hv_vmbus_g_context.hv_msg_tq[cpu], 199297221Ssephe &hv_vmbus_g_context.hv_msg_task[cpu]); 200250199Sgrehan } 201250199Sgrehan 202293873Ssephe return (FILTER_HANDLED); 203250199Sgrehan} 204250199Sgrehan 205293874Ssepheu_long *hv_vmbus_intr_cpu[MAXCPU]; 206282212Swhu 207282212Swhuvoid 208282212Swhuhv_vector_handler(struct trapframe *trap_frame) 209282212Swhu{ 210282212Swhu int cpu; 211282212Swhu 212282212Swhu /* 213282212Swhu * Disable preemption. 214282212Swhu */ 215282212Swhu critical_enter(); 216282212Swhu 217282212Swhu /* 218282212Swhu * Do a little interrupt counting. 219282212Swhu */ 220282212Swhu cpu = PCPU_GET(cpuid); 221293874Ssephe (*hv_vmbus_intr_cpu[cpu])++; 222282212Swhu 223293873Ssephe hv_vmbus_isr(trap_frame); 224282212Swhu 225282212Swhu /* 226282212Swhu * Enable preemption. 227282212Swhu */ 228282212Swhu critical_exit(); 229282212Swhu} 230282212Swhu 231250199Sgrehanstatic int 232250199Sgrehanvmbus_read_ivar( 233250199Sgrehan device_t dev, 234250199Sgrehan device_t child, 235250199Sgrehan int index, 236250199Sgrehan uintptr_t* result) 237250199Sgrehan{ 238250199Sgrehan struct hv_device *child_dev_ctx = device_get_ivars(child); 239250199Sgrehan 240250199Sgrehan switch (index) { 241250199Sgrehan 242250199Sgrehan case HV_VMBUS_IVAR_TYPE: 243250199Sgrehan *result = (uintptr_t) &child_dev_ctx->class_id; 244250199Sgrehan return (0); 245250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 246250199Sgrehan *result = (uintptr_t) &child_dev_ctx->device_id; 247250199Sgrehan return (0); 248250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 249250199Sgrehan *result = (uintptr_t) child_dev_ctx; 250250199Sgrehan return (0); 251250199Sgrehan case HV_VMBUS_IVAR_NODE: 252250199Sgrehan *result = (uintptr_t) child_dev_ctx->device; 253250199Sgrehan return (0); 254250199Sgrehan } 255250199Sgrehan return (ENOENT); 256250199Sgrehan} 257250199Sgrehan 258250199Sgrehanstatic int 259250199Sgrehanvmbus_write_ivar( 260250199Sgrehan device_t dev, 261250199Sgrehan device_t child, 262250199Sgrehan int index, 263250199Sgrehan uintptr_t value) 264250199Sgrehan{ 265250199Sgrehan switch (index) { 266250199Sgrehan 267250199Sgrehan case HV_VMBUS_IVAR_TYPE: 268250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 269250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 270250199Sgrehan case HV_VMBUS_IVAR_NODE: 271250199Sgrehan /* read-only */ 272250199Sgrehan return (EINVAL); 273250199Sgrehan } 274250199Sgrehan return (ENOENT); 275250199Sgrehan} 276250199Sgrehan 277297143Ssephestatic int 278297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 279297143Ssephe{ 280297143Ssephe char guidbuf[40]; 281297143Ssephe struct hv_device *dev_ctx = device_get_ivars(child); 282297143Ssephe 283298449Ssephe if (dev_ctx == NULL) 284298449Ssephe return (0); 285298449Ssephe 286297143Ssephe strlcat(buf, "classid=", buflen); 287297143Ssephe snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id); 288297143Ssephe strlcat(buf, guidbuf, buflen); 289297143Ssephe 290297143Ssephe strlcat(buf, " deviceid=", buflen); 291297143Ssephe snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id); 292297143Ssephe strlcat(buf, guidbuf, buflen); 293297143Ssephe 294297143Ssephe return (0); 295297143Ssephe} 296297143Ssephe 297250199Sgrehanstruct hv_device* 298250199Sgrehanhv_vmbus_child_device_create( 299250199Sgrehan hv_guid type, 300250199Sgrehan hv_guid instance, 301250199Sgrehan hv_vmbus_channel* channel) 302250199Sgrehan{ 303250199Sgrehan hv_device* child_dev; 304250199Sgrehan 305250199Sgrehan /* 306250199Sgrehan * Allocate the new child device 307250199Sgrehan */ 308250199Sgrehan child_dev = malloc(sizeof(hv_device), M_DEVBUF, 309295308Ssephe M_WAITOK | M_ZERO); 310250199Sgrehan 311250199Sgrehan child_dev->channel = channel; 312250199Sgrehan memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 313250199Sgrehan memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 314250199Sgrehan 315250199Sgrehan return (child_dev); 316250199Sgrehan} 317250199Sgrehan 318297142Ssepheint 319297142Ssephesnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid) 320250199Sgrehan{ 321297142Ssephe int cnt; 322297142Ssephe const unsigned char *d = guid->data; 323297142Ssephe 324297142Ssephe cnt = snprintf(buf, sz, 325297142Ssephe "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 326297142Ssephe d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], 327297142Ssephe d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); 328297142Ssephe return (cnt); 329250199Sgrehan} 330250199Sgrehan 331250199Sgrehanint 332250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev) 333250199Sgrehan{ 334250199Sgrehan device_t child; 335250199Sgrehan 336297142Ssephe if (bootverbose) { 337297142Ssephe char name[40]; 338297142Ssephe snprintf_hv_guid(name, sizeof(name), &child_dev->class_id); 339297142Ssephe printf("VMBUS: Class ID: %s\n", name); 340297142Ssephe } 341250199Sgrehan 342250199Sgrehan child = device_add_child(vmbus_devp, NULL, -1); 343250199Sgrehan child_dev->device = child; 344250199Sgrehan device_set_ivars(child, child_dev); 345250199Sgrehan 346250199Sgrehan return (0); 347250199Sgrehan} 348250199Sgrehan 349250199Sgrehanint 350250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev) 351250199Sgrehan{ 352250199Sgrehan int ret = 0; 353250199Sgrehan /* 354250199Sgrehan * XXXKYS: Ensure that this is the opposite of 355250199Sgrehan * device_add_child() 356250199Sgrehan */ 357250199Sgrehan mtx_lock(&Giant); 358250199Sgrehan ret = device_delete_child(vmbus_devp, child_dev->device); 359250199Sgrehan mtx_unlock(&Giant); 360250199Sgrehan return(ret); 361250199Sgrehan} 362250199Sgrehan 363250199Sgrehanstatic int 364250199Sgrehanvmbus_probe(device_t dev) { 365293870Ssephe if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || 366293870Ssephe device_get_unit(dev) != 0) 367293870Ssephe return (ENXIO); 368250199Sgrehan 369250199Sgrehan device_set_desc(dev, "Vmbus Devices"); 370250199Sgrehan 371293870Ssephe return (BUS_PROBE_DEFAULT); 372250199Sgrehan} 373250199Sgrehan 374297641Ssepheextern inthand_t IDTVEC(hv_vmbus_callback); 375282212Swhu 376282212Swhu/** 377250199Sgrehan * @brief Main vmbus driver initialization routine. 378250199Sgrehan * 379250199Sgrehan * Here, we 380250199Sgrehan * - initialize the vmbus driver context 381250199Sgrehan * - setup various driver entry points 382250199Sgrehan * - invoke the vmbus hv main init routine 383250199Sgrehan * - get the irq resource 384250199Sgrehan * - invoke the vmbus to add the vmbus root device 385250199Sgrehan * - setup the vmbus root device 386250199Sgrehan * - retrieve the channel offers 387250199Sgrehan */ 388250199Sgrehanstatic int 389250199Sgrehanvmbus_bus_init(void) 390250199Sgrehan{ 391300107Ssephe struct vmbus_softc *sc; 392282212Swhu int i, j, n, ret; 393293874Ssephe char buf[MAXCOMLEN + 1]; 394294886Ssephe cpuset_t cpu_mask; 395250199Sgrehan 396250199Sgrehan if (vmbus_inited) 397250199Sgrehan return (0); 398250199Sgrehan 399250199Sgrehan vmbus_inited = 1; 400300107Ssephe sc = vmbus_get_softc(); 401250199Sgrehan 402250199Sgrehan ret = hv_vmbus_init(); 403250199Sgrehan 404250199Sgrehan if (ret) { 405250199Sgrehan if(bootverbose) 406252645Sgrehan printf("Error VMBUS: Hypervisor Initialization Failed!\n"); 407250199Sgrehan return (ret); 408250199Sgrehan } 409250199Sgrehan 410250199Sgrehan /* 411282212Swhu * Find a free IDT slot for vmbus callback. 412250199Sgrehan */ 413297641Ssephe hv_vmbus_g_context.hv_cb_vector = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback)); 414297641Ssephe if (hv_vmbus_g_context.hv_cb_vector < 0) { 415282212Swhu if(bootverbose) 416282212Swhu printf("Error VMBUS: Cannot find free IDT slot for " 417282212Swhu "vmbus callback!\n"); 418282212Swhu goto cleanup; 419282212Swhu } 420250199Sgrehan 421282212Swhu if(bootverbose) 422282212Swhu printf("VMBUS: vmbus callback vector %d\n", 423282212Swhu hv_vmbus_g_context.hv_cb_vector); 424250199Sgrehan 425282212Swhu /* 426282212Swhu * Notify the hypervisor of our vector. 427282212Swhu */ 428282212Swhu setup_args.vector = hv_vmbus_g_context.hv_cb_vector; 429250199Sgrehan 430282212Swhu CPU_FOREACH(j) { 431293874Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); 432293874Ssephe intrcnt_add(buf, &hv_vmbus_intr_cpu[j]); 433293874Ssephe 434282212Swhu for (i = 0; i < 2; i++) 435282212Swhu setup_args.page_buffers[2 * j + i] = NULL; 436250199Sgrehan } 437250199Sgrehan 438250199Sgrehan /* 439282212Swhu * Per cpu setup. 440250199Sgrehan */ 441282212Swhu CPU_FOREACH(j) { 442282212Swhu /* 443294886Ssephe * Setup taskqueue to handle events 444294886Ssephe */ 445294886Ssephe hv_vmbus_g_context.hv_event_queue[j] = taskqueue_create_fast("hyperv event", M_WAITOK, 446294886Ssephe taskqueue_thread_enqueue, &hv_vmbus_g_context.hv_event_queue[j]); 447294886Ssephe CPU_SETOF(j, &cpu_mask); 448294886Ssephe taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_event_queue[j], 1, PI_NET, &cpu_mask, 449294886Ssephe "hvevent%d", j); 450294886Ssephe 451294886Ssephe /* 452297221Ssephe * Setup per-cpu tasks and taskqueues to handle msg. 453282212Swhu */ 454297221Ssephe hv_vmbus_g_context.hv_msg_tq[j] = taskqueue_create_fast( 455297221Ssephe "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 456297221Ssephe &hv_vmbus_g_context.hv_msg_tq[j]); 457297221Ssephe CPU_SETOF(j, &cpu_mask); 458297221Ssephe taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_msg_tq[j], 459297221Ssephe 1, PI_NET, &cpu_mask, "hvmsg%d", j); 460297221Ssephe TASK_INIT(&hv_vmbus_g_context.hv_msg_task[j], 0, 461297221Ssephe vmbus_msg_swintr, (void *)(long)j); 462297221Ssephe 463282212Swhu /* 464282212Swhu * Prepare the per cpu msg and event pages to be called on each cpu. 465282212Swhu */ 466282212Swhu for(i = 0; i < 2; i++) { 467282212Swhu setup_args.page_buffers[2 * j + i] = 468295308Ssephe malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 469255414Sgrehan } 470255414Sgrehan } 471250199Sgrehan 472282212Swhu if (bootverbose) 473282212Swhu printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n", 474282212Swhu smp_started); 475282212Swhu 476255414Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); 477250199Sgrehan 478255414Sgrehan /* 479250199Sgrehan * Connect to VMBus in the root partition 480250199Sgrehan */ 481250199Sgrehan ret = hv_vmbus_connect(); 482250199Sgrehan 483255414Sgrehan if (ret != 0) 484282212Swhu goto cleanup1; 485250199Sgrehan 486300107Ssephe if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || 487300107Ssephe hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) 488300107Ssephe sc->vmbus_event_proc = vmbus_event_proc_compat; 489300107Ssephe else 490300107Ssephe sc->vmbus_event_proc = vmbus_event_proc; 491300107Ssephe 492250199Sgrehan hv_vmbus_request_channel_offers(); 493298260Ssephe 494298260Ssephe vmbus_scan(); 495298260Ssephe bus_generic_attach(vmbus_devp); 496298260Ssephe device_printf(vmbus_devp, "device scan, probe and attach done\n"); 497298260Ssephe 498250199Sgrehan return (ret); 499250199Sgrehan 500282212Swhu cleanup1: 501282212Swhu /* 502282212Swhu * Free pages alloc'ed 503282212Swhu */ 504282212Swhu for (n = 0; n < 2 * MAXCPU; n++) 505282212Swhu if (setup_args.page_buffers[n] != NULL) 506282212Swhu free(setup_args.page_buffers[n], M_DEVBUF); 507250199Sgrehan 508250199Sgrehan /* 509282212Swhu * remove swi and vmbus callback vector; 510250199Sgrehan */ 511282212Swhu CPU_FOREACH(j) { 512297177Ssephe if (hv_vmbus_g_context.hv_event_queue[j] != NULL) { 513294886Ssephe taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]); 514297177Ssephe hv_vmbus_g_context.hv_event_queue[j] = NULL; 515297177Ssephe } 516282212Swhu } 517250199Sgrehan 518297641Ssephe lapic_ipi_free(hv_vmbus_g_context.hv_cb_vector); 519250199Sgrehan 520250199Sgrehan cleanup: 521250199Sgrehan hv_vmbus_cleanup(); 522250199Sgrehan 523250199Sgrehan return (ret); 524250199Sgrehan} 525250199Sgrehan 526300107Ssephestatic void 527300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 528300107Ssephe{ 529300107Ssephe} 530300107Ssephe 531250199Sgrehanstatic int 532250199Sgrehanvmbus_attach(device_t dev) 533250199Sgrehan{ 534250199Sgrehan if(bootverbose) 535250199Sgrehan device_printf(dev, "VMBUS: attach dev: %p\n", dev); 536300102Ssephe 537250199Sgrehan vmbus_devp = dev; 538300102Ssephe vmbus_sc = device_get_softc(dev); 539250199Sgrehan 540300107Ssephe /* 541300107Ssephe * Event processing logic will be configured: 542300107Ssephe * - After the vmbus protocol version negotiation. 543300107Ssephe * - Before we request channel offers. 544300107Ssephe */ 545300107Ssephe vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 546300107Ssephe 547299746Sjhb#ifndef EARLY_AP_STARTUP 548250199Sgrehan /* 549250199Sgrehan * If the system has already booted and thread 550250199Sgrehan * scheduling is possible indicated by the global 551250199Sgrehan * cold set to zero, we just call the driver 552250199Sgrehan * initialization directly. 553250199Sgrehan */ 554250199Sgrehan if (!cold) 555299746Sjhb#endif 556250199Sgrehan vmbus_bus_init(); 557250199Sgrehan 558298449Ssephe bus_generic_probe(dev); 559250199Sgrehan return (0); 560250199Sgrehan} 561250199Sgrehan 562250199Sgrehanstatic void 563250199Sgrehanvmbus_init(void) 564250199Sgrehan{ 565300102Ssephe if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL) 566256425Sgibbs return; 567256425Sgibbs 568299746Sjhb#ifndef EARLY_AP_STARTUP 569250199Sgrehan /* 570250199Sgrehan * If the system has already booted and thread 571256425Sgibbs * scheduling is possible, as indicated by the 572256425Sgibbs * global cold set to zero, we just call the driver 573250199Sgrehan * initialization directly. 574250199Sgrehan */ 575250199Sgrehan if (!cold) 576299746Sjhb#endif 577250199Sgrehan vmbus_bus_init(); 578250199Sgrehan} 579250199Sgrehan 580250199Sgrehanstatic void 581250199Sgrehanvmbus_bus_exit(void) 582250199Sgrehan{ 583255414Sgrehan int i; 584255414Sgrehan 585250199Sgrehan hv_vmbus_release_unattached_channels(); 586250199Sgrehan hv_vmbus_disconnect(); 587250199Sgrehan 588250199Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 589250199Sgrehan 590282212Swhu for(i = 0; i < 2 * MAXCPU; i++) { 591297841Ssephe if (setup_args.page_buffers[i] != NULL) 592255414Sgrehan free(setup_args.page_buffers[i], M_DEVBUF); 593255414Sgrehan } 594255414Sgrehan 595250199Sgrehan hv_vmbus_cleanup(); 596250199Sgrehan 597282212Swhu /* remove swi */ 598282212Swhu CPU_FOREACH(i) { 599297177Ssephe if (hv_vmbus_g_context.hv_event_queue[i] != NULL) { 600294886Ssephe taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]); 601297177Ssephe hv_vmbus_g_context.hv_event_queue[i] = NULL; 602297177Ssephe } 603282212Swhu } 604250199Sgrehan 605297641Ssephe lapic_ipi_free(hv_vmbus_g_context.hv_cb_vector); 606250199Sgrehan 607250199Sgrehan return; 608250199Sgrehan} 609250199Sgrehan 610250199Sgrehanstatic void 611250199Sgrehanvmbus_exit(void) 612250199Sgrehan{ 613250199Sgrehan vmbus_bus_exit(); 614250199Sgrehan} 615250199Sgrehan 616250199Sgrehanstatic int 617250199Sgrehanvmbus_detach(device_t dev) 618250199Sgrehan{ 619250199Sgrehan vmbus_exit(); 620250199Sgrehan return (0); 621250199Sgrehan} 622250199Sgrehan 623250199Sgrehanstatic void 624250199Sgrehanvmbus_mod_load(void) 625250199Sgrehan{ 626250199Sgrehan if(bootverbose) 627250199Sgrehan printf("VMBUS: load\n"); 628250199Sgrehan} 629250199Sgrehan 630250199Sgrehanstatic void 631250199Sgrehanvmbus_mod_unload(void) 632250199Sgrehan{ 633250199Sgrehan if(bootverbose) 634250199Sgrehan printf("VMBUS: unload\n"); 635250199Sgrehan} 636250199Sgrehan 637250199Sgrehanstatic int 638250199Sgrehanvmbus_modevent(module_t mod, int what, void *arg) 639250199Sgrehan{ 640250199Sgrehan switch (what) { 641250199Sgrehan 642250199Sgrehan case MOD_LOAD: 643299746Sjhb#ifdef EARLY_AP_STARTUP 644299746Sjhb vmbus_init(); 645299746Sjhb#endif 646250199Sgrehan vmbus_mod_load(); 647250199Sgrehan break; 648250199Sgrehan case MOD_UNLOAD: 649250199Sgrehan vmbus_mod_unload(); 650250199Sgrehan break; 651250199Sgrehan } 652250199Sgrehan 653250199Sgrehan return (0); 654250199Sgrehan} 655250199Sgrehan 656250199Sgrehanstatic device_method_t vmbus_methods[] = { 657250199Sgrehan /** Device interface */ 658250199Sgrehan DEVMETHOD(device_probe, vmbus_probe), 659250199Sgrehan DEVMETHOD(device_attach, vmbus_attach), 660250199Sgrehan DEVMETHOD(device_detach, vmbus_detach), 661250199Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 662250199Sgrehan DEVMETHOD(device_suspend, bus_generic_suspend), 663250199Sgrehan DEVMETHOD(device_resume, bus_generic_resume), 664250199Sgrehan 665250199Sgrehan /** Bus interface */ 666250199Sgrehan DEVMETHOD(bus_add_child, bus_generic_add_child), 667250199Sgrehan DEVMETHOD(bus_print_child, bus_generic_print_child), 668250199Sgrehan DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 669250199Sgrehan DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 670297143Ssephe DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 671250199Sgrehan 672250199Sgrehan { 0, 0 } }; 673250199Sgrehan 674300102Ssephestatic driver_t vmbus_driver = { 675300102Ssephe "vmbus", 676300102Ssephe vmbus_methods, 677300102Ssephe sizeof(struct vmbus_softc) 678300102Ssephe}; 679250199Sgrehan 680250199Sgrehandevclass_t vmbus_devclass; 681250199Sgrehan 682293870SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); 683293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 684293870SsepheMODULE_VERSION(vmbus, 1); 685250199Sgrehan 686299746Sjhb#ifndef EARLY_AP_STARTUP 687282212Swhu/* We want to be started after SMP is initialized */ 688282212SwhuSYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL); 689299746Sjhb#endif 690