vmbus.c revision 301484
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/vmbus.c 301484 2016-06-06 06:18:18Z 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>
64300654Ssephe#include <dev/hyperv/vmbus/hyperv_reg.h>
65300834Ssephe#include <dev/hyperv/vmbus/hyperv_var.h>
66301019Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h>
67300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
68250199Sgrehan
69293870Ssephe#include <contrib/dev/acpica/include/acpi.h>
70293870Ssephe#include "acpi_if.h"
71250199Sgrehan
72300102Ssephestruct vmbus_softc	*vmbus_sc;
73300102Ssephe
74301015Ssepheextern inthand_t IDTVEC(vmbus_isr);
75300574Ssephe
76250199Sgrehanstatic void
77300572Ssephevmbus_msg_task(void *xsc, int pending __unused)
78250199Sgrehan{
79300572Ssephe	struct vmbus_softc *sc = xsc;
80301019Ssephe	volatile struct vmbus_message *msg;
81250199Sgrehan
82300988Ssephe	msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE;
83300108Ssephe	for (;;) {
84300108Ssephe		const hv_vmbus_channel_msg_table_entry *entry;
85300108Ssephe		hv_vmbus_channel_msg_header *hdr;
86300108Ssephe		hv_vmbus_channel_msg_type msg_type;
87282212Swhu
88301484Ssephe		if (msg->msg_type == VMBUS_MSGTYPE_NONE) {
89301484Ssephe			/* No message */
90301484Ssephe			break;
91301484Ssephe		} else if (msg->msg_type != VMBUS_MSGTYPE_CHANNEL) {
92301484Ssephe			/* Not a channel message */
93301484Ssephe			goto handled;
94301484Ssephe		}
95292861Sdelphij
96301019Ssephe		/* XXX: update messageHandler interface */
97301019Ssephe		hdr = __DEVOLATILE(hv_vmbus_channel_msg_header *,
98301019Ssephe		    msg->msg_data);
99292861Sdelphij		msg_type = hdr->message_type;
100292861Sdelphij
101295307Ssephe		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
102292861Sdelphij			printf("VMBUS: unknown message type = %d\n", msg_type);
103292861Sdelphij			goto handled;
104292861Sdelphij		}
105292861Sdelphij
106292861Sdelphij		entry = &g_channel_message_table[msg_type];
107295307Ssephe		if (entry->messageHandler)
108292861Sdelphij			entry->messageHandler(hdr);
109292861Sdelphijhandled:
110301019Ssephe		msg->msg_type = VMBUS_MSGTYPE_NONE;
111300108Ssephe		/*
112301019Ssephe		 * Make sure the write to msg_type (i.e. set to
113301019Ssephe		 * VMBUS_MSGTYPE_NONE) happens before we read the
114301019Ssephe		 * msg_flags and EOMing. Otherwise, the EOMing will
115301019Ssephe		 * not deliver any more messages since there is no
116301019Ssephe		 * empty slot
117300108Ssephe		 *
118300108Ssephe		 * NOTE:
119300108Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
120300108Ssephe		 * will become compiler fence on UP kernel.
121300108Ssephe		 */
122300108Ssephe		mb();
123301019Ssephe		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
124250199Sgrehan			/*
125250199Sgrehan			 * This will cause message queue rescan to possibly
126250199Sgrehan			 * deliver another msg from the hypervisor
127250199Sgrehan			 */
128300830Ssephe			wrmsr(MSR_HV_EOM, 0);
129300108Ssephe		}
130250199Sgrehan	}
131250199Sgrehan}
132250199Sgrehan
133301015Ssephestatic __inline int
134301015Ssephevmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
135250199Sgrehan{
136301019Ssephe	volatile struct vmbus_message *msg;
137301019Ssephe	struct vmbus_message *msg_base;
138250199Sgrehan
139301009Ssephe	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
140301009Ssephe
141250199Sgrehan	/*
142301009Ssephe	 * Check event timer.
143301009Ssephe	 *
144301009Ssephe	 * TODO: move this to independent IDT vector.
145250199Sgrehan	 */
146300988Ssephe	msg = msg_base + VMBUS_SINT_TIMER;
147301019Ssephe	if (msg->msg_type == VMBUS_MSGTYPE_TIMER_EXPIRED) {
148301019Ssephe		msg->msg_type = VMBUS_MSGTYPE_NONE;
149293873Ssephe
150300988Ssephe		vmbus_et_intr(frame);
151297176Ssephe
152293873Ssephe		/*
153301019Ssephe		 * Make sure the write to msg_type (i.e. set to
154301019Ssephe		 * VMBUS_MSGTYPE_NONE) happens before we read the
155301019Ssephe		 * msg_flags and EOMing. Otherwise, the EOMing will
156301019Ssephe		 * not deliver any more messages since there is no
157301019Ssephe		 * empty slot
158297634Ssephe		 *
159297634Ssephe		 * NOTE:
160297634Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
161297636Ssephe		 * will become compiler fence on UP kernel.
162293873Ssephe		 */
163297634Ssephe		mb();
164301019Ssephe		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
165293873Ssephe			/*
166293873Ssephe			 * This will cause message queue rescan to possibly
167293873Ssephe			 * deliver another msg from the hypervisor
168293873Ssephe			 */
169300830Ssephe			wrmsr(MSR_HV_EOM, 0);
170293873Ssephe		}
171293873Ssephe	}
172293873Ssephe
173301009Ssephe	/*
174301009Ssephe	 * Check events.  Hot path for network and storage I/O data; high rate.
175301009Ssephe	 *
176301009Ssephe	 * NOTE:
177301009Ssephe	 * As recommended by the Windows guest fellows, we check events before
178301009Ssephe	 * checking messages.
179301009Ssephe	 */
180301009Ssephe	sc->vmbus_event_proc(sc, cpu);
181301009Ssephe
182301009Ssephe	/*
183301009Ssephe	 * Check messages.  Mainly management stuffs; ultra low rate.
184301009Ssephe	 */
185300988Ssephe	msg = msg_base + VMBUS_SINT_MESSAGE;
186301019Ssephe	if (__predict_false(msg->msg_type != VMBUS_MSGTYPE_NONE)) {
187300646Ssephe		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
188300646Ssephe		    VMBUS_PCPU_PTR(sc, message_task, cpu));
189250199Sgrehan	}
190250199Sgrehan
191293873Ssephe	return (FILTER_HANDLED);
192250199Sgrehan}
193250199Sgrehan
194282212Swhuvoid
195301015Ssephevmbus_handle_intr(struct trapframe *trap_frame)
196282212Swhu{
197300565Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
198300565Ssephe	int cpu = curcpu;
199282212Swhu
200282212Swhu	/*
201282212Swhu	 * Disable preemption.
202282212Swhu	 */
203282212Swhu	critical_enter();
204282212Swhu
205282212Swhu	/*
206282212Swhu	 * Do a little interrupt counting.
207282212Swhu	 */
208300573Ssephe	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
209282212Swhu
210301015Ssephe	vmbus_handle_intr1(sc, trap_frame, cpu);
211282212Swhu
212282212Swhu	/*
213282212Swhu	 * Enable preemption.
214282212Swhu	 */
215282212Swhu	critical_exit();
216282212Swhu}
217282212Swhu
218300571Ssephestatic void
219300652Ssephevmbus_synic_setup(void *xsc)
220300571Ssephe{
221300652Ssephe	struct vmbus_softc *sc = xsc;
222300654Ssephe	int cpu = curcpu;
223300654Ssephe	uint64_t val, orig;
224300654Ssephe	uint32_t sint;
225300571Ssephe
226300834Ssephe	if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
227300834Ssephe		/*
228300834Ssephe		 * Save virtual processor id.
229300834Ssephe		 */
230300834Ssephe		VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX);
231300834Ssephe	} else {
232300834Ssephe		/*
233300834Ssephe		 * XXX
234300834Ssephe		 * Virtual processoor id is only used by a pretty broken
235300834Ssephe		 * channel selection code from storvsc.  It's nothing
236300834Ssephe		 * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep
237300834Ssephe		 * moving on.
238300834Ssephe		 */
239300834Ssephe		VMBUS_PCPU_GET(sc, vcpuid, cpu) = cpu;
240300834Ssephe	}
241300571Ssephe
242300571Ssephe	/*
243300654Ssephe	 * Setup the SynIC message.
244300571Ssephe	 */
245300654Ssephe	orig = rdmsr(MSR_HV_SIMP);
246300654Ssephe	val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
247300654Ssephe	    ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) <<
248300654Ssephe	     MSR_HV_SIMP_PGSHIFT);
249300654Ssephe	wrmsr(MSR_HV_SIMP, val);
250300571Ssephe
251300571Ssephe	/*
252300654Ssephe	 * Setup the SynIC event flags.
253300571Ssephe	 */
254300654Ssephe	orig = rdmsr(MSR_HV_SIEFP);
255300654Ssephe	val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
256301106Ssephe	    ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu)
257301106Ssephe	      >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT);
258300654Ssephe	wrmsr(MSR_HV_SIEFP, val);
259300571Ssephe
260300571Ssephe
261300654Ssephe	/*
262300654Ssephe	 * Configure and unmask SINT for message and event flags.
263300654Ssephe	 */
264300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
265300654Ssephe	orig = rdmsr(sint);
266300654Ssephe	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
267300654Ssephe	    (orig & MSR_HV_SINT_RSVD_MASK);
268300654Ssephe	wrmsr(sint, val);
269300571Ssephe
270300654Ssephe	/*
271300654Ssephe	 * Configure and unmask SINT for timer.
272300654Ssephe	 */
273300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
274300654Ssephe	orig = rdmsr(sint);
275300654Ssephe	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
276300654Ssephe	    (orig & MSR_HV_SINT_RSVD_MASK);
277300654Ssephe	wrmsr(sint, val);
278300571Ssephe
279300571Ssephe	/*
280300654Ssephe	 * All done; enable SynIC.
281300571Ssephe	 */
282300654Ssephe	orig = rdmsr(MSR_HV_SCONTROL);
283300654Ssephe	val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
284300654Ssephe	wrmsr(MSR_HV_SCONTROL, val);
285300571Ssephe}
286300571Ssephe
287300571Ssephestatic void
288300571Ssephevmbus_synic_teardown(void *arg)
289300571Ssephe{
290300654Ssephe	uint64_t orig;
291300654Ssephe	uint32_t sint;
292300571Ssephe
293300654Ssephe	/*
294300654Ssephe	 * Disable SynIC.
295300654Ssephe	 */
296300654Ssephe	orig = rdmsr(MSR_HV_SCONTROL);
297300654Ssephe	wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
298300571Ssephe
299300654Ssephe	/*
300300654Ssephe	 * Mask message and event flags SINT.
301300654Ssephe	 */
302300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
303300654Ssephe	orig = rdmsr(sint);
304300654Ssephe	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
305300571Ssephe
306300571Ssephe	/*
307300654Ssephe	 * Mask timer SINT.
308300571Ssephe	 */
309300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
310300654Ssephe	orig = rdmsr(sint);
311300654Ssephe	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
312300571Ssephe
313300654Ssephe	/*
314300654Ssephe	 * Teardown SynIC message.
315300654Ssephe	 */
316300654Ssephe	orig = rdmsr(MSR_HV_SIMP);
317300654Ssephe	wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
318300571Ssephe
319300571Ssephe	/*
320300654Ssephe	 * Teardown SynIC event flags.
321300571Ssephe	 */
322300654Ssephe	orig = rdmsr(MSR_HV_SIEFP);
323300654Ssephe	wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
324300571Ssephe}
325300571Ssephe
326300644Ssephestatic int
327300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc)
328300572Ssephe{
329300572Ssephe	int cpu;
330300572Ssephe
331300572Ssephe	CPU_FOREACH(cpu) {
332300644Ssephe		void *ptr;
333300644Ssephe
334300572Ssephe		/*
335300572Ssephe		 * Per-cpu messages and event flags.
336300572Ssephe		 */
337300644Ssephe		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
338300644Ssephe		    PAGE_SIZE, 0, PAGE_SIZE,
339300573Ssephe		    VMBUS_PCPU_PTR(sc, message_dma, cpu),
340300572Ssephe		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
341300644Ssephe		if (ptr == NULL)
342300644Ssephe			return ENOMEM;
343300644Ssephe		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
344300644Ssephe
345300644Ssephe		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
346300644Ssephe		    PAGE_SIZE, 0, PAGE_SIZE,
347301106Ssephe		    VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
348300572Ssephe		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
349300644Ssephe		if (ptr == NULL)
350300644Ssephe			return ENOMEM;
351301106Ssephe		VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr;
352300572Ssephe	}
353300644Ssephe	return 0;
354300572Ssephe}
355300572Ssephe
356300572Ssephestatic void
357300572Ssephevmbus_dma_free(struct vmbus_softc *sc)
358300572Ssephe{
359300572Ssephe	int cpu;
360300572Ssephe
361300572Ssephe	CPU_FOREACH(cpu) {
362300573Ssephe		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
363300572Ssephe			hyperv_dmamem_free(
364300573Ssephe			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
365300573Ssephe			    VMBUS_PCPU_GET(sc, message, cpu));
366300573Ssephe			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
367300572Ssephe		}
368301106Ssephe		if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) {
369300572Ssephe			hyperv_dmamem_free(
370301106Ssephe			    VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
371301106Ssephe			    VMBUS_PCPU_GET(sc, event_flags, cpu));
372301106Ssephe			VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL;
373300572Ssephe		}
374300572Ssephe	}
375300572Ssephe}
376300572Ssephe
377250199Sgrehanstatic int
378300574Ssephevmbus_intr_setup(struct vmbus_softc *sc)
379300574Ssephe{
380300574Ssephe	int cpu;
381300574Ssephe
382300574Ssephe	CPU_FOREACH(cpu) {
383300574Ssephe		char buf[MAXCOMLEN + 1];
384300645Ssephe		cpuset_t cpu_mask;
385300574Ssephe
386300645Ssephe		/* Allocate an interrupt counter for Hyper-V interrupt */
387300574Ssephe		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
388300574Ssephe		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
389300574Ssephe
390300574Ssephe		/*
391300645Ssephe		 * Setup taskqueue to handle events.  Task will be per-
392300645Ssephe		 * channel.
393300574Ssephe		 */
394300646Ssephe		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
395300646Ssephe		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
396300646Ssephe		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
397300574Ssephe		CPU_SETOF(cpu, &cpu_mask);
398300574Ssephe		taskqueue_start_threads_cpuset(
399300646Ssephe		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
400300646Ssephe		    "hvevent%d", cpu);
401300574Ssephe
402300574Ssephe		/*
403300645Ssephe		 * Setup tasks and taskqueues to handle messages.
404300574Ssephe		 */
405300646Ssephe		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
406300574Ssephe		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
407300646Ssephe		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
408300574Ssephe		CPU_SETOF(cpu, &cpu_mask);
409300574Ssephe		taskqueue_start_threads_cpuset(
410300646Ssephe		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
411300646Ssephe		    "hvmsg%d", cpu);
412300646Ssephe		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
413300574Ssephe		    vmbus_msg_task, sc);
414300574Ssephe	}
415300645Ssephe
416300645Ssephe	/*
417300645Ssephe	 * All Hyper-V ISR required resources are setup, now let's find a
418300645Ssephe	 * free IDT vector for Hyper-V ISR and set it up.
419300645Ssephe	 */
420301015Ssephe	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr));
421300645Ssephe	if (sc->vmbus_idtvec < 0) {
422300645Ssephe		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
423300645Ssephe		return ENXIO;
424300645Ssephe	}
425300645Ssephe	if(bootverbose) {
426300645Ssephe		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
427300645Ssephe		    sc->vmbus_idtvec);
428300645Ssephe	}
429300574Ssephe	return 0;
430300574Ssephe}
431300574Ssephe
432300574Ssephestatic void
433300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc)
434300574Ssephe{
435300574Ssephe	int cpu;
436300574Ssephe
437300645Ssephe	if (sc->vmbus_idtvec >= 0) {
438300645Ssephe		lapic_ipi_free(sc->vmbus_idtvec);
439300645Ssephe		sc->vmbus_idtvec = -1;
440300645Ssephe	}
441300645Ssephe
442300574Ssephe	CPU_FOREACH(cpu) {
443300646Ssephe		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
444300646Ssephe			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
445300646Ssephe			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
446300574Ssephe		}
447300646Ssephe		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
448300646Ssephe			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
449300646Ssephe			    VMBUS_PCPU_PTR(sc, message_task, cpu));
450300646Ssephe			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
451300646Ssephe			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
452300576Ssephe		}
453300574Ssephe	}
454300574Ssephe}
455300574Ssephe
456300574Ssephestatic int
457300651Ssephevmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
458250199Sgrehan{
459250199Sgrehan	struct hv_device *child_dev_ctx = device_get_ivars(child);
460250199Sgrehan
461250199Sgrehan	switch (index) {
462250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
463301020Ssephe		*result = (uintptr_t)&child_dev_ctx->class_id;
464250199Sgrehan		return (0);
465301020Ssephe
466250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
467301020Ssephe		*result = (uintptr_t)&child_dev_ctx->device_id;
468250199Sgrehan		return (0);
469301020Ssephe
470250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
471301020Ssephe		*result = (uintptr_t)child_dev_ctx;
472250199Sgrehan		return (0);
473301020Ssephe
474250199Sgrehan	case HV_VMBUS_IVAR_NODE:
475301020Ssephe		*result = (uintptr_t)child_dev_ctx->device;
476250199Sgrehan		return (0);
477250199Sgrehan	}
478250199Sgrehan	return (ENOENT);
479250199Sgrehan}
480250199Sgrehan
481250199Sgrehanstatic int
482300651Ssephevmbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
483250199Sgrehan{
484250199Sgrehan	switch (index) {
485250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
486250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
487250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
488250199Sgrehan	case HV_VMBUS_IVAR_NODE:
489250199Sgrehan		/* read-only */
490250199Sgrehan		return (EINVAL);
491250199Sgrehan	}
492250199Sgrehan	return (ENOENT);
493250199Sgrehan}
494250199Sgrehan
495297143Ssephestatic int
496297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
497297143Ssephe{
498297143Ssephe	struct hv_device *dev_ctx = device_get_ivars(child);
499301021Ssephe	char guidbuf[HYPERV_GUID_STRLEN];
500297143Ssephe
501298449Ssephe	if (dev_ctx == NULL)
502298449Ssephe		return (0);
503298449Ssephe
504297143Ssephe	strlcat(buf, "classid=", buflen);
505301021Ssephe	hyperv_guid2str(&dev_ctx->class_id, guidbuf, sizeof(guidbuf));
506297143Ssephe	strlcat(buf, guidbuf, buflen);
507297143Ssephe
508297143Ssephe	strlcat(buf, " deviceid=", buflen);
509301021Ssephe	hyperv_guid2str(&dev_ctx->device_id, guidbuf, sizeof(guidbuf));
510297143Ssephe	strlcat(buf, guidbuf, buflen);
511297143Ssephe
512297143Ssephe	return (0);
513297143Ssephe}
514297143Ssephe
515300651Ssephestruct hv_device *
516300651Ssephehv_vmbus_child_device_create(hv_guid type, hv_guid instance,
517300651Ssephe    hv_vmbus_channel *channel)
518250199Sgrehan{
519300651Ssephe	hv_device *child_dev;
520250199Sgrehan
521250199Sgrehan	/*
522250199Sgrehan	 * Allocate the new child device
523250199Sgrehan	 */
524300651Ssephe	child_dev = malloc(sizeof(hv_device), M_DEVBUF, M_WAITOK | M_ZERO);
525250199Sgrehan
526250199Sgrehan	child_dev->channel = channel;
527250199Sgrehan	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
528250199Sgrehan	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
529250199Sgrehan
530250199Sgrehan	return (child_dev);
531250199Sgrehan}
532250199Sgrehan
533297142Ssepheint
534250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev)
535250199Sgrehan{
536301021Ssephe	device_t child, parent;
537250199Sgrehan
538301021Ssephe	parent = vmbus_get_device();
539297142Ssephe	if (bootverbose) {
540301021Ssephe		char name[HYPERV_GUID_STRLEN];
541301021Ssephe
542301021Ssephe		hyperv_guid2str(&child_dev->class_id, name, sizeof(name));
543301021Ssephe		device_printf(parent, "add device, classid: %s\n", name);
544297142Ssephe	}
545250199Sgrehan
546301021Ssephe	child = device_add_child(parent, NULL, -1);
547250199Sgrehan	child_dev->device = child;
548250199Sgrehan	device_set_ivars(child, child_dev);
549250199Sgrehan
550250199Sgrehan	return (0);
551250199Sgrehan}
552250199Sgrehan
553250199Sgrehanint
554250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev)
555250199Sgrehan{
556250199Sgrehan	int ret = 0;
557250199Sgrehan	/*
558250199Sgrehan	 * XXXKYS: Ensure that this is the opposite of
559250199Sgrehan	 * device_add_child()
560250199Sgrehan	 */
561250199Sgrehan	mtx_lock(&Giant);
562300486Ssephe	ret = device_delete_child(vmbus_get_device(), child_dev->device);
563250199Sgrehan	mtx_unlock(&Giant);
564250199Sgrehan	return(ret);
565250199Sgrehan}
566250199Sgrehan
567250199Sgrehanstatic int
568300127Ssephevmbus_probe(device_t dev)
569300127Ssephe{
570301018Ssephe	char *id[] = { "VMBUS", NULL };
571301018Ssephe
572301018Ssephe	if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL ||
573300834Ssephe	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV ||
574300834Ssephe	    (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
575293870Ssephe		return (ENXIO);
576250199Sgrehan
577300129Ssephe	device_set_desc(dev, "Hyper-V Vmbus");
578250199Sgrehan
579293870Ssephe	return (BUS_PROBE_DEFAULT);
580250199Sgrehan}
581250199Sgrehan
582282212Swhu/**
583250199Sgrehan * @brief Main vmbus driver initialization routine.
584250199Sgrehan *
585250199Sgrehan * Here, we
586250199Sgrehan * - initialize the vmbus driver context
587250199Sgrehan * - setup various driver entry points
588250199Sgrehan * - invoke the vmbus hv main init routine
589250199Sgrehan * - get the irq resource
590250199Sgrehan * - invoke the vmbus to add the vmbus root device
591250199Sgrehan * - setup the vmbus root device
592250199Sgrehan * - retrieve the channel offers
593250199Sgrehan */
594250199Sgrehanstatic int
595250199Sgrehanvmbus_bus_init(void)
596250199Sgrehan{
597300650Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
598300574Ssephe	int ret;
599250199Sgrehan
600300650Ssephe	if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
601250199Sgrehan		return (0);
602300650Ssephe	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
603250199Sgrehan
604250199Sgrehan	/*
605300645Ssephe	 * Allocate DMA stuffs.
606250199Sgrehan	 */
607300645Ssephe	ret = vmbus_dma_alloc(sc);
608300574Ssephe	if (ret != 0)
609282212Swhu		goto cleanup;
610250199Sgrehan
611250199Sgrehan	/*
612300645Ssephe	 * Setup interrupt.
613250199Sgrehan	 */
614300645Ssephe	ret = vmbus_intr_setup(sc);
615300644Ssephe	if (ret != 0)
616300644Ssephe		goto cleanup;
617300572Ssephe
618300650Ssephe	/*
619300650Ssephe	 * Setup SynIC.
620300650Ssephe	 */
621282212Swhu	if (bootverbose)
622300650Ssephe		device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
623300652Ssephe	smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc);
624300650Ssephe	sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
625282212Swhu
626255414Sgrehan	/*
627250199Sgrehan	 * Connect to VMBus in the root partition
628250199Sgrehan	 */
629250199Sgrehan	ret = hv_vmbus_connect();
630250199Sgrehan
631255414Sgrehan	if (ret != 0)
632300574Ssephe		goto cleanup;
633250199Sgrehan
634300107Ssephe	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
635300107Ssephe	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
636300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc_compat;
637300107Ssephe	else
638300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc;
639300107Ssephe
640250199Sgrehan	hv_vmbus_request_channel_offers();
641298260Ssephe
642298260Ssephe	vmbus_scan();
643300486Ssephe	bus_generic_attach(sc->vmbus_dev);
644300486Ssephe	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
645298260Ssephe
646250199Sgrehan	return (ret);
647250199Sgrehan
648300574Ssephecleanup:
649300645Ssephe	vmbus_intr_teardown(sc);
650300572Ssephe	vmbus_dma_free(sc);
651250199Sgrehan
652250199Sgrehan	return (ret);
653250199Sgrehan}
654250199Sgrehan
655300107Ssephestatic void
656300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
657300107Ssephe{
658300107Ssephe}
659300107Ssephe
660250199Sgrehanstatic int
661250199Sgrehanvmbus_attach(device_t dev)
662250199Sgrehan{
663300102Ssephe	vmbus_sc = device_get_softc(dev);
664300486Ssephe	vmbus_sc->vmbus_dev = dev;
665300574Ssephe	vmbus_sc->vmbus_idtvec = -1;
666250199Sgrehan
667300107Ssephe	/*
668300107Ssephe	 * Event processing logic will be configured:
669300107Ssephe	 * - After the vmbus protocol version negotiation.
670300107Ssephe	 * - Before we request channel offers.
671300107Ssephe	 */
672300107Ssephe	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
673300107Ssephe
674299746Sjhb#ifndef EARLY_AP_STARTUP
675250199Sgrehan	/*
676250199Sgrehan	 * If the system has already booted and thread
677250199Sgrehan	 * scheduling is possible indicated by the global
678250199Sgrehan	 * cold set to zero, we just call the driver
679250199Sgrehan	 * initialization directly.
680250199Sgrehan	 */
681250199Sgrehan	if (!cold)
682299746Sjhb#endif
683250199Sgrehan		vmbus_bus_init();
684250199Sgrehan
685298449Ssephe	bus_generic_probe(dev);
686250199Sgrehan	return (0);
687250199Sgrehan}
688250199Sgrehan
689250199Sgrehanstatic void
690300126Ssephevmbus_sysinit(void *arg __unused)
691250199Sgrehan{
692300102Ssephe	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
693256425Sgibbs		return;
694256425Sgibbs
695299746Sjhb#ifndef EARLY_AP_STARTUP
696250199Sgrehan	/*
697250199Sgrehan	 * If the system has already booted and thread
698256425Sgibbs	 * scheduling is possible, as indicated by the
699256425Sgibbs	 * global cold set to zero, we just call the driver
700250199Sgrehan	 * initialization directly.
701250199Sgrehan	 */
702250199Sgrehan	if (!cold)
703299746Sjhb#endif
704250199Sgrehan		vmbus_bus_init();
705250199Sgrehan}
706250199Sgrehan
707300121Ssephestatic int
708300121Ssephevmbus_detach(device_t dev)
709250199Sgrehan{
710300487Ssephe	struct vmbus_softc *sc = device_get_softc(dev);
711255414Sgrehan
712250199Sgrehan	hv_vmbus_release_unattached_channels();
713250199Sgrehan	hv_vmbus_disconnect();
714250199Sgrehan
715300650Ssephe	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
716300650Ssephe		sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
717300650Ssephe		smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
718300650Ssephe	}
719250199Sgrehan
720300645Ssephe	vmbus_intr_teardown(sc);
721300572Ssephe	vmbus_dma_free(sc);
722255414Sgrehan
723250199Sgrehan	return (0);
724250199Sgrehan}
725250199Sgrehan
726250199Sgrehanstatic device_method_t vmbus_methods[] = {
727300124Ssephe	/* Device interface */
728300124Ssephe	DEVMETHOD(device_probe,			vmbus_probe),
729300124Ssephe	DEVMETHOD(device_attach,		vmbus_attach),
730300124Ssephe	DEVMETHOD(device_detach,		vmbus_detach),
731300124Ssephe	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
732300124Ssephe	DEVMETHOD(device_suspend,		bus_generic_suspend),
733300124Ssephe	DEVMETHOD(device_resume,		bus_generic_resume),
734250199Sgrehan
735300124Ssephe	/* Bus interface */
736300124Ssephe	DEVMETHOD(bus_add_child,		bus_generic_add_child),
737300124Ssephe	DEVMETHOD(bus_print_child,		bus_generic_print_child),
738300124Ssephe	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
739300124Ssephe	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
740300124Ssephe	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
741250199Sgrehan
742300124Ssephe	DEVMETHOD_END
743300124Ssephe};
744250199Sgrehan
745300102Ssephestatic driver_t vmbus_driver = {
746300102Ssephe	"vmbus",
747300102Ssephe	vmbus_methods,
748300102Ssephe	sizeof(struct vmbus_softc)
749300102Ssephe};
750250199Sgrehan
751300123Ssephestatic devclass_t vmbus_devclass;
752250199Sgrehan
753300120SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
754293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1);
755293870SsepheMODULE_VERSION(vmbus, 1);
756250199Sgrehan
757299746Sjhb#ifndef EARLY_AP_STARTUP
758300126Ssephe/*
759300126Ssephe * NOTE:
760300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
761300126Ssephe * initialized.
762300126Ssephe */
763300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
764299746Sjhb#endif
765