vmbus.c revision 300650
1250199Sgrehan/*-
2298446Ssephe * Copyright (c) 2009-2012,2016 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2012 NetApp Inc.
4250199Sgrehan * Copyright (c) 2012 Citrix Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29250199Sgrehan/*
30250199Sgrehan * VM Bus Driver Implementation
31250199Sgrehan */
32256276Sdim#include <sys/cdefs.h>
33256276Sdim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 300650 2016-05-25 05:22:35Z sephe $");
34250199Sgrehan
35250199Sgrehan#include <sys/param.h>
36250199Sgrehan#include <sys/bus.h>
37250199Sgrehan#include <sys/kernel.h>
38250199Sgrehan#include <sys/lock.h>
39250199Sgrehan#include <sys/malloc.h>
40250199Sgrehan#include <sys/module.h>
41293873Ssephe#include <sys/proc.h>
42250199Sgrehan#include <sys/sysctl.h>
43250199Sgrehan#include <sys/syslog.h>
44250199Sgrehan#include <sys/systm.h>
45250199Sgrehan#include <sys/rtprio.h>
46250199Sgrehan#include <sys/interrupt.h>
47250199Sgrehan#include <sys/sx.h>
48250199Sgrehan#include <sys/taskqueue.h>
49250199Sgrehan#include <sys/mutex.h>
50250199Sgrehan#include <sys/smp.h>
51250199Sgrehan
52250199Sgrehan#include <machine/resource.h>
53250199Sgrehan#include <sys/rman.h>
54250199Sgrehan
55250199Sgrehan#include <machine/stdarg.h>
56250199Sgrehan#include <machine/intr_machdep.h>
57282212Swhu#include <machine/md_var.h>
58282212Swhu#include <machine/segments.h>
59250199Sgrehan#include <sys/pcpu.h>
60282212Swhu#include <x86/apicvar.h>
61250199Sgrehan
62297142Ssephe#include <dev/hyperv/include/hyperv.h>
63300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
64300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
65250199Sgrehan
66293870Ssephe#include <contrib/dev/acpica/include/acpi.h>
67293870Ssephe#include "acpi_if.h"
68250199Sgrehan
69300102Ssephestruct vmbus_softc	*vmbus_sc;
70300102Ssephe
71293870Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL };
72293870Ssephe
73300574Ssepheextern inthand_t IDTVEC(hv_vmbus_callback);
74300574Ssephe
75250199Sgrehanstatic void
76300572Ssephevmbus_msg_task(void *xsc, int pending __unused)
77250199Sgrehan{
78300572Ssephe	struct vmbus_softc *sc = xsc;
79300108Ssephe	hv_vmbus_message *msg;
80250199Sgrehan
81300573Ssephe	msg = VMBUS_PCPU_GET(sc, message, curcpu) + HV_VMBUS_MESSAGE_SINT;
82300108Ssephe	for (;;) {
83300108Ssephe		const hv_vmbus_channel_msg_table_entry *entry;
84300108Ssephe		hv_vmbus_channel_msg_header *hdr;
85300108Ssephe		hv_vmbus_channel_msg_type msg_type;
86282212Swhu
87292861Sdelphij		if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
88250199Sgrehan			break; /* no message */
89292861Sdelphij
90292861Sdelphij		hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
91292861Sdelphij		msg_type = hdr->message_type;
92292861Sdelphij
93295307Ssephe		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
94292861Sdelphij			printf("VMBUS: unknown message type = %d\n", msg_type);
95292861Sdelphij			goto handled;
96292861Sdelphij		}
97292861Sdelphij
98292861Sdelphij		entry = &g_channel_message_table[msg_type];
99295307Ssephe		if (entry->messageHandler)
100292861Sdelphij			entry->messageHandler(hdr);
101292861Sdelphijhandled:
102300108Ssephe		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
103300108Ssephe		/*
104300108Ssephe		 * Make sure the write to message_type (ie set to
105300108Ssephe		 * HV_MESSAGE_TYPE_NONE) happens before we read the
106300108Ssephe		 * message_pending and EOMing. Otherwise, the EOMing will
107300108Ssephe		 * not deliver any more messages
108300108Ssephe		 * since there is no empty slot
109300108Ssephe		 *
110300108Ssephe		 * NOTE:
111300108Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
112300108Ssephe		 * will become compiler fence on UP kernel.
113300108Ssephe		 */
114300108Ssephe		mb();
115300108Ssephe		if (msg->header.message_flags.u.message_pending) {
116250199Sgrehan			/*
117250199Sgrehan			 * This will cause message queue rescan to possibly
118250199Sgrehan			 * deliver another msg from the hypervisor
119250199Sgrehan			 */
120255414Sgrehan			wrmsr(HV_X64_MSR_EOM, 0);
121300108Ssephe		}
122250199Sgrehan	}
123250199Sgrehan}
124250199Sgrehan
125250199Sgrehan/**
126250199Sgrehan * @brief Interrupt filter routine for VMBUS.
127250199Sgrehan *
128250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol
129250199Sgrehan * message to process - an event or a channel message.
130250199Sgrehan */
131282212Swhustatic inline int
132300567Ssephehv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
133250199Sgrehan{
134300481Ssephe	hv_vmbus_message *msg, *msg_base;
135250199Sgrehan
136250199Sgrehan	/*
137250199Sgrehan	 * The Windows team has advised that we check for events
138250199Sgrehan	 * before checking for messages. This is the way they do it
139250199Sgrehan	 * in Windows when running as a guest in Hyper-V
140250199Sgrehan	 */
141300107Ssephe	sc->vmbus_event_proc(sc, cpu);
142250199Sgrehan
143250199Sgrehan	/* Check if there are actual msgs to be process */
144300573Ssephe	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
145300481Ssephe	msg = msg_base + HV_VMBUS_TIMER_SINT;
146250199Sgrehan
147293873Ssephe	/* we call eventtimer process the message */
148293873Ssephe	if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) {
149293873Ssephe		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
150293873Ssephe
151297176Ssephe		/* call intrrupt handler of event timer */
152297176Ssephe		hv_et_intr(frame);
153297176Ssephe
154293873Ssephe		/*
155293873Ssephe		 * Make sure the write to message_type (ie set to
156293873Ssephe		 * HV_MESSAGE_TYPE_NONE) happens before we read the
157293873Ssephe		 * message_pending and EOMing. Otherwise, the EOMing will
158293873Ssephe		 * not deliver any more messages
159293873Ssephe		 * since there is no empty slot
160297634Ssephe		 *
161297634Ssephe		 * NOTE:
162297634Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
163297636Ssephe		 * will become compiler fence on UP kernel.
164293873Ssephe		 */
165297634Ssephe		mb();
166293873Ssephe
167293873Ssephe		if (msg->header.message_flags.u.message_pending) {
168293873Ssephe			/*
169293873Ssephe			 * This will cause message queue rescan to possibly
170293873Ssephe			 * deliver another msg from the hypervisor
171293873Ssephe			 */
172293873Ssephe			wrmsr(HV_X64_MSR_EOM, 0);
173293873Ssephe		}
174293873Ssephe	}
175293873Ssephe
176300481Ssephe	msg = msg_base + HV_VMBUS_MESSAGE_SINT;
177250199Sgrehan	if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) {
178300646Ssephe		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
179300646Ssephe		    VMBUS_PCPU_PTR(sc, message_task, cpu));
180250199Sgrehan	}
181250199Sgrehan
182293873Ssephe	return (FILTER_HANDLED);
183250199Sgrehan}
184250199Sgrehan
185282212Swhuvoid
186282212Swhuhv_vector_handler(struct trapframe *trap_frame)
187282212Swhu{
188300565Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
189300565Ssephe	int cpu = curcpu;
190282212Swhu
191282212Swhu	/*
192282212Swhu	 * Disable preemption.
193282212Swhu	 */
194282212Swhu	critical_enter();
195282212Swhu
196282212Swhu	/*
197282212Swhu	 * Do a little interrupt counting.
198282212Swhu	 */
199300573Ssephe	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
200282212Swhu
201300567Ssephe	hv_vmbus_isr(sc, trap_frame, cpu);
202282212Swhu
203282212Swhu	/*
204282212Swhu	 * Enable preemption.
205282212Swhu	 */
206282212Swhu	critical_exit();
207282212Swhu}
208282212Swhu
209300571Ssephestatic void
210300572Ssephevmbus_synic_setup(void *arg __unused)
211300571Ssephe{
212300571Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
213300571Ssephe	int			cpu;
214300571Ssephe	hv_vmbus_synic_simp	simp;
215300571Ssephe	hv_vmbus_synic_siefp	siefp;
216300571Ssephe	hv_vmbus_synic_scontrol sctrl;
217300571Ssephe	hv_vmbus_synic_sint	shared_sint;
218300571Ssephe	uint64_t		version;
219300571Ssephe
220300571Ssephe	cpu = PCPU_GET(cpuid);
221300571Ssephe
222300571Ssephe	/*
223300571Ssephe	 * TODO: Check the version
224300571Ssephe	 */
225300571Ssephe	version = rdmsr(HV_X64_MSR_SVERSION);
226300571Ssephe
227300571Ssephe	/*
228300571Ssephe	 * Setup the Synic's message page
229300571Ssephe	 */
230300571Ssephe	simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
231300571Ssephe	simp.u.simp_enabled = 1;
232300572Ssephe	simp.u.base_simp_gpa =
233300573Ssephe	    VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT;
234300571Ssephe
235300571Ssephe	wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
236300571Ssephe
237300571Ssephe	/*
238300571Ssephe	 * Setup the Synic's event page
239300571Ssephe	 */
240300571Ssephe	siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
241300571Ssephe	siefp.u.siefp_enabled = 1;
242300572Ssephe	siefp.u.base_siefp_gpa =
243300573Ssephe	    VMBUS_PCPU_GET(sc, event_flag_dma.hv_paddr, cpu) >> PAGE_SHIFT;
244300571Ssephe
245300571Ssephe	wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
246300571Ssephe
247300571Ssephe	/*HV_SHARED_SINT_IDT_VECTOR + 0x20; */
248300571Ssephe	shared_sint.as_uint64_t = 0;
249300571Ssephe	shared_sint.u.vector = sc->vmbus_idtvec;
250300571Ssephe	shared_sint.u.masked = FALSE;
251300571Ssephe	shared_sint.u.auto_eoi = TRUE;
252300571Ssephe
253300571Ssephe	wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
254300571Ssephe	    shared_sint.as_uint64_t);
255300571Ssephe
256300571Ssephe	wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
257300571Ssephe	    shared_sint.as_uint64_t);
258300571Ssephe
259300571Ssephe	/* Enable the global synic bit */
260300571Ssephe	sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL);
261300571Ssephe	sctrl.u.enable = 1;
262300571Ssephe
263300571Ssephe	wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t);
264300571Ssephe
265300571Ssephe	/*
266300571Ssephe	 * Set up the cpuid mapping from Hyper-V to FreeBSD.
267300571Ssephe	 * The array is indexed using FreeBSD cpuid.
268300571Ssephe	 */
269300647Ssephe	VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(HV_X64_MSR_VP_INDEX);
270300571Ssephe}
271300571Ssephe
272300571Ssephestatic void
273300571Ssephevmbus_synic_teardown(void *arg)
274300571Ssephe{
275300571Ssephe	hv_vmbus_synic_sint	shared_sint;
276300571Ssephe	hv_vmbus_synic_simp	simp;
277300571Ssephe	hv_vmbus_synic_siefp	siefp;
278300571Ssephe
279300571Ssephe	shared_sint.as_uint64_t = rdmsr(
280300571Ssephe	    HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT);
281300571Ssephe
282300571Ssephe	shared_sint.u.masked = 1;
283300571Ssephe
284300571Ssephe	/*
285300571Ssephe	 * Disable the interrupt 0
286300571Ssephe	 */
287300571Ssephe	wrmsr(
288300571Ssephe	    HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
289300571Ssephe	    shared_sint.as_uint64_t);
290300571Ssephe
291300571Ssephe	shared_sint.as_uint64_t = rdmsr(
292300571Ssephe	    HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT);
293300571Ssephe
294300571Ssephe	shared_sint.u.masked = 1;
295300571Ssephe
296300571Ssephe	/*
297300571Ssephe	 * Disable the interrupt 1
298300571Ssephe	 */
299300571Ssephe	wrmsr(
300300571Ssephe	    HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
301300571Ssephe	    shared_sint.as_uint64_t);
302300571Ssephe	simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
303300571Ssephe	simp.u.simp_enabled = 0;
304300571Ssephe	simp.u.base_simp_gpa = 0;
305300571Ssephe
306300571Ssephe	wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
307300571Ssephe
308300571Ssephe	siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
309300571Ssephe	siefp.u.siefp_enabled = 0;
310300571Ssephe	siefp.u.base_siefp_gpa = 0;
311300571Ssephe
312300571Ssephe	wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
313300571Ssephe}
314300571Ssephe
315300644Ssephestatic int
316300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc)
317300572Ssephe{
318300572Ssephe	int cpu;
319300572Ssephe
320300572Ssephe	CPU_FOREACH(cpu) {
321300644Ssephe		void *ptr;
322300644Ssephe
323300572Ssephe		/*
324300572Ssephe		 * Per-cpu messages and event flags.
325300572Ssephe		 */
326300644Ssephe		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
327300644Ssephe		    PAGE_SIZE, 0, PAGE_SIZE,
328300573Ssephe		    VMBUS_PCPU_PTR(sc, message_dma, cpu),
329300572Ssephe		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
330300644Ssephe		if (ptr == NULL)
331300644Ssephe			return ENOMEM;
332300644Ssephe		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
333300644Ssephe
334300644Ssephe		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
335300644Ssephe		    PAGE_SIZE, 0, PAGE_SIZE,
336300573Ssephe		    VMBUS_PCPU_PTR(sc, event_flag_dma, cpu),
337300572Ssephe		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
338300644Ssephe		if (ptr == NULL)
339300644Ssephe			return ENOMEM;
340300644Ssephe		VMBUS_PCPU_GET(sc, event_flag, cpu) = ptr;
341300572Ssephe	}
342300644Ssephe	return 0;
343300572Ssephe}
344300572Ssephe
345300572Ssephestatic void
346300572Ssephevmbus_dma_free(struct vmbus_softc *sc)
347300572Ssephe{
348300572Ssephe	int cpu;
349300572Ssephe
350300572Ssephe	CPU_FOREACH(cpu) {
351300573Ssephe		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
352300572Ssephe			hyperv_dmamem_free(
353300573Ssephe			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
354300573Ssephe			    VMBUS_PCPU_GET(sc, message, cpu));
355300573Ssephe			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
356300572Ssephe		}
357300573Ssephe		if (VMBUS_PCPU_GET(sc, event_flag, cpu) != NULL) {
358300572Ssephe			hyperv_dmamem_free(
359300573Ssephe			    VMBUS_PCPU_PTR(sc, event_flag_dma, cpu),
360300573Ssephe			    VMBUS_PCPU_GET(sc, event_flag, cpu));
361300573Ssephe			VMBUS_PCPU_GET(sc, event_flag, cpu) = NULL;
362300572Ssephe		}
363300572Ssephe	}
364300572Ssephe}
365300572Ssephe
366250199Sgrehanstatic int
367300574Ssephevmbus_intr_setup(struct vmbus_softc *sc)
368300574Ssephe{
369300574Ssephe	int cpu;
370300574Ssephe
371300574Ssephe	CPU_FOREACH(cpu) {
372300574Ssephe		char buf[MAXCOMLEN + 1];
373300645Ssephe		cpuset_t cpu_mask;
374300574Ssephe
375300645Ssephe		/* Allocate an interrupt counter for Hyper-V interrupt */
376300574Ssephe		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
377300574Ssephe		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
378300574Ssephe
379300574Ssephe		/*
380300645Ssephe		 * Setup taskqueue to handle events.  Task will be per-
381300645Ssephe		 * channel.
382300574Ssephe		 */
383300646Ssephe		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
384300646Ssephe		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
385300646Ssephe		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
386300574Ssephe		CPU_SETOF(cpu, &cpu_mask);
387300574Ssephe		taskqueue_start_threads_cpuset(
388300646Ssephe		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
389300646Ssephe		    "hvevent%d", cpu);
390300574Ssephe
391300574Ssephe		/*
392300645Ssephe		 * Setup tasks and taskqueues to handle messages.
393300574Ssephe		 */
394300646Ssephe		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
395300574Ssephe		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
396300646Ssephe		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
397300574Ssephe		CPU_SETOF(cpu, &cpu_mask);
398300574Ssephe		taskqueue_start_threads_cpuset(
399300646Ssephe		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
400300646Ssephe		    "hvmsg%d", cpu);
401300646Ssephe		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
402300574Ssephe		    vmbus_msg_task, sc);
403300574Ssephe	}
404300645Ssephe
405300645Ssephe	/*
406300645Ssephe	 * All Hyper-V ISR required resources are setup, now let's find a
407300645Ssephe	 * free IDT vector for Hyper-V ISR and set it up.
408300645Ssephe	 */
409300645Ssephe	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback));
410300645Ssephe	if (sc->vmbus_idtvec < 0) {
411300645Ssephe		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
412300645Ssephe		return ENXIO;
413300645Ssephe	}
414300645Ssephe	if(bootverbose) {
415300645Ssephe		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
416300645Ssephe		    sc->vmbus_idtvec);
417300645Ssephe	}
418300574Ssephe	return 0;
419300574Ssephe}
420300574Ssephe
421300574Ssephestatic void
422300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc)
423300574Ssephe{
424300574Ssephe	int cpu;
425300574Ssephe
426300645Ssephe	if (sc->vmbus_idtvec >= 0) {
427300645Ssephe		lapic_ipi_free(sc->vmbus_idtvec);
428300645Ssephe		sc->vmbus_idtvec = -1;
429300645Ssephe	}
430300645Ssephe
431300574Ssephe	CPU_FOREACH(cpu) {
432300646Ssephe		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
433300646Ssephe			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
434300646Ssephe			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
435300574Ssephe		}
436300646Ssephe		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
437300646Ssephe			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
438300646Ssephe			    VMBUS_PCPU_PTR(sc, message_task, cpu));
439300646Ssephe			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
440300646Ssephe			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
441300576Ssephe		}
442300574Ssephe	}
443300574Ssephe}
444300574Ssephe
445300574Ssephestatic int
446250199Sgrehanvmbus_read_ivar(
447250199Sgrehan	device_t	dev,
448250199Sgrehan	device_t	child,
449250199Sgrehan	int		index,
450250199Sgrehan	uintptr_t*	result)
451250199Sgrehan{
452250199Sgrehan	struct hv_device *child_dev_ctx = device_get_ivars(child);
453250199Sgrehan
454250199Sgrehan	switch (index) {
455250199Sgrehan
456250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
457250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->class_id;
458250199Sgrehan		return (0);
459250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
460250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->device_id;
461250199Sgrehan		return (0);
462250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
463250199Sgrehan		*result = (uintptr_t) child_dev_ctx;
464250199Sgrehan		return (0);
465250199Sgrehan	case HV_VMBUS_IVAR_NODE:
466250199Sgrehan		*result = (uintptr_t) child_dev_ctx->device;
467250199Sgrehan		return (0);
468250199Sgrehan	}
469250199Sgrehan	return (ENOENT);
470250199Sgrehan}
471250199Sgrehan
472250199Sgrehanstatic int
473250199Sgrehanvmbus_write_ivar(
474250199Sgrehan	device_t	dev,
475250199Sgrehan	device_t	child,
476250199Sgrehan	int		index,
477250199Sgrehan	uintptr_t	value)
478250199Sgrehan{
479250199Sgrehan	switch (index) {
480250199Sgrehan
481250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
482250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
483250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
484250199Sgrehan	case HV_VMBUS_IVAR_NODE:
485250199Sgrehan		/* read-only */
486250199Sgrehan		return (EINVAL);
487250199Sgrehan	}
488250199Sgrehan	return (ENOENT);
489250199Sgrehan}
490250199Sgrehan
491297143Ssephestatic int
492297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
493297143Ssephe{
494297143Ssephe	char guidbuf[40];
495297143Ssephe	struct hv_device *dev_ctx = device_get_ivars(child);
496297143Ssephe
497298449Ssephe	if (dev_ctx == NULL)
498298449Ssephe		return (0);
499298449Ssephe
500297143Ssephe	strlcat(buf, "classid=", buflen);
501297143Ssephe	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id);
502297143Ssephe	strlcat(buf, guidbuf, buflen);
503297143Ssephe
504297143Ssephe	strlcat(buf, " deviceid=", buflen);
505297143Ssephe	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id);
506297143Ssephe	strlcat(buf, guidbuf, buflen);
507297143Ssephe
508297143Ssephe	return (0);
509297143Ssephe}
510297143Ssephe
511250199Sgrehanstruct hv_device*
512250199Sgrehanhv_vmbus_child_device_create(
513250199Sgrehan	hv_guid		type,
514250199Sgrehan	hv_guid		instance,
515250199Sgrehan	hv_vmbus_channel*	channel)
516250199Sgrehan{
517250199Sgrehan	hv_device* child_dev;
518250199Sgrehan
519250199Sgrehan	/*
520250199Sgrehan	 * Allocate the new child device
521250199Sgrehan	 */
522250199Sgrehan	child_dev = malloc(sizeof(hv_device), M_DEVBUF,
523295308Ssephe			M_WAITOK |  M_ZERO);
524250199Sgrehan
525250199Sgrehan	child_dev->channel = channel;
526250199Sgrehan	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
527250199Sgrehan	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
528250199Sgrehan
529250199Sgrehan	return (child_dev);
530250199Sgrehan}
531250199Sgrehan
532297142Ssepheint
533297142Ssephesnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid)
534250199Sgrehan{
535297142Ssephe	int cnt;
536297142Ssephe	const unsigned char *d = guid->data;
537297142Ssephe
538297142Ssephe	cnt = snprintf(buf, sz,
539297142Ssephe		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
540297142Ssephe		d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
541297142Ssephe		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
542297142Ssephe	return (cnt);
543250199Sgrehan}
544250199Sgrehan
545250199Sgrehanint
546250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev)
547250199Sgrehan{
548250199Sgrehan	device_t child;
549250199Sgrehan
550297142Ssephe	if (bootverbose) {
551297142Ssephe		char name[40];
552297142Ssephe		snprintf_hv_guid(name, sizeof(name), &child_dev->class_id);
553297142Ssephe		printf("VMBUS: Class ID: %s\n", name);
554297142Ssephe	}
555250199Sgrehan
556300486Ssephe	child = device_add_child(vmbus_get_device(), NULL, -1);
557250199Sgrehan	child_dev->device = child;
558250199Sgrehan	device_set_ivars(child, child_dev);
559250199Sgrehan
560250199Sgrehan	return (0);
561250199Sgrehan}
562250199Sgrehan
563250199Sgrehanint
564250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev)
565250199Sgrehan{
566250199Sgrehan	int ret = 0;
567250199Sgrehan	/*
568250199Sgrehan	 * XXXKYS: Ensure that this is the opposite of
569250199Sgrehan	 * device_add_child()
570250199Sgrehan	 */
571250199Sgrehan	mtx_lock(&Giant);
572300486Ssephe	ret = device_delete_child(vmbus_get_device(), child_dev->device);
573250199Sgrehan	mtx_unlock(&Giant);
574250199Sgrehan	return(ret);
575250199Sgrehan}
576250199Sgrehan
577250199Sgrehanstatic int
578300127Ssephevmbus_probe(device_t dev)
579300127Ssephe{
580293870Ssephe	if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL ||
581300480Ssephe	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV)
582293870Ssephe		return (ENXIO);
583250199Sgrehan
584300129Ssephe	device_set_desc(dev, "Hyper-V Vmbus");
585250199Sgrehan
586293870Ssephe	return (BUS_PROBE_DEFAULT);
587250199Sgrehan}
588250199Sgrehan
589282212Swhu/**
590250199Sgrehan * @brief Main vmbus driver initialization routine.
591250199Sgrehan *
592250199Sgrehan * Here, we
593250199Sgrehan * - initialize the vmbus driver context
594250199Sgrehan * - setup various driver entry points
595250199Sgrehan * - invoke the vmbus hv main init routine
596250199Sgrehan * - get the irq resource
597250199Sgrehan * - invoke the vmbus to add the vmbus root device
598250199Sgrehan * - setup the vmbus root device
599250199Sgrehan * - retrieve the channel offers
600250199Sgrehan */
601250199Sgrehanstatic int
602250199Sgrehanvmbus_bus_init(void)
603250199Sgrehan{
604300650Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
605300574Ssephe	int ret;
606250199Sgrehan
607300650Ssephe	if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
608250199Sgrehan		return (0);
609300650Ssephe	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
610250199Sgrehan
611250199Sgrehan	/*
612300645Ssephe	 * Allocate DMA stuffs.
613250199Sgrehan	 */
614300645Ssephe	ret = vmbus_dma_alloc(sc);
615300574Ssephe	if (ret != 0)
616282212Swhu		goto cleanup;
617250199Sgrehan
618250199Sgrehan	/*
619300645Ssephe	 * Setup interrupt.
620250199Sgrehan	 */
621300645Ssephe	ret = vmbus_intr_setup(sc);
622300644Ssephe	if (ret != 0)
623300644Ssephe		goto cleanup;
624300572Ssephe
625300650Ssephe	/*
626300650Ssephe	 * Setup SynIC.
627300650Ssephe	 */
628282212Swhu	if (bootverbose)
629300650Ssephe		device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
630300572Ssephe	smp_rendezvous(NULL, vmbus_synic_setup, NULL, NULL);
631300650Ssephe	sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
632282212Swhu
633255414Sgrehan	/*
634250199Sgrehan	 * Connect to VMBus in the root partition
635250199Sgrehan	 */
636250199Sgrehan	ret = hv_vmbus_connect();
637250199Sgrehan
638255414Sgrehan	if (ret != 0)
639300574Ssephe		goto cleanup;
640250199Sgrehan
641300107Ssephe	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
642300107Ssephe	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
643300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc_compat;
644300107Ssephe	else
645300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc;
646300107Ssephe
647250199Sgrehan	hv_vmbus_request_channel_offers();
648298260Ssephe
649298260Ssephe	vmbus_scan();
650300486Ssephe	bus_generic_attach(sc->vmbus_dev);
651300486Ssephe	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
652298260Ssephe
653250199Sgrehan	return (ret);
654250199Sgrehan
655300574Ssephecleanup:
656300645Ssephe	vmbus_intr_teardown(sc);
657300572Ssephe	vmbus_dma_free(sc);
658250199Sgrehan
659250199Sgrehan	return (ret);
660250199Sgrehan}
661250199Sgrehan
662300107Ssephestatic void
663300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
664300107Ssephe{
665300107Ssephe}
666300107Ssephe
667250199Sgrehanstatic int
668250199Sgrehanvmbus_attach(device_t dev)
669250199Sgrehan{
670300102Ssephe	vmbus_sc = device_get_softc(dev);
671300486Ssephe	vmbus_sc->vmbus_dev = dev;
672300574Ssephe	vmbus_sc->vmbus_idtvec = -1;
673250199Sgrehan
674300107Ssephe	/*
675300107Ssephe	 * Event processing logic will be configured:
676300107Ssephe	 * - After the vmbus protocol version negotiation.
677300107Ssephe	 * - Before we request channel offers.
678300107Ssephe	 */
679300107Ssephe	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
680300107Ssephe
681299746Sjhb#ifndef EARLY_AP_STARTUP
682250199Sgrehan	/*
683250199Sgrehan	 * If the system has already booted and thread
684250199Sgrehan	 * scheduling is possible indicated by the global
685250199Sgrehan	 * cold set to zero, we just call the driver
686250199Sgrehan	 * initialization directly.
687250199Sgrehan	 */
688250199Sgrehan	if (!cold)
689299746Sjhb#endif
690250199Sgrehan		vmbus_bus_init();
691250199Sgrehan
692298449Ssephe	bus_generic_probe(dev);
693250199Sgrehan	return (0);
694250199Sgrehan}
695250199Sgrehan
696250199Sgrehanstatic void
697300126Ssephevmbus_sysinit(void *arg __unused)
698250199Sgrehan{
699300102Ssephe	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
700256425Sgibbs		return;
701256425Sgibbs
702299746Sjhb#ifndef EARLY_AP_STARTUP
703250199Sgrehan	/*
704250199Sgrehan	 * If the system has already booted and thread
705256425Sgibbs	 * scheduling is possible, as indicated by the
706256425Sgibbs	 * global cold set to zero, we just call the driver
707250199Sgrehan	 * initialization directly.
708250199Sgrehan	 */
709250199Sgrehan	if (!cold)
710299746Sjhb#endif
711250199Sgrehan		vmbus_bus_init();
712250199Sgrehan}
713250199Sgrehan
714300121Ssephestatic int
715300121Ssephevmbus_detach(device_t dev)
716250199Sgrehan{
717300487Ssephe	struct vmbus_softc *sc = device_get_softc(dev);
718255414Sgrehan
719250199Sgrehan	hv_vmbus_release_unattached_channels();
720250199Sgrehan	hv_vmbus_disconnect();
721250199Sgrehan
722300650Ssephe	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
723300650Ssephe		sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
724300650Ssephe		smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
725300650Ssephe	}
726250199Sgrehan
727300645Ssephe	vmbus_intr_teardown(sc);
728300572Ssephe	vmbus_dma_free(sc);
729255414Sgrehan
730250199Sgrehan	return (0);
731250199Sgrehan}
732250199Sgrehan
733250199Sgrehanstatic device_method_t vmbus_methods[] = {
734300124Ssephe	/* Device interface */
735300124Ssephe	DEVMETHOD(device_probe,			vmbus_probe),
736300124Ssephe	DEVMETHOD(device_attach,		vmbus_attach),
737300124Ssephe	DEVMETHOD(device_detach,		vmbus_detach),
738300124Ssephe	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
739300124Ssephe	DEVMETHOD(device_suspend,		bus_generic_suspend),
740300124Ssephe	DEVMETHOD(device_resume,		bus_generic_resume),
741250199Sgrehan
742300124Ssephe	/* Bus interface */
743300124Ssephe	DEVMETHOD(bus_add_child,		bus_generic_add_child),
744300124Ssephe	DEVMETHOD(bus_print_child,		bus_generic_print_child),
745300124Ssephe	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
746300124Ssephe	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
747300124Ssephe	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
748250199Sgrehan
749300124Ssephe	DEVMETHOD_END
750300124Ssephe};
751250199Sgrehan
752300102Ssephestatic driver_t vmbus_driver = {
753300102Ssephe	"vmbus",
754300102Ssephe	vmbus_methods,
755300102Ssephe	sizeof(struct vmbus_softc)
756300102Ssephe};
757250199Sgrehan
758300123Ssephestatic devclass_t vmbus_devclass;
759250199Sgrehan
760300120SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
761293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1);
762293870SsepheMODULE_VERSION(vmbus, 1);
763250199Sgrehan
764299746Sjhb#ifndef EARLY_AP_STARTUP
765300126Ssephe/*
766300126Ssephe * NOTE:
767300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
768300126Ssephe * initialized.
769300126Ssephe */
770300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
771299746Sjhb#endif
772