vmbus.c revision 301484
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/vmbus.c 301484 2016-06-06 06:18:18Z 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> 64300654Ssephe#include <dev/hyperv/vmbus/hyperv_reg.h> 65300834Ssephe#include <dev/hyperv/vmbus/hyperv_var.h> 66301019Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h> 67300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h> 68250199Sgrehan 69293870Ssephe#include <contrib/dev/acpica/include/acpi.h> 70293870Ssephe#include "acpi_if.h" 71250199Sgrehan 72300102Ssephestruct vmbus_softc *vmbus_sc; 73300102Ssephe 74301015Ssepheextern inthand_t IDTVEC(vmbus_isr); 75300574Ssephe 76250199Sgrehanstatic void 77300572Ssephevmbus_msg_task(void *xsc, int pending __unused) 78250199Sgrehan{ 79300572Ssephe struct vmbus_softc *sc = xsc; 80301019Ssephe volatile struct vmbus_message *msg; 81250199Sgrehan 82300988Ssephe msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE; 83300108Ssephe for (;;) { 84300108Ssephe const hv_vmbus_channel_msg_table_entry *entry; 85300108Ssephe hv_vmbus_channel_msg_header *hdr; 86300108Ssephe hv_vmbus_channel_msg_type msg_type; 87282212Swhu 88301484Ssephe if (msg->msg_type == VMBUS_MSGTYPE_NONE) { 89301484Ssephe /* No message */ 90301484Ssephe break; 91301484Ssephe } else if (msg->msg_type != VMBUS_MSGTYPE_CHANNEL) { 92301484Ssephe /* Not a channel message */ 93301484Ssephe goto handled; 94301484Ssephe } 95292861Sdelphij 96301019Ssephe /* XXX: update messageHandler interface */ 97301019Ssephe hdr = __DEVOLATILE(hv_vmbus_channel_msg_header *, 98301019Ssephe msg->msg_data); 99292861Sdelphij msg_type = hdr->message_type; 100292861Sdelphij 101295307Ssephe if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { 102292861Sdelphij printf("VMBUS: unknown message type = %d\n", msg_type); 103292861Sdelphij goto handled; 104292861Sdelphij } 105292861Sdelphij 106292861Sdelphij entry = &g_channel_message_table[msg_type]; 107295307Ssephe if (entry->messageHandler) 108292861Sdelphij entry->messageHandler(hdr); 109292861Sdelphijhandled: 110301019Ssephe msg->msg_type = VMBUS_MSGTYPE_NONE; 111300108Ssephe /* 112301019Ssephe * Make sure the write to msg_type (i.e. set to 113301019Ssephe * VMBUS_MSGTYPE_NONE) happens before we read the 114301019Ssephe * msg_flags and EOMing. Otherwise, the EOMing will 115301019Ssephe * not deliver any more messages since there is no 116301019Ssephe * empty slot 117300108Ssephe * 118300108Ssephe * NOTE: 119300108Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 120300108Ssephe * will become compiler fence on UP kernel. 121300108Ssephe */ 122300108Ssephe mb(); 123301019Ssephe if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { 124250199Sgrehan /* 125250199Sgrehan * This will cause message queue rescan to possibly 126250199Sgrehan * deliver another msg from the hypervisor 127250199Sgrehan */ 128300830Ssephe wrmsr(MSR_HV_EOM, 0); 129300108Ssephe } 130250199Sgrehan } 131250199Sgrehan} 132250199Sgrehan 133301015Ssephestatic __inline int 134301015Ssephevmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu) 135250199Sgrehan{ 136301019Ssephe volatile struct vmbus_message *msg; 137301019Ssephe struct vmbus_message *msg_base; 138250199Sgrehan 139301009Ssephe msg_base = VMBUS_PCPU_GET(sc, message, cpu); 140301009Ssephe 141250199Sgrehan /* 142301009Ssephe * Check event timer. 143301009Ssephe * 144301009Ssephe * TODO: move this to independent IDT vector. 145250199Sgrehan */ 146300988Ssephe msg = msg_base + VMBUS_SINT_TIMER; 147301019Ssephe if (msg->msg_type == VMBUS_MSGTYPE_TIMER_EXPIRED) { 148301019Ssephe msg->msg_type = VMBUS_MSGTYPE_NONE; 149293873Ssephe 150300988Ssephe vmbus_et_intr(frame); 151297176Ssephe 152293873Ssephe /* 153301019Ssephe * Make sure the write to msg_type (i.e. set to 154301019Ssephe * VMBUS_MSGTYPE_NONE) happens before we read the 155301019Ssephe * msg_flags and EOMing. Otherwise, the EOMing will 156301019Ssephe * not deliver any more messages since there is no 157301019Ssephe * empty slot 158297634Ssephe * 159297634Ssephe * NOTE: 160297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 161297636Ssephe * will become compiler fence on UP kernel. 162293873Ssephe */ 163297634Ssephe mb(); 164301019Ssephe if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { 165293873Ssephe /* 166293873Ssephe * This will cause message queue rescan to possibly 167293873Ssephe * deliver another msg from the hypervisor 168293873Ssephe */ 169300830Ssephe wrmsr(MSR_HV_EOM, 0); 170293873Ssephe } 171293873Ssephe } 172293873Ssephe 173301009Ssephe /* 174301009Ssephe * Check events. Hot path for network and storage I/O data; high rate. 175301009Ssephe * 176301009Ssephe * NOTE: 177301009Ssephe * As recommended by the Windows guest fellows, we check events before 178301009Ssephe * checking messages. 179301009Ssephe */ 180301009Ssephe sc->vmbus_event_proc(sc, cpu); 181301009Ssephe 182301009Ssephe /* 183301009Ssephe * Check messages. Mainly management stuffs; ultra low rate. 184301009Ssephe */ 185300988Ssephe msg = msg_base + VMBUS_SINT_MESSAGE; 186301019Ssephe if (__predict_false(msg->msg_type != VMBUS_MSGTYPE_NONE)) { 187300646Ssephe taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), 188300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 189250199Sgrehan } 190250199Sgrehan 191293873Ssephe return (FILTER_HANDLED); 192250199Sgrehan} 193250199Sgrehan 194282212Swhuvoid 195301015Ssephevmbus_handle_intr(struct trapframe *trap_frame) 196282212Swhu{ 197300565Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 198300565Ssephe int cpu = curcpu; 199282212Swhu 200282212Swhu /* 201282212Swhu * Disable preemption. 202282212Swhu */ 203282212Swhu critical_enter(); 204282212Swhu 205282212Swhu /* 206282212Swhu * Do a little interrupt counting. 207282212Swhu */ 208300573Ssephe (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++; 209282212Swhu 210301015Ssephe vmbus_handle_intr1(sc, trap_frame, cpu); 211282212Swhu 212282212Swhu /* 213282212Swhu * Enable preemption. 214282212Swhu */ 215282212Swhu critical_exit(); 216282212Swhu} 217282212Swhu 218300571Ssephestatic void 219300652Ssephevmbus_synic_setup(void *xsc) 220300571Ssephe{ 221300652Ssephe struct vmbus_softc *sc = xsc; 222300654Ssephe int cpu = curcpu; 223300654Ssephe uint64_t val, orig; 224300654Ssephe uint32_t sint; 225300571Ssephe 226300834Ssephe if (hyperv_features & CPUID_HV_MSR_VP_INDEX) { 227300834Ssephe /* 228300834Ssephe * Save virtual processor id. 229300834Ssephe */ 230300834Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX); 231300834Ssephe } else { 232300834Ssephe /* 233300834Ssephe * XXX 234300834Ssephe * Virtual processoor id is only used by a pretty broken 235300834Ssephe * channel selection code from storvsc. It's nothing 236300834Ssephe * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep 237300834Ssephe * moving on. 238300834Ssephe */ 239300834Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = cpu; 240300834Ssephe } 241300571Ssephe 242300571Ssephe /* 243300654Ssephe * Setup the SynIC message. 244300571Ssephe */ 245300654Ssephe orig = rdmsr(MSR_HV_SIMP); 246300654Ssephe val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) | 247300654Ssephe ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) << 248300654Ssephe MSR_HV_SIMP_PGSHIFT); 249300654Ssephe wrmsr(MSR_HV_SIMP, val); 250300571Ssephe 251300571Ssephe /* 252300654Ssephe * Setup the SynIC event flags. 253300571Ssephe */ 254300654Ssephe orig = rdmsr(MSR_HV_SIEFP); 255300654Ssephe val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) | 256301106Ssephe ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu) 257301106Ssephe >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT); 258300654Ssephe wrmsr(MSR_HV_SIEFP, val); 259300571Ssephe 260300571Ssephe 261300654Ssephe /* 262300654Ssephe * Configure and unmask SINT for message and event flags. 263300654Ssephe */ 264300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; 265300654Ssephe orig = rdmsr(sint); 266300654Ssephe val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | 267300654Ssephe (orig & MSR_HV_SINT_RSVD_MASK); 268300654Ssephe wrmsr(sint, val); 269300571Ssephe 270300654Ssephe /* 271300654Ssephe * Configure and unmask SINT for timer. 272300654Ssephe */ 273300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; 274300654Ssephe orig = rdmsr(sint); 275300654Ssephe val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | 276300654Ssephe (orig & MSR_HV_SINT_RSVD_MASK); 277300654Ssephe wrmsr(sint, val); 278300571Ssephe 279300571Ssephe /* 280300654Ssephe * All done; enable SynIC. 281300571Ssephe */ 282300654Ssephe orig = rdmsr(MSR_HV_SCONTROL); 283300654Ssephe val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK); 284300654Ssephe wrmsr(MSR_HV_SCONTROL, val); 285300571Ssephe} 286300571Ssephe 287300571Ssephestatic void 288300571Ssephevmbus_synic_teardown(void *arg) 289300571Ssephe{ 290300654Ssephe uint64_t orig; 291300654Ssephe uint32_t sint; 292300571Ssephe 293300654Ssephe /* 294300654Ssephe * Disable SynIC. 295300654Ssephe */ 296300654Ssephe orig = rdmsr(MSR_HV_SCONTROL); 297300654Ssephe wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK)); 298300571Ssephe 299300654Ssephe /* 300300654Ssephe * Mask message and event flags SINT. 301300654Ssephe */ 302300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; 303300654Ssephe orig = rdmsr(sint); 304300654Ssephe wrmsr(sint, orig | MSR_HV_SINT_MASKED); 305300571Ssephe 306300571Ssephe /* 307300654Ssephe * Mask timer SINT. 308300571Ssephe */ 309300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; 310300654Ssephe orig = rdmsr(sint); 311300654Ssephe wrmsr(sint, orig | MSR_HV_SINT_MASKED); 312300571Ssephe 313300654Ssephe /* 314300654Ssephe * Teardown SynIC message. 315300654Ssephe */ 316300654Ssephe orig = rdmsr(MSR_HV_SIMP); 317300654Ssephe wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK)); 318300571Ssephe 319300571Ssephe /* 320300654Ssephe * Teardown SynIC event flags. 321300571Ssephe */ 322300654Ssephe orig = rdmsr(MSR_HV_SIEFP); 323300654Ssephe wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK)); 324300571Ssephe} 325300571Ssephe 326300644Ssephestatic int 327300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc) 328300572Ssephe{ 329300572Ssephe int cpu; 330300572Ssephe 331300572Ssephe CPU_FOREACH(cpu) { 332300644Ssephe void *ptr; 333300644Ssephe 334300572Ssephe /* 335300572Ssephe * Per-cpu messages and event flags. 336300572Ssephe */ 337300644Ssephe ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 338300644Ssephe PAGE_SIZE, 0, PAGE_SIZE, 339300573Ssephe VMBUS_PCPU_PTR(sc, message_dma, cpu), 340300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 341300644Ssephe if (ptr == NULL) 342300644Ssephe return ENOMEM; 343300644Ssephe VMBUS_PCPU_GET(sc, message, cpu) = ptr; 344300644Ssephe 345300644Ssephe ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 346300644Ssephe PAGE_SIZE, 0, PAGE_SIZE, 347301106Ssephe VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), 348300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 349300644Ssephe if (ptr == NULL) 350300644Ssephe return ENOMEM; 351301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr; 352300572Ssephe } 353300644Ssephe return 0; 354300572Ssephe} 355300572Ssephe 356300572Ssephestatic void 357300572Ssephevmbus_dma_free(struct vmbus_softc *sc) 358300572Ssephe{ 359300572Ssephe int cpu; 360300572Ssephe 361300572Ssephe CPU_FOREACH(cpu) { 362300573Ssephe if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) { 363300572Ssephe hyperv_dmamem_free( 364300573Ssephe VMBUS_PCPU_PTR(sc, message_dma, cpu), 365300573Ssephe VMBUS_PCPU_GET(sc, message, cpu)); 366300573Ssephe VMBUS_PCPU_GET(sc, message, cpu) = NULL; 367300572Ssephe } 368301106Ssephe if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) { 369300572Ssephe hyperv_dmamem_free( 370301106Ssephe VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), 371301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu)); 372301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL; 373300572Ssephe } 374300572Ssephe } 375300572Ssephe} 376300572Ssephe 377250199Sgrehanstatic int 378300574Ssephevmbus_intr_setup(struct vmbus_softc *sc) 379300574Ssephe{ 380300574Ssephe int cpu; 381300574Ssephe 382300574Ssephe CPU_FOREACH(cpu) { 383300574Ssephe char buf[MAXCOMLEN + 1]; 384300645Ssephe cpuset_t cpu_mask; 385300574Ssephe 386300645Ssephe /* Allocate an interrupt counter for Hyper-V interrupt */ 387300574Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu); 388300574Ssephe intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu)); 389300574Ssephe 390300574Ssephe /* 391300645Ssephe * Setup taskqueue to handle events. Task will be per- 392300645Ssephe * channel. 393300574Ssephe */ 394300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast( 395300646Ssephe "hyperv event", M_WAITOK, taskqueue_thread_enqueue, 396300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu)); 397300574Ssephe CPU_SETOF(cpu, &cpu_mask); 398300574Ssephe taskqueue_start_threads_cpuset( 399300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask, 400300646Ssephe "hvevent%d", cpu); 401300574Ssephe 402300574Ssephe /* 403300645Ssephe * Setup tasks and taskqueues to handle messages. 404300574Ssephe */ 405300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast( 406300574Ssephe "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 407300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu)); 408300574Ssephe CPU_SETOF(cpu, &cpu_mask); 409300574Ssephe taskqueue_start_threads_cpuset( 410300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask, 411300646Ssephe "hvmsg%d", cpu); 412300646Ssephe TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0, 413300574Ssephe vmbus_msg_task, sc); 414300574Ssephe } 415300645Ssephe 416300645Ssephe /* 417300645Ssephe * All Hyper-V ISR required resources are setup, now let's find a 418300645Ssephe * free IDT vector for Hyper-V ISR and set it up. 419300645Ssephe */ 420301015Ssephe sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr)); 421300645Ssephe if (sc->vmbus_idtvec < 0) { 422300645Ssephe device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); 423300645Ssephe return ENXIO; 424300645Ssephe } 425300645Ssephe if(bootverbose) { 426300645Ssephe device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", 427300645Ssephe sc->vmbus_idtvec); 428300645Ssephe } 429300574Ssephe return 0; 430300574Ssephe} 431300574Ssephe 432300574Ssephestatic void 433300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc) 434300574Ssephe{ 435300574Ssephe int cpu; 436300574Ssephe 437300645Ssephe if (sc->vmbus_idtvec >= 0) { 438300645Ssephe lapic_ipi_free(sc->vmbus_idtvec); 439300645Ssephe sc->vmbus_idtvec = -1; 440300645Ssephe } 441300645Ssephe 442300574Ssephe CPU_FOREACH(cpu) { 443300646Ssephe if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) { 444300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu)); 445300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL; 446300574Ssephe } 447300646Ssephe if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) { 448300646Ssephe taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu), 449300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 450300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu)); 451300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL; 452300576Ssephe } 453300574Ssephe } 454300574Ssephe} 455300574Ssephe 456300574Ssephestatic int 457300651Ssephevmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 458250199Sgrehan{ 459250199Sgrehan struct hv_device *child_dev_ctx = device_get_ivars(child); 460250199Sgrehan 461250199Sgrehan switch (index) { 462250199Sgrehan case HV_VMBUS_IVAR_TYPE: 463301020Ssephe *result = (uintptr_t)&child_dev_ctx->class_id; 464250199Sgrehan return (0); 465301020Ssephe 466250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 467301020Ssephe *result = (uintptr_t)&child_dev_ctx->device_id; 468250199Sgrehan return (0); 469301020Ssephe 470250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 471301020Ssephe *result = (uintptr_t)child_dev_ctx; 472250199Sgrehan return (0); 473301020Ssephe 474250199Sgrehan case HV_VMBUS_IVAR_NODE: 475301020Ssephe *result = (uintptr_t)child_dev_ctx->device; 476250199Sgrehan return (0); 477250199Sgrehan } 478250199Sgrehan return (ENOENT); 479250199Sgrehan} 480250199Sgrehan 481250199Sgrehanstatic int 482300651Ssephevmbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 483250199Sgrehan{ 484250199Sgrehan switch (index) { 485250199Sgrehan case HV_VMBUS_IVAR_TYPE: 486250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 487250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 488250199Sgrehan case HV_VMBUS_IVAR_NODE: 489250199Sgrehan /* read-only */ 490250199Sgrehan return (EINVAL); 491250199Sgrehan } 492250199Sgrehan return (ENOENT); 493250199Sgrehan} 494250199Sgrehan 495297143Ssephestatic int 496297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 497297143Ssephe{ 498297143Ssephe struct hv_device *dev_ctx = device_get_ivars(child); 499301021Ssephe char guidbuf[HYPERV_GUID_STRLEN]; 500297143Ssephe 501298449Ssephe if (dev_ctx == NULL) 502298449Ssephe return (0); 503298449Ssephe 504297143Ssephe strlcat(buf, "classid=", buflen); 505301021Ssephe hyperv_guid2str(&dev_ctx->class_id, guidbuf, sizeof(guidbuf)); 506297143Ssephe strlcat(buf, guidbuf, buflen); 507297143Ssephe 508297143Ssephe strlcat(buf, " deviceid=", buflen); 509301021Ssephe hyperv_guid2str(&dev_ctx->device_id, guidbuf, sizeof(guidbuf)); 510297143Ssephe strlcat(buf, guidbuf, buflen); 511297143Ssephe 512297143Ssephe return (0); 513297143Ssephe} 514297143Ssephe 515300651Ssephestruct hv_device * 516300651Ssephehv_vmbus_child_device_create(hv_guid type, hv_guid instance, 517300651Ssephe hv_vmbus_channel *channel) 518250199Sgrehan{ 519300651Ssephe hv_device *child_dev; 520250199Sgrehan 521250199Sgrehan /* 522250199Sgrehan * Allocate the new child device 523250199Sgrehan */ 524300651Ssephe child_dev = malloc(sizeof(hv_device), M_DEVBUF, M_WAITOK | M_ZERO); 525250199Sgrehan 526250199Sgrehan child_dev->channel = channel; 527250199Sgrehan memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 528250199Sgrehan memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 529250199Sgrehan 530250199Sgrehan return (child_dev); 531250199Sgrehan} 532250199Sgrehan 533297142Ssepheint 534250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev) 535250199Sgrehan{ 536301021Ssephe device_t child, parent; 537250199Sgrehan 538301021Ssephe parent = vmbus_get_device(); 539297142Ssephe if (bootverbose) { 540301021Ssephe char name[HYPERV_GUID_STRLEN]; 541301021Ssephe 542301021Ssephe hyperv_guid2str(&child_dev->class_id, name, sizeof(name)); 543301021Ssephe device_printf(parent, "add device, classid: %s\n", name); 544297142Ssephe } 545250199Sgrehan 546301021Ssephe child = device_add_child(parent, NULL, -1); 547250199Sgrehan child_dev->device = child; 548250199Sgrehan device_set_ivars(child, child_dev); 549250199Sgrehan 550250199Sgrehan return (0); 551250199Sgrehan} 552250199Sgrehan 553250199Sgrehanint 554250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev) 555250199Sgrehan{ 556250199Sgrehan int ret = 0; 557250199Sgrehan /* 558250199Sgrehan * XXXKYS: Ensure that this is the opposite of 559250199Sgrehan * device_add_child() 560250199Sgrehan */ 561250199Sgrehan mtx_lock(&Giant); 562300486Ssephe ret = device_delete_child(vmbus_get_device(), child_dev->device); 563250199Sgrehan mtx_unlock(&Giant); 564250199Sgrehan return(ret); 565250199Sgrehan} 566250199Sgrehan 567250199Sgrehanstatic int 568300127Ssephevmbus_probe(device_t dev) 569300127Ssephe{ 570301018Ssephe char *id[] = { "VMBUS", NULL }; 571301018Ssephe 572301018Ssephe if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL || 573300834Ssephe device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV || 574300834Ssephe (hyperv_features & CPUID_HV_MSR_SYNIC) == 0) 575293870Ssephe return (ENXIO); 576250199Sgrehan 577300129Ssephe device_set_desc(dev, "Hyper-V Vmbus"); 578250199Sgrehan 579293870Ssephe return (BUS_PROBE_DEFAULT); 580250199Sgrehan} 581250199Sgrehan 582282212Swhu/** 583250199Sgrehan * @brief Main vmbus driver initialization routine. 584250199Sgrehan * 585250199Sgrehan * Here, we 586250199Sgrehan * - initialize the vmbus driver context 587250199Sgrehan * - setup various driver entry points 588250199Sgrehan * - invoke the vmbus hv main init routine 589250199Sgrehan * - get the irq resource 590250199Sgrehan * - invoke the vmbus to add the vmbus root device 591250199Sgrehan * - setup the vmbus root device 592250199Sgrehan * - retrieve the channel offers 593250199Sgrehan */ 594250199Sgrehanstatic int 595250199Sgrehanvmbus_bus_init(void) 596250199Sgrehan{ 597300650Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 598300574Ssephe int ret; 599250199Sgrehan 600300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED) 601250199Sgrehan return (0); 602300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_ATTACHED; 603250199Sgrehan 604250199Sgrehan /* 605300645Ssephe * Allocate DMA stuffs. 606250199Sgrehan */ 607300645Ssephe ret = vmbus_dma_alloc(sc); 608300574Ssephe if (ret != 0) 609282212Swhu goto cleanup; 610250199Sgrehan 611250199Sgrehan /* 612300645Ssephe * Setup interrupt. 613250199Sgrehan */ 614300645Ssephe ret = vmbus_intr_setup(sc); 615300644Ssephe if (ret != 0) 616300644Ssephe goto cleanup; 617300572Ssephe 618300650Ssephe /* 619300650Ssephe * Setup SynIC. 620300650Ssephe */ 621282212Swhu if (bootverbose) 622300650Ssephe device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started); 623300652Ssephe smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc); 624300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_SYNIC; 625282212Swhu 626255414Sgrehan /* 627250199Sgrehan * Connect to VMBus in the root partition 628250199Sgrehan */ 629250199Sgrehan ret = hv_vmbus_connect(); 630250199Sgrehan 631255414Sgrehan if (ret != 0) 632300574Ssephe goto cleanup; 633250199Sgrehan 634300107Ssephe if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || 635300107Ssephe hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) 636300107Ssephe sc->vmbus_event_proc = vmbus_event_proc_compat; 637300107Ssephe else 638300107Ssephe sc->vmbus_event_proc = vmbus_event_proc; 639300107Ssephe 640250199Sgrehan hv_vmbus_request_channel_offers(); 641298260Ssephe 642298260Ssephe vmbus_scan(); 643300486Ssephe bus_generic_attach(sc->vmbus_dev); 644300486Ssephe device_printf(sc->vmbus_dev, "device scan, probe and attach done\n"); 645298260Ssephe 646250199Sgrehan return (ret); 647250199Sgrehan 648300574Ssephecleanup: 649300645Ssephe vmbus_intr_teardown(sc); 650300572Ssephe vmbus_dma_free(sc); 651250199Sgrehan 652250199Sgrehan return (ret); 653250199Sgrehan} 654250199Sgrehan 655300107Ssephestatic void 656300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 657300107Ssephe{ 658300107Ssephe} 659300107Ssephe 660250199Sgrehanstatic int 661250199Sgrehanvmbus_attach(device_t dev) 662250199Sgrehan{ 663300102Ssephe vmbus_sc = device_get_softc(dev); 664300486Ssephe vmbus_sc->vmbus_dev = dev; 665300574Ssephe vmbus_sc->vmbus_idtvec = -1; 666250199Sgrehan 667300107Ssephe /* 668300107Ssephe * Event processing logic will be configured: 669300107Ssephe * - After the vmbus protocol version negotiation. 670300107Ssephe * - Before we request channel offers. 671300107Ssephe */ 672300107Ssephe vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 673300107Ssephe 674299746Sjhb#ifndef EARLY_AP_STARTUP 675250199Sgrehan /* 676250199Sgrehan * If the system has already booted and thread 677250199Sgrehan * scheduling is possible indicated by the global 678250199Sgrehan * cold set to zero, we just call the driver 679250199Sgrehan * initialization directly. 680250199Sgrehan */ 681250199Sgrehan if (!cold) 682299746Sjhb#endif 683250199Sgrehan vmbus_bus_init(); 684250199Sgrehan 685298449Ssephe bus_generic_probe(dev); 686250199Sgrehan return (0); 687250199Sgrehan} 688250199Sgrehan 689250199Sgrehanstatic void 690300126Ssephevmbus_sysinit(void *arg __unused) 691250199Sgrehan{ 692300102Ssephe if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL) 693256425Sgibbs return; 694256425Sgibbs 695299746Sjhb#ifndef EARLY_AP_STARTUP 696250199Sgrehan /* 697250199Sgrehan * If the system has already booted and thread 698256425Sgibbs * scheduling is possible, as indicated by the 699256425Sgibbs * global cold set to zero, we just call the driver 700250199Sgrehan * initialization directly. 701250199Sgrehan */ 702250199Sgrehan if (!cold) 703299746Sjhb#endif 704250199Sgrehan vmbus_bus_init(); 705250199Sgrehan} 706250199Sgrehan 707300121Ssephestatic int 708300121Ssephevmbus_detach(device_t dev) 709250199Sgrehan{ 710300487Ssephe struct vmbus_softc *sc = device_get_softc(dev); 711255414Sgrehan 712250199Sgrehan hv_vmbus_release_unattached_channels(); 713250199Sgrehan hv_vmbus_disconnect(); 714250199Sgrehan 715300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) { 716300650Ssephe sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC; 717300650Ssephe smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL); 718300650Ssephe } 719250199Sgrehan 720300645Ssephe vmbus_intr_teardown(sc); 721300572Ssephe vmbus_dma_free(sc); 722255414Sgrehan 723250199Sgrehan return (0); 724250199Sgrehan} 725250199Sgrehan 726250199Sgrehanstatic device_method_t vmbus_methods[] = { 727300124Ssephe /* Device interface */ 728300124Ssephe DEVMETHOD(device_probe, vmbus_probe), 729300124Ssephe DEVMETHOD(device_attach, vmbus_attach), 730300124Ssephe DEVMETHOD(device_detach, vmbus_detach), 731300124Ssephe DEVMETHOD(device_shutdown, bus_generic_shutdown), 732300124Ssephe DEVMETHOD(device_suspend, bus_generic_suspend), 733300124Ssephe DEVMETHOD(device_resume, bus_generic_resume), 734250199Sgrehan 735300124Ssephe /* Bus interface */ 736300124Ssephe DEVMETHOD(bus_add_child, bus_generic_add_child), 737300124Ssephe DEVMETHOD(bus_print_child, bus_generic_print_child), 738300124Ssephe DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 739300124Ssephe DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 740300124Ssephe DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 741250199Sgrehan 742300124Ssephe DEVMETHOD_END 743300124Ssephe}; 744250199Sgrehan 745300102Ssephestatic driver_t vmbus_driver = { 746300102Ssephe "vmbus", 747300102Ssephe vmbus_methods, 748300102Ssephe sizeof(struct vmbus_softc) 749300102Ssephe}; 750250199Sgrehan 751300123Ssephestatic devclass_t vmbus_devclass; 752250199Sgrehan 753300120SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL); 754293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 755293870SsepheMODULE_VERSION(vmbus, 1); 756250199Sgrehan 757299746Sjhb#ifndef EARLY_AP_STARTUP 758300126Ssephe/* 759300126Ssephe * NOTE: 760300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is 761300126Ssephe * initialized. 762300126Ssephe */ 763300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL); 764299746Sjhb#endif 765