hv_vmbus_drv_freebsd.c revision 295789
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: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 295789 2016-02-19 02:03:14Z 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> 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 62250199Sgrehan#include "hv_vmbus_priv.h" 63250199Sgrehan 64295789Ssephe#include <contrib/dev/acpica/include/acpi.h> 65295789Ssephe#include "acpi_if.h" 66250199Sgrehan 67250199Sgrehanstatic device_t vmbus_devp; 68250199Sgrehanstatic int vmbus_inited; 69255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */ 70250199Sgrehan 71295789Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL }; 72295789Ssephe 73250199Sgrehan/** 74250199Sgrehan * @brief Software interrupt thread routine to handle channel messages from 75250199Sgrehan * the hypervisor. 76250199Sgrehan */ 77250199Sgrehanstatic void 78283280Swhuvmbus_msg_swintr(void *arg) 79250199Sgrehan{ 80250199Sgrehan int cpu; 81250199Sgrehan void* page_addr; 82293820Sdelphij hv_vmbus_channel_msg_header *hdr; 83293820Sdelphij hv_vmbus_channel_msg_table_entry *entry; 84293820Sdelphij hv_vmbus_channel_msg_type msg_type; 85250199Sgrehan hv_vmbus_message* msg; 86250199Sgrehan hv_vmbus_message* copied; 87293820Sdelphij static bool warned = false; 88250199Sgrehan 89283280Swhu cpu = (int)(long)arg; 90283280Swhu KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: " 91283280Swhu "cpu out of range!")); 92283280Swhu 93250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 94250199Sgrehan msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 95250199Sgrehan 96250199Sgrehan for (;;) { 97293820Sdelphij if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) 98250199Sgrehan break; /* no message */ 99293820Sdelphij 100293820Sdelphij hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; 101293820Sdelphij msg_type = hdr->message_type; 102293820Sdelphij 103293820Sdelphij if (msg_type >= HV_CHANNEL_MESSAGE_COUNT && !warned) { 104293820Sdelphij warned = true; 105293820Sdelphij printf("VMBUS: unknown message type = %d\n", msg_type); 106293820Sdelphij goto handled; 107293820Sdelphij } 108293820Sdelphij 109293820Sdelphij entry = &g_channel_message_table[msg_type]; 110293820Sdelphij 111293820Sdelphij if (entry->handler_no_sleep) 112293820Sdelphij entry->messageHandler(hdr); 113293820Sdelphij else { 114293820Sdelphij 115250199Sgrehan copied = malloc(sizeof(hv_vmbus_message), 116250199Sgrehan M_DEVBUF, M_NOWAIT); 117250199Sgrehan KASSERT(copied != NULL, 118250199Sgrehan ("Error VMBUS: malloc failed to allocate" 119250199Sgrehan " hv_vmbus_message!")); 120250199Sgrehan if (copied == NULL) 121250199Sgrehan continue; 122293820Sdelphij 123250199Sgrehan memcpy(copied, msg, sizeof(hv_vmbus_message)); 124250199Sgrehan hv_queue_work_item(hv_vmbus_g_connection.work_queue, 125293820Sdelphij hv_vmbus_on_channel_message, 126293820Sdelphij copied); 127293820Sdelphij } 128293820Sdelphijhandled: 129250199Sgrehan msg->header.message_type = HV_MESSAGE_TYPE_NONE; 130250199Sgrehan 131250199Sgrehan /* 132250199Sgrehan * Make sure the write to message_type (ie set to 133250199Sgrehan * HV_MESSAGE_TYPE_NONE) happens before we read the 134250199Sgrehan * message_pending and EOMing. Otherwise, the EOMing will 135250199Sgrehan * not deliver any more messages 136250199Sgrehan * since there is no empty slot 137250199Sgrehan */ 138250199Sgrehan wmb(); 139250199Sgrehan 140256276Sdim if (msg->header.message_flags.u.message_pending) { 141250199Sgrehan /* 142250199Sgrehan * This will cause message queue rescan to possibly 143250199Sgrehan * deliver another msg from the hypervisor 144250199Sgrehan */ 145255414Sgrehan wrmsr(HV_X64_MSR_EOM, 0); 146250199Sgrehan } 147250199Sgrehan } 148250199Sgrehan} 149250199Sgrehan 150250199Sgrehan/** 151250199Sgrehan * @brief Interrupt filter routine for VMBUS. 152250199Sgrehan * 153250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol 154250199Sgrehan * message to process - an event or a channel message. 155250199Sgrehan */ 156283280Swhustatic inline int 157295789Ssephehv_vmbus_isr(struct trapframe *frame) 158250199Sgrehan{ 159250199Sgrehan int cpu; 160250199Sgrehan hv_vmbus_message* msg; 161250199Sgrehan hv_vmbus_synic_event_flags* event; 162250199Sgrehan void* page_addr; 163250199Sgrehan 164250199Sgrehan cpu = PCPU_GET(cpuid); 165250199Sgrehan 166250199Sgrehan /* 167250199Sgrehan * The Windows team has advised that we check for events 168250199Sgrehan * before checking for messages. This is the way they do it 169250199Sgrehan * in Windows when running as a guest in Hyper-V 170250199Sgrehan */ 171250199Sgrehan 172250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 173250199Sgrehan event = (hv_vmbus_synic_event_flags*) 174250199Sgrehan page_addr + HV_VMBUS_MESSAGE_SINT; 175250199Sgrehan 176283280Swhu if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 177283280Swhu (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 178283280Swhu /* Since we are a child, we only need to check bit 0 */ 179283280Swhu if (synch_test_and_clear_bit(0, &event->flags32[0])) { 180283280Swhu swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); 181283280Swhu } 182283280Swhu } else { 183283280Swhu /* 184283280Swhu * On host with Win8 or above, we can directly look at 185283280Swhu * the event page. If bit n is set, we have an interrupt 186283280Swhu * on the channel with id n. 187283280Swhu * Directly schedule the event software interrupt on 188283280Swhu * current cpu. 189283280Swhu */ 190283280Swhu swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0); 191250199Sgrehan } 192250199Sgrehan 193250199Sgrehan /* Check if there are actual msgs to be process */ 194250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 195250199Sgrehan msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 196250199Sgrehan 197295789Ssephe /* we call eventtimer process the message */ 198295789Ssephe if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { 199295789Ssephe msg->header.message_type = HV_MESSAGE_TYPE_NONE; 200295789Ssephe 201295789Ssephe /* 202295789Ssephe * Make sure the write to message_type (ie set to 203295789Ssephe * HV_MESSAGE_TYPE_NONE) happens before we read the 204295789Ssephe * message_pending and EOMing. Otherwise, the EOMing will 205295789Ssephe * not deliver any more messages 206295789Ssephe * since there is no empty slot 207295789Ssephe */ 208295789Ssephe wmb(); 209295789Ssephe 210295789Ssephe if (msg->header.message_flags.u.message_pending) { 211295789Ssephe /* 212295789Ssephe * This will cause message queue rescan to possibly 213295789Ssephe * deliver another msg from the hypervisor 214295789Ssephe */ 215295789Ssephe wrmsr(HV_X64_MSR_EOM, 0); 216295789Ssephe } 217295789Ssephe hv_et_intr(frame); 218295789Ssephe return (FILTER_HANDLED); 219295789Ssephe } 220295789Ssephe 221250199Sgrehan if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 222283280Swhu swi_sched(hv_vmbus_g_context.msg_swintr[cpu], 0); 223250199Sgrehan } 224250199Sgrehan 225295789Ssephe return (FILTER_HANDLED); 226250199Sgrehan} 227250199Sgrehan 228283280Swhuuint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; 229295789Ssepheu_long *hv_vmbus_intr_cpu[MAXCPU]; 230283280Swhu 231283280Swhuvoid 232283280Swhuhv_vector_handler(struct trapframe *trap_frame) 233283280Swhu{ 234283280Swhu int cpu; 235283280Swhu 236283280Swhu /* 237283280Swhu * Disable preemption. 238283280Swhu */ 239283280Swhu critical_enter(); 240283280Swhu 241283280Swhu /* 242283280Swhu * Do a little interrupt counting. 243283280Swhu */ 244283280Swhu cpu = PCPU_GET(cpuid); 245295789Ssephe (*hv_vmbus_intr_cpu[cpu])++; 246283280Swhu 247295789Ssephe hv_vmbus_isr(trap_frame); 248283280Swhu 249283280Swhu /* 250283280Swhu * Enable preemption. 251283280Swhu */ 252283280Swhu critical_exit(); 253283280Swhu} 254283280Swhu 255250199Sgrehanstatic int 256250199Sgrehanvmbus_read_ivar( 257250199Sgrehan device_t dev, 258250199Sgrehan device_t child, 259250199Sgrehan int index, 260250199Sgrehan uintptr_t* result) 261250199Sgrehan{ 262250199Sgrehan struct hv_device *child_dev_ctx = device_get_ivars(child); 263250199Sgrehan 264250199Sgrehan switch (index) { 265250199Sgrehan 266250199Sgrehan case HV_VMBUS_IVAR_TYPE: 267250199Sgrehan *result = (uintptr_t) &child_dev_ctx->class_id; 268250199Sgrehan return (0); 269250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 270250199Sgrehan *result = (uintptr_t) &child_dev_ctx->device_id; 271250199Sgrehan return (0); 272250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 273250199Sgrehan *result = (uintptr_t) child_dev_ctx; 274250199Sgrehan return (0); 275250199Sgrehan case HV_VMBUS_IVAR_NODE: 276250199Sgrehan *result = (uintptr_t) child_dev_ctx->device; 277250199Sgrehan return (0); 278250199Sgrehan } 279250199Sgrehan return (ENOENT); 280250199Sgrehan} 281250199Sgrehan 282250199Sgrehanstatic int 283250199Sgrehanvmbus_write_ivar( 284250199Sgrehan device_t dev, 285250199Sgrehan device_t child, 286250199Sgrehan int index, 287250199Sgrehan uintptr_t value) 288250199Sgrehan{ 289250199Sgrehan switch (index) { 290250199Sgrehan 291250199Sgrehan case HV_VMBUS_IVAR_TYPE: 292250199Sgrehan case HV_VMBUS_IVAR_INSTANCE: 293250199Sgrehan case HV_VMBUS_IVAR_DEVCTX: 294250199Sgrehan case HV_VMBUS_IVAR_NODE: 295250199Sgrehan /* read-only */ 296250199Sgrehan return (EINVAL); 297250199Sgrehan } 298250199Sgrehan return (ENOENT); 299250199Sgrehan} 300250199Sgrehan 301250199Sgrehanstruct hv_device* 302250199Sgrehanhv_vmbus_child_device_create( 303250199Sgrehan hv_guid type, 304250199Sgrehan hv_guid instance, 305250199Sgrehan hv_vmbus_channel* channel) 306250199Sgrehan{ 307250199Sgrehan hv_device* child_dev; 308250199Sgrehan 309250199Sgrehan /* 310250199Sgrehan * Allocate the new child device 311250199Sgrehan */ 312250199Sgrehan child_dev = malloc(sizeof(hv_device), M_DEVBUF, 313250199Sgrehan M_NOWAIT | M_ZERO); 314250199Sgrehan KASSERT(child_dev != NULL, 315250199Sgrehan ("Error VMBUS: malloc failed to allocate hv_device!")); 316250199Sgrehan 317250199Sgrehan if (child_dev == NULL) 318250199Sgrehan return (NULL); 319250199Sgrehan 320250199Sgrehan child_dev->channel = channel; 321250199Sgrehan memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 322250199Sgrehan memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 323250199Sgrehan 324250199Sgrehan return (child_dev); 325250199Sgrehan} 326250199Sgrehan 327250199Sgrehanstatic void 328250199Sgrehanprint_dev_guid(struct hv_device *dev) 329250199Sgrehan{ 330250199Sgrehan int i; 331250199Sgrehan unsigned char guid_name[100]; 332250199Sgrehan for (i = 0; i < 32; i += 2) 333250199Sgrehan sprintf(&guid_name[i], "%02x", dev->class_id.data[i / 2]); 334250199Sgrehan if(bootverbose) 335250199Sgrehan printf("VMBUS: Class ID: %s\n", guid_name); 336250199Sgrehan} 337250199Sgrehan 338250199Sgrehanint 339250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev) 340250199Sgrehan{ 341250199Sgrehan device_t child; 342250199Sgrehan int ret = 0; 343250199Sgrehan 344250199Sgrehan print_dev_guid(child_dev); 345250199Sgrehan 346250199Sgrehan 347250199Sgrehan child = device_add_child(vmbus_devp, NULL, -1); 348250199Sgrehan child_dev->device = child; 349250199Sgrehan device_set_ivars(child, child_dev); 350250199Sgrehan 351250199Sgrehan mtx_lock(&Giant); 352250199Sgrehan ret = device_probe_and_attach(child); 353250199Sgrehan mtx_unlock(&Giant); 354250199Sgrehan 355250199Sgrehan return (0); 356250199Sgrehan} 357250199Sgrehan 358250199Sgrehanint 359250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev) 360250199Sgrehan{ 361250199Sgrehan int ret = 0; 362250199Sgrehan /* 363250199Sgrehan * XXXKYS: Ensure that this is the opposite of 364250199Sgrehan * device_add_child() 365250199Sgrehan */ 366250199Sgrehan mtx_lock(&Giant); 367250199Sgrehan ret = device_delete_child(vmbus_devp, child_dev->device); 368250199Sgrehan mtx_unlock(&Giant); 369250199Sgrehan return(ret); 370250199Sgrehan} 371250199Sgrehan 372250199Sgrehanstatic int 373250199Sgrehanvmbus_probe(device_t dev) { 374295789Ssephe if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || 375295789Ssephe device_get_unit(dev) != 0) 376295789Ssephe return (ENXIO); 377250199Sgrehan 378250199Sgrehan device_set_desc(dev, "Vmbus Devices"); 379250199Sgrehan 380295789Ssephe return (BUS_PROBE_DEFAULT); 381250199Sgrehan} 382250199Sgrehan 383283280Swhu#ifdef HYPERV 384283280Swhuextern inthand_t IDTVEC(rsvd), IDTVEC(hv_vmbus_callback); 385283280Swhu 386250199Sgrehan/** 387283280Swhu * @brief Find a free IDT slot and setup the interrupt handler. 388283280Swhu */ 389283280Swhustatic int 390283280Swhuvmbus_vector_alloc(void) 391283280Swhu{ 392283280Swhu int vector; 393283280Swhu uintptr_t func; 394283280Swhu struct gate_descriptor *ip; 395283280Swhu 396283280Swhu /* 397283280Swhu * Search backwards form the highest IDT vector available for use 398283280Swhu * as vmbus channel callback vector. We install 'hv_vmbus_callback' 399283280Swhu * handler at that vector and use it to interrupt vcpus. 400283280Swhu */ 401283280Swhu vector = APIC_SPURIOUS_INT; 402283280Swhu while (--vector >= APIC_IPI_INTS) { 403283280Swhu ip = &idt[vector]; 404283280Swhu func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); 405283280Swhu if (func == (uintptr_t)&IDTVEC(rsvd)) { 406283280Swhu#ifdef __i386__ 407283280Swhu setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYS386IGT, 408283280Swhu SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 409283280Swhu#else 410283280Swhu setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYSIGT, 411283280Swhu SEL_KPL, 0); 412283280Swhu#endif 413283280Swhu 414283280Swhu return (vector); 415283280Swhu } 416283280Swhu } 417283280Swhu return (0); 418283280Swhu} 419283280Swhu 420283280Swhu/** 421283280Swhu * @brief Restore the IDT slot to rsvd. 422283280Swhu */ 423283280Swhustatic void 424283280Swhuvmbus_vector_free(int vector) 425283280Swhu{ 426283280Swhu uintptr_t func; 427283280Swhu struct gate_descriptor *ip; 428283280Swhu 429283280Swhu if (vector == 0) 430283280Swhu return; 431283280Swhu 432283280Swhu KASSERT(vector >= APIC_IPI_INTS && vector < APIC_SPURIOUS_INT, 433283280Swhu ("invalid vector %d", vector)); 434283280Swhu 435283280Swhu ip = &idt[vector]; 436283280Swhu func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); 437283280Swhu KASSERT(func == (uintptr_t)&IDTVEC(hv_vmbus_callback), 438283280Swhu ("invalid vector %d", vector)); 439283280Swhu 440283280Swhu setidt(vector, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0); 441283280Swhu} 442283280Swhu 443283280Swhu#else /* HYPERV */ 444283280Swhu 445283280Swhustatic int 446283280Swhuvmbus_vector_alloc(void) 447283280Swhu{ 448283280Swhu return(0); 449283280Swhu} 450283280Swhu 451283280Swhustatic void 452283280Swhuvmbus_vector_free(int vector) 453283280Swhu{ 454283280Swhu} 455283280Swhu 456283280Swhu#endif /* HYPERV */ 457283280Swhu 458283280Swhu/** 459250199Sgrehan * @brief Main vmbus driver initialization routine. 460250199Sgrehan * 461250199Sgrehan * Here, we 462250199Sgrehan * - initialize the vmbus driver context 463250199Sgrehan * - setup various driver entry points 464250199Sgrehan * - invoke the vmbus hv main init routine 465250199Sgrehan * - get the irq resource 466250199Sgrehan * - invoke the vmbus to add the vmbus root device 467250199Sgrehan * - setup the vmbus root device 468250199Sgrehan * - retrieve the channel offers 469250199Sgrehan */ 470250199Sgrehanstatic int 471250199Sgrehanvmbus_bus_init(void) 472250199Sgrehan{ 473283280Swhu int i, j, n, ret; 474295789Ssephe char buf[MAXCOMLEN + 1]; 475250199Sgrehan 476250199Sgrehan if (vmbus_inited) 477250199Sgrehan return (0); 478250199Sgrehan 479250199Sgrehan vmbus_inited = 1; 480250199Sgrehan 481250199Sgrehan ret = hv_vmbus_init(); 482250199Sgrehan 483250199Sgrehan if (ret) { 484250199Sgrehan if(bootverbose) 485252645Sgrehan printf("Error VMBUS: Hypervisor Initialization Failed!\n"); 486250199Sgrehan return (ret); 487250199Sgrehan } 488250199Sgrehan 489250199Sgrehan /* 490283280Swhu * Find a free IDT slot for vmbus callback. 491250199Sgrehan */ 492283280Swhu hv_vmbus_g_context.hv_cb_vector = vmbus_vector_alloc(); 493250199Sgrehan 494283280Swhu if (hv_vmbus_g_context.hv_cb_vector == 0) { 495283280Swhu if(bootverbose) 496283280Swhu printf("Error VMBUS: Cannot find free IDT slot for " 497283280Swhu "vmbus callback!\n"); 498283280Swhu goto cleanup; 499283280Swhu } 500250199Sgrehan 501283280Swhu if(bootverbose) 502283280Swhu printf("VMBUS: vmbus callback vector %d\n", 503283280Swhu hv_vmbus_g_context.hv_cb_vector); 504250199Sgrehan 505283280Swhu /* 506283280Swhu * Notify the hypervisor of our vector. 507283280Swhu */ 508283280Swhu setup_args.vector = hv_vmbus_g_context.hv_cb_vector; 509250199Sgrehan 510283280Swhu CPU_FOREACH(j) { 511283280Swhu hv_vmbus_swintr_event_cpu[j] = 0; 512283280Swhu hv_vmbus_g_context.hv_event_intr_event[j] = NULL; 513283280Swhu hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 514283280Swhu hv_vmbus_g_context.event_swintr[j] = NULL; 515283280Swhu hv_vmbus_g_context.msg_swintr[j] = NULL; 516250199Sgrehan 517295789Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); 518295789Ssephe intrcnt_add(buf, &hv_vmbus_intr_cpu[j]); 519295789Ssephe 520283280Swhu for (i = 0; i < 2; i++) 521283280Swhu setup_args.page_buffers[2 * j + i] = NULL; 522250199Sgrehan } 523250199Sgrehan 524250199Sgrehan /* 525283280Swhu * Per cpu setup. 526250199Sgrehan */ 527283280Swhu CPU_FOREACH(j) { 528283280Swhu /* 529283280Swhu * Setup software interrupt thread and handler for msg handling. 530283280Swhu */ 531283280Swhu ret = swi_add(&hv_vmbus_g_context.hv_msg_intr_event[j], 532283280Swhu "hv_msg", vmbus_msg_swintr, (void *)(long)j, SWI_CLOCK, 0, 533283280Swhu &hv_vmbus_g_context.msg_swintr[j]); 534283280Swhu if (ret) { 535283280Swhu if(bootverbose) 536283280Swhu printf("VMBUS: failed to setup msg swi for " 537283280Swhu "cpu %d\n", j); 538283280Swhu goto cleanup1; 539283280Swhu } 540250199Sgrehan 541283280Swhu /* 542283280Swhu * Bind the swi thread to the cpu. 543283280Swhu */ 544283280Swhu ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j], 545283280Swhu j); 546283280Swhu if (ret) { 547283280Swhu if(bootverbose) 548283280Swhu printf("VMBUS: failed to bind msg swi thread " 549283280Swhu "to cpu %d\n", j); 550283280Swhu goto cleanup1; 551283280Swhu } 552250199Sgrehan 553283280Swhu /* 554283280Swhu * Setup software interrupt thread and handler for 555283280Swhu * event handling. 556283280Swhu */ 557283280Swhu ret = swi_add(&hv_vmbus_g_context.hv_event_intr_event[j], 558283280Swhu "hv_event", hv_vmbus_on_events, (void *)(long)j, 559283280Swhu SWI_CLOCK, 0, &hv_vmbus_g_context.event_swintr[j]); 560283280Swhu if (ret) { 561283280Swhu if(bootverbose) 562283280Swhu printf("VMBUS: failed to setup event swi for " 563283280Swhu "cpu %d\n", j); 564283280Swhu goto cleanup1; 565283280Swhu } 566250199Sgrehan 567283280Swhu /* 568283280Swhu * Prepare the per cpu msg and event pages to be called on each cpu. 569283280Swhu */ 570283280Swhu for(i = 0; i < 2; i++) { 571283280Swhu setup_args.page_buffers[2 * j + i] = 572255414Sgrehan malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); 573283280Swhu if (setup_args.page_buffers[2 * j + i] == NULL) { 574283280Swhu KASSERT(setup_args.page_buffers[2 * j + i] != NULL, 575255414Sgrehan ("Error VMBUS: malloc failed!")); 576283280Swhu goto cleanup1; 577283280Swhu } 578255414Sgrehan } 579255414Sgrehan } 580250199Sgrehan 581283280Swhu if (bootverbose) 582283280Swhu printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n", 583283280Swhu smp_started); 584283280Swhu 585255414Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); 586250199Sgrehan 587255414Sgrehan /* 588250199Sgrehan * Connect to VMBus in the root partition 589250199Sgrehan */ 590250199Sgrehan ret = hv_vmbus_connect(); 591250199Sgrehan 592255414Sgrehan if (ret != 0) 593283280Swhu goto cleanup1; 594250199Sgrehan 595250199Sgrehan hv_vmbus_request_channel_offers(); 596250199Sgrehan return (ret); 597250199Sgrehan 598283280Swhu cleanup1: 599283280Swhu /* 600283280Swhu * Free pages alloc'ed 601283280Swhu */ 602283280Swhu for (n = 0; n < 2 * MAXCPU; n++) 603283280Swhu if (setup_args.page_buffers[n] != NULL) 604283280Swhu free(setup_args.page_buffers[n], M_DEVBUF); 605250199Sgrehan 606250199Sgrehan /* 607283280Swhu * remove swi and vmbus callback vector; 608250199Sgrehan */ 609283280Swhu CPU_FOREACH(j) { 610283280Swhu if (hv_vmbus_g_context.msg_swintr[j] != NULL) 611283280Swhu swi_remove(hv_vmbus_g_context.msg_swintr[j]); 612283280Swhu if (hv_vmbus_g_context.event_swintr[j] != NULL) 613283280Swhu swi_remove(hv_vmbus_g_context.event_swintr[j]); 614283280Swhu hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 615283280Swhu hv_vmbus_g_context.hv_event_intr_event[j] = NULL; 616283280Swhu } 617250199Sgrehan 618283280Swhu vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); 619250199Sgrehan 620250199Sgrehan cleanup: 621250199Sgrehan hv_vmbus_cleanup(); 622250199Sgrehan 623250199Sgrehan return (ret); 624250199Sgrehan} 625250199Sgrehan 626250199Sgrehanstatic int 627250199Sgrehanvmbus_attach(device_t dev) 628250199Sgrehan{ 629250199Sgrehan if(bootverbose) 630250199Sgrehan device_printf(dev, "VMBUS: attach dev: %p\n", dev); 631250199Sgrehan vmbus_devp = dev; 632250199Sgrehan 633250199Sgrehan /* 634250199Sgrehan * If the system has already booted and thread 635250199Sgrehan * scheduling is possible indicated by the global 636250199Sgrehan * cold set to zero, we just call the driver 637250199Sgrehan * initialization directly. 638250199Sgrehan */ 639250199Sgrehan if (!cold) 640250199Sgrehan vmbus_bus_init(); 641250199Sgrehan 642250199Sgrehan return (0); 643250199Sgrehan} 644250199Sgrehan 645250199Sgrehanstatic void 646250199Sgrehanvmbus_init(void) 647250199Sgrehan{ 648256758Sgibbs if (vm_guest != VM_GUEST_HV) 649256758Sgibbs return; 650256758Sgibbs 651250199Sgrehan /* 652250199Sgrehan * If the system has already booted and thread 653256758Sgibbs * scheduling is possible, as indicated by the 654256758Sgibbs * global cold set to zero, we just call the driver 655250199Sgrehan * initialization directly. 656250199Sgrehan */ 657250199Sgrehan if (!cold) 658250199Sgrehan vmbus_bus_init(); 659250199Sgrehan} 660250199Sgrehan 661250199Sgrehanstatic void 662250199Sgrehanvmbus_bus_exit(void) 663250199Sgrehan{ 664255414Sgrehan int i; 665255414Sgrehan 666250199Sgrehan hv_vmbus_release_unattached_channels(); 667250199Sgrehan hv_vmbus_disconnect(); 668250199Sgrehan 669250199Sgrehan smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 670250199Sgrehan 671283280Swhu for(i = 0; i < 2 * MAXCPU; i++) { 672255414Sgrehan if (setup_args.page_buffers[i] != 0) 673255414Sgrehan free(setup_args.page_buffers[i], M_DEVBUF); 674255414Sgrehan } 675255414Sgrehan 676250199Sgrehan hv_vmbus_cleanup(); 677250199Sgrehan 678283280Swhu /* remove swi */ 679283280Swhu CPU_FOREACH(i) { 680283280Swhu if (hv_vmbus_g_context.msg_swintr[i] != NULL) 681283280Swhu swi_remove(hv_vmbus_g_context.msg_swintr[i]); 682283280Swhu if (hv_vmbus_g_context.event_swintr[i] != NULL) 683283280Swhu swi_remove(hv_vmbus_g_context.event_swintr[i]); 684283280Swhu hv_vmbus_g_context.hv_msg_intr_event[i] = NULL; 685283280Swhu hv_vmbus_g_context.hv_event_intr_event[i] = NULL; 686283280Swhu } 687250199Sgrehan 688283280Swhu vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); 689250199Sgrehan 690250199Sgrehan return; 691250199Sgrehan} 692250199Sgrehan 693250199Sgrehanstatic void 694250199Sgrehanvmbus_exit(void) 695250199Sgrehan{ 696250199Sgrehan vmbus_bus_exit(); 697250199Sgrehan} 698250199Sgrehan 699250199Sgrehanstatic int 700250199Sgrehanvmbus_detach(device_t dev) 701250199Sgrehan{ 702250199Sgrehan vmbus_exit(); 703250199Sgrehan return (0); 704250199Sgrehan} 705250199Sgrehan 706250199Sgrehanstatic void 707250199Sgrehanvmbus_mod_load(void) 708250199Sgrehan{ 709250199Sgrehan if(bootverbose) 710250199Sgrehan printf("VMBUS: load\n"); 711250199Sgrehan} 712250199Sgrehan 713250199Sgrehanstatic void 714250199Sgrehanvmbus_mod_unload(void) 715250199Sgrehan{ 716250199Sgrehan if(bootverbose) 717250199Sgrehan printf("VMBUS: unload\n"); 718250199Sgrehan} 719250199Sgrehan 720250199Sgrehanstatic int 721250199Sgrehanvmbus_modevent(module_t mod, int what, void *arg) 722250199Sgrehan{ 723250199Sgrehan switch (what) { 724250199Sgrehan 725250199Sgrehan case MOD_LOAD: 726250199Sgrehan vmbus_mod_load(); 727250199Sgrehan break; 728250199Sgrehan case MOD_UNLOAD: 729250199Sgrehan vmbus_mod_unload(); 730250199Sgrehan break; 731250199Sgrehan } 732250199Sgrehan 733250199Sgrehan return (0); 734250199Sgrehan} 735250199Sgrehan 736250199Sgrehanstatic device_method_t vmbus_methods[] = { 737250199Sgrehan /** Device interface */ 738250199Sgrehan DEVMETHOD(device_probe, vmbus_probe), 739250199Sgrehan DEVMETHOD(device_attach, vmbus_attach), 740250199Sgrehan DEVMETHOD(device_detach, vmbus_detach), 741250199Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 742250199Sgrehan DEVMETHOD(device_suspend, bus_generic_suspend), 743250199Sgrehan DEVMETHOD(device_resume, bus_generic_resume), 744250199Sgrehan 745250199Sgrehan /** Bus interface */ 746250199Sgrehan DEVMETHOD(bus_add_child, bus_generic_add_child), 747250199Sgrehan DEVMETHOD(bus_print_child, bus_generic_print_child), 748250199Sgrehan DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 749250199Sgrehan DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 750250199Sgrehan 751250199Sgrehan { 0, 0 } }; 752250199Sgrehan 753250199Sgrehanstatic char driver_name[] = "vmbus"; 754250199Sgrehanstatic driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; 755250199Sgrehan 756250199Sgrehan 757250199Sgrehandevclass_t vmbus_devclass; 758250199Sgrehan 759295789SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); 760295789SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 761295789SsepheMODULE_VERSION(vmbus, 1); 762250199Sgrehan 763283280Swhu/* We want to be started after SMP is initialized */ 764283280SwhuSYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL); 765250199Sgrehan 766