hv_vmbus_drv_freebsd.c revision 253411
1168404Spjd/*- 2168404Spjd * Copyright (c) 2009-2012 Microsoft Corp. 3168404Spjd * Copyright (c) 2012 NetApp Inc. 4168404Spjd * Copyright (c) 2012 Citrix Inc. 5168404Spjd * All rights reserved. 6168404Spjd * 7168404Spjd * Redistribution and use in source and binary forms, with or without 8168404Spjd * modification, are permitted provided that the following conditions 9168404Spjd * are met: 10168404Spjd * 1. Redistributions of source code must retain the above copyright 11168404Spjd * notice unmodified, this list of conditions, and the following 12168404Spjd * disclaimer. 13168404Spjd * 2. Redistributions in binary form must reproduce the above copyright 14168404Spjd * notice, this list of conditions and the following disclaimer in the 15168404Spjd * documentation and/or other materials provided with the distribution. 16168404Spjd * 17168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18168404Spjd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19168404Spjd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20168404Spjd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21168404Spjd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22219089Spjd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23260183Sdelphij * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24246586Sdelphij * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25255750Sdelphij * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26168404Spjd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27168404Spjd */ 28219089Spjd 29219089Spjd/* 30168404Spjd * VM Bus Driver Implementation 31168404Spjd */ 32185029Spjd 33168404Spjd#include <sys/param.h> 34168404Spjd#include <sys/bus.h> 35185029Spjd#include <sys/kernel.h> 36168404Spjd#include <sys/lock.h> 37168404Spjd#include <sys/malloc.h> 38185029Spjd#include <sys/module.h> 39168404Spjd#include <sys/sysctl.h> 40168404Spjd#include <sys/syslog.h> 41168404Spjd#include <sys/systm.h> 42168404Spjd#include <sys/rtprio.h> 43168404Spjd#include <sys/interrupt.h> 44168404Spjd#include <sys/sx.h> 45168404Spjd#include <sys/taskqueue.h> 46168404Spjd#include <sys/mutex.h> 47168404Spjd#include <sys/smp.h> 48185029Spjd 49168404Spjd#include <machine/resource.h> 50209962Smm#include <sys/rman.h> 51209962Smm 52209962Smm#include <machine/stdarg.h> 53209962Smm#include <machine/intr_machdep.h> 54209962Smm#include <sys/pcpu.h> 55209962Smm 56209962Smm#include "hv_vmbus_priv.h" 57209962Smm 58185029Spjd 59185029Spjd#define VMBUS_IRQ 0x5 60168404Spjd 61185029Spjdstatic struct intr_event *hv_msg_intr_event; 62168404Spjdstatic struct intr_event *hv_event_intr_event; 63168404Spjdstatic void *msg_swintr; 64185029Spjdstatic void *event_swintr; 65185029Spjdstatic device_t vmbus_devp; 66168404Spjdstatic void *vmbus_cookiep; 67185029Spjdstatic int vmbus_rid; 68185029Spjdstruct resource *intr_res; 69185029Spjdstatic int vmbus_irq = VMBUS_IRQ; 70185029Spjdstatic int vmbus_inited; 71185029Spjd 72185029Spjd/** 73255750Sdelphij * @brief Software interrupt thread routine to handle channel messages from 74185029Spjd * the hypervisor. 75185029Spjd */ 76168404Spjdstatic void 77219089Spjdvmbus_msg_swintr(void *dummy) 78219089Spjd{ 79219089Spjd int cpu; 80219089Spjd void* page_addr; 81219089Spjd hv_vmbus_message* msg; 82219089Spjd hv_vmbus_message* copied; 83219089Spjd 84219089Spjd cpu = PCPU_GET(cpuid); 85219089Spjd page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 86219089Spjd msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 87185029Spjd 88185029Spjd for (;;) { 89185029Spjd if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) { 90185029Spjd break; /* no message */ 91185029Spjd } else { 92185029Spjd copied = malloc(sizeof(hv_vmbus_message), 93185029Spjd M_DEVBUF, M_NOWAIT); 94185029Spjd KASSERT(copied != NULL, 95185029Spjd ("Error VMBUS: malloc failed to allocate" 96185029Spjd " hv_vmbus_message!")); 97185029Spjd if (copied == NULL) 98185029Spjd continue; 99185029Spjd memcpy(copied, msg, sizeof(hv_vmbus_message)); 100185029Spjd hv_queue_work_item(hv_vmbus_g_connection.work_queue, 101219089Spjd hv_vmbus_on_channel_message, copied); 102246586Sdelphij } 103185029Spjd 104185029Spjd msg->header.message_type = HV_MESSAGE_TYPE_NONE; 105168404Spjd 106185029Spjd /* 107185029Spjd * Make sure the write to message_type (ie set to 108185029Spjd * HV_MESSAGE_TYPE_NONE) happens before we read the 109185029Spjd * message_pending and EOMing. Otherwise, the EOMing will 110185029Spjd * not deliver any more messages 111168404Spjd * since there is no empty slot 112224174Smm */ 113224174Smm wmb(); 114224174Smm 115224174Smm if (msg->header.message_flags.message_pending) { 116243560Smm /* 117224174Smm * This will cause message queue rescan to possibly 118224174Smm * deliver another msg from the hypervisor 119224174Smm */ 120185029Spjd hv_vmbus_write_msr(HV_X64_MSR_EOM, 0); 121185029Spjd } 122185029Spjd } 123185029Spjd} 124185029Spjd 125185029Spjd/** 126201143Sdelphij * @brief Interrupt filter routine for VMBUS. 127185029Spjd * 128185029Spjd * The purpose of this routine is to determine the type of VMBUS protocol 129168404Spjd * message to process - an event or a channel message. 130185029Spjd * As this is an interrupt filter routine, the function runs in a very 131185029Spjd * restricted envinronment. From the manpage for bus_setup_intr(9) 132185029Spjd * 133185029Spjd * In this restricted environment, care must be taken to account for all 134185029Spjd * races. A careful analysis of races should be done as well. It is gener- 135185029Spjd * ally cheaper to take an extra interrupt, for example, than to protect 136168404Spjd * variables with spinlocks. Read, modify, write cycles of hardware regis- 137185029Spjd * ters need to be carefully analyzed if other threads are accessing the 138185029Spjd * same registers. 139185029Spjd */ 140185029Spjdstatic int 141185029Spjdhv_vmbus_isr(void *unused) 142185029Spjd{ 143168404Spjd int cpu; 144185029Spjd hv_vmbus_message* msg; 145185029Spjd hv_vmbus_synic_event_flags* event; 146185029Spjd void* page_addr; 147185029Spjd 148185029Spjd cpu = PCPU_GET(cpuid); 149185029Spjd /* (Temporary limit) */ 150185029Spjd KASSERT(cpu == 0, ("hv_vmbus_isr: Interrupt on CPU other than zero")); 151185029Spjd 152185029Spjd /* 153185029Spjd * The Windows team has advised that we check for events 154185029Spjd * before checking for messages. This is the way they do it 155185029Spjd * in Windows when running as a guest in Hyper-V 156185029Spjd */ 157168404Spjd 158185029Spjd page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 159185029Spjd event = (hv_vmbus_synic_event_flags*) 160185029Spjd page_addr + HV_VMBUS_MESSAGE_SINT; 161185029Spjd 162209962Smm /* Since we are a child, we only need to check bit 0 */ 163219089Spjd if (synch_test_and_clear_bit(0, &event->flags32[0])) { 164185029Spjd swi_sched(event_swintr, 0); 165185029Spjd } 166185029Spjd 167168404Spjd /* Check if there are actual msgs to be process */ 168185029Spjd page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 169185029Spjd msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 170185029Spjd 171185029Spjd if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 172185029Spjd swi_sched(msg_swintr, 0); 173168404Spjd } 174219089Spjd 175219089Spjd return FILTER_HANDLED; 176219089Spjd} 177219089Spjd 178219089Spjdstatic int 179219089Spjdvmbus_read_ivar( 180185029Spjd device_t dev, 181185029Spjd device_t child, 182185029Spjd int index, 183185029Spjd uintptr_t* result) 184185029Spjd{ 185185029Spjd struct hv_device *child_dev_ctx = device_get_ivars(child); 186168404Spjd 187185029Spjd switch (index) { 188185029Spjd 189185029Spjd case HV_VMBUS_IVAR_TYPE: 190185029Spjd *result = (uintptr_t) &child_dev_ctx->class_id; 191185029Spjd return (0); 192185029Spjd case HV_VMBUS_IVAR_INSTANCE: 193168404Spjd *result = (uintptr_t) &child_dev_ctx->device_id; 194219089Spjd return (0); 195219089Spjd case HV_VMBUS_IVAR_DEVCTX: 196219089Spjd *result = (uintptr_t) child_dev_ctx; 197219089Spjd return (0); 198219089Spjd case HV_VMBUS_IVAR_NODE: 199219089Spjd *result = (uintptr_t) child_dev_ctx->device; 200219089Spjd return (0); 201264145Smav } 202264145Smav return (ENOENT); 203264145Smav} 204264145Smav 205264145Smavstatic int 206264145Smavvmbus_write_ivar( 207264145Smav device_t dev, 208264145Smav device_t child, 209185029Spjd int index, 210219089Spjd uintptr_t value) 211185029Spjd{ 212219089Spjd switch (index) { 213219089Spjd 214219089Spjd case HV_VMBUS_IVAR_TYPE: 215219089Spjd case HV_VMBUS_IVAR_INSTANCE: 216219089Spjd case HV_VMBUS_IVAR_DEVCTX: 217185029Spjd case HV_VMBUS_IVAR_NODE: 218185029Spjd /* read-only */ 219219089Spjd return (EINVAL); 220219089Spjd } 221219089Spjd return (ENOENT); 222219089Spjd} 223219089Spjd 224185029Spjdstruct hv_device* 225185029Spjdhv_vmbus_child_device_create( 226246586Sdelphij hv_guid type, 227246586Sdelphij hv_guid instance, 228219089Spjd hv_vmbus_channel* channel) 229185029Spjd{ 230185029Spjd hv_device* child_dev; 231224174Smm 232224174Smm /* 233243560Smm * Allocate the new child device 234243560Smm */ 235219089Spjd child_dev = malloc(sizeof(hv_device), M_DEVBUF, 236219089Spjd M_NOWAIT | M_ZERO); 237201143Sdelphij KASSERT(child_dev != NULL, 238185029Spjd ("Error VMBUS: malloc failed to allocate hv_device!")); 239219089Spjd 240219089Spjd if (child_dev == NULL) 241185029Spjd return (NULL); 242219089Spjd 243185029Spjd child_dev->channel = channel; 244185029Spjd memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 245185029Spjd memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 246219089Spjd 247185029Spjd return (child_dev); 248185029Spjd} 249185029Spjd 250219089Spjdstatic void 251219089Spjdprint_dev_guid(struct hv_device *dev) 252219089Spjd{ 253264145Smav int i; 254264145Smav unsigned char guid_name[100]; 255264145Smav for (i = 0; i < 32; i += 2) 256264145Smav sprintf(&guid_name[i], "%02x", dev->class_id.data[i / 2]); 257168404Spjd if(bootverbose) 258185029Spjd printf("VMBUS: Class ID: %s\n", guid_name); 259219089Spjd} 260185029Spjd 261219089Spjdint 262185029Spjdhv_vmbus_child_device_register(struct hv_device *child_dev) 263185029Spjd{ 264219089Spjd device_t child; 265185029Spjd int ret = 0; 266185029Spjd 267219089Spjd print_dev_guid(child_dev); 268185029Spjd 269185029Spjd 270219089Spjd child = device_add_child(vmbus_devp, NULL, -1); 271185029Spjd child_dev->device = child; 272185029Spjd device_set_ivars(child, child_dev); 273219089Spjd 274185029Spjd mtx_lock(&Giant); 275219089Spjd ret = device_probe_and_attach(child); 276185029Spjd mtx_unlock(&Giant); 277185029Spjd 278219089Spjd return (0); 279185029Spjd} 280185029Spjd 281219089Spjdint 282185029Spjdhv_vmbus_child_device_unregister(struct hv_device *child_dev) 283185029Spjd{ 284168404Spjd int ret = 0; 285185029Spjd /* 286219089Spjd * XXXKYS: Ensure that this is the opposite of 287185029Spjd * device_add_child() 288228103Smm */ 289219089Spjd mtx_lock(&Giant); 290185029Spjd ret = device_delete_child(vmbus_devp, child_dev->device); 291185029Spjd mtx_unlock(&Giant); 292168404Spjd return(ret); 293185029Spjd} 294219089Spjd 295185029Spjdstatic void vmbus_identify(driver_t *driver, device_t parent) { 296219089Spjd BUS_ADD_CHILD(parent, 0, "vmbus", 0); 297219089Spjd if (device_find_child(parent, "vmbus", 0) == NULL) { 298219089Spjd BUS_ADD_CHILD(parent, 0, "vmbus", 0); 299185029Spjd } 300185029Spjd} 301219089Spjd 302185029Spjdstatic int 303185029Spjdvmbus_probe(device_t dev) { 304185029Spjd if(bootverbose) 305219089Spjd device_printf(dev, "VMBUS: probe\n"); 306219089Spjd 307219089Spjd if (!hv_vmbus_query_hypervisor_presence()) 308185029Spjd return (ENXIO); 309185029Spjd 310185029Spjd device_set_desc(dev, "Vmbus Devices"); 311219089Spjd 312185029Spjd return (0); 313185029Spjd} 314185029Spjd 315185029Spjd/** 316219089Spjd * @brief Main vmbus driver initialization routine. 317185029Spjd * 318228103Smm * Here, we 319228103Smm * - initialize the vmbus driver context 320219089Spjd * - setup various driver entry points 321219089Spjd * - invoke the vmbus hv main init routine 322219089Spjd * - get the irq resource 323219089Spjd * - invoke the vmbus to add the vmbus root device 324219089Spjd * - setup the vmbus root device 325219089Spjd * - retrieve the channel offers 326219089Spjd */ 327260183Sdelphijstatic int 328260183Sdelphijvmbus_bus_init(void) 329219089Spjd{ 330219089Spjd struct ioapic_intsrc { 331219089Spjd struct intsrc io_intsrc; 332219089Spjd u_int io_irq; 333219089Spjd u_int io_intpin:8; 334219089Spjd u_int io_vector:8; 335185029Spjd u_int io_cpu:8; 336185029Spjd u_int io_activehi:1; 337219089Spjd u_int io_edgetrigger:1; 338185029Spjd u_int io_masked:1; 339219089Spjd int io_bus:4; 340185029Spjd uint32_t io_lowreg; 341219089Spjd }; 342219089Spjd 343219089Spjd int ret; 344185029Spjd unsigned int vector = 0; 345185029Spjd struct intsrc *isrc; 346223623Smm struct ioapic_intsrc *intpin; 347223623Smm 348223623Smm if (vmbus_inited) 349219089Spjd return (0); 350219089Spjd 351185029Spjd vmbus_inited = 1; 352219089Spjd 353219089Spjd ret = hv_vmbus_init(); 354219089Spjd 355219089Spjd if (ret) { 356219089Spjd if(bootverbose) 357219089Spjd printf("Error VMBUS: Hypervisor Initialization Failed!\n"); 358219089Spjd return (ret); 359219089Spjd } 360219089Spjd 361219089Spjd ret = swi_add(&hv_msg_intr_event, "hv_msg", vmbus_msg_swintr, 362185029Spjd NULL, SWI_CLOCK, 0, &msg_swintr); 363185029Spjd 364219089Spjd if (ret) 365219089Spjd goto cleanup; 366228103Smm 367228103Smm /* 368247585Smm * Message SW interrupt handler checks a per-CPU page and 369247585Smm * thus the thread needs to be bound to CPU-0 - which is where 370247585Smm * all interrupts are processed. 371247585Smm */ 372185029Spjd ret = intr_event_bind(hv_msg_intr_event, 0); 373185029Spjd 374219089Spjd if (ret) 375185029Spjd goto cleanup1; 376219089Spjd 377219089Spjd ret = swi_add(&hv_event_intr_event, "hv_event", hv_vmbus_on_events, 378219089Spjd NULL, SWI_CLOCK, 0, &event_swintr); 379219089Spjd 380185029Spjd if (ret) 381219089Spjd goto cleanup1; 382185029Spjd 383219089Spjd intr_res = bus_alloc_resource(vmbus_devp, 384185029Spjd SYS_RES_IRQ, &vmbus_rid, vmbus_irq, vmbus_irq, 1, RF_ACTIVE); 385185029Spjd 386264835Sdelphij if (intr_res == NULL) { 387264835Sdelphij ret = ENOMEM; /* XXXKYS: Need a better errno */ 388264835Sdelphij goto cleanup2; 389264835Sdelphij } 390264835Sdelphij 391264835Sdelphij /* 392264835Sdelphij * Setup interrupt filter handler 393264835Sdelphij */ 394264835Sdelphij ret = bus_setup_intr(vmbus_devp, intr_res, 395264835Sdelphij INTR_TYPE_NET | INTR_MPSAFE, hv_vmbus_isr, NULL, 396264835Sdelphij NULL, &vmbus_cookiep); 397264835Sdelphij 398185029Spjd if (ret != 0) 399185029Spjd goto cleanup3; 400219089Spjd 401219089Spjd ret = bus_bind_intr(vmbus_devp, intr_res, 0); 402185029Spjd if (ret != 0) 403185029Spjd goto cleanup4; 404185029Spjd 405219089Spjd isrc = intr_lookup_source(vmbus_irq); 406260183Sdelphij if ((isrc == NULL) || (isrc->is_event == NULL)) { 407219089Spjd ret = EINVAL; 408219089Spjd goto cleanup4; 409219089Spjd } 410260183Sdelphij 411219089Spjd /* vector = isrc->is_event->ie_vector; */ 412219089Spjd intpin = (struct ioapic_intsrc *)isrc; 413219089Spjd vector = intpin->io_vector; 414219089Spjd 415219089Spjd if(bootverbose) 416219089Spjd printf("VMBUS: irq 0x%x vector 0x%x\n", vmbus_irq, vector); 417260183Sdelphij 418219089Spjd /** 419219089Spjd * Notify the hypervisor of our irq. 420219089Spjd */ 421219089Spjd 422219089Spjd smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &vector); 423219089Spjd 424219089Spjd /** 425253819Sdelphij * Connect to VMBus in the root partition 426253819Sdelphij */ 427185029Spjd ret = hv_vmbus_connect(); 428185029Spjd 429219089Spjd if (ret) 430260183Sdelphij goto cleanup4; 431185029Spjd 432168404Spjd hv_vmbus_request_channel_offers(); 433168404Spjd return (ret); 434185029Spjd 435185029Spjd cleanup4: 436168404Spjd 437185029Spjd /* 438219089Spjd * remove swi, bus and intr resource 439219089Spjd */ 440219089Spjd bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); 441219089Spjd 442219089Spjd cleanup3: 443185029Spjd 444168404Spjd bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); 445168404Spjd 446168404Spjd cleanup2: 447168404Spjd swi_remove(event_swintr); 448168404Spjd 449168404Spjd cleanup1: 450168404Spjd swi_remove(msg_swintr); 451168404Spjd 452185029Spjd cleanup: 453168404Spjd hv_vmbus_cleanup(); 454168404Spjd 455168404Spjd return (ret); 456168404Spjd} 457168404Spjd 458168404Spjdstatic int 459168404Spjdvmbus_attach(device_t dev) 460168404Spjd{ 461168404Spjd if(bootverbose) 462168404Spjd device_printf(dev, "VMBUS: attach dev: %p\n", dev); 463168404Spjd vmbus_devp = dev; 464168404Spjd 465168404Spjd /* 466168404Spjd * If the system has already booted and thread 467168404Spjd * scheduling is possible indicated by the global 468168404Spjd * cold set to zero, we just call the driver 469168404Spjd * initialization directly. 470168404Spjd */ 471168404Spjd if (!cold) 472168404Spjd vmbus_bus_init(); 473168404Spjd 474168404Spjd return (0); 475168404Spjd} 476168404Spjd 477168404Spjdstatic void 478168404Spjdvmbus_init(void) 479168404Spjd{ 480168404Spjd /* 481168404Spjd * If the system has already booted and thread 482168404Spjd * scheduling is possible indicated by the global 483168404Spjd * cold set to zero, we just call the driver 484168404Spjd * initialization directly. 485168404Spjd */ 486168404Spjd if (!cold) 487168404Spjd vmbus_bus_init(); 488168404Spjd} 489168404Spjd 490168404Spjdstatic void 491168404Spjdvmbus_bus_exit(void) 492209962Smm{ 493209962Smm hv_vmbus_release_unattached_channels(); 494209962Smm hv_vmbus_disconnect(); 495209962Smm 496209962Smm smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 497209962Smm 498209962Smm hv_vmbus_cleanup(); 499209962Smm 500209962Smm /* remove swi, bus and intr resource */ 501209962Smm bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); 502209962Smm 503209962Smm bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); 504209962Smm 505209962Smm swi_remove(msg_swintr); 506209962Smm swi_remove(event_swintr); 507209962Smm 508209962Smm return; 509209962Smm} 510209962Smm 511209962Smmstatic void 512228103Smmvmbus_exit(void) 513228103Smm{ 514228103Smm vmbus_bus_exit(); 515228103Smm} 516228103Smm 517228103Smmstatic int 518228103Smmvmbus_detach(device_t dev) 519228103Smm{ 520228103Smm vmbus_exit(); 521228103Smm return (0); 522228103Smm} 523228103Smm 524185029Spjdstatic void 525185029Spjdvmbus_mod_load(void) 526168404Spjd{ 527185029Spjd if(bootverbose) 528185029Spjd printf("VMBUS: load\n"); 529168404Spjd} 530185029Spjd 531168404Spjdstatic void 532168404Spjdvmbus_mod_unload(void) 533185029Spjd{ 534185029Spjd if(bootverbose) 535168404Spjd printf("VMBUS: unload\n"); 536185029Spjd} 537168404Spjd 538168404Spjdstatic int 539219089Spjdvmbus_modevent(module_t mod, int what, void *arg) 540219089Spjd{ 541219089Spjd switch (what) { 542219089Spjd 543219089Spjd case MOD_LOAD: 544219089Spjd vmbus_mod_load(); 545168404Spjd break; 546185029Spjd case MOD_UNLOAD: 547168404Spjd vmbus_mod_unload(); 548185029Spjd break; 549185029Spjd } 550168404Spjd 551185029Spjd return (0); 552168404Spjd} 553168404Spjd 554185029Spjdstatic device_method_t vmbus_methods[] = { 555185029Spjd /** Device interface */ 556168404Spjd DEVMETHOD(device_identify, vmbus_identify), 557185029Spjd DEVMETHOD(device_probe, vmbus_probe), 558168404Spjd DEVMETHOD(device_attach, vmbus_attach), 559168404Spjd DEVMETHOD(device_detach, vmbus_detach), 560168404Spjd DEVMETHOD(device_shutdown, bus_generic_shutdown), 561185029Spjd DEVMETHOD(device_suspend, bus_generic_suspend), 562168404Spjd DEVMETHOD(device_resume, bus_generic_resume), 563185029Spjd 564185029Spjd /** Bus interface */ 565168404Spjd DEVMETHOD(bus_add_child, bus_generic_add_child), 566185029Spjd DEVMETHOD(bus_print_child, bus_generic_print_child), 567185029Spjd DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 568168404Spjd DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 569168404Spjd 570168404Spjd { 0, 0 } }; 571185029Spjd 572168404Spjdstatic char driver_name[] = "vmbus"; 573185029Spjdstatic driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; 574185029Spjd 575168404Spjd 576185029Spjddevclass_t vmbus_devclass; 577168404Spjd 578168404SpjdDRIVER_MODULE(vmbus, nexus, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); 579185029SpjdMODULE_VERSION(vmbus,1); 580185029Spjd 581185029Spjd/* TODO: We want to be earlier than SI_SUB_VFS */ 582185029SpjdSYSINIT(vmb_init, SI_SUB_VFS, SI_ORDER_MIDDLE, vmbus_init, NULL); 583185029Spjd 584168404Spjd