vmbus.c revision 300647
1/*-
2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
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.
16 *
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.
27 */
28
29/*
30 * VM Bus Driver Implementation
31 */
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 300647 2016-05-25 05:06:15Z sephe $");
34
35#include <sys/param.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/lock.h>
39#include <sys/malloc.h>
40#include <sys/module.h>
41#include <sys/proc.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>
47#include <sys/sx.h>
48#include <sys/taskqueue.h>
49#include <sys/mutex.h>
50#include <sys/smp.h>
51
52#include <machine/resource.h>
53#include <sys/rman.h>
54
55#include <machine/stdarg.h>
56#include <machine/intr_machdep.h>
57#include <machine/md_var.h>
58#include <machine/segments.h>
59#include <sys/pcpu.h>
60#include <x86/apicvar.h>
61
62#include <dev/hyperv/include/hyperv.h>
63#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
64#include <dev/hyperv/vmbus/vmbus_var.h>
65
66#include <contrib/dev/acpica/include/acpi.h>
67#include "acpi_if.h"
68
69struct vmbus_softc	*vmbus_sc;
70
71static int vmbus_inited;
72
73static char *vmbus_ids[] = { "VMBUS", NULL };
74
75extern inthand_t IDTVEC(hv_vmbus_callback);
76
77static void
78vmbus_msg_task(void *xsc, int pending __unused)
79{
80	struct vmbus_softc *sc = xsc;
81	hv_vmbus_message *msg;
82
83	msg = VMBUS_PCPU_GET(sc, message, curcpu) + HV_VMBUS_MESSAGE_SINT;
84	for (;;) {
85		const hv_vmbus_channel_msg_table_entry *entry;
86		hv_vmbus_channel_msg_header *hdr;
87		hv_vmbus_channel_msg_type msg_type;
88
89		if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
90			break; /* no message */
91
92		hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
93		msg_type = hdr->message_type;
94
95		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
96			printf("VMBUS: unknown message type = %d\n", msg_type);
97			goto handled;
98		}
99
100		entry = &g_channel_message_table[msg_type];
101		if (entry->messageHandler)
102			entry->messageHandler(hdr);
103handled:
104		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
105		/*
106		 * Make sure the write to message_type (ie set to
107		 * HV_MESSAGE_TYPE_NONE) happens before we read the
108		 * message_pending and EOMing. Otherwise, the EOMing will
109		 * not deliver any more messages
110		 * since there is no empty slot
111		 *
112		 * NOTE:
113		 * mb() is used here, since atomic_thread_fence_seq_cst()
114		 * will become compiler fence on UP kernel.
115		 */
116		mb();
117		if (msg->header.message_flags.u.message_pending) {
118			/*
119			 * This will cause message queue rescan to possibly
120			 * deliver another msg from the hypervisor
121			 */
122			wrmsr(HV_X64_MSR_EOM, 0);
123		}
124	}
125}
126
127/**
128 * @brief Interrupt filter routine for VMBUS.
129 *
130 * The purpose of this routine is to determine the type of VMBUS protocol
131 * message to process - an event or a channel message.
132 */
133static inline int
134hv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
135{
136	hv_vmbus_message *msg, *msg_base;
137
138	/*
139	 * The Windows team has advised that we check for events
140	 * before checking for messages. This is the way they do it
141	 * in Windows when running as a guest in Hyper-V
142	 */
143	sc->vmbus_event_proc(sc, cpu);
144
145	/* Check if there are actual msgs to be process */
146	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
147	msg = msg_base + HV_VMBUS_TIMER_SINT;
148
149	/* we call eventtimer process the message */
150	if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) {
151		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
152
153		/* call intrrupt handler of event timer */
154		hv_et_intr(frame);
155
156		/*
157		 * Make sure the write to message_type (ie set to
158		 * HV_MESSAGE_TYPE_NONE) happens before we read the
159		 * message_pending and EOMing. Otherwise, the EOMing will
160		 * not deliver any more messages
161		 * since there is no empty slot
162		 *
163		 * NOTE:
164		 * mb() is used here, since atomic_thread_fence_seq_cst()
165		 * will become compiler fence on UP kernel.
166		 */
167		mb();
168
169		if (msg->header.message_flags.u.message_pending) {
170			/*
171			 * This will cause message queue rescan to possibly
172			 * deliver another msg from the hypervisor
173			 */
174			wrmsr(HV_X64_MSR_EOM, 0);
175		}
176	}
177
178	msg = msg_base + HV_VMBUS_MESSAGE_SINT;
179	if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) {
180		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
181		    VMBUS_PCPU_PTR(sc, message_task, cpu));
182	}
183
184	return (FILTER_HANDLED);
185}
186
187void
188hv_vector_handler(struct trapframe *trap_frame)
189{
190	struct vmbus_softc *sc = vmbus_get_softc();
191	int cpu = curcpu;
192
193	/*
194	 * Disable preemption.
195	 */
196	critical_enter();
197
198	/*
199	 * Do a little interrupt counting.
200	 */
201	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
202
203	hv_vmbus_isr(sc, trap_frame, cpu);
204
205	/*
206	 * Enable preemption.
207	 */
208	critical_exit();
209}
210
211static void
212vmbus_synic_setup(void *arg __unused)
213{
214	struct vmbus_softc *sc = vmbus_get_softc();
215	int			cpu;
216	hv_vmbus_synic_simp	simp;
217	hv_vmbus_synic_siefp	siefp;
218	hv_vmbus_synic_scontrol sctrl;
219	hv_vmbus_synic_sint	shared_sint;
220	uint64_t		version;
221
222	cpu = PCPU_GET(cpuid);
223
224	/*
225	 * TODO: Check the version
226	 */
227	version = rdmsr(HV_X64_MSR_SVERSION);
228
229	/*
230	 * Setup the Synic's message page
231	 */
232	simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
233	simp.u.simp_enabled = 1;
234	simp.u.base_simp_gpa =
235	    VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT;
236
237	wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
238
239	/*
240	 * Setup the Synic's event page
241	 */
242	siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
243	siefp.u.siefp_enabled = 1;
244	siefp.u.base_siefp_gpa =
245	    VMBUS_PCPU_GET(sc, event_flag_dma.hv_paddr, cpu) >> PAGE_SHIFT;
246
247	wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
248
249	/*HV_SHARED_SINT_IDT_VECTOR + 0x20; */
250	shared_sint.as_uint64_t = 0;
251	shared_sint.u.vector = sc->vmbus_idtvec;
252	shared_sint.u.masked = FALSE;
253	shared_sint.u.auto_eoi = TRUE;
254
255	wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
256	    shared_sint.as_uint64_t);
257
258	wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
259	    shared_sint.as_uint64_t);
260
261	/* Enable the global synic bit */
262	sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL);
263	sctrl.u.enable = 1;
264
265	wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t);
266
267	hv_vmbus_g_context.syn_ic_initialized = TRUE;
268
269	/*
270	 * Set up the cpuid mapping from Hyper-V to FreeBSD.
271	 * The array is indexed using FreeBSD cpuid.
272	 */
273	VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(HV_X64_MSR_VP_INDEX);
274}
275
276static void
277vmbus_synic_teardown(void *arg)
278{
279	hv_vmbus_synic_sint	shared_sint;
280	hv_vmbus_synic_simp	simp;
281	hv_vmbus_synic_siefp	siefp;
282
283	if (!hv_vmbus_g_context.syn_ic_initialized)
284	    return;
285
286	shared_sint.as_uint64_t = rdmsr(
287	    HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT);
288
289	shared_sint.u.masked = 1;
290
291	/*
292	 * Disable the interrupt 0
293	 */
294	wrmsr(
295	    HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
296	    shared_sint.as_uint64_t);
297
298	shared_sint.as_uint64_t = rdmsr(
299	    HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT);
300
301	shared_sint.u.masked = 1;
302
303	/*
304	 * Disable the interrupt 1
305	 */
306	wrmsr(
307	    HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
308	    shared_sint.as_uint64_t);
309	simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
310	simp.u.simp_enabled = 0;
311	simp.u.base_simp_gpa = 0;
312
313	wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
314
315	siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
316	siefp.u.siefp_enabled = 0;
317	siefp.u.base_siefp_gpa = 0;
318
319	wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
320}
321
322static int
323vmbus_dma_alloc(struct vmbus_softc *sc)
324{
325	int cpu;
326
327	CPU_FOREACH(cpu) {
328		void *ptr;
329
330		/*
331		 * Per-cpu messages and event flags.
332		 */
333		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
334		    PAGE_SIZE, 0, PAGE_SIZE,
335		    VMBUS_PCPU_PTR(sc, message_dma, cpu),
336		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
337		if (ptr == NULL)
338			return ENOMEM;
339		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
340
341		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
342		    PAGE_SIZE, 0, PAGE_SIZE,
343		    VMBUS_PCPU_PTR(sc, event_flag_dma, cpu),
344		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
345		if (ptr == NULL)
346			return ENOMEM;
347		VMBUS_PCPU_GET(sc, event_flag, cpu) = ptr;
348	}
349	return 0;
350}
351
352static void
353vmbus_dma_free(struct vmbus_softc *sc)
354{
355	int cpu;
356
357	CPU_FOREACH(cpu) {
358		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
359			hyperv_dmamem_free(
360			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
361			    VMBUS_PCPU_GET(sc, message, cpu));
362			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
363		}
364		if (VMBUS_PCPU_GET(sc, event_flag, cpu) != NULL) {
365			hyperv_dmamem_free(
366			    VMBUS_PCPU_PTR(sc, event_flag_dma, cpu),
367			    VMBUS_PCPU_GET(sc, event_flag, cpu));
368			VMBUS_PCPU_GET(sc, event_flag, cpu) = NULL;
369		}
370	}
371}
372
373static int
374vmbus_intr_setup(struct vmbus_softc *sc)
375{
376	int cpu;
377
378	CPU_FOREACH(cpu) {
379		char buf[MAXCOMLEN + 1];
380		cpuset_t cpu_mask;
381
382		/* Allocate an interrupt counter for Hyper-V interrupt */
383		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
384		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
385
386		/*
387		 * Setup taskqueue to handle events.  Task will be per-
388		 * channel.
389		 */
390		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
391		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
392		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
393		CPU_SETOF(cpu, &cpu_mask);
394		taskqueue_start_threads_cpuset(
395		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
396		    "hvevent%d", cpu);
397
398		/*
399		 * Setup tasks and taskqueues to handle messages.
400		 */
401		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
402		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
403		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
404		CPU_SETOF(cpu, &cpu_mask);
405		taskqueue_start_threads_cpuset(
406		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
407		    "hvmsg%d", cpu);
408		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
409		    vmbus_msg_task, sc);
410	}
411
412	/*
413	 * All Hyper-V ISR required resources are setup, now let's find a
414	 * free IDT vector for Hyper-V ISR and set it up.
415	 */
416	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback));
417	if (sc->vmbus_idtvec < 0) {
418		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
419		return ENXIO;
420	}
421	if(bootverbose) {
422		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
423		    sc->vmbus_idtvec);
424	}
425	return 0;
426}
427
428static void
429vmbus_intr_teardown(struct vmbus_softc *sc)
430{
431	int cpu;
432
433	if (sc->vmbus_idtvec >= 0) {
434		lapic_ipi_free(sc->vmbus_idtvec);
435		sc->vmbus_idtvec = -1;
436	}
437
438	CPU_FOREACH(cpu) {
439		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
440			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
441			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
442		}
443		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
444			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
445			    VMBUS_PCPU_PTR(sc, message_task, cpu));
446			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
447			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
448		}
449	}
450}
451
452static int
453vmbus_read_ivar(
454	device_t	dev,
455	device_t	child,
456	int		index,
457	uintptr_t*	result)
458{
459	struct hv_device *child_dev_ctx = device_get_ivars(child);
460
461	switch (index) {
462
463	case HV_VMBUS_IVAR_TYPE:
464		*result = (uintptr_t) &child_dev_ctx->class_id;
465		return (0);
466	case HV_VMBUS_IVAR_INSTANCE:
467		*result = (uintptr_t) &child_dev_ctx->device_id;
468		return (0);
469	case HV_VMBUS_IVAR_DEVCTX:
470		*result = (uintptr_t) child_dev_ctx;
471		return (0);
472	case HV_VMBUS_IVAR_NODE:
473		*result = (uintptr_t) child_dev_ctx->device;
474		return (0);
475	}
476	return (ENOENT);
477}
478
479static int
480vmbus_write_ivar(
481	device_t	dev,
482	device_t	child,
483	int		index,
484	uintptr_t	value)
485{
486	switch (index) {
487
488	case HV_VMBUS_IVAR_TYPE:
489	case HV_VMBUS_IVAR_INSTANCE:
490	case HV_VMBUS_IVAR_DEVCTX:
491	case HV_VMBUS_IVAR_NODE:
492		/* read-only */
493		return (EINVAL);
494	}
495	return (ENOENT);
496}
497
498static int
499vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
500{
501	char guidbuf[40];
502	struct hv_device *dev_ctx = device_get_ivars(child);
503
504	if (dev_ctx == NULL)
505		return (0);
506
507	strlcat(buf, "classid=", buflen);
508	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id);
509	strlcat(buf, guidbuf, buflen);
510
511	strlcat(buf, " deviceid=", buflen);
512	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id);
513	strlcat(buf, guidbuf, buflen);
514
515	return (0);
516}
517
518struct hv_device*
519hv_vmbus_child_device_create(
520	hv_guid		type,
521	hv_guid		instance,
522	hv_vmbus_channel*	channel)
523{
524	hv_device* child_dev;
525
526	/*
527	 * Allocate the new child device
528	 */
529	child_dev = malloc(sizeof(hv_device), M_DEVBUF,
530			M_WAITOK |  M_ZERO);
531
532	child_dev->channel = channel;
533	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
534	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
535
536	return (child_dev);
537}
538
539int
540snprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid)
541{
542	int cnt;
543	const unsigned char *d = guid->data;
544
545	cnt = snprintf(buf, sz,
546		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
547		d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
548		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
549	return (cnt);
550}
551
552int
553hv_vmbus_child_device_register(struct hv_device *child_dev)
554{
555	device_t child;
556
557	if (bootverbose) {
558		char name[40];
559		snprintf_hv_guid(name, sizeof(name), &child_dev->class_id);
560		printf("VMBUS: Class ID: %s\n", name);
561	}
562
563	child = device_add_child(vmbus_get_device(), NULL, -1);
564	child_dev->device = child;
565	device_set_ivars(child, child_dev);
566
567	return (0);
568}
569
570int
571hv_vmbus_child_device_unregister(struct hv_device *child_dev)
572{
573	int ret = 0;
574	/*
575	 * XXXKYS: Ensure that this is the opposite of
576	 * device_add_child()
577	 */
578	mtx_lock(&Giant);
579	ret = device_delete_child(vmbus_get_device(), child_dev->device);
580	mtx_unlock(&Giant);
581	return(ret);
582}
583
584static int
585vmbus_probe(device_t dev)
586{
587	if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL ||
588	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV)
589		return (ENXIO);
590
591	device_set_desc(dev, "Hyper-V Vmbus");
592
593	return (BUS_PROBE_DEFAULT);
594}
595
596/**
597 * @brief Main vmbus driver initialization routine.
598 *
599 * Here, we
600 * - initialize the vmbus driver context
601 * - setup various driver entry points
602 * - invoke the vmbus hv main init routine
603 * - get the irq resource
604 * - invoke the vmbus to add the vmbus root device
605 * - setup the vmbus root device
606 * - retrieve the channel offers
607 */
608static int
609vmbus_bus_init(void)
610{
611	struct vmbus_softc *sc;
612	int ret;
613
614	if (vmbus_inited)
615		return (0);
616
617	vmbus_inited = 1;
618	sc = vmbus_get_softc();
619
620	/*
621	 * Allocate DMA stuffs.
622	 */
623	ret = vmbus_dma_alloc(sc);
624	if (ret != 0)
625		goto cleanup;
626
627	/*
628	 * Setup interrupt.
629	 */
630	ret = vmbus_intr_setup(sc);
631	if (ret != 0)
632		goto cleanup;
633
634	if (bootverbose)
635		printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n",
636		    smp_started);
637	smp_rendezvous(NULL, vmbus_synic_setup, NULL, NULL);
638
639	/*
640	 * Connect to VMBus in the root partition
641	 */
642	ret = hv_vmbus_connect();
643
644	if (ret != 0)
645		goto cleanup;
646
647	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
648	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
649		sc->vmbus_event_proc = vmbus_event_proc_compat;
650	else
651		sc->vmbus_event_proc = vmbus_event_proc;
652
653	hv_vmbus_request_channel_offers();
654
655	vmbus_scan();
656	bus_generic_attach(sc->vmbus_dev);
657	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
658
659	return (ret);
660
661cleanup:
662	vmbus_intr_teardown(sc);
663	vmbus_dma_free(sc);
664
665	return (ret);
666}
667
668static void
669vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
670{
671}
672
673static int
674vmbus_attach(device_t dev)
675{
676	vmbus_sc = device_get_softc(dev);
677	vmbus_sc->vmbus_dev = dev;
678	vmbus_sc->vmbus_idtvec = -1;
679
680	/*
681	 * Event processing logic will be configured:
682	 * - After the vmbus protocol version negotiation.
683	 * - Before we request channel offers.
684	 */
685	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
686
687#ifndef EARLY_AP_STARTUP
688	/*
689	 * If the system has already booted and thread
690	 * scheduling is possible indicated by the global
691	 * cold set to zero, we just call the driver
692	 * initialization directly.
693	 */
694	if (!cold)
695#endif
696		vmbus_bus_init();
697
698	bus_generic_probe(dev);
699	return (0);
700}
701
702static void
703vmbus_sysinit(void *arg __unused)
704{
705	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
706		return;
707
708#ifndef EARLY_AP_STARTUP
709	/*
710	 * If the system has already booted and thread
711	 * scheduling is possible, as indicated by the
712	 * global cold set to zero, we just call the driver
713	 * initialization directly.
714	 */
715	if (!cold)
716#endif
717		vmbus_bus_init();
718}
719
720static int
721vmbus_detach(device_t dev)
722{
723	struct vmbus_softc *sc = device_get_softc(dev);
724
725	hv_vmbus_release_unattached_channels();
726	hv_vmbus_disconnect();
727
728	smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
729
730	vmbus_intr_teardown(sc);
731	vmbus_dma_free(sc);
732
733	return (0);
734}
735
736static device_method_t vmbus_methods[] = {
737	/* Device interface */
738	DEVMETHOD(device_probe,			vmbus_probe),
739	DEVMETHOD(device_attach,		vmbus_attach),
740	DEVMETHOD(device_detach,		vmbus_detach),
741	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
742	DEVMETHOD(device_suspend,		bus_generic_suspend),
743	DEVMETHOD(device_resume,		bus_generic_resume),
744
745	/* Bus interface */
746	DEVMETHOD(bus_add_child,		bus_generic_add_child),
747	DEVMETHOD(bus_print_child,		bus_generic_print_child),
748	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
749	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
750	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
751
752	DEVMETHOD_END
753};
754
755static driver_t vmbus_driver = {
756	"vmbus",
757	vmbus_methods,
758	sizeof(struct vmbus_softc)
759};
760
761static devclass_t vmbus_devclass;
762
763DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
764MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
765MODULE_VERSION(vmbus, 1);
766
767#ifndef EARLY_AP_STARTUP
768/*
769 * NOTE:
770 * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
771 * initialized.
772 */
773SYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
774#endif
775