2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * VM Bus Driver Implementation
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
37 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
42 #include <sys/sysctl.h>
43 #include <sys/syslog.h>
44 #include <sys/systm.h>
45 #include <sys/rtprio.h>
46 #include <sys/interrupt.h>
48 #include <sys/taskqueue.h>
49 #include <sys/mutex.h>
52 #include <machine/resource.h>
55 #include <machine/stdarg.h>
56 #include <machine/intr_machdep.h>
57 #include <machine/md_var.h>
58 #include <machine/segments.h>
60 #include <machine/apicvar.h>
62 #include <dev/hyperv/include/hyperv.h>
63 #include <dev/hyperv/vmbus/hv_vmbus_priv.h>
64 #include <dev/hyperv/vmbus/hyperv_reg.h>
65 #include <dev/hyperv/vmbus/hyperv_var.h>
66 #include <dev/hyperv/vmbus/vmbus_reg.h>
67 #include <dev/hyperv/vmbus/vmbus_var.h>
69 #include <contrib/dev/acpica/include/acpi.h>
72 struct vmbus_softc *vmbus_sc;
74 extern inthand_t IDTVEC(rsvd), IDTVEC(vmbus_isr);
77 vmbus_msg_task(void *xsc, int pending __unused)
79 struct vmbus_softc *sc = xsc;
80 volatile struct vmbus_message *msg;
82 msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE;
84 if (msg->msg_type == VMBUS_MSGTYPE_NONE) {
87 } else if (msg->msg_type == VMBUS_MSGTYPE_CHANNEL) {
89 vmbus_chan_msgproc(sc,
90 __DEVOLATILE(const struct vmbus_message *, msg));
93 msg->msg_type = VMBUS_MSGTYPE_NONE;
95 * Make sure the write to msg_type (i.e. set to
96 * VMBUS_MSGTYPE_NONE) happens before we read the
97 * msg_flags and EOMing. Otherwise, the EOMing will
98 * not deliver any more messages since there is no
102 * mb() is used here, since atomic_thread_fence_seq_cst()
103 * will become compiler fence on UP kernel.
106 if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
108 * This will cause message queue rescan to possibly
109 * deliver another msg from the hypervisor
111 wrmsr(MSR_HV_EOM, 0);
117 vmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
119 volatile struct vmbus_message *msg;
120 struct vmbus_message *msg_base;
122 msg_base = VMBUS_PCPU_GET(sc, message, cpu);
127 * TODO: move this to independent IDT vector.
129 msg = msg_base + VMBUS_SINT_TIMER;
130 if (msg->msg_type == VMBUS_MSGTYPE_TIMER_EXPIRED) {
131 msg->msg_type = VMBUS_MSGTYPE_NONE;
133 vmbus_et_intr(frame);
136 * Make sure the write to msg_type (i.e. set to
137 * VMBUS_MSGTYPE_NONE) happens before we read the
138 * msg_flags and EOMing. Otherwise, the EOMing will
139 * not deliver any more messages since there is no
143 * mb() is used here, since atomic_thread_fence_seq_cst()
144 * will become compiler fence on UP kernel.
147 if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
149 * This will cause message queue rescan to possibly
150 * deliver another msg from the hypervisor
152 wrmsr(MSR_HV_EOM, 0);
157 * Check events. Hot path for network and storage I/O data; high rate.
160 * As recommended by the Windows guest fellows, we check events before
163 sc->vmbus_event_proc(sc, cpu);
166 * Check messages. Mainly management stuffs; ultra low rate.
168 msg = msg_base + VMBUS_SINT_MESSAGE;
169 if (__predict_false(msg->msg_type != VMBUS_MSGTYPE_NONE)) {
170 taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
171 VMBUS_PCPU_PTR(sc, message_task, cpu));
174 return (FILTER_HANDLED);
178 vmbus_handle_intr(struct trapframe *trap_frame)
180 struct vmbus_softc *sc = vmbus_get_softc();
184 * Disable preemption.
189 * Do a little interrupt counting.
191 (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
193 vmbus_handle_intr1(sc, trap_frame, cpu);
202 vmbus_synic_setup(void *xsc)
204 struct vmbus_softc *sc = xsc;
209 if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
211 * Save virtual processor id.
213 VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX);
217 * Virtual processoor id is only used by a pretty broken
218 * channel selection code from storvsc. It's nothing
219 * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep
222 VMBUS_PCPU_GET(sc, vcpuid, cpu) = cpu;
226 * Setup the SynIC message.
228 orig = rdmsr(MSR_HV_SIMP);
229 val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
230 ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) <<
231 MSR_HV_SIMP_PGSHIFT);
232 wrmsr(MSR_HV_SIMP, val);
235 * Setup the SynIC event flags.
237 orig = rdmsr(MSR_HV_SIEFP);
238 val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
239 ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu)
240 >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT);
241 wrmsr(MSR_HV_SIEFP, val);
245 * Configure and unmask SINT for message and event flags.
247 sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
249 val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
250 (orig & MSR_HV_SINT_RSVD_MASK);
254 * Configure and unmask SINT for timer.
256 sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
258 val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
259 (orig & MSR_HV_SINT_RSVD_MASK);
263 * All done; enable SynIC.
265 orig = rdmsr(MSR_HV_SCONTROL);
266 val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
267 wrmsr(MSR_HV_SCONTROL, val);
271 vmbus_synic_teardown(void *arg)
279 orig = rdmsr(MSR_HV_SCONTROL);
280 wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
283 * Mask message and event flags SINT.
285 sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
287 wrmsr(sint, orig | MSR_HV_SINT_MASKED);
292 sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
294 wrmsr(sint, orig | MSR_HV_SINT_MASKED);
297 * Teardown SynIC message.
299 orig = rdmsr(MSR_HV_SIMP);
300 wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
303 * Teardown SynIC event flags.
305 orig = rdmsr(MSR_HV_SIEFP);
306 wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
310 vmbus_dma_alloc(struct vmbus_softc *sc)
312 bus_dma_tag_t parent_dtag;
316 parent_dtag = bus_get_dma_tag(sc->vmbus_dev);
321 * Per-cpu messages and event flags.
323 ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
324 PAGE_SIZE, VMBUS_PCPU_PTR(sc, message_dma, cpu),
325 BUS_DMA_WAITOK | BUS_DMA_ZERO);
328 VMBUS_PCPU_GET(sc, message, cpu) = ptr;
330 ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
331 PAGE_SIZE, VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
332 BUS_DMA_WAITOK | BUS_DMA_ZERO);
335 VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr;
338 evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
339 PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
340 if (evtflags == NULL)
342 sc->vmbus_rx_evtflags = (u_long *)evtflags;
343 sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2));
344 sc->vmbus_evtflags = evtflags;
346 sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
347 PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
348 if (sc->vmbus_mnf1 == NULL)
351 sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
352 PAGE_SIZE, &sc->vmbus_mnf2_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
353 if (sc->vmbus_mnf2 == NULL)
360 vmbus_dma_free(struct vmbus_softc *sc)
364 if (sc->vmbus_evtflags != NULL) {
365 hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags);
366 sc->vmbus_evtflags = NULL;
367 sc->vmbus_rx_evtflags = NULL;
368 sc->vmbus_tx_evtflags = NULL;
370 if (sc->vmbus_mnf1 != NULL) {
371 hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1);
372 sc->vmbus_mnf1 = NULL;
374 if (sc->vmbus_mnf2 != NULL) {
375 hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2);
376 sc->vmbus_mnf2 = NULL;
380 if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
382 VMBUS_PCPU_PTR(sc, message_dma, cpu),
383 VMBUS_PCPU_GET(sc, message, cpu));
384 VMBUS_PCPU_GET(sc, message, cpu) = NULL;
386 if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) {
388 VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
389 VMBUS_PCPU_GET(sc, event_flags, cpu));
390 VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL;
396 * @brief Find a free IDT slot and setup the interrupt handler.
399 vmbus_vector_alloc(void)
403 struct gate_descriptor *ip;
406 * Search backwards form the highest IDT vector available for use
407 * as vmbus channel callback vector. We install 'hv_vmbus_callback'
408 * handler at that vector and use it to interrupt vcpus.
410 vector = APIC_SPURIOUS_INT;
411 while (--vector >= APIC_IPI_INTS) {
413 func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset);
414 if (func == (uintptr_t)&IDTVEC(rsvd)) {
416 setidt(vector , IDTVEC(vmbus_isr), SDT_SYS386IGT,
417 SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
419 setidt(vector , IDTVEC(vmbus_isr), SDT_SYSIGT,
430 * @brief Restore the IDT slot to rsvd.
433 vmbus_vector_free(int vector)
436 struct gate_descriptor *ip;
441 KASSERT(vector >= APIC_IPI_INTS && vector < APIC_SPURIOUS_INT,
442 ("invalid vector %d", vector));
445 func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset);
446 KASSERT(func == (uintptr_t)&IDTVEC(hv_vmbus_callback),
447 ("invalid vector %d", vector));
449 setidt(vector, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0);
453 vmbus_cpuset_setthread_task(void *xmask, int pending __unused)
455 cpuset_t *mask = xmask;
458 error = cpuset_setthread(curthread->td_tid, mask);
460 panic("curthread=%ju: can't pin; error=%d",
461 (uintmax_t)curthread->td_tid, error);
466 vmbus_intr_setup(struct vmbus_softc *sc)
471 struct task cpuset_task;
472 char buf[MAXCOMLEN + 1];
475 /* Allocate an interrupt counter for Hyper-V interrupt */
476 snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
477 intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
480 * Setup taskqueue to handle events. Task will be per-
483 VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
484 "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
485 VMBUS_PCPU_PTR(sc, event_tq, cpu));
486 taskqueue_start_threads(VMBUS_PCPU_PTR(sc, event_tq, cpu),
487 1, PI_NET, "hvevent%d", cpu);
489 CPU_SETOF(cpu, &cpu_mask);
490 TASK_INIT(&cpuset_task, 0, vmbus_cpuset_setthread_task,
492 taskqueue_enqueue(VMBUS_PCPU_GET(sc, event_tq, cpu),
494 taskqueue_drain(VMBUS_PCPU_GET(sc, event_tq, cpu),
498 * Setup tasks and taskqueues to handle messages.
500 VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
501 "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
502 VMBUS_PCPU_PTR(sc, message_tq, cpu));
503 taskqueue_start_threads(VMBUS_PCPU_PTR(sc, message_tq, cpu), 1,
504 PI_NET, "hvmsg%d", cpu);
505 TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
508 CPU_SETOF(cpu, &cpu_mask);
509 TASK_INIT(&cpuset_task, 0, vmbus_cpuset_setthread_task,
511 taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
513 taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
518 * All Hyper-V ISR required resources are setup, now let's find a
519 * free IDT vector for Hyper-V ISR and set it up.
521 sc->vmbus_idtvec = vmbus_vector_alloc();
522 if (sc->vmbus_idtvec == 0) {
523 device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
527 device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
534 vmbus_intr_teardown(struct vmbus_softc *sc)
538 vmbus_vector_free(sc->vmbus_idtvec);
541 if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
542 taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
543 VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
545 if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
546 taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
547 VMBUS_PCPU_PTR(sc, message_task, cpu));
548 taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
549 VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
555 vmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
557 struct hv_device *child_dev_ctx = device_get_ivars(child);
560 case HV_VMBUS_IVAR_TYPE:
561 *result = (uintptr_t)&child_dev_ctx->class_id;
564 case HV_VMBUS_IVAR_INSTANCE:
565 *result = (uintptr_t)&child_dev_ctx->device_id;
568 case HV_VMBUS_IVAR_DEVCTX:
569 *result = (uintptr_t)child_dev_ctx;
572 case HV_VMBUS_IVAR_NODE:
573 *result = (uintptr_t)child_dev_ctx->device;
580 vmbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
583 case HV_VMBUS_IVAR_TYPE:
584 case HV_VMBUS_IVAR_INSTANCE:
585 case HV_VMBUS_IVAR_DEVCTX:
586 case HV_VMBUS_IVAR_NODE:
594 vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
596 struct hv_device *dev_ctx = device_get_ivars(child);
597 char guidbuf[HYPERV_GUID_STRLEN];
602 strlcat(buf, "classid=", buflen);
603 hyperv_guid2str(&dev_ctx->class_id, guidbuf, sizeof(guidbuf));
604 strlcat(buf, guidbuf, buflen);
606 strlcat(buf, " deviceid=", buflen);
607 hyperv_guid2str(&dev_ctx->device_id, guidbuf, sizeof(guidbuf));
608 strlcat(buf, guidbuf, buflen);
614 hv_vmbus_child_device_create(hv_guid type, hv_guid instance,
615 hv_vmbus_channel *channel)
617 hv_device *child_dev;
620 * Allocate the new child device
622 child_dev = malloc(sizeof(hv_device), M_DEVBUF, M_WAITOK | M_ZERO);
624 child_dev->channel = channel;
625 memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
626 memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
632 hv_vmbus_child_device_register(struct hv_device *child_dev)
634 device_t child, parent;
636 parent = vmbus_get_device();
638 char name[HYPERV_GUID_STRLEN];
640 hyperv_guid2str(&child_dev->class_id, name, sizeof(name));
641 device_printf(parent, "add device, classid: %s\n", name);
644 child = device_add_child(parent, NULL, -1);
645 child_dev->device = child;
646 device_set_ivars(child, child_dev);
652 hv_vmbus_child_device_unregister(struct hv_device *child_dev)
656 * XXXKYS: Ensure that this is the opposite of
660 ret = device_delete_child(vmbus_get_device(), child_dev->device);
666 vmbus_probe(device_t dev)
668 char *id[] = { "VMBUS", NULL };
670 if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL ||
671 device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV ||
672 (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
675 device_set_desc(dev, "Hyper-V Vmbus");
677 return (BUS_PROBE_DEFAULT);
681 * @brief Main vmbus driver initialization routine.
684 * - initialize the vmbus driver context
685 * - setup various driver entry points
686 * - invoke the vmbus hv main init routine
687 * - get the irq resource
688 * - invoke the vmbus to add the vmbus root device
689 * - setup the vmbus root device
690 * - retrieve the channel offers
695 struct vmbus_softc *sc = vmbus_get_softc();
698 if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
700 sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
703 * Allocate DMA stuffs.
705 ret = vmbus_dma_alloc(sc);
712 ret = vmbus_intr_setup(sc);
720 device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
721 smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc);
722 sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
725 * Connect to VMBus in the root partition
727 ret = hv_vmbus_connect(sc);
731 if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
732 hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
733 sc->vmbus_event_proc = vmbus_event_proc_compat;
735 sc->vmbus_event_proc = vmbus_event_proc;
737 hv_vmbus_request_channel_offers();
740 bus_generic_attach(sc->vmbus_dev);
741 device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
746 vmbus_intr_teardown(sc);
753 vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
758 vmbus_attach(device_t dev)
760 vmbus_sc = device_get_softc(dev);
761 vmbus_sc->vmbus_dev = dev;
764 * Event processing logic will be configured:
765 * - After the vmbus protocol version negotiation.
766 * - Before we request channel offers.
768 vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
771 * If the system has already booted and thread
772 * scheduling is possible indicated by the global
773 * cold set to zero, we just call the driver
774 * initialization directly.
779 bus_generic_probe(dev);
784 vmbus_sysinit(void *arg __unused)
786 if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
790 * If the system has already booted and thread
791 * scheduling is possible, as indicated by the
792 * global cold set to zero, we just call the driver
793 * initialization directly.
800 vmbus_detach(device_t dev)
802 struct vmbus_softc *sc = device_get_softc(dev);
804 hv_vmbus_release_unattached_channels();
805 hv_vmbus_disconnect();
807 if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
808 sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
809 smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
812 vmbus_intr_teardown(sc);
818 static device_method_t vmbus_methods[] = {
819 /* Device interface */
820 DEVMETHOD(device_probe, vmbus_probe),
821 DEVMETHOD(device_attach, vmbus_attach),
822 DEVMETHOD(device_detach, vmbus_detach),
823 DEVMETHOD(device_shutdown, bus_generic_shutdown),
824 DEVMETHOD(device_suspend, bus_generic_suspend),
825 DEVMETHOD(device_resume, bus_generic_resume),
828 DEVMETHOD(bus_add_child, bus_generic_add_child),
829 DEVMETHOD(bus_print_child, bus_generic_print_child),
830 DEVMETHOD(bus_read_ivar, vmbus_read_ivar),
831 DEVMETHOD(bus_write_ivar, vmbus_write_ivar),
832 DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str),
837 static driver_t vmbus_driver = {
840 sizeof(struct vmbus_softc)
843 static devclass_t vmbus_devclass;
845 DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
846 MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
847 MODULE_VERSION(vmbus, 1);
851 * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
854 SYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);