hv_vmbus_drv_freebsd.c revision 265999
1130561Sobrien/*- 2130561Sobrien * Copyright (c) 2009-2012 Microsoft Corp. 3218822Sdim * Copyright (c) 2012 NetApp Inc. 433965Sjdp * Copyright (c) 2012 Citrix Inc. 5218822Sdim * All rights reserved. 6130561Sobrien * 7130561Sobrien * Redistribution and use in source and binary forms, with or without 8218822Sdim * modification, are permitted provided that the following conditions 9218822Sdim * are met: 10218822Sdim * 1. Redistributions of source code must retain the above copyright 1133965Sjdp * notice unmodified, this list of conditions, and the following 12218822Sdim * disclaimer. 13218822Sdim * 2. Redistributions in binary form must reproduce the above copyright 14218822Sdim * notice, this list of conditions and the following disclaimer in the 15218822Sdim * documentation and/or other materials provided with the distribution. 16218822Sdim * 17218822Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18218822Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19218822Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20218822Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21218822Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22218822Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23218822Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24218822Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25218822Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26218822Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27218822Sdim */ 28218822Sdim 29218822Sdim/* 30218822Sdim * VM Bus Driver Implementation 31218822Sdim */ 32218822Sdim#include <sys/cdefs.h> 33218822Sdim__FBSDID("$FreeBSD: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 265999 2014-05-14 01:35:43Z ian $"); 34218822Sdim 35218822Sdim#include <sys/param.h> 36218822Sdim#include <sys/bus.h> 37218822Sdim#include <sys/kernel.h> 38218822Sdim#include <sys/lock.h> 39218822Sdim#include <sys/malloc.h> 40218822Sdim#include <sys/module.h> 41218822Sdim#include <sys/sysctl.h> 42218822Sdim#include <sys/syslog.h> 43218822Sdim#include <sys/systm.h> 44218822Sdim#include <sys/rtprio.h> 45218822Sdim#include <sys/interrupt.h> 46218822Sdim#include <sys/sx.h> 47218822Sdim#include <sys/taskqueue.h> 48218822Sdim#include <sys/mutex.h> 49218822Sdim#include <sys/smp.h> 50218822Sdim 51218822Sdim#include <machine/resource.h> 52218822Sdim#include <sys/rman.h> 53218822Sdim 54218822Sdim#include <machine/stdarg.h> 55218822Sdim#include <machine/intr_machdep.h> 56218822Sdim#include <sys/pcpu.h> 57218822Sdim 58218822Sdim#include "hv_vmbus_priv.h" 59218822Sdim 60218822Sdim 61218822Sdim#define VMBUS_IRQ 0x5 62218822Sdim 63218822Sdimstatic struct intr_event *hv_msg_intr_event; 64218822Sdimstatic struct intr_event *hv_event_intr_event; 65218822Sdimstatic void *msg_swintr; 66218822Sdimstatic void *event_swintr; 67218822Sdimstatic device_t vmbus_devp; 68218822Sdimstatic void *vmbus_cookiep; 69218822Sdimstatic int vmbus_rid; 70218822Sdimstruct resource *intr_res; 71218822Sdimstatic int vmbus_irq = VMBUS_IRQ; 72218822Sdimstatic int vmbus_inited; 73218822Sdimstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */ 74218822Sdim 75218822Sdim/** 76218822Sdim * @brief Software interrupt thread routine to handle channel messages from 77218822Sdim * the hypervisor. 78218822Sdim */ 79218822Sdimstatic void 80218822Sdimvmbus_msg_swintr(void *dummy) 81218822Sdim{ 82218822Sdim int cpu; 83218822Sdim void* page_addr; 84218822Sdim hv_vmbus_message* msg; 85218822Sdim hv_vmbus_message* copied; 86218822Sdim 87218822Sdim cpu = PCPU_GET(cpuid); 88218822Sdim page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 89218822Sdim msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 90218822Sdim 91218822Sdim for (;;) { 92218822Sdim if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) { 93218822Sdim break; /* no message */ 94218822Sdim } else { 95218822Sdim copied = malloc(sizeof(hv_vmbus_message), 96218822Sdim M_DEVBUF, M_NOWAIT); 97218822Sdim KASSERT(copied != NULL, 98218822Sdim ("Error VMBUS: malloc failed to allocate" 99218822Sdim " hv_vmbus_message!")); 100218822Sdim if (copied == NULL) 101218822Sdim continue; 102218822Sdim memcpy(copied, msg, sizeof(hv_vmbus_message)); 103218822Sdim hv_queue_work_item(hv_vmbus_g_connection.work_queue, 104218822Sdim hv_vmbus_on_channel_message, copied); 105218822Sdim } 106218822Sdim 107218822Sdim msg->header.message_type = HV_MESSAGE_TYPE_NONE; 108218822Sdim 109218822Sdim /* 110218822Sdim * Make sure the write to message_type (ie set to 111218822Sdim * HV_MESSAGE_TYPE_NONE) happens before we read the 112218822Sdim * message_pending and EOMing. Otherwise, the EOMing will 113218822Sdim * not deliver any more messages 114218822Sdim * since there is no empty slot 115218822Sdim */ 116218822Sdim wmb(); 117218822Sdim 118218822Sdim if (msg->header.message_flags.u.message_pending) { 119218822Sdim /* 120218822Sdim * This will cause message queue rescan to possibly 121218822Sdim * deliver another msg from the hypervisor 122218822Sdim */ 123218822Sdim wrmsr(HV_X64_MSR_EOM, 0); 124218822Sdim } 125218822Sdim } 126218822Sdim} 127218822Sdim 128218822Sdim/** 129218822Sdim * @brief Interrupt filter routine for VMBUS. 130218822Sdim * 131218822Sdim * The purpose of this routine is to determine the type of VMBUS protocol 132218822Sdim * message to process - an event or a channel message. 133218822Sdim * As this is an interrupt filter routine, the function runs in a very 134218822Sdim * restricted envinronment. From the manpage for bus_setup_intr(9) 135218822Sdim * 136218822Sdim * In this restricted environment, care must be taken to account for all 137218822Sdim * races. A careful analysis of races should be done as well. It is gener- 138218822Sdim * ally cheaper to take an extra interrupt, for example, than to protect 139218822Sdim * variables with spinlocks. Read, modify, write cycles of hardware regis- 140218822Sdim * ters need to be carefully analyzed if other threads are accessing the 141218822Sdim * same registers. 142218822Sdim */ 143218822Sdimstatic int 144218822Sdimhv_vmbus_isr(void *unused) 145218822Sdim{ 146218822Sdim int cpu; 147218822Sdim hv_vmbus_message* msg; 148218822Sdim hv_vmbus_synic_event_flags* event; 149218822Sdim void* page_addr; 150218822Sdim 151218822Sdim cpu = PCPU_GET(cpuid); 152218822Sdim /* (Temporary limit) */ 153218822Sdim KASSERT(cpu == 0, ("hv_vmbus_isr: Interrupt on CPU other than zero")); 154218822Sdim 155218822Sdim /* 156218822Sdim * The Windows team has advised that we check for events 157218822Sdim * before checking for messages. This is the way they do it 158218822Sdim * in Windows when running as a guest in Hyper-V 159218822Sdim */ 160218822Sdim 161218822Sdim page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 162218822Sdim event = (hv_vmbus_synic_event_flags*) 163218822Sdim page_addr + HV_VMBUS_MESSAGE_SINT; 164218822Sdim 165218822Sdim /* Since we are a child, we only need to check bit 0 */ 166218822Sdim if (synch_test_and_clear_bit(0, &event->flags32[0])) { 167218822Sdim swi_sched(event_swintr, 0); 168218822Sdim } 169218822Sdim 170218822Sdim /* Check if there are actual msgs to be process */ 171218822Sdim page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; 172218822Sdim msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; 173218822Sdim 174218822Sdim if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { 175218822Sdim swi_sched(msg_swintr, 0); 176218822Sdim } 177218822Sdim 178218822Sdim return FILTER_HANDLED; 179218822Sdim} 180218822Sdim 181218822Sdimstatic int 182218822Sdimvmbus_read_ivar( 183218822Sdim device_t dev, 184218822Sdim device_t child, 185218822Sdim int index, 186218822Sdim uintptr_t* result) 187218822Sdim{ 188218822Sdim struct hv_device *child_dev_ctx = device_get_ivars(child); 189218822Sdim 190218822Sdim switch (index) { 191218822Sdim 192218822Sdim case HV_VMBUS_IVAR_TYPE: 193218822Sdim *result = (uintptr_t) &child_dev_ctx->class_id; 194218822Sdim return (0); 195218822Sdim case HV_VMBUS_IVAR_INSTANCE: 196218822Sdim *result = (uintptr_t) &child_dev_ctx->device_id; 197218822Sdim return (0); 198218822Sdim case HV_VMBUS_IVAR_DEVCTX: 199218822Sdim *result = (uintptr_t) child_dev_ctx; 200218822Sdim return (0); 201218822Sdim case HV_VMBUS_IVAR_NODE: 202218822Sdim *result = (uintptr_t) child_dev_ctx->device; 203218822Sdim return (0); 204218822Sdim } 205218822Sdim return (ENOENT); 206218822Sdim} 207218822Sdim 208218822Sdimstatic int 209218822Sdimvmbus_write_ivar( 210218822Sdim device_t dev, 211218822Sdim device_t child, 212218822Sdim int index, 213218822Sdim uintptr_t value) 214218822Sdim{ 215218822Sdim switch (index) { 216218822Sdim 217218822Sdim case HV_VMBUS_IVAR_TYPE: 218218822Sdim case HV_VMBUS_IVAR_INSTANCE: 219218822Sdim case HV_VMBUS_IVAR_DEVCTX: 220218822Sdim case HV_VMBUS_IVAR_NODE: 221218822Sdim /* read-only */ 222218822Sdim return (EINVAL); 223218822Sdim } 224218822Sdim return (ENOENT); 225218822Sdim} 226218822Sdim 227218822Sdimstruct hv_device* 228218822Sdimhv_vmbus_child_device_create( 229218822Sdim hv_guid type, 230218822Sdim hv_guid instance, 231218822Sdim hv_vmbus_channel* channel) 232218822Sdim{ 233218822Sdim hv_device* child_dev; 234218822Sdim 235218822Sdim /* 236218822Sdim * Allocate the new child device 237218822Sdim */ 238218822Sdim child_dev = malloc(sizeof(hv_device), M_DEVBUF, 239218822Sdim M_NOWAIT | M_ZERO); 240218822Sdim KASSERT(child_dev != NULL, 241218822Sdim ("Error VMBUS: malloc failed to allocate hv_device!")); 242218822Sdim 243218822Sdim if (child_dev == NULL) 244218822Sdim return (NULL); 245218822Sdim 246218822Sdim child_dev->channel = channel; 247218822Sdim memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); 248218822Sdim memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); 249218822Sdim 250218822Sdim return (child_dev); 251218822Sdim} 252218822Sdim 253218822Sdimstatic void 254130561Sobrienprint_dev_guid(struct hv_device *dev) 255218822Sdim{ 256218822Sdim int i; 257218822Sdim unsigned char guid_name[100]; 258218822Sdim for (i = 0; i < 32; i += 2) 259218822Sdim sprintf(&guid_name[i], "%02x", dev->class_id.data[i / 2]); 260218822Sdim if(bootverbose) 26133965Sjdp printf("VMBUS: Class ID: %s\n", guid_name); 262218822Sdim} 263218822Sdim 264218822Sdimint 265218822Sdimhv_vmbus_child_device_register(struct hv_device *child_dev) 266218822Sdim{ 267218822Sdim device_t child; 268218822Sdim int ret = 0; 269218822Sdim 270218822Sdim print_dev_guid(child_dev); 271218822Sdim 272218822Sdim 273218822Sdim child = device_add_child(vmbus_devp, NULL, -1); 274218822Sdim child_dev->device = child; 275218822Sdim device_set_ivars(child, child_dev); 276218822Sdim 277218822Sdim mtx_lock(&Giant); 278130561Sobrien ret = device_probe_and_attach(child); 279218822Sdim mtx_unlock(&Giant); 280218822Sdim 281130561Sobrien return (0); 282130561Sobrien} 283218822Sdim 284130561Sobrienint 285130561Sobrienhv_vmbus_child_device_unregister(struct hv_device *child_dev) 286130561Sobrien{ 287130561Sobrien int ret = 0; 288130561Sobrien /* 289130561Sobrien * XXXKYS: Ensure that this is the opposite of 290130561Sobrien * device_add_child() 291130561Sobrien */ 29233965Sjdp mtx_lock(&Giant); 29333965Sjdp ret = device_delete_child(vmbus_devp, child_dev->device); 294130561Sobrien mtx_unlock(&Giant); 295130561Sobrien return(ret); 296130561Sobrien} 297218822Sdim 298218822Sdimstatic void 299218822Sdimvmbus_identify(driver_t *driver, device_t parent) 300218822Sdim{ 301218822Sdim if (!hv_vmbus_query_hypervisor_presence()) 302218822Sdim return; 30338889Sjdp 30438889Sjdp vm_guest = VM_GUEST_HV; 30538889Sjdp 30638889Sjdp BUS_ADD_CHILD(parent, 0, "vmbus", 0); 30738889Sjdp} 30838889Sjdp 30938889Sjdpstatic int 31038889Sjdpvmbus_probe(device_t dev) { 31138889Sjdp if(bootverbose) 31238889Sjdp device_printf(dev, "VMBUS: probe\n"); 31338889Sjdp 31438889Sjdp device_set_desc(dev, "Vmbus Devices"); 31533965Sjdp 316130561Sobrien return (BUS_PROBE_NOWILDCARD); 317130561Sobrien} 318130561Sobrien 319130561Sobrien/** 320130561Sobrien * @brief Main vmbus driver initialization routine. 321130561Sobrien * 322130561Sobrien * Here, we 323130561Sobrien * - initialize the vmbus driver context 324130561Sobrien * - setup various driver entry points 325130561Sobrien * - invoke the vmbus hv main init routine 326218822Sdim * - get the irq resource 327130561Sobrien * - invoke the vmbus to add the vmbus root device 328130561Sobrien * - setup the vmbus root device 329130561Sobrien * - retrieve the channel offers 330218822Sdim */ 331130561Sobrienstatic int 332130561Sobrienvmbus_bus_init(void) 333130561Sobrien{ 334130561Sobrien struct ioapic_intsrc { 335218822Sdim struct intsrc io_intsrc; 336130561Sobrien u_int io_irq; 337130561Sobrien u_int io_intpin:8; 338218822Sdim u_int io_vector:8; 339130561Sobrien u_int io_cpu:8; 340218822Sdim u_int io_activehi:1; 341130561Sobrien u_int io_edgetrigger:1; 342130561Sobrien u_int io_masked:1; 343130561Sobrien int io_bus:4; 344130561Sobrien uint32_t io_lowreg; 345130561Sobrien }; 346130561Sobrien int i, ret; 347218822Sdim unsigned int vector = 0; 348130561Sobrien struct intsrc *isrc; 349218822Sdim struct ioapic_intsrc *intpin; 350218822Sdim 351218822Sdim if (vmbus_inited) 352130561Sobrien return (0); 353130561Sobrien 354130561Sobrien vmbus_inited = 1; 355130561Sobrien 356218822Sdim ret = hv_vmbus_init(); 357130561Sobrien 358130561Sobrien if (ret) { 359218822Sdim if(bootverbose) 360130561Sobrien printf("Error VMBUS: Hypervisor Initialization Failed!\n"); 361218822Sdim return (ret); 362218822Sdim } 363218822Sdim 364218822Sdim ret = swi_add(&hv_msg_intr_event, "hv_msg", vmbus_msg_swintr, 365218822Sdim NULL, SWI_CLOCK, 0, &msg_swintr); 366130561Sobrien 367130561Sobrien if (ret) 368218822Sdim goto cleanup; 369130561Sobrien 370218822Sdim /* 371218822Sdim * Message SW interrupt handler checks a per-CPU page and 372218822Sdim * thus the thread needs to be bound to CPU-0 - which is where 373218822Sdim * all interrupts are processed. 374218822Sdim */ 375218822Sdim ret = intr_event_bind(hv_msg_intr_event, 0); 376130561Sobrien 377130561Sobrien if (ret) 378218822Sdim goto cleanup1; 379130561Sobrien 380130561Sobrien ret = swi_add(&hv_event_intr_event, "hv_event", hv_vmbus_on_events, 381130561Sobrien NULL, SWI_CLOCK, 0, &event_swintr); 382130561Sobrien 383130561Sobrien if (ret) 384130561Sobrien goto cleanup1; 385130561Sobrien 386130561Sobrien intr_res = bus_alloc_resource(vmbus_devp, 387218822Sdim SYS_RES_IRQ, &vmbus_rid, vmbus_irq, vmbus_irq, 1, RF_ACTIVE); 388130561Sobrien 389130561Sobrien if (intr_res == NULL) { 390130561Sobrien ret = ENOMEM; /* XXXKYS: Need a better errno */ 391130561Sobrien goto cleanup2; 392130561Sobrien } 393218822Sdim 394218822Sdim /* 395218822Sdim * Setup interrupt filter handler 396218822Sdim */ 397218822Sdim ret = bus_setup_intr(vmbus_devp, intr_res, 398218822Sdim INTR_TYPE_NET | INTR_MPSAFE, hv_vmbus_isr, NULL, 399130561Sobrien NULL, &vmbus_cookiep); 400130561Sobrien 401218822Sdim if (ret != 0) 402130561Sobrien goto cleanup3; 403218822Sdim 404130561Sobrien ret = bus_bind_intr(vmbus_devp, intr_res, 0); 405130561Sobrien if (ret != 0) 406130561Sobrien goto cleanup4; 407130561Sobrien 408130561Sobrien isrc = intr_lookup_source(vmbus_irq); 409130561Sobrien if ((isrc == NULL) || (isrc->is_event == NULL)) { 410218822Sdim ret = EINVAL; 411130561Sobrien goto cleanup4; 412130561Sobrien } 413130561Sobrien 414130561Sobrien /* vector = isrc->is_event->ie_vector; */ 415218822Sdim intpin = (struct ioapic_intsrc *)isrc; 416130561Sobrien vector = intpin->io_vector; 417130561Sobrien 418130561Sobrien if(bootverbose) 419130561Sobrien printf("VMBUS: irq 0x%x vector 0x%x\n", vmbus_irq, vector); 420218822Sdim 421130561Sobrien /** 422130561Sobrien * Notify the hypervisor of our irq. 423130561Sobrien */ 424130561Sobrien setup_args.vector = vector; 425130561Sobrien for(i = 0; i < 2; i++) { 426130561Sobrien setup_args.page_buffers[i] = 427218822Sdim malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); 428130561Sobrien if (setup_args.page_buffers[i] == NULL) { 429130561Sobrien KASSERT(setup_args.page_buffers[i] != NULL, 430130561Sobrien ("Error VMBUS: malloc failed!")); 431130561Sobrien if (i > 0) 432130561Sobrien free(setup_args.page_buffers[0], M_DEVBUF); 433130561Sobrien goto cleanup4; 434130561Sobrien } 435130561Sobrien } 436218822Sdim 437130561Sobrien /* only CPU #0 supported at this time */ 438130561Sobrien smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); 439130561Sobrien 440130561Sobrien /* 441218822Sdim * Connect to VMBus in the root partition 442130561Sobrien */ 443130561Sobrien ret = hv_vmbus_connect(); 444130561Sobrien 445130561Sobrien if (ret != 0) 446130561Sobrien goto cleanup4; 447130561Sobrien 448218822Sdim hv_vmbus_request_channel_offers(); 449130561Sobrien return (ret); 450130561Sobrien 451130561Sobrien cleanup4: 452130561Sobrien 453130561Sobrien /* 454130561Sobrien * remove swi, bus and intr resource 455130561Sobrien */ 456130561Sobrien bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); 457130561Sobrien 458130561Sobrien cleanup3: 459130561Sobrien bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); 460130561Sobrien 461130561Sobrien cleanup2: 462218822Sdim swi_remove(event_swintr); 463130561Sobrien 464130561Sobrien cleanup1: 465130561Sobrien swi_remove(msg_swintr); 466130561Sobrien 467218822Sdim cleanup: 468130561Sobrien hv_vmbus_cleanup(); 469130561Sobrien 470130561Sobrien return (ret); 471130561Sobrien} 472130561Sobrien 473130561Sobrienstatic int 474218822Sdimvmbus_attach(device_t dev) 475130561Sobrien{ 476130561Sobrien if(bootverbose) 477130561Sobrien device_printf(dev, "VMBUS: attach dev: %p\n", dev); 478130561Sobrien vmbus_devp = dev; 479130561Sobrien 480130561Sobrien /* 481218822Sdim * If the system has already booted and thread 482130561Sobrien * scheduling is possible indicated by the global 483130561Sobrien * cold set to zero, we just call the driver 484130561Sobrien * initialization directly. 485130561Sobrien */ 486130561Sobrien if (!cold) 487130561Sobrien vmbus_bus_init(); 488130561Sobrien 489130561Sobrien return (0); 490130561Sobrien} 491130561Sobrien 492130561Sobrienstatic void 493130561Sobrienvmbus_init(void) 494130561Sobrien{ 495130561Sobrien if (vm_guest != VM_GUEST_HV) 496130561Sobrien return; 497130561Sobrien 498218822Sdim /* 499130561Sobrien * If the system has already booted and thread 500130561Sobrien * scheduling is possible, as indicated by the 501130561Sobrien * global cold set to zero, we just call the driver 502130561Sobrien * initialization directly. 503130561Sobrien */ 504130561Sobrien if (!cold) 505130561Sobrien vmbus_bus_init(); 506130561Sobrien} 507130561Sobrien 508218822Sdimstatic void 509130561Sobrienvmbus_bus_exit(void) 510130561Sobrien{ 511130561Sobrien int i; 512130561Sobrien 513130561Sobrien hv_vmbus_release_unattached_channels(); 514130561Sobrien hv_vmbus_disconnect(); 515130561Sobrien 516130561Sobrien smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); 517130561Sobrien 518130561Sobrien for(i = 0; i < 2; i++) { 519218822Sdim if (setup_args.page_buffers[i] != 0) 520130561Sobrien free(setup_args.page_buffers[i], M_DEVBUF); 521130561Sobrien } 522130561Sobrien 523130561Sobrien hv_vmbus_cleanup(); 524218822Sdim 525130561Sobrien /* remove swi, bus and intr resource */ 526130561Sobrien bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); 527130561Sobrien 528130561Sobrien bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); 529218822Sdim 530130561Sobrien swi_remove(msg_swintr); 531130561Sobrien swi_remove(event_swintr); 532130561Sobrien 533130561Sobrien return; 534130561Sobrien} 535130561Sobrien 536218822Sdimstatic void 537130561Sobrienvmbus_exit(void) 538130561Sobrien{ 539218822Sdim vmbus_bus_exit(); 540130561Sobrien} 541218822Sdim 542130561Sobrienstatic int 543130561Sobrienvmbus_detach(device_t dev) 544130561Sobrien{ 545130561Sobrien vmbus_exit(); 546218822Sdim return (0); 547218822Sdim} 548130561Sobrien 549130561Sobrienstatic void 550218822Sdimvmbus_mod_load(void) 551130561Sobrien{ 552218822Sdim if(bootverbose) 553218822Sdim printf("VMBUS: load\n"); 554218822Sdim} 555130561Sobrien 556218822Sdimstatic void 557218822Sdimvmbus_mod_unload(void) 558130561Sobrien{ 559130561Sobrien if(bootverbose) 560218822Sdim printf("VMBUS: unload\n"); 561130561Sobrien} 562130561Sobrien 563218822Sdimstatic int 564130561Sobrienvmbus_modevent(module_t mod, int what, void *arg) 565218822Sdim{ 566218822Sdim switch (what) { 567218822Sdim 568218822Sdim case MOD_LOAD: 569218822Sdim vmbus_mod_load(); 570130561Sobrien break; 571130561Sobrien case MOD_UNLOAD: 572130561Sobrien vmbus_mod_unload(); 573130561Sobrien break; 574130561Sobrien } 575130561Sobrien 576130561Sobrien return (0); 577130561Sobrien} 578130561Sobrien 579130561Sobrienstatic device_method_t vmbus_methods[] = { 580218822Sdim /** Device interface */ 581130561Sobrien DEVMETHOD(device_identify, vmbus_identify), 582130561Sobrien DEVMETHOD(device_probe, vmbus_probe), 583130561Sobrien DEVMETHOD(device_attach, vmbus_attach), 584130561Sobrien DEVMETHOD(device_detach, vmbus_detach), 585130561Sobrien DEVMETHOD(device_shutdown, bus_generic_shutdown), 586130561Sobrien DEVMETHOD(device_suspend, bus_generic_suspend), 587218822Sdim DEVMETHOD(device_resume, bus_generic_resume), 588130561Sobrien 589218822Sdim /** Bus interface */ 590218822Sdim DEVMETHOD(bus_add_child, bus_generic_add_child), 591218822Sdim DEVMETHOD(bus_print_child, bus_generic_print_child), 592130561Sobrien DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 593130561Sobrien DEVMETHOD(bus_write_ivar, vmbus_write_ivar), 594218822Sdim 595218822Sdim { 0, 0 } }; 596218822Sdim 597218822Sdimstatic char driver_name[] = "vmbus"; 598218822Sdimstatic driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; 599218822Sdim 600218822Sdim 601218822Sdimdevclass_t vmbus_devclass; 602218822Sdim 603218822SdimDRIVER_MODULE(vmbus, nexus, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); 604130561SobrienMODULE_VERSION(vmbus,1); 605218822Sdim 606218822Sdim/* TODO: We want to be earlier than SI_SUB_VFS */ 607218822SdimSYSINIT(vmb_init, SI_SUB_VFS, SI_ORDER_MIDDLE, vmbus_init, NULL); 608218822Sdim 609218822Sdim