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