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