vmbus.c revision 300650
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 300650 2016-05-25 05:22:35Z 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 71293870Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL }; 72293870Ssephe 73300574Ssepheextern inthand_t IDTVEC(hv_vmbus_callback); 74300574Ssephe 75250199Sgrehanstatic void 76300572Ssephevmbus_msg_task(void *xsc, int pending __unused) 77250199Sgrehan{ 78300572Ssephe struct vmbus_softc *sc = xsc; 79300108Ssephe hv_vmbus_message *msg; 80250199Sgrehan 81300573Ssephe msg = VMBUS_PCPU_GET(sc, message, curcpu) + HV_VMBUS_MESSAGE_SINT; 82300108Ssephe for (;;) { 83300108Ssephe const hv_vmbus_channel_msg_table_entry *entry; 84300108Ssephe hv_vmbus_channel_msg_header *hdr; 85300108Ssephe hv_vmbus_channel_msg_type msg_type; 86282212Swhu 87292861Sdelphij if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 88250199Sgrehan break; /* no message */ 89292861Sdelphij 90292861Sdelphij hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 91292861Sdelphij msg_type = hdr->message_type; 92292861Sdelphij 93295307Ssephe if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { 94292861Sdelphij printf("VMBUS: unknown message type = %d\n", msg_type); 95292861Sdelphij goto handled; 96292861Sdelphij } 97292861Sdelphij 98292861Sdelphij entry = &g_channel_message_table[msg_type]; 99295307Ssephe if (entry->messageHandler) 100292861Sdelphij entry->messageHandler(hdr); 101292861Sdelphijhandled: 102300108Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 103300108Ssephe /* 104300108Ssephe * Make sure the write to message_type (ie set to 105300108Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 106300108Ssephe * message_pending and EOMing. Otherwise, the EOMing will 107300108Ssephe * not deliver any more messages 108300108Ssephe * since there is no empty slot 109300108Ssephe * 110300108Ssephe * NOTE: 111300108Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 112300108Ssephe * will become compiler fence on UP kernel. 113300108Ssephe */ 114300108Ssephe mb(); 115300108Ssephe if (msg->header.message_flags.u.message_pending) { 116250199Sgrehan /* 117250199Sgrehan * This will cause message queue rescan to possibly 118250199Sgrehan * deliver another msg from the hypervisor 119250199Sgrehan */ 120255414Sgrehan wrmsr(HV_X64_MSR_EOM, 0); 121300108Ssephe } 122250199Sgrehan } 123250199Sgrehan} 124250199Sgrehan 125250199Sgrehan/** 126250199Sgrehan * @brief Interrupt filter routine for VMBUS. 127250199Sgrehan * 128250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol 129250199Sgrehan * message to process - an event or a channel message. 130250199Sgrehan */ 131282212Swhustatic inline int 132300567Ssephehv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu) 133250199Sgrehan{ 134300481Ssephe hv_vmbus_message *msg, *msg_base; 135250199Sgrehan 136250199Sgrehan /* 137250199Sgrehan * The Windows team has advised that we check for events 138250199Sgrehan * before checking for messages. This is the way they do it 139250199Sgrehan * in Windows when running as a guest in Hyper-V 140250199Sgrehan */ 141300107Ssephe sc->vmbus_event_proc(sc, cpu); 142250199Sgrehan 143250199Sgrehan /* Check if there are actual msgs to be process */ 144300573Ssephe msg_base = VMBUS_PCPU_GET(sc, message, cpu); 145300481Ssephe msg = msg_base + HV_VMBUS_TIMER_SINT; 146250199Sgrehan 147293873Ssephe /* we call eventtimer process the message */ 148293873Ssephe if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { 149293873Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 150293873Ssephe 151297176Ssephe /* call intrrupt handler of event timer */ 152297176Ssephe hv_et_intr(frame); 153297176Ssephe 154293873Ssephe /* 155293873Ssephe * Make sure the write to message_type (ie set to 156293873Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 157293873Ssephe * message_pending and EOMing. Otherwise, the EOMing will 158293873Ssephe * not deliver any more messages 159293873Ssephe * since there is no empty slot 160297634Ssephe * 161297634Ssephe * NOTE: 162297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 163297636Ssephe * will become compiler fence on UP kernel. 164293873Ssephe */ 165297634Ssephe mb(); 166293873Ssephe 167293873Ssephe if (msg->header.message_flags.u.message_pending) { 168293873Ssephe /* 169293873Ssephe * This will cause message queue rescan to possibly 170293873Ssephe * deliver another msg from the hypervisor 171293873Ssephe */ 172293873Ssephe wrmsr(HV_X64_MSR_EOM, 0); 173293873Ssephe } 174293873Ssephe } 175293873Ssephe 176300481Ssephe msg = msg_base + HV_VMBUS_MESSAGE_SINT; 177250199Sgrehan if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 178300646Ssephe taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), 179300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 180250199Sgrehan } 181250199Sgrehan 182293873Ssephe return (FILTER_HANDLED); 183250199Sgrehan} 184250199Sgrehan 185282212Swhuvoid 186282212Swhuhv_vector_handler(struct trapframe *trap_frame) 187282212Swhu{ 188300565Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 189300565Ssephe int cpu = curcpu; 190282212Swhu 191282212Swhu /* 192282212Swhu * Disable preemption. 193282212Swhu */ 194282212Swhu critical_enter(); 195282212Swhu 196282212Swhu /* 197282212Swhu * Do a little interrupt counting. 198282212Swhu */ 199300573Ssephe (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++; 200282212Swhu 201300567Ssephe hv_vmbus_isr(sc, trap_frame, cpu); 202282212Swhu 203282212Swhu /* 204282212Swhu * Enable preemption. 205282212Swhu */ 206282212Swhu critical_exit(); 207282212Swhu} 208282212Swhu 209300571Ssephestatic void 210300572Ssephevmbus_synic_setup(void *arg __unused) 211300571Ssephe{ 212300571Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 213300571Ssephe int cpu; 214300571Ssephe hv_vmbus_synic_simp simp; 215300571Ssephe hv_vmbus_synic_siefp siefp; 216300571Ssephe hv_vmbus_synic_scontrol sctrl; 217300571Ssephe hv_vmbus_synic_sint shared_sint; 218300571Ssephe uint64_t version; 219300571Ssephe 220300571Ssephe cpu = PCPU_GET(cpuid); 221300571Ssephe 222300571Ssephe /* 223300571Ssephe * TODO: Check the version 224300571Ssephe */ 225300571Ssephe version = rdmsr(HV_X64_MSR_SVERSION); 226300571Ssephe 227300571Ssephe /* 228300571Ssephe * Setup the Synic's message page 229300571Ssephe */ 230300571Ssephe simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); 231300571Ssephe simp.u.simp_enabled = 1; 232300572Ssephe simp.u.base_simp_gpa = 233300573Ssephe VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT; 234300571Ssephe 235300571Ssephe wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); 236300571Ssephe 237300571Ssephe /* 238300571Ssephe * Setup the Synic's event page 239300571Ssephe */ 240300571Ssephe siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); 241300571Ssephe siefp.u.siefp_enabled = 1; 242300572Ssephe siefp.u.base_siefp_gpa = 243300573Ssephe VMBUS_PCPU_GET(sc, event_flag_dma.hv_paddr, cpu) >> PAGE_SHIFT; 244300571Ssephe 245300571Ssephe wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); 246300571Ssephe 247300571Ssephe /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */ 248300571Ssephe shared_sint.as_uint64_t = 0; 249300571Ssephe shared_sint.u.vector = sc->vmbus_idtvec; 250300571Ssephe shared_sint.u.masked = FALSE; 251300571Ssephe shared_sint.u.auto_eoi = TRUE; 252300571Ssephe 253300571Ssephe wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, 254300571Ssephe shared_sint.as_uint64_t); 255300571Ssephe 256300571Ssephe wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT, 257300571Ssephe shared_sint.as_uint64_t); 258300571Ssephe 259300571Ssephe /* Enable the global synic bit */ 260300571Ssephe sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL); 261300571Ssephe sctrl.u.enable = 1; 262300571Ssephe 263300571Ssephe wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t); 264300571Ssephe 265300571Ssephe /* 266300571Ssephe * Set up the cpuid mapping from Hyper-V to FreeBSD. 267300571Ssephe * The array is indexed using FreeBSD cpuid. 268300571Ssephe */ 269300647Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(HV_X64_MSR_VP_INDEX); 270300571Ssephe} 271300571Ssephe 272300571Ssephestatic void 273300571Ssephevmbus_synic_teardown(void *arg) 274300571Ssephe{ 275300571Ssephe hv_vmbus_synic_sint shared_sint; 276300571Ssephe hv_vmbus_synic_simp simp; 277300571Ssephe hv_vmbus_synic_siefp siefp; 278300571Ssephe 279300571Ssephe shared_sint.as_uint64_t = rdmsr( 280300571Ssephe HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT); 281300571Ssephe 282300571Ssephe shared_sint.u.masked = 1; 283300571Ssephe 284300571Ssephe /* 285300571Ssephe * Disable the interrupt 0 286300571Ssephe */ 287300571Ssephe wrmsr( 288300571Ssephe HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, 289300571Ssephe shared_sint.as_uint64_t); 290300571Ssephe 291300571Ssephe shared_sint.as_uint64_t = rdmsr( 292300571Ssephe HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT); 293300571Ssephe 294300571Ssephe shared_sint.u.masked = 1; 295300571Ssephe 296300571Ssephe /* 297300571Ssephe * Disable the interrupt 1 298300571Ssephe */ 299300571Ssephe wrmsr( 300300571Ssephe HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT, 301300571Ssephe shared_sint.as_uint64_t); 302300571Ssephe simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); 303300571Ssephe simp.u.simp_enabled = 0; 304300571Ssephe simp.u.base_simp_gpa = 0; 305300571Ssephe 306300571Ssephe wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); 307300571Ssephe 308300571Ssephe siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); 309300571Ssephe siefp.u.siefp_enabled = 0; 310300571Ssephe siefp.u.base_siefp_gpa = 0; 311300571Ssephe 312300571Ssephe wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); 313300571Ssephe} 314300571Ssephe 315300644Ssephestatic int 316300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc) 317300572Ssephe{ 318300572Ssephe int cpu; 319300572Ssephe 320300572Ssephe CPU_FOREACH(cpu) { 321300644Ssephe void *ptr; 322300644Ssephe 323300572Ssephe /* 324300572Ssephe * Per-cpu messages and event flags. 325300572Ssephe */ 326300644Ssephe ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 327300644Ssephe PAGE_SIZE, 0, PAGE_SIZE, 328300573Ssephe VMBUS_PCPU_PTR(sc, message_dma, cpu), 329300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 330300644Ssephe if (ptr == NULL) 331300644Ssephe return ENOMEM; 332300644Ssephe VMBUS_PCPU_GET(sc, message, cpu) = ptr; 333300644Ssephe 334300644Ssephe ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 335300644Ssephe PAGE_SIZE, 0, PAGE_SIZE, 336300573Ssephe VMBUS_PCPU_PTR(sc, event_flag_dma, cpu), 337300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 338300644Ssephe if (ptr == NULL) 339300644Ssephe return ENOMEM; 340300644Ssephe VMBUS_PCPU_GET(sc, event_flag, cpu) = ptr; 341300572Ssephe } 342300644Ssephe return 0; 343300572Ssephe} 344300572Ssephe 345300572Ssephestatic void 346300572Ssephevmbus_dma_free(struct vmbus_softc *sc) 347300572Ssephe{ 348300572Ssephe int cpu; 349300572Ssephe 350300572Ssephe CPU_FOREACH(cpu) { 351300573Ssephe if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) { 352300572Ssephe hyperv_dmamem_free( 353300573Ssephe VMBUS_PCPU_PTR(sc, message_dma, cpu), 354300573Ssephe VMBUS_PCPU_GET(sc, message, cpu)); 355300573Ssephe VMBUS_PCPU_GET(sc, message, cpu) = NULL; 356300572Ssephe } 357300573Ssephe if (VMBUS_PCPU_GET(sc, event_flag, cpu) != NULL) { 358300572Ssephe hyperv_dmamem_free( 359300573Ssephe VMBUS_PCPU_PTR(sc, event_flag_dma, cpu), 360300573Ssephe VMBUS_PCPU_GET(sc, event_flag, cpu)); 361300573Ssephe VMBUS_PCPU_GET(sc, event_flag, cpu) = NULL; 362300572Ssephe } 363300572Ssephe } 364300572Ssephe} 365300572Ssephe 366250199Sgrehanstatic int 367300574Ssephevmbus_intr_setup(struct vmbus_softc *sc) 368300574Ssephe{ 369300574Ssephe int cpu; 370300574Ssephe 371300574Ssephe CPU_FOREACH(cpu) { 372300574Ssephe char buf[MAXCOMLEN + 1]; 373300645Ssephe cpuset_t cpu_mask; 374300574Ssephe 375300645Ssephe /* Allocate an interrupt counter for Hyper-V interrupt */ 376300574Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu); 377300574Ssephe intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu)); 378300574Ssephe 379300574Ssephe /* 380300645Ssephe * Setup taskqueue to handle events. Task will be per- 381300645Ssephe * channel. 382300574Ssephe */ 383300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast( 384300646Ssephe "hyperv event", M_WAITOK, taskqueue_thread_enqueue, 385300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu)); 386300574Ssephe CPU_SETOF(cpu, &cpu_mask); 387300574Ssephe taskqueue_start_threads_cpuset( 388300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask, 389300646Ssephe "hvevent%d", cpu); 390300574Ssephe 391300574Ssephe /* 392300645Ssephe * Setup tasks and taskqueues to handle messages. 393300574Ssephe */ 394300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast( 395300574Ssephe "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 396300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu)); 397300574Ssephe CPU_SETOF(cpu, &cpu_mask); 398300574Ssephe taskqueue_start_threads_cpuset( 399300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask, 400300646Ssephe "hvmsg%d", cpu); 401300646Ssephe TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0, 402300574Ssephe vmbus_msg_task, sc); 403300574Ssephe } 404300645Ssephe 405300645Ssephe /* 406300645Ssephe * All Hyper-V ISR required resources are setup, now let's find a 407300645Ssephe * free IDT vector for Hyper-V ISR and set it up. 408300645Ssephe */ 409300645Ssephe sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback)); 410300645Ssephe if (sc->vmbus_idtvec < 0) { 411300645Ssephe device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); 412300645Ssephe return ENXIO; 413300645Ssephe } 414300645Ssephe if(bootverbose) { 415300645Ssephe device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", 416300645Ssephe sc->vmbus_idtvec); 417300645Ssephe } 418300574Ssephe return 0; 419300574Ssephe} 420300574Ssephe 421300574Ssephestatic void 422300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc) 423300574Ssephe{ 424300574Ssephe int cpu; 425300574Ssephe 426300645Ssephe if (sc->vmbus_idtvec >= 0) { 427300645Ssephe lapic_ipi_free(sc->vmbus_idtvec); 428300645Ssephe sc->vmbus_idtvec = -1; 429300645Ssephe } 430300645Ssephe 431300574Ssephe CPU_FOREACH(cpu) { 432300646Ssephe if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) { 433300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu)); 434300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL; 435300574Ssephe } 436300646Ssephe if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) { 437300646Ssephe taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu), 438300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 439300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu)); 440300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL; 441300576Ssephe } 442300574Ssephe } 443300574Ssephe} 444300574Ssephe 445300574Ssephestatic int 446250199Sgrehanvmbus_read_ivar( 447250199Sgrehan device_t dev, 448250199Sgrehan device_t child, 449250199Sgrehan int index, 450250199Sgrehan uintptr_t* result) 451250199Sgrehan{ 452250199Sgrehan struct hv_device *child_dev_ctx = device_get_ivars(child); 453250199Sgrehan 454250199Sgrehan switch (index) { 455250199Sgrehan 456250199Sgrehan case HV_VMBUS_IVAR_TYPE: 457250199Sgrehan *result = (uintptr_t) &child_dev_ctx->class_id; 458250199Sgrehan return (0); 459250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 460250199Sgrehan *result = (uintptr_t) &child_dev_ctx->device_id; 461250199Sgrehan return (0); 462250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 463250199Sgrehan *result = (uintptr_t) child_dev_ctx; 464250199Sgrehan return (0); 465250199Sgrehan case HV_VMBUS_IVAR_NODE: 466250199Sgrehan *result = (uintptr_t) child_dev_ctx->device; 467250199Sgrehan return (0); 468250199Sgrehan } 469250199Sgrehan return (ENOENT); 470250199Sgrehan} 471250199Sgrehan 472250199Sgrehanstatic int 473250199Sgrehanvmbus_write_ivar( 474250199Sgrehan device_t dev, 475250199Sgrehan device_t child, 476250199Sgrehan int index, 477250199Sgrehan uintptr_t value) 478250199Sgrehan{ 479250199Sgrehan switch (index) { 480250199Sgrehan 481250199Sgrehan case HV_VMBUS_IVAR_TYPE: 482250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 483250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 484250199Sgrehan case HV_VMBUS_IVAR_NODE: 485250199Sgrehan /* read-only */ 486250199Sgrehan return (EINVAL); 487250199Sgrehan } 488250199Sgrehan return (ENOENT); 489250199Sgrehan} 490250199Sgrehan 491297143Ssephestatic int 492297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 493297143Ssephe{ 494297143Ssephe char guidbuf[40]; 495297143Ssephe struct hv_device *dev_ctx = device_get_ivars(child); 496297143Ssephe 497298449Ssephe if (dev_ctx == NULL) 498298449Ssephe return (0); 499298449Ssephe 500297143Ssephe strlcat(buf, "classid=", buflen); 501297143Ssephe snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id); 502297143Ssephe strlcat(buf, guidbuf, buflen); 503297143Ssephe 504297143Ssephe strlcat(buf, " deviceid=", buflen); 505297143Ssephe snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id); 506297143Ssephe strlcat(buf, guidbuf, buflen); 507297143Ssephe 508297143Ssephe return (0); 509297143Ssephe} 510297143Ssephe 511250199Sgrehanstruct hv_device* 512250199Sgrehanhv_vmbus_child_device_create( 513250199Sgrehan hv_guid type, 514250199Sgrehan hv_guid instance, 515250199Sgrehan hv_vmbus_channel* channel) 516250199Sgrehan{ 517250199Sgrehan hv_device* child_dev; 518250199Sgrehan 519250199Sgrehan /* 520250199Sgrehan * Allocate the new child device 521250199Sgrehan */ 522250199Sgrehan child_dev = malloc(sizeof(hv_device), M_DEVBUF, 523295308Ssephe M_WAITOK | M_ZERO); 524250199Sgrehan 525250199Sgrehan child_dev->channel = channel; 526250199Sgrehan memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 527250199Sgrehan memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 528250199Sgrehan 529250199Sgrehan return (child_dev); 530250199Sgrehan} 531250199Sgrehan 532297142Ssepheint 533297142Ssephesnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid) 534250199Sgrehan{ 535297142Ssephe int cnt; 536297142Ssephe const unsigned char *d = guid->data; 537297142Ssephe 538297142Ssephe cnt = snprintf(buf, sz, 539297142Ssephe "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 540297142Ssephe d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], 541297142Ssephe d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); 542297142Ssephe return (cnt); 543250199Sgrehan} 544250199Sgrehan 545250199Sgrehanint 546250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev) 547250199Sgrehan{ 548250199Sgrehan device_t child; 549250199Sgrehan 550297142Ssephe if (bootverbose) { 551297142Ssephe char name[40]; 552297142Ssephe snprintf_hv_guid(name, sizeof(name), &child_dev->class_id); 553297142Ssephe printf("VMBUS: Class ID: %s\n", name); 554297142Ssephe } 555250199Sgrehan 556300486Ssephe child = device_add_child(vmbus_get_device(), NULL, -1); 557250199Sgrehan child_dev->device = child; 558250199Sgrehan device_set_ivars(child, child_dev); 559250199Sgrehan 560250199Sgrehan return (0); 561250199Sgrehan} 562250199Sgrehan 563250199Sgrehanint 564250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev) 565250199Sgrehan{ 566250199Sgrehan int ret = 0; 567250199Sgrehan /* 568250199Sgrehan * XXXKYS: Ensure that this is the opposite of 569250199Sgrehan * device_add_child() 570250199Sgrehan */ 571250199Sgrehan mtx_lock(&Giant); 572300486Ssephe ret = device_delete_child(vmbus_get_device(), child_dev->device); 573250199Sgrehan mtx_unlock(&Giant); 574250199Sgrehan return(ret); 575250199Sgrehan} 576250199Sgrehan 577250199Sgrehanstatic int 578300127Ssephevmbus_probe(device_t dev) 579300127Ssephe{ 580293870Ssephe if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || 581300480Ssephe device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV) 582293870Ssephe return (ENXIO); 583250199Sgrehan 584300129Ssephe device_set_desc(dev, "Hyper-V Vmbus"); 585250199Sgrehan 586293870Ssephe return (BUS_PROBE_DEFAULT); 587250199Sgrehan} 588250199Sgrehan 589282212Swhu/** 590250199Sgrehan * @brief Main vmbus driver initialization routine. 591250199Sgrehan * 592250199Sgrehan * Here, we 593250199Sgrehan * - initialize the vmbus driver context 594250199Sgrehan * - setup various driver entry points 595250199Sgrehan * - invoke the vmbus hv main init routine 596250199Sgrehan * - get the irq resource 597250199Sgrehan * - invoke the vmbus to add the vmbus root device 598250199Sgrehan * - setup the vmbus root device 599250199Sgrehan * - retrieve the channel offers 600250199Sgrehan */ 601250199Sgrehanstatic int 602250199Sgrehanvmbus_bus_init(void) 603250199Sgrehan{ 604300650Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 605300574Ssephe int ret; 606250199Sgrehan 607300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED) 608250199Sgrehan return (0); 609300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_ATTACHED; 610250199Sgrehan 611250199Sgrehan /* 612300645Ssephe * Allocate DMA stuffs. 613250199Sgrehan */ 614300645Ssephe ret = vmbus_dma_alloc(sc); 615300574Ssephe if (ret != 0) 616282212Swhu goto cleanup; 617250199Sgrehan 618250199Sgrehan /* 619300645Ssephe * Setup interrupt. 620250199Sgrehan */ 621300645Ssephe ret = vmbus_intr_setup(sc); 622300644Ssephe if (ret != 0) 623300644Ssephe goto cleanup; 624300572Ssephe 625300650Ssephe /* 626300650Ssephe * Setup SynIC. 627300650Ssephe */ 628282212Swhu if (bootverbose) 629300650Ssephe device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started); 630300572Ssephe smp_rendezvous(NULL, vmbus_synic_setup, NULL, NULL); 631300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_SYNIC; 632282212Swhu 633255414Sgrehan /* 634250199Sgrehan * Connect to VMBus in the root partition 635250199Sgrehan */ 636250199Sgrehan ret = hv_vmbus_connect(); 637250199Sgrehan 638255414Sgrehan if (ret != 0) 639300574Ssephe goto cleanup; 640250199Sgrehan 641300107Ssephe if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || 642300107Ssephe hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) 643300107Ssephe sc->vmbus_event_proc = vmbus_event_proc_compat; 644300107Ssephe else 645300107Ssephe sc->vmbus_event_proc = vmbus_event_proc; 646300107Ssephe 647250199Sgrehan hv_vmbus_request_channel_offers(); 648298260Ssephe 649298260Ssephe vmbus_scan(); 650300486Ssephe bus_generic_attach(sc->vmbus_dev); 651300486Ssephe device_printf(sc->vmbus_dev, "device scan, probe and attach done\n"); 652298260Ssephe 653250199Sgrehan return (ret); 654250199Sgrehan 655300574Ssephecleanup: 656300645Ssephe vmbus_intr_teardown(sc); 657300572Ssephe vmbus_dma_free(sc); 658250199Sgrehan 659250199Sgrehan return (ret); 660250199Sgrehan} 661250199Sgrehan 662300107Ssephestatic void 663300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 664300107Ssephe{ 665300107Ssephe} 666300107Ssephe 667250199Sgrehanstatic int 668250199Sgrehanvmbus_attach(device_t dev) 669250199Sgrehan{ 670300102Ssephe vmbus_sc = device_get_softc(dev); 671300486Ssephe vmbus_sc->vmbus_dev = dev; 672300574Ssephe vmbus_sc->vmbus_idtvec = -1; 673250199Sgrehan 674300107Ssephe /* 675300107Ssephe * Event processing logic will be configured: 676300107Ssephe * - After the vmbus protocol version negotiation. 677300107Ssephe * - Before we request channel offers. 678300107Ssephe */ 679300107Ssephe vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 680300107Ssephe 681299746Sjhb#ifndef EARLY_AP_STARTUP 682250199Sgrehan /* 683250199Sgrehan * If the system has already booted and thread 684250199Sgrehan * scheduling is possible indicated by the global 685250199Sgrehan * cold set to zero, we just call the driver 686250199Sgrehan * initialization directly. 687250199Sgrehan */ 688250199Sgrehan if (!cold) 689299746Sjhb#endif 690250199Sgrehan vmbus_bus_init(); 691250199Sgrehan 692298449Ssephe bus_generic_probe(dev); 693250199Sgrehan return (0); 694250199Sgrehan} 695250199Sgrehan 696250199Sgrehanstatic void 697300126Ssephevmbus_sysinit(void *arg __unused) 698250199Sgrehan{ 699300102Ssephe if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL) 700256425Sgibbs return; 701256425Sgibbs 702299746Sjhb#ifndef EARLY_AP_STARTUP 703250199Sgrehan /* 704250199Sgrehan * If the system has already booted and thread 705256425Sgibbs * scheduling is possible, as indicated by the 706256425Sgibbs * global cold set to zero, we just call the driver 707250199Sgrehan * initialization directly. 708250199Sgrehan */ 709250199Sgrehan if (!cold) 710299746Sjhb#endif 711250199Sgrehan vmbus_bus_init(); 712250199Sgrehan} 713250199Sgrehan 714300121Ssephestatic int 715300121Ssephevmbus_detach(device_t dev) 716250199Sgrehan{ 717300487Ssephe struct vmbus_softc *sc = device_get_softc(dev); 718255414Sgrehan 719250199Sgrehan hv_vmbus_release_unattached_channels(); 720250199Sgrehan hv_vmbus_disconnect(); 721250199Sgrehan 722300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) { 723300650Ssephe sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC; 724300650Ssephe smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL); 725300650Ssephe } 726250199Sgrehan 727300645Ssephe vmbus_intr_teardown(sc); 728300572Ssephe vmbus_dma_free(sc); 729255414Sgrehan 730250199Sgrehan return (0); 731250199Sgrehan} 732250199Sgrehan 733250199Sgrehanstatic device_method_t vmbus_methods[] = { 734300124Ssephe /* Device interface */ 735300124Ssephe DEVMETHOD(device_probe, vmbus_probe), 736300124Ssephe DEVMETHOD(device_attach, vmbus_attach), 737300124Ssephe DEVMETHOD(device_detach, vmbus_detach), 738300124Ssephe DEVMETHOD(device_shutdown, bus_generic_shutdown), 739300124Ssephe DEVMETHOD(device_suspend, bus_generic_suspend), 740300124Ssephe DEVMETHOD(device_resume, bus_generic_resume), 741250199Sgrehan 742300124Ssephe /* Bus interface */ 743300124Ssephe DEVMETHOD(bus_add_child, bus_generic_add_child), 744300124Ssephe DEVMETHOD(bus_print_child, bus_generic_print_child), 745300124Ssephe DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 746300124Ssephe DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 747300124Ssephe DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 748250199Sgrehan 749300124Ssephe DEVMETHOD_END 750300124Ssephe}; 751250199Sgrehan 752300102Ssephestatic driver_t vmbus_driver = { 753300102Ssephe "vmbus", 754300102Ssephe vmbus_methods, 755300102Ssephe sizeof(struct vmbus_softc) 756300102Ssephe}; 757250199Sgrehan 758300123Ssephestatic devclass_t vmbus_devclass; 759250199Sgrehan 760300120SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL); 761293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 762293870SsepheMODULE_VERSION(vmbus, 1); 763250199Sgrehan 764299746Sjhb#ifndef EARLY_AP_STARTUP 765300126Ssephe/* 766300126Ssephe * NOTE: 767300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is 768300126Ssephe * initialized. 769300126Ssephe */ 770300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL); 771299746Sjhb#endif 772