1250199Sgrehan/*- 2250199Sgrehan * Copyright (c) 2009-2012 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: releng/10.3/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 320912 2017-07-12 08:07:55Z delphij $"); 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> 41295789Ssephe#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> 57283280Swhu#include <machine/md_var.h> 58283280Swhu#include <machine/segments.h> 59250199Sgrehan#include <sys/pcpu.h> 60283280Swhu#include <machine/apicvar.h> 61250199Sgrehan 62320912Sdelphij#include <dev/hyperv/include/hyperv.h> 63250199Sgrehan#include "hv_vmbus_priv.h" 64250199Sgrehan 65295789Ssephe#include <contrib/dev/acpica/include/acpi.h> 66295789Ssephe#include "acpi_if.h" 67250199Sgrehan 68250199Sgrehanstatic device_t vmbus_devp; 69250199Sgrehanstatic int vmbus_inited; 70255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */ 71250199Sgrehan 72295789Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL }; 73295789Ssephe 74250199Sgrehan/** 75250199Sgrehan * @brief Software interrupt thread routine to handle channel messages from 76250199Sgrehan * the hypervisor. 77250199Sgrehan */ 78250199Sgrehanstatic void 79283280Swhuvmbus_msg_swintr(void *arg) 80250199Sgrehan{ 81250199Sgrehan int cpu; 82250199Sgrehan void* page_addr; 83293820Sdelphij hv_vmbus_channel_msg_header *hdr; 84293820Sdelphij hv_vmbus_channel_msg_table_entry *entry; 85293820Sdelphij hv_vmbus_channel_msg_type msg_type; 86250199Sgrehan hv_vmbus_message* msg; 87250199Sgrehan hv_vmbus_message* copied; 88293820Sdelphij static bool warned = false; 89250199Sgrehan 90283280Swhu cpu = (int)(long)arg; 91283280Swhu KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: " 92283280Swhu "cpu out of range!")); 93283280Swhu 94250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 95250199Sgrehan msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 96250199Sgrehan 97250199Sgrehan for (;;) { 98293820Sdelphij if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 99250199Sgrehan break; /* no message */ 100293820Sdelphij 101293820Sdelphij hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 102293820Sdelphij msg_type = hdr->message_type; 103293820Sdelphij 104293820Sdelphij if (msg_type >= HV_CHANNEL_MESSAGE_COUNT && !warned) { 105293820Sdelphij warned = true; 106293820Sdelphij printf("VMBUS: unknown message type = %d\n", msg_type); 107293820Sdelphij goto handled; 108293820Sdelphij } 109293820Sdelphij 110293820Sdelphij entry = &g_channel_message_table[msg_type]; 111293820Sdelphij 112293820Sdelphij if (entry->handler_no_sleep) 113293820Sdelphij entry->messageHandler(hdr); 114293820Sdelphij else { 115293820Sdelphij 116250199Sgrehan copied = malloc(sizeof(hv_vmbus_message), 117250199Sgrehan M_DEVBUF, M_NOWAIT); 118250199Sgrehan KASSERT(copied != NULL, 119250199Sgrehan ("Error VMBUS: malloc failed to allocate" 120250199Sgrehan " hv_vmbus_message!")); 121250199Sgrehan if (copied == NULL) 122250199Sgrehan continue; 123293820Sdelphij 124250199Sgrehan memcpy(copied, msg, sizeof(hv_vmbus_message)); 125250199Sgrehan hv_queue_work_item(hv_vmbus_g_connection.work_queue, 126293820Sdelphij hv_vmbus_on_channel_message, 127293820Sdelphij copied); 128293820Sdelphij } 129293820Sdelphijhandled: 130250199Sgrehan msg->header.message_type = HV_MESSAGE_TYPE_NONE; 131250199Sgrehan 132250199Sgrehan /* 133250199Sgrehan * Make sure the write to message_type (ie set to 134250199Sgrehan * HV_MESSAGE_TYPE_NONE) happens before we read the 135250199Sgrehan * message_pending and EOMing. Otherwise, the EOMing will 136250199Sgrehan * not deliver any more messages 137250199Sgrehan * since there is no empty slot 138250199Sgrehan */ 139250199Sgrehan wmb(); 140250199Sgrehan 141256276Sdim if (msg->header.message_flags.u.message_pending) { 142250199Sgrehan /* 143250199Sgrehan * This will cause message queue rescan to possibly 144250199Sgrehan * deliver another msg from the hypervisor 145250199Sgrehan */ 146255414Sgrehan wrmsr(HV_X64_MSR_EOM, 0); 147250199Sgrehan } 148250199Sgrehan } 149250199Sgrehan} 150250199Sgrehan 151250199Sgrehan/** 152250199Sgrehan * @brief Interrupt filter routine for VMBUS. 153250199Sgrehan * 154250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol 155250199Sgrehan * message to process - an event or a channel message. 156250199Sgrehan */ 157283280Swhustatic inline int 158295789Ssephehv_vmbus_isr(struct trapframe *frame) 159250199Sgrehan{ 160250199Sgrehan int cpu; 161250199Sgrehan hv_vmbus_message* msg; 162250199Sgrehan hv_vmbus_synic_event_flags* event; 163250199Sgrehan void* page_addr; 164250199Sgrehan 165250199Sgrehan cpu = PCPU_GET(cpuid); 166250199Sgrehan 167250199Sgrehan /* 168250199Sgrehan * The Windows team has advised that we check for events 169250199Sgrehan * before checking for messages. This is the way they do it 170250199Sgrehan * in Windows when running as a guest in Hyper-V 171250199Sgrehan */ 172250199Sgrehan 173250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 174250199Sgrehan event = (hv_vmbus_synic_event_flags*) 175250199Sgrehan page_addr + HV_VMBUS_MESSAGE_SINT; 176250199Sgrehan 177283280Swhu if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 178283280Swhu (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 179283280Swhu /* Since we are a child, we only need to check bit 0 */ 180283280Swhu if (synch_test_and_clear_bit(0, &event->flags32[0])) { 181283280Swhu swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); 182283280Swhu } 183283280Swhu } else { 184283280Swhu /* 185283280Swhu * On host with Win8 or above, we can directly look at 186283280Swhu * the event page. If bit n is set, we have an interrupt 187283280Swhu * on the channel with id n. 188283280Swhu * Directly schedule the event software interrupt on 189283280Swhu * current cpu. 190283280Swhu */ 191283280Swhu swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); 192250199Sgrehan } 193250199Sgrehan 194250199Sgrehan /* Check if there are actual msgs to be process */ 195250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 196250199Sgrehan msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 197250199Sgrehan 198295789Ssephe /* we call eventtimer process the message */ 199295789Ssephe if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { 200295789Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 201295789Ssephe 202295789Ssephe /* 203295789Ssephe * Make sure the write to message_type (ie set to 204295789Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 205295789Ssephe * message_pending and EOMing. Otherwise, the EOMing will 206295789Ssephe * not deliver any more messages 207295789Ssephe * since there is no empty slot 208295789Ssephe */ 209295789Ssephe wmb(); 210295789Ssephe 211295789Ssephe if (msg->header.message_flags.u.message_pending) { 212295789Ssephe /* 213295789Ssephe * This will cause message queue rescan to possibly 214295789Ssephe * deliver another msg from the hypervisor 215295789Ssephe */ 216295789Ssephe wrmsr(HV_X64_MSR_EOM, 0); 217295789Ssephe } 218295789Ssephe hv_et_intr(frame); 219295789Ssephe return (FILTER_HANDLED); 220295789Ssephe } 221295789Ssephe 222250199Sgrehan if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 223283280Swhu swi_sched(hv_vmbus_g_context.msg_swintr[cpu], 0); 224250199Sgrehan } 225250199Sgrehan 226295789Ssephe return (FILTER_HANDLED); 227250199Sgrehan} 228250199Sgrehan 229283280Swhuuint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; 230295789Ssepheu_long *hv_vmbus_intr_cpu[MAXCPU]; 231283280Swhu 232283280Swhuvoid 233283280Swhuhv_vector_handler(struct trapframe *trap_frame) 234283280Swhu{ 235283280Swhu int cpu; 236283280Swhu 237283280Swhu /* 238283280Swhu * Disable preemption. 239283280Swhu */ 240283280Swhu critical_enter(); 241283280Swhu 242283280Swhu /* 243283280Swhu * Do a little interrupt counting. 244283280Swhu */ 245283280Swhu cpu = PCPU_GET(cpuid); 246295789Ssephe (*hv_vmbus_intr_cpu[cpu])++; 247283280Swhu 248295789Ssephe hv_vmbus_isr(trap_frame); 249283280Swhu 250283280Swhu /* 251283280Swhu * Enable preemption. 252283280Swhu */ 253283280Swhu critical_exit(); 254283280Swhu} 255283280Swhu 256250199Sgrehanstatic int 257250199Sgrehanvmbus_read_ivar( 258250199Sgrehan device_t dev, 259250199Sgrehan device_t child, 260250199Sgrehan int index, 261250199Sgrehan uintptr_t* result) 262250199Sgrehan{ 263250199Sgrehan struct hv_device *child_dev_ctx = device_get_ivars(child); 264250199Sgrehan 265250199Sgrehan switch (index) { 266250199Sgrehan 267250199Sgrehan case HV_VMBUS_IVAR_TYPE: 268250199Sgrehan *result = (uintptr_t) &child_dev_ctx->class_id; 269250199Sgrehan return (0); 270250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 271250199Sgrehan *result = (uintptr_t) &child_dev_ctx->device_id; 272250199Sgrehan return (0); 273250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 274250199Sgrehan *result = (uintptr_t) child_dev_ctx; 275250199Sgrehan return (0); 276250199Sgrehan case HV_VMBUS_IVAR_NODE: 277250199Sgrehan *result = (uintptr_t) child_dev_ctx->device; 278250199Sgrehan return (0); 279250199Sgrehan } 280250199Sgrehan return (ENOENT); 281250199Sgrehan} 282250199Sgrehan 283250199Sgrehanstatic int 284250199Sgrehanvmbus_write_ivar( 285250199Sgrehan device_t dev, 286250199Sgrehan device_t child, 287250199Sgrehan int index, 288250199Sgrehan uintptr_t value) 289250199Sgrehan{ 290250199Sgrehan switch (index) { 291250199Sgrehan 292250199Sgrehan case HV_VMBUS_IVAR_TYPE: 293250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 294250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 295250199Sgrehan case HV_VMBUS_IVAR_NODE: 296250199Sgrehan /* read-only */ 297250199Sgrehan return (EINVAL); 298250199Sgrehan } 299250199Sgrehan return (ENOENT); 300250199Sgrehan} 301250199Sgrehan 302320912Sdelphijstatic int 303320912Sdelphijvmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 304320912Sdelphij{ 305320912Sdelphij char guidbuf[40]; 306320912Sdelphij struct hv_device *dev_ctx = device_get_ivars(child); 307320912Sdelphij 308320912Sdelphij strlcat(buf, "classid=", buflen); 309320912Sdelphij snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id); 310320912Sdelphij strlcat(buf, guidbuf, buflen); 311320912Sdelphij 312320912Sdelphij strlcat(buf, " deviceid=", buflen); 313320912Sdelphij snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id); 314320912Sdelphij strlcat(buf, guidbuf, buflen); 315320912Sdelphij 316320912Sdelphij return (0); 317320912Sdelphij} 318320912Sdelphij 319250199Sgrehanstruct hv_device* 320250199Sgrehanhv_vmbus_child_device_create( 321250199Sgrehan hv_guid type, 322250199Sgrehan hv_guid instance, 323250199Sgrehan hv_vmbus_channel* channel) 324250199Sgrehan{ 325250199Sgrehan hv_device* child_dev; 326250199Sgrehan 327250199Sgrehan /* 328250199Sgrehan * Allocate the new child device 329250199Sgrehan */ 330250199Sgrehan child_dev = malloc(sizeof(hv_device), M_DEVBUF, 331250199Sgrehan M_NOWAIT | M_ZERO); 332250199Sgrehan KASSERT(child_dev != NULL, 333250199Sgrehan ("Error VMBUS: malloc failed to allocate hv_device!")); 334250199Sgrehan 335250199Sgrehan if (child_dev == NULL) 336250199Sgrehan return (NULL); 337250199Sgrehan 338250199Sgrehan child_dev->channel = channel; 339250199Sgrehan memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 340250199Sgrehan memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 341250199Sgrehan 342250199Sgrehan return (child_dev); 343250199Sgrehan} 344250199Sgrehan 345320912Sdelphijint 346320912Sdelphijsnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid) 347250199Sgrehan{ 348320912Sdelphij int cnt; 349320912Sdelphij const unsigned char *d = guid->data; 350320912Sdelphij 351320912Sdelphij cnt = snprintf(buf, sz, 352320912Sdelphij "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 353320912Sdelphij d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], 354320912Sdelphij d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); 355320912Sdelphij return (cnt); 356250199Sgrehan} 357250199Sgrehan 358250199Sgrehanint 359250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev) 360250199Sgrehan{ 361250199Sgrehan device_t child; 362250199Sgrehan int ret = 0; 363250199Sgrehan 364320912Sdelphij if (bootverbose) { 365320912Sdelphij char name[40]; 366320912Sdelphij snprintf_hv_guid(name, sizeof(name), &child_dev->class_id); 367320912Sdelphij printf("VMBUS: Class ID: %s\n", name); 368320912Sdelphij } 369250199Sgrehan 370250199Sgrehan child = device_add_child(vmbus_devp, NULL, -1); 371250199Sgrehan child_dev->device = child; 372250199Sgrehan device_set_ivars(child, child_dev); 373250199Sgrehan 374250199Sgrehan mtx_lock(&Giant); 375250199Sgrehan ret = device_probe_and_attach(child); 376250199Sgrehan mtx_unlock(&Giant); 377250199Sgrehan 378250199Sgrehan return (0); 379250199Sgrehan} 380250199Sgrehan 381250199Sgrehanint 382250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev) 383250199Sgrehan{ 384250199Sgrehan int ret = 0; 385250199Sgrehan /* 386250199Sgrehan * XXXKYS: Ensure that this is the opposite of 387250199Sgrehan * device_add_child() 388250199Sgrehan */ 389250199Sgrehan mtx_lock(&Giant); 390250199Sgrehan ret = device_delete_child(vmbus_devp, child_dev->device); 391250199Sgrehan mtx_unlock(&Giant); 392250199Sgrehan return(ret); 393250199Sgrehan} 394250199Sgrehan 395250199Sgrehanstatic int 396250199Sgrehanvmbus_probe(device_t dev) { 397295789Ssephe if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || 398295789Ssephe device_get_unit(dev) != 0) 399295789Ssephe return (ENXIO); 400250199Sgrehan 401250199Sgrehan device_set_desc(dev, "Vmbus Devices"); 402250199Sgrehan 403295789Ssephe return (BUS_PROBE_DEFAULT); 404250199Sgrehan} 405250199Sgrehan 406283280Swhu#ifdef HYPERV 407283280Swhuextern inthand_t IDTVEC(rsvd), IDTVEC(hv_vmbus_callback); 408283280Swhu 409250199Sgrehan/** 410283280Swhu * @brief Find a free IDT slot and setup the interrupt handler. 411283280Swhu */ 412283280Swhustatic int 413283280Swhuvmbus_vector_alloc(void) 414283280Swhu{ 415283280Swhu int vector; 416283280Swhu uintptr_t func; 417283280Swhu struct gate_descriptor *ip; 418283280Swhu 419283280Swhu /* 420283280Swhu * Search backwards form the highest IDT vector available for use 421283280Swhu * as vmbus channel callback vector. We install 'hv_vmbus_callback' 422283280Swhu * handler at that vector and use it to interrupt vcpus. 423283280Swhu */ 424283280Swhu vector = APIC_SPURIOUS_INT; 425283280Swhu while (--vector >= APIC_IPI_INTS) { 426283280Swhu ip = &idt[vector]; 427283280Swhu func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); 428283280Swhu if (func == (uintptr_t)&IDTVEC(rsvd)) { 429283280Swhu#ifdef __i386__ 430283280Swhu setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYS386IGT, 431283280Swhu SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 432283280Swhu#else 433283280Swhu setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYSIGT, 434283280Swhu SEL_KPL, 0); 435283280Swhu#endif 436283280Swhu 437283280Swhu return (vector); 438283280Swhu } 439283280Swhu } 440283280Swhu return (0); 441283280Swhu} 442283280Swhu 443283280Swhu/** 444283280Swhu * @brief Restore the IDT slot to rsvd. 445283280Swhu */ 446283280Swhustatic void 447283280Swhuvmbus_vector_free(int vector) 448283280Swhu{ 449283280Swhu uintptr_t func; 450283280Swhu struct gate_descriptor *ip; 451283280Swhu 452283280Swhu if (vector == 0) 453283280Swhu return; 454283280Swhu 455283280Swhu KASSERT(vector >= APIC_IPI_INTS && vector < APIC_SPURIOUS_INT, 456283280Swhu ("invalid vector %d", vector)); 457283280Swhu 458283280Swhu ip = &idt[vector]; 459283280Swhu func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); 460283280Swhu KASSERT(func == (uintptr_t)&IDTVEC(hv_vmbus_callback), 461283280Swhu ("invalid vector %d", vector)); 462283280Swhu 463283280Swhu setidt(vector, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0); 464283280Swhu} 465283280Swhu 466283280Swhu#else /* HYPERV */ 467283280Swhu 468283280Swhustatic int 469283280Swhuvmbus_vector_alloc(void) 470283280Swhu{ 471283280Swhu return(0); 472283280Swhu} 473283280Swhu 474283280Swhustatic void 475283280Swhuvmbus_vector_free(int vector) 476283280Swhu{ 477283280Swhu} 478283280Swhu 479283280Swhu#endif /* HYPERV */ 480283280Swhu 481283280Swhu/** 482250199Sgrehan * @brief Main vmbus driver initialization routine. 483250199Sgrehan * 484250199Sgrehan * Here, we 485250199Sgrehan * - initialize the vmbus driver context 486250199Sgrehan * - setup various driver entry points 487250199Sgrehan * - invoke the vmbus hv main init routine 488250199Sgrehan * - get the irq resource 489250199Sgrehan * - invoke the vmbus to add the vmbus root device 490250199Sgrehan * - setup the vmbus root device 491250199Sgrehan * - retrieve the channel offers 492250199Sgrehan */ 493250199Sgrehanstatic int 494250199Sgrehanvmbus_bus_init(void) 495250199Sgrehan{ 496283280Swhu int i, j, n, ret; 497295789Ssephe char buf[MAXCOMLEN + 1]; 498250199Sgrehan 499250199Sgrehan if (vmbus_inited) 500250199Sgrehan return (0); 501250199Sgrehan 502250199Sgrehan vmbus_inited = 1; 503250199Sgrehan 504250199Sgrehan ret = hv_vmbus_init(); 505250199Sgrehan 506250199Sgrehan if (ret) { 507250199Sgrehan if(bootverbose) 508252645Sgrehan printf("Error VMBUS: Hypervisor Initialization Failed!\n"); 509250199Sgrehan return (ret); 510250199Sgrehan } 511250199Sgrehan 512250199Sgrehan /* 513283280Swhu * Find a free IDT slot for vmbus callback. 514250199Sgrehan */ 515283280Swhu hv_vmbus_g_context.hv_cb_vector = vmbus_vector_alloc(); 516250199Sgrehan 517283280Swhu if (hv_vmbus_g_context.hv_cb_vector == 0) { 518283280Swhu if(bootverbose) 519283280Swhu printf("Error VMBUS: Cannot find free IDT slot for " 520283280Swhu "vmbus callback!\n"); 521283280Swhu goto cleanup; 522283280Swhu } 523250199Sgrehan 524283280Swhu if(bootverbose) 525283280Swhu printf("VMBUS: vmbus callback vector %d\n", 526283280Swhu hv_vmbus_g_context.hv_cb_vector); 527250199Sgrehan 528283280Swhu /* 529283280Swhu * Notify the hypervisor of our vector. 530283280Swhu */ 531283280Swhu setup_args.vector = hv_vmbus_g_context.hv_cb_vector; 532250199Sgrehan 533283280Swhu CPU_FOREACH(j) { 534283280Swhu hv_vmbus_swintr_event_cpu[j] = 0; 535283280Swhu hv_vmbus_g_context.hv_event_intr_event[j] = NULL; 536283280Swhu hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 537283280Swhu hv_vmbus_g_context.event_swintr[j] = NULL; 538283280Swhu hv_vmbus_g_context.msg_swintr[j] = NULL; 539250199Sgrehan 540295789Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); 541295789Ssephe intrcnt_add(buf, &hv_vmbus_intr_cpu[j]); 542295789Ssephe 543283280Swhu for (i = 0; i < 2; i++) 544283280Swhu setup_args.page_buffers[2 * j + i] = NULL; 545250199Sgrehan } 546250199Sgrehan 547250199Sgrehan /* 548283280Swhu * Per cpu setup. 549250199Sgrehan */ 550283280Swhu CPU_FOREACH(j) { 551283280Swhu /* 552283280Swhu * Setup software interrupt thread and handler for msg handling. 553283280Swhu */ 554283280Swhu ret = swi_add(&hv_vmbus_g_context.hv_msg_intr_event[j], 555283280Swhu "hv_msg", vmbus_msg_swintr, (void *)(long)j, SWI_CLOCK, 0, 556283280Swhu &hv_vmbus_g_context.msg_swintr[j]); 557283280Swhu if (ret) { 558283280Swhu if(bootverbose) 559283280Swhu printf("VMBUS: failed to setup msg swi for " 560283280Swhu "cpu %d\n", j); 561283280Swhu goto cleanup1; 562283280Swhu } 563250199Sgrehan 564283280Swhu /* 565283280Swhu * Bind the swi thread to the cpu. 566283280Swhu */ 567283280Swhu ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j], 568283280Swhu j); 569283280Swhu if (ret) { 570283280Swhu if(bootverbose) 571283280Swhu printf("VMBUS: failed to bind msg swi thread " 572283280Swhu "to cpu %d\n", j); 573283280Swhu goto cleanup1; 574283280Swhu } 575250199Sgrehan 576283280Swhu /* 577283280Swhu * Setup software interrupt thread and handler for 578283280Swhu * event handling. 579283280Swhu */ 580283280Swhu ret = swi_add(&hv_vmbus_g_context.hv_event_intr_event[j], 581283280Swhu "hv_event", hv_vmbus_on_events, (void *)(long)j, 582283280Swhu SWI_CLOCK, 0, &hv_vmbus_g_context.event_swintr[j]); 583283280Swhu if (ret) { 584283280Swhu if(bootverbose) 585283280Swhu printf("VMBUS: failed to setup event swi for " 586283280Swhu "cpu %d\n", j); 587283280Swhu goto cleanup1; 588283280Swhu } 589250199Sgrehan 590283280Swhu /* 591283280Swhu * Prepare the per cpu msg and event pages to be called on each cpu. 592283280Swhu */ 593283280Swhu for(i = 0; i < 2; i++) { 594283280Swhu setup_args.page_buffers[2 * j + i] = 595255414Sgrehan malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); 596283280Swhu if (setup_args.page_buffers[2 * j + i] == NULL) { 597283280Swhu KASSERT(setup_args.page_buffers[2 * j + i] != NULL, 598255414Sgrehan ("Error VMBUS: malloc failed!")); 599283280Swhu goto cleanup1; 600283280Swhu } 601255414Sgrehan } 602255414Sgrehan } 603250199Sgrehan 604283280Swhu if (bootverbose) 605283280Swhu printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n", 606283280Swhu smp_started); 607283280Swhu 608255414Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); 609250199Sgrehan 610255414Sgrehan /* 611250199Sgrehan * Connect to VMBus in the root partition 612250199Sgrehan */ 613250199Sgrehan ret = hv_vmbus_connect(); 614250199Sgrehan 615255414Sgrehan if (ret != 0) 616283280Swhu goto cleanup1; 617250199Sgrehan 618250199Sgrehan hv_vmbus_request_channel_offers(); 619250199Sgrehan return (ret); 620250199Sgrehan 621283280Swhu cleanup1: 622283280Swhu /* 623283280Swhu * Free pages alloc'ed 624283280Swhu */ 625283280Swhu for (n = 0; n < 2 * MAXCPU; n++) 626283280Swhu if (setup_args.page_buffers[n] != NULL) 627283280Swhu free(setup_args.page_buffers[n], M_DEVBUF); 628250199Sgrehan 629250199Sgrehan /* 630283280Swhu * remove swi and vmbus callback vector; 631250199Sgrehan */ 632283280Swhu CPU_FOREACH(j) { 633283280Swhu if (hv_vmbus_g_context.msg_swintr[j] != NULL) 634283280Swhu swi_remove(hv_vmbus_g_context.msg_swintr[j]); 635283280Swhu if (hv_vmbus_g_context.event_swintr[j] != NULL) 636283280Swhu swi_remove(hv_vmbus_g_context.event_swintr[j]); 637283280Swhu hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 638283280Swhu hv_vmbus_g_context.hv_event_intr_event[j] = NULL; 639283280Swhu } 640250199Sgrehan 641283280Swhu vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); 642250199Sgrehan 643250199Sgrehan cleanup: 644250199Sgrehan hv_vmbus_cleanup(); 645250199Sgrehan 646250199Sgrehan return (ret); 647250199Sgrehan} 648250199Sgrehan 649250199Sgrehanstatic int 650250199Sgrehanvmbus_attach(device_t dev) 651250199Sgrehan{ 652250199Sgrehan if(bootverbose) 653250199Sgrehan device_printf(dev, "VMBUS: attach dev: %p\n", dev); 654250199Sgrehan vmbus_devp = dev; 655250199Sgrehan 656250199Sgrehan /* 657250199Sgrehan * If the system has already booted and thread 658250199Sgrehan * scheduling is possible indicated by the global 659250199Sgrehan * cold set to zero, we just call the driver 660250199Sgrehan * initialization directly. 661250199Sgrehan */ 662250199Sgrehan if (!cold) 663250199Sgrehan vmbus_bus_init(); 664250199Sgrehan 665250199Sgrehan return (0); 666250199Sgrehan} 667250199Sgrehan 668250199Sgrehanstatic void 669250199Sgrehanvmbus_init(void) 670250199Sgrehan{ 671256758Sgibbs if (vm_guest != VM_GUEST_HV) 672256758Sgibbs return; 673256758Sgibbs 674250199Sgrehan /* 675250199Sgrehan * If the system has already booted and thread 676256758Sgibbs * scheduling is possible, as indicated by the 677256758Sgibbs * global cold set to zero, we just call the driver 678250199Sgrehan * initialization directly. 679250199Sgrehan */ 680250199Sgrehan if (!cold) 681250199Sgrehan vmbus_bus_init(); 682250199Sgrehan} 683250199Sgrehan 684250199Sgrehanstatic void 685250199Sgrehanvmbus_bus_exit(void) 686250199Sgrehan{ 687255414Sgrehan int i; 688255414Sgrehan 689250199Sgrehan hv_vmbus_release_unattached_channels(); 690250199Sgrehan hv_vmbus_disconnect(); 691250199Sgrehan 692250199Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 693250199Sgrehan 694283280Swhu for(i = 0; i < 2 * MAXCPU; i++) { 695255414Sgrehan if (setup_args.page_buffers[i] != 0) 696255414Sgrehan free(setup_args.page_buffers[i], M_DEVBUF); 697255414Sgrehan } 698255414Sgrehan 699250199Sgrehan hv_vmbus_cleanup(); 700250199Sgrehan 701283280Swhu /* remove swi */ 702283280Swhu CPU_FOREACH(i) { 703283280Swhu if (hv_vmbus_g_context.msg_swintr[i] != NULL) 704283280Swhu swi_remove(hv_vmbus_g_context.msg_swintr[i]); 705283280Swhu if (hv_vmbus_g_context.event_swintr[i] != NULL) 706283280Swhu swi_remove(hv_vmbus_g_context.event_swintr[i]); 707283280Swhu hv_vmbus_g_context.hv_msg_intr_event[i] = NULL; 708283280Swhu hv_vmbus_g_context.hv_event_intr_event[i] = NULL; 709283280Swhu } 710250199Sgrehan 711283280Swhu vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); 712250199Sgrehan 713250199Sgrehan return; 714250199Sgrehan} 715250199Sgrehan 716250199Sgrehanstatic void 717250199Sgrehanvmbus_exit(void) 718250199Sgrehan{ 719250199Sgrehan vmbus_bus_exit(); 720250199Sgrehan} 721250199Sgrehan 722250199Sgrehanstatic int 723250199Sgrehanvmbus_detach(device_t dev) 724250199Sgrehan{ 725250199Sgrehan vmbus_exit(); 726250199Sgrehan return (0); 727250199Sgrehan} 728250199Sgrehan 729250199Sgrehanstatic void 730250199Sgrehanvmbus_mod_load(void) 731250199Sgrehan{ 732250199Sgrehan if(bootverbose) 733250199Sgrehan printf("VMBUS: load\n"); 734250199Sgrehan} 735250199Sgrehan 736250199Sgrehanstatic void 737250199Sgrehanvmbus_mod_unload(void) 738250199Sgrehan{ 739250199Sgrehan if(bootverbose) 740250199Sgrehan printf("VMBUS: unload\n"); 741250199Sgrehan} 742250199Sgrehan 743250199Sgrehanstatic int 744250199Sgrehanvmbus_modevent(module_t mod, int what, void *arg) 745250199Sgrehan{ 746250199Sgrehan switch (what) { 747250199Sgrehan 748250199Sgrehan case MOD_LOAD: 749250199Sgrehan vmbus_mod_load(); 750250199Sgrehan break; 751250199Sgrehan case MOD_UNLOAD: 752250199Sgrehan vmbus_mod_unload(); 753250199Sgrehan break; 754250199Sgrehan } 755250199Sgrehan 756250199Sgrehan return (0); 757250199Sgrehan} 758250199Sgrehan 759250199Sgrehanstatic device_method_t vmbus_methods[] = { 760250199Sgrehan /** Device interface */ 761250199Sgrehan DEVMETHOD(device_probe, vmbus_probe), 762250199Sgrehan DEVMETHOD(device_attach, vmbus_attach), 763250199Sgrehan DEVMETHOD(device_detach, vmbus_detach), 764250199Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 765250199Sgrehan DEVMETHOD(device_suspend, bus_generic_suspend), 766250199Sgrehan DEVMETHOD(device_resume, bus_generic_resume), 767250199Sgrehan 768250199Sgrehan /** Bus interface */ 769250199Sgrehan DEVMETHOD(bus_add_child, bus_generic_add_child), 770250199Sgrehan DEVMETHOD(bus_print_child, bus_generic_print_child), 771250199Sgrehan DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 772250199Sgrehan DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 773320912Sdelphij DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 774250199Sgrehan 775250199Sgrehan { 0, 0 } }; 776250199Sgrehan 777250199Sgrehanstatic char driver_name[] = "vmbus"; 778250199Sgrehanstatic driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; 779250199Sgrehan 780250199Sgrehan 781250199Sgrehandevclass_t vmbus_devclass; 782250199Sgrehan 783295789SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); 784295789SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 785295789SsepheMODULE_VERSION(vmbus, 1); 786250199Sgrehan 787283280Swhu/* We want to be started after SMP is initialized */ 788283280SwhuSYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL); 789250199Sgrehan 790