xref: /freebsd/sys/dev/hyperv/vmbus/vmbus.c (revision 236764b1c24542d51f1e8410297a8c9e3299a1b2)
1b7bb4816SSepherosa Ziehau /*-
2b7bb4816SSepherosa Ziehau  * Copyright (c) 2009-2012,2016 Microsoft Corp.
3b7bb4816SSepherosa Ziehau  * Copyright (c) 2012 NetApp Inc.
4b7bb4816SSepherosa Ziehau  * Copyright (c) 2012 Citrix Inc.
5b7bb4816SSepherosa Ziehau  * All rights reserved.
6b7bb4816SSepherosa Ziehau  *
7b7bb4816SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
8b7bb4816SSepherosa Ziehau  * modification, are permitted provided that the following conditions
9b7bb4816SSepherosa Ziehau  * are met:
10b7bb4816SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
11b7bb4816SSepherosa Ziehau  *    notice unmodified, this list of conditions, and the following
12b7bb4816SSepherosa Ziehau  *    disclaimer.
13b7bb4816SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
14b7bb4816SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
15b7bb4816SSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
16b7bb4816SSepherosa Ziehau  *
17b7bb4816SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18b7bb4816SSepherosa Ziehau  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19b7bb4816SSepherosa Ziehau  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20b7bb4816SSepherosa Ziehau  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21b7bb4816SSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22b7bb4816SSepherosa Ziehau  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23b7bb4816SSepherosa Ziehau  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24b7bb4816SSepherosa Ziehau  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25b7bb4816SSepherosa Ziehau  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26b7bb4816SSepherosa Ziehau  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27b7bb4816SSepherosa Ziehau  */
28b7bb4816SSepherosa Ziehau 
29b7bb4816SSepherosa Ziehau /*
30b7bb4816SSepherosa Ziehau  * VM Bus Driver Implementation
31b7bb4816SSepherosa Ziehau  */
32b7bb4816SSepherosa Ziehau #include <sys/cdefs.h>
33b7bb4816SSepherosa Ziehau __FBSDID("$FreeBSD$");
34b7bb4816SSepherosa Ziehau 
35b7bb4816SSepherosa Ziehau #include <sys/param.h>
36b7bb4816SSepherosa Ziehau #include <sys/bus.h>
37b7bb4816SSepherosa Ziehau #include <sys/kernel.h>
38b7bb4816SSepherosa Ziehau #include <sys/lock.h>
39b7bb4816SSepherosa Ziehau #include <sys/malloc.h>
40b7bb4816SSepherosa Ziehau #include <sys/module.h>
41b7bb4816SSepherosa Ziehau #include <sys/proc.h>
42b7bb4816SSepherosa Ziehau #include <sys/sysctl.h>
43b7bb4816SSepherosa Ziehau #include <sys/syslog.h>
44b7bb4816SSepherosa Ziehau #include <sys/systm.h>
45b7bb4816SSepherosa Ziehau #include <sys/rtprio.h>
46b7bb4816SSepherosa Ziehau #include <sys/interrupt.h>
47b7bb4816SSepherosa Ziehau #include <sys/sx.h>
48b7bb4816SSepherosa Ziehau #include <sys/taskqueue.h>
49b7bb4816SSepherosa Ziehau #include <sys/mutex.h>
50b7bb4816SSepherosa Ziehau #include <sys/smp.h>
51b7bb4816SSepherosa Ziehau 
52b7bb4816SSepherosa Ziehau #include <machine/resource.h>
53b7bb4816SSepherosa Ziehau #include <sys/rman.h>
54b7bb4816SSepherosa Ziehau 
55b7bb4816SSepherosa Ziehau #include <machine/stdarg.h>
56b7bb4816SSepherosa Ziehau #include <machine/intr_machdep.h>
57b7bb4816SSepherosa Ziehau #include <machine/md_var.h>
58b7bb4816SSepherosa Ziehau #include <machine/segments.h>
59b7bb4816SSepherosa Ziehau #include <sys/pcpu.h>
60b7bb4816SSepherosa Ziehau #include <x86/apicvar.h>
61b7bb4816SSepherosa Ziehau 
62b7bb4816SSepherosa Ziehau #include <dev/hyperv/include/hyperv.h>
63b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/hv_vmbus_priv.h>
64b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/hyperv_reg.h>
65b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/hyperv_var.h>
66b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/vmbus_reg.h>
67b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/vmbus_var.h>
68b7bb4816SSepherosa Ziehau 
69b7bb4816SSepherosa Ziehau #include <contrib/dev/acpica/include/acpi.h>
70b7bb4816SSepherosa Ziehau #include "acpi_if.h"
71b7bb4816SSepherosa Ziehau 
72*236764b1SSepherosa Ziehau /*
73*236764b1SSepherosa Ziehau  * NOTE: DO NOT CHANGE THESE
74*236764b1SSepherosa Ziehau  */
75*236764b1SSepherosa Ziehau #define VMBUS_CONNID_MESSAGE		1
76*236764b1SSepherosa Ziehau #define VMBUS_CONNID_EVENT		2
77*236764b1SSepherosa Ziehau 
78*236764b1SSepherosa Ziehau struct vmbus_msghc {
79*236764b1SSepherosa Ziehau 	struct hypercall_postmsg_in	*mh_inprm;
80*236764b1SSepherosa Ziehau 	struct hypercall_postmsg_in	mh_inprm_save;
81*236764b1SSepherosa Ziehau 	struct hyperv_dma		mh_inprm_dma;
82*236764b1SSepherosa Ziehau 
83*236764b1SSepherosa Ziehau 	struct vmbus_message		*mh_resp;
84*236764b1SSepherosa Ziehau 	struct vmbus_message		mh_resp0;
85*236764b1SSepherosa Ziehau };
86*236764b1SSepherosa Ziehau 
87*236764b1SSepherosa Ziehau struct vmbus_msghc_ctx {
88*236764b1SSepherosa Ziehau 	struct vmbus_msghc		*mhc_free;
89*236764b1SSepherosa Ziehau 	struct mtx			mhc_free_lock;
90*236764b1SSepherosa Ziehau 	uint32_t			mhc_flags;
91*236764b1SSepherosa Ziehau 
92*236764b1SSepherosa Ziehau 	struct vmbus_msghc		*mhc_active;
93*236764b1SSepherosa Ziehau 	struct mtx			mhc_active_lock;
94*236764b1SSepherosa Ziehau };
95*236764b1SSepherosa Ziehau 
96*236764b1SSepherosa Ziehau #define VMBUS_MSGHC_CTXF_DESTROY	0x0001
97*236764b1SSepherosa Ziehau 
98*236764b1SSepherosa Ziehau static int			vmbus_init(struct vmbus_softc *);
99*236764b1SSepherosa Ziehau static int			vmbus_init_contact(struct vmbus_softc *,
100*236764b1SSepherosa Ziehau 				    uint32_t);
101*236764b1SSepherosa Ziehau 
102*236764b1SSepherosa Ziehau static struct vmbus_msghc_ctx	*vmbus_msghc_ctx_create(bus_dma_tag_t);
103*236764b1SSepherosa Ziehau static void			vmbus_msghc_ctx_destroy(
104*236764b1SSepherosa Ziehau 				    struct vmbus_msghc_ctx *);
105*236764b1SSepherosa Ziehau static void			vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *);
106*236764b1SSepherosa Ziehau static struct vmbus_msghc	*vmbus_msghc_alloc(bus_dma_tag_t);
107*236764b1SSepherosa Ziehau static void			vmbus_msghc_free(struct vmbus_msghc *);
108*236764b1SSepherosa Ziehau static struct vmbus_msghc	*vmbus_msghc_get1(struct vmbus_msghc_ctx *,
109*236764b1SSepherosa Ziehau 				    uint32_t);
110*236764b1SSepherosa Ziehau 
111b7bb4816SSepherosa Ziehau struct vmbus_softc	*vmbus_sc;
112b7bb4816SSepherosa Ziehau 
113b7bb4816SSepherosa Ziehau extern inthand_t IDTVEC(vmbus_isr);
114b7bb4816SSepherosa Ziehau 
115*236764b1SSepherosa Ziehau static const uint32_t		vmbus_version[] = {
116*236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WIN8_1,
117*236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WIN8,
118*236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WIN7,
119*236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WS2008
120*236764b1SSepherosa Ziehau };
121*236764b1SSepherosa Ziehau 
122*236764b1SSepherosa Ziehau static struct vmbus_msghc *
123*236764b1SSepherosa Ziehau vmbus_msghc_alloc(bus_dma_tag_t parent_dtag)
124*236764b1SSepherosa Ziehau {
125*236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
126*236764b1SSepherosa Ziehau 
127*236764b1SSepherosa Ziehau 	mh = malloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO);
128*236764b1SSepherosa Ziehau 
129*236764b1SSepherosa Ziehau 	mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag,
130*236764b1SSepherosa Ziehau 	    HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE,
131*236764b1SSepherosa Ziehau 	    &mh->mh_inprm_dma, BUS_DMA_WAITOK);
132*236764b1SSepherosa Ziehau 	if (mh->mh_inprm == NULL) {
133*236764b1SSepherosa Ziehau 		free(mh, M_DEVBUF);
134*236764b1SSepherosa Ziehau 		return NULL;
135*236764b1SSepherosa Ziehau 	}
136*236764b1SSepherosa Ziehau 	return mh;
137*236764b1SSepherosa Ziehau }
138*236764b1SSepherosa Ziehau 
139*236764b1SSepherosa Ziehau static void
140*236764b1SSepherosa Ziehau vmbus_msghc_free(struct vmbus_msghc *mh)
141*236764b1SSepherosa Ziehau {
142*236764b1SSepherosa Ziehau 	hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm);
143*236764b1SSepherosa Ziehau 	free(mh, M_DEVBUF);
144*236764b1SSepherosa Ziehau }
145*236764b1SSepherosa Ziehau 
146*236764b1SSepherosa Ziehau static void
147*236764b1SSepherosa Ziehau vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc)
148*236764b1SSepherosa Ziehau {
149*236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall"));
150*236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg"));
151*236764b1SSepherosa Ziehau 
152*236764b1SSepherosa Ziehau 	mtx_destroy(&mhc->mhc_free_lock);
153*236764b1SSepherosa Ziehau 	mtx_destroy(&mhc->mhc_active_lock);
154*236764b1SSepherosa Ziehau 	free(mhc, M_DEVBUF);
155*236764b1SSepherosa Ziehau }
156*236764b1SSepherosa Ziehau 
157*236764b1SSepherosa Ziehau static struct vmbus_msghc_ctx *
158*236764b1SSepherosa Ziehau vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag)
159*236764b1SSepherosa Ziehau {
160*236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc;
161*236764b1SSepherosa Ziehau 
162*236764b1SSepherosa Ziehau 	mhc = malloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO);
163*236764b1SSepherosa Ziehau 	mtx_init(&mhc->mhc_free_lock, "vmbus msghc free", NULL, MTX_DEF);
164*236764b1SSepherosa Ziehau 	mtx_init(&mhc->mhc_active_lock, "vmbus msghc act", NULL, MTX_DEF);
165*236764b1SSepherosa Ziehau 
166*236764b1SSepherosa Ziehau 	mhc->mhc_free = vmbus_msghc_alloc(parent_dtag);
167*236764b1SSepherosa Ziehau 	if (mhc->mhc_free == NULL) {
168*236764b1SSepherosa Ziehau 		vmbus_msghc_ctx_free(mhc);
169*236764b1SSepherosa Ziehau 		return NULL;
170*236764b1SSepherosa Ziehau 	}
171*236764b1SSepherosa Ziehau 	return mhc;
172*236764b1SSepherosa Ziehau }
173*236764b1SSepherosa Ziehau 
174*236764b1SSepherosa Ziehau static struct vmbus_msghc *
175*236764b1SSepherosa Ziehau vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag)
176*236764b1SSepherosa Ziehau {
177*236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
178*236764b1SSepherosa Ziehau 
179*236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_free_lock);
180*236764b1SSepherosa Ziehau 
181*236764b1SSepherosa Ziehau 	while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL) {
182*236764b1SSepherosa Ziehau 		mtx_sleep(&mhc->mhc_free, &mhc->mhc_free_lock, 0,
183*236764b1SSepherosa Ziehau 		    "gmsghc", 0);
184*236764b1SSepherosa Ziehau 	}
185*236764b1SSepherosa Ziehau 	if (mhc->mhc_flags & dtor_flag) {
186*236764b1SSepherosa Ziehau 		/* Being destroyed */
187*236764b1SSepherosa Ziehau 		mh = NULL;
188*236764b1SSepherosa Ziehau 	} else {
189*236764b1SSepherosa Ziehau 		mh = mhc->mhc_free;
190*236764b1SSepherosa Ziehau 		KASSERT(mh != NULL, ("no free hypercall msg"));
191*236764b1SSepherosa Ziehau 		KASSERT(mh->mh_resp == NULL,
192*236764b1SSepherosa Ziehau 		    ("hypercall msg has pending response"));
193*236764b1SSepherosa Ziehau 		mhc->mhc_free = NULL;
194*236764b1SSepherosa Ziehau 	}
195*236764b1SSepherosa Ziehau 
196*236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_free_lock);
197*236764b1SSepherosa Ziehau 
198*236764b1SSepherosa Ziehau 	return mh;
199*236764b1SSepherosa Ziehau }
200*236764b1SSepherosa Ziehau 
201*236764b1SSepherosa Ziehau struct vmbus_msghc *
202*236764b1SSepherosa Ziehau vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize)
203*236764b1SSepherosa Ziehau {
204*236764b1SSepherosa Ziehau 	struct hypercall_postmsg_in *inprm;
205*236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
206*236764b1SSepherosa Ziehau 
207*236764b1SSepherosa Ziehau 	if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
208*236764b1SSepherosa Ziehau 		return NULL;
209*236764b1SSepherosa Ziehau 
210*236764b1SSepherosa Ziehau 	mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY);
211*236764b1SSepherosa Ziehau 	if (mh == NULL)
212*236764b1SSepherosa Ziehau 		return NULL;
213*236764b1SSepherosa Ziehau 
214*236764b1SSepherosa Ziehau 	inprm = mh->mh_inprm;
215*236764b1SSepherosa Ziehau 	memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE);
216*236764b1SSepherosa Ziehau 	inprm->hc_connid = VMBUS_CONNID_MESSAGE;
217*236764b1SSepherosa Ziehau 	inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL;
218*236764b1SSepherosa Ziehau 	inprm->hc_dsize = dsize;
219*236764b1SSepherosa Ziehau 
220*236764b1SSepherosa Ziehau 	return mh;
221*236764b1SSepherosa Ziehau }
222*236764b1SSepherosa Ziehau 
223*236764b1SSepherosa Ziehau void
224*236764b1SSepherosa Ziehau vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh)
225*236764b1SSepherosa Ziehau {
226*236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
227*236764b1SSepherosa Ziehau 
228*236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active"));
229*236764b1SSepherosa Ziehau 	mh->mh_resp = NULL;
230*236764b1SSepherosa Ziehau 
231*236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_free_lock);
232*236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg"));
233*236764b1SSepherosa Ziehau 	mhc->mhc_free = mh;
234*236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_free_lock);
235*236764b1SSepherosa Ziehau 	wakeup(&mhc->mhc_free);
236*236764b1SSepherosa Ziehau }
237*236764b1SSepherosa Ziehau 
238*236764b1SSepherosa Ziehau void *
239*236764b1SSepherosa Ziehau vmbus_msghc_dataptr(struct vmbus_msghc *mh)
240*236764b1SSepherosa Ziehau {
241*236764b1SSepherosa Ziehau 	return mh->mh_inprm->hc_data;
242*236764b1SSepherosa Ziehau }
243*236764b1SSepherosa Ziehau 
244*236764b1SSepherosa Ziehau static void
245*236764b1SSepherosa Ziehau vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc)
246*236764b1SSepherosa Ziehau {
247*236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
248*236764b1SSepherosa Ziehau 
249*236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_free_lock);
250*236764b1SSepherosa Ziehau 	mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY;
251*236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_free_lock);
252*236764b1SSepherosa Ziehau 	wakeup(&mhc->mhc_free);
253*236764b1SSepherosa Ziehau 
254*236764b1SSepherosa Ziehau 	mh = vmbus_msghc_get1(mhc, 0);
255*236764b1SSepherosa Ziehau 	if (mh == NULL)
256*236764b1SSepherosa Ziehau 		panic("can't get msghc");
257*236764b1SSepherosa Ziehau 
258*236764b1SSepherosa Ziehau 	vmbus_msghc_free(mh);
259*236764b1SSepherosa Ziehau 	vmbus_msghc_ctx_free(mhc);
260*236764b1SSepherosa Ziehau }
261*236764b1SSepherosa Ziehau 
262*236764b1SSepherosa Ziehau int
263*236764b1SSepherosa Ziehau vmbus_msghc_exec_noresult(struct vmbus_msghc *mh)
264*236764b1SSepherosa Ziehau {
265*236764b1SSepherosa Ziehau 	sbintime_t time = SBT_1MS;
266*236764b1SSepherosa Ziehau 	int i;
267*236764b1SSepherosa Ziehau 
268*236764b1SSepherosa Ziehau 	/*
269*236764b1SSepherosa Ziehau 	 * Save the input parameter so that we could restore the input
270*236764b1SSepherosa Ziehau 	 * parameter if the Hypercall failed.
271*236764b1SSepherosa Ziehau 	 *
272*236764b1SSepherosa Ziehau 	 * XXX
273*236764b1SSepherosa Ziehau 	 * Is this really necessary?!  i.e. Will the Hypercall ever
274*236764b1SSepherosa Ziehau 	 * overwrite the input parameter?
275*236764b1SSepherosa Ziehau 	 */
276*236764b1SSepherosa Ziehau 	memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE);
277*236764b1SSepherosa Ziehau 
278*236764b1SSepherosa Ziehau 	/*
279*236764b1SSepherosa Ziehau 	 * In order to cope with transient failures, e.g. insufficient
280*236764b1SSepherosa Ziehau 	 * resources on host side, we retry the post message Hypercall
281*236764b1SSepherosa Ziehau 	 * several times.  20 retries seem sufficient.
282*236764b1SSepherosa Ziehau 	 */
283*236764b1SSepherosa Ziehau #define HC_RETRY_MAX	20
284*236764b1SSepherosa Ziehau 
285*236764b1SSepherosa Ziehau 	for (i = 0; i < HC_RETRY_MAX; ++i) {
286*236764b1SSepherosa Ziehau 		uint64_t status;
287*236764b1SSepherosa Ziehau 
288*236764b1SSepherosa Ziehau 		status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr);
289*236764b1SSepherosa Ziehau 		if (status == HYPERCALL_STATUS_SUCCESS)
290*236764b1SSepherosa Ziehau 			return 0;
291*236764b1SSepherosa Ziehau 
292*236764b1SSepherosa Ziehau 		pause_sbt("hcpmsg", time, 0, C_HARDCLOCK);
293*236764b1SSepherosa Ziehau 		if (time < SBT_1S * 2)
294*236764b1SSepherosa Ziehau 			time *= 2;
295*236764b1SSepherosa Ziehau 
296*236764b1SSepherosa Ziehau 		/* Restore input parameter and try again */
297*236764b1SSepherosa Ziehau 		memcpy(mh->mh_inprm, &mh->mh_inprm_save,
298*236764b1SSepherosa Ziehau 		    HYPERCALL_POSTMSGIN_SIZE);
299*236764b1SSepherosa Ziehau 	}
300*236764b1SSepherosa Ziehau 
301*236764b1SSepherosa Ziehau #undef HC_RETRY_MAX
302*236764b1SSepherosa Ziehau 
303*236764b1SSepherosa Ziehau 	return EIO;
304*236764b1SSepherosa Ziehau }
305*236764b1SSepherosa Ziehau 
306*236764b1SSepherosa Ziehau int
307*236764b1SSepherosa Ziehau vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh)
308*236764b1SSepherosa Ziehau {
309*236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
310*236764b1SSepherosa Ziehau 	int error;
311*236764b1SSepherosa Ziehau 
312*236764b1SSepherosa Ziehau 	KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response"));
313*236764b1SSepherosa Ziehau 
314*236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_active_lock);
315*236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall"));
316*236764b1SSepherosa Ziehau 	mhc->mhc_active = mh;
317*236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_active_lock);
318*236764b1SSepherosa Ziehau 
319*236764b1SSepherosa Ziehau 	error = vmbus_msghc_exec_noresult(mh);
320*236764b1SSepherosa Ziehau 	if (error) {
321*236764b1SSepherosa Ziehau 		mtx_lock(&mhc->mhc_active_lock);
322*236764b1SSepherosa Ziehau 		KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
323*236764b1SSepherosa Ziehau 		mhc->mhc_active = NULL;
324*236764b1SSepherosa Ziehau 		mtx_unlock(&mhc->mhc_active_lock);
325*236764b1SSepherosa Ziehau 	}
326*236764b1SSepherosa Ziehau 	return error;
327*236764b1SSepherosa Ziehau }
328*236764b1SSepherosa Ziehau 
329*236764b1SSepherosa Ziehau const struct vmbus_message *
330*236764b1SSepherosa Ziehau vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh)
331*236764b1SSepherosa Ziehau {
332*236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
333*236764b1SSepherosa Ziehau 
334*236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_active_lock);
335*236764b1SSepherosa Ziehau 
336*236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
337*236764b1SSepherosa Ziehau 	while (mh->mh_resp == NULL) {
338*236764b1SSepherosa Ziehau 		mtx_sleep(&mhc->mhc_active, &mhc->mhc_active_lock, 0,
339*236764b1SSepherosa Ziehau 		    "wmsghc", 0);
340*236764b1SSepherosa Ziehau 	}
341*236764b1SSepherosa Ziehau 	mhc->mhc_active = NULL;
342*236764b1SSepherosa Ziehau 
343*236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_active_lock);
344*236764b1SSepherosa Ziehau 
345*236764b1SSepherosa Ziehau 	return mh->mh_resp;
346*236764b1SSepherosa Ziehau }
347*236764b1SSepherosa Ziehau 
348*236764b1SSepherosa Ziehau void
349*236764b1SSepherosa Ziehau vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg)
350*236764b1SSepherosa Ziehau {
351*236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
352*236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
353*236764b1SSepherosa Ziehau 
354*236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_active_lock);
355*236764b1SSepherosa Ziehau 
356*236764b1SSepherosa Ziehau 	mh = mhc->mhc_active;
357*236764b1SSepherosa Ziehau 	KASSERT(mh != NULL, ("no pending msg hypercall"));
358*236764b1SSepherosa Ziehau 	memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0));
359*236764b1SSepherosa Ziehau 	mh->mh_resp = &mh->mh_resp0;
360*236764b1SSepherosa Ziehau 
361*236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_active_lock);
362*236764b1SSepherosa Ziehau 	wakeup(&mhc->mhc_active);
363*236764b1SSepherosa Ziehau }
364*236764b1SSepherosa Ziehau 
365*236764b1SSepherosa Ziehau static int
366*236764b1SSepherosa Ziehau vmbus_init_contact(struct vmbus_softc *sc, uint32_t version)
367*236764b1SSepherosa Ziehau {
368*236764b1SSepherosa Ziehau 	struct vmbus_chanmsg_init_contact *req;
369*236764b1SSepherosa Ziehau 	const struct vmbus_chanmsg_version_resp *resp;
370*236764b1SSepherosa Ziehau 	const struct vmbus_message *msg;
371*236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
372*236764b1SSepherosa Ziehau 	int error, supp = 0;
373*236764b1SSepherosa Ziehau 
374*236764b1SSepherosa Ziehau 	mh = vmbus_msghc_get(sc, sizeof(*req));
375*236764b1SSepherosa Ziehau 	if (mh == NULL)
376*236764b1SSepherosa Ziehau 		return ENXIO;
377*236764b1SSepherosa Ziehau 
378*236764b1SSepherosa Ziehau 	req = vmbus_msghc_dataptr(mh);
379*236764b1SSepherosa Ziehau 	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT;
380*236764b1SSepherosa Ziehau 	req->chm_ver = version;
381*236764b1SSepherosa Ziehau 	req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr;
382*236764b1SSepherosa Ziehau 	req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr;
383*236764b1SSepherosa Ziehau 	req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr;
384*236764b1SSepherosa Ziehau 
385*236764b1SSepherosa Ziehau 	error = vmbus_msghc_exec(sc, mh);
386*236764b1SSepherosa Ziehau 	if (error) {
387*236764b1SSepherosa Ziehau 		vmbus_msghc_put(sc, mh);
388*236764b1SSepherosa Ziehau 		return error;
389*236764b1SSepherosa Ziehau 	}
390*236764b1SSepherosa Ziehau 
391*236764b1SSepherosa Ziehau 	msg = vmbus_msghc_wait_result(sc, mh);
392*236764b1SSepherosa Ziehau 	resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data;
393*236764b1SSepherosa Ziehau 	supp = resp->chm_supp;
394*236764b1SSepherosa Ziehau 
395*236764b1SSepherosa Ziehau 	vmbus_msghc_put(sc, mh);
396*236764b1SSepherosa Ziehau 
397*236764b1SSepherosa Ziehau 	return (supp ? 0 : EOPNOTSUPP);
398*236764b1SSepherosa Ziehau }
399*236764b1SSepherosa Ziehau 
400*236764b1SSepherosa Ziehau static int
401*236764b1SSepherosa Ziehau vmbus_init(struct vmbus_softc *sc)
402*236764b1SSepherosa Ziehau {
403*236764b1SSepherosa Ziehau 	int i;
404*236764b1SSepherosa Ziehau 
405*236764b1SSepherosa Ziehau 	for (i = 0; i < nitems(vmbus_version); ++i) {
406*236764b1SSepherosa Ziehau 		int error;
407*236764b1SSepherosa Ziehau 
408*236764b1SSepherosa Ziehau 		error = vmbus_init_contact(sc, vmbus_version[i]);
409*236764b1SSepherosa Ziehau 		if (!error) {
410*236764b1SSepherosa Ziehau 			hv_vmbus_protocal_version = vmbus_version[i];
411*236764b1SSepherosa Ziehau 			device_printf(sc->vmbus_dev, "version %u.%u\n",
412*236764b1SSepherosa Ziehau 			    (hv_vmbus_protocal_version >> 16),
413*236764b1SSepherosa Ziehau 			    (hv_vmbus_protocal_version & 0xffff));
414*236764b1SSepherosa Ziehau 			return 0;
415*236764b1SSepherosa Ziehau 		}
416*236764b1SSepherosa Ziehau 	}
417*236764b1SSepherosa Ziehau 	return ENXIO;
418*236764b1SSepherosa Ziehau }
419*236764b1SSepherosa Ziehau 
420b7bb4816SSepherosa Ziehau static void
421b7bb4816SSepherosa Ziehau vmbus_msg_task(void *xsc, int pending __unused)
422b7bb4816SSepherosa Ziehau {
423b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = xsc;
424b7bb4816SSepherosa Ziehau 	volatile struct vmbus_message *msg;
425b7bb4816SSepherosa Ziehau 
426b7bb4816SSepherosa Ziehau 	msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE;
427b7bb4816SSepherosa Ziehau 	for (;;) {
428*236764b1SSepherosa Ziehau 		if (msg->msg_type == HYPERV_MSGTYPE_NONE) {
429fa4828b0SSepherosa Ziehau 			/* No message */
430fa4828b0SSepherosa Ziehau 			break;
431*236764b1SSepherosa Ziehau 		} else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) {
43212ea25aeSSepherosa Ziehau 			/* Channel message */
43343926ba1SSepherosa Ziehau 			vmbus_chan_msgproc(sc,
43443926ba1SSepherosa Ziehau 			    __DEVOLATILE(const struct vmbus_message *, msg));
435fa4828b0SSepherosa Ziehau 		}
436b7bb4816SSepherosa Ziehau 
437*236764b1SSepherosa Ziehau 		msg->msg_type = HYPERV_MSGTYPE_NONE;
438b7bb4816SSepherosa Ziehau 		/*
439b7bb4816SSepherosa Ziehau 		 * Make sure the write to msg_type (i.e. set to
440*236764b1SSepherosa Ziehau 		 * HYPERV_MSGTYPE_NONE) happens before we read the
441b7bb4816SSepherosa Ziehau 		 * msg_flags and EOMing. Otherwise, the EOMing will
442b7bb4816SSepherosa Ziehau 		 * not deliver any more messages since there is no
443b7bb4816SSepherosa Ziehau 		 * empty slot
444b7bb4816SSepherosa Ziehau 		 *
445b7bb4816SSepherosa Ziehau 		 * NOTE:
446b7bb4816SSepherosa Ziehau 		 * mb() is used here, since atomic_thread_fence_seq_cst()
447b7bb4816SSepherosa Ziehau 		 * will become compiler fence on UP kernel.
448b7bb4816SSepherosa Ziehau 		 */
449b7bb4816SSepherosa Ziehau 		mb();
450b7bb4816SSepherosa Ziehau 		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
451b7bb4816SSepherosa Ziehau 			/*
452b7bb4816SSepherosa Ziehau 			 * This will cause message queue rescan to possibly
453b7bb4816SSepherosa Ziehau 			 * deliver another msg from the hypervisor
454b7bb4816SSepherosa Ziehau 			 */
455b7bb4816SSepherosa Ziehau 			wrmsr(MSR_HV_EOM, 0);
456b7bb4816SSepherosa Ziehau 		}
457b7bb4816SSepherosa Ziehau 	}
458b7bb4816SSepherosa Ziehau }
459b7bb4816SSepherosa Ziehau 
460b7bb4816SSepherosa Ziehau static __inline int
461b7bb4816SSepherosa Ziehau vmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
462b7bb4816SSepherosa Ziehau {
463b7bb4816SSepherosa Ziehau 	volatile struct vmbus_message *msg;
464b7bb4816SSepherosa Ziehau 	struct vmbus_message *msg_base;
465b7bb4816SSepherosa Ziehau 
466b7bb4816SSepherosa Ziehau 	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
467b7bb4816SSepherosa Ziehau 
468b7bb4816SSepherosa Ziehau 	/*
469b7bb4816SSepherosa Ziehau 	 * Check event timer.
470b7bb4816SSepherosa Ziehau 	 *
471b7bb4816SSepherosa Ziehau 	 * TODO: move this to independent IDT vector.
472b7bb4816SSepherosa Ziehau 	 */
473b7bb4816SSepherosa Ziehau 	msg = msg_base + VMBUS_SINT_TIMER;
474*236764b1SSepherosa Ziehau 	if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) {
475*236764b1SSepherosa Ziehau 		msg->msg_type = HYPERV_MSGTYPE_NONE;
476b7bb4816SSepherosa Ziehau 
477b7bb4816SSepherosa Ziehau 		vmbus_et_intr(frame);
478b7bb4816SSepherosa Ziehau 
479b7bb4816SSepherosa Ziehau 		/*
480b7bb4816SSepherosa Ziehau 		 * Make sure the write to msg_type (i.e. set to
481*236764b1SSepherosa Ziehau 		 * HYPERV_MSGTYPE_NONE) happens before we read the
482b7bb4816SSepherosa Ziehau 		 * msg_flags and EOMing. Otherwise, the EOMing will
483b7bb4816SSepherosa Ziehau 		 * not deliver any more messages since there is no
484b7bb4816SSepherosa Ziehau 		 * empty slot
485b7bb4816SSepherosa Ziehau 		 *
486b7bb4816SSepherosa Ziehau 		 * NOTE:
487b7bb4816SSepherosa Ziehau 		 * mb() is used here, since atomic_thread_fence_seq_cst()
488b7bb4816SSepherosa Ziehau 		 * will become compiler fence on UP kernel.
489b7bb4816SSepherosa Ziehau 		 */
490b7bb4816SSepherosa Ziehau 		mb();
491b7bb4816SSepherosa Ziehau 		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
492b7bb4816SSepherosa Ziehau 			/*
493b7bb4816SSepherosa Ziehau 			 * This will cause message queue rescan to possibly
494b7bb4816SSepherosa Ziehau 			 * deliver another msg from the hypervisor
495b7bb4816SSepherosa Ziehau 			 */
496b7bb4816SSepherosa Ziehau 			wrmsr(MSR_HV_EOM, 0);
497b7bb4816SSepherosa Ziehau 		}
498b7bb4816SSepherosa Ziehau 	}
499b7bb4816SSepherosa Ziehau 
500b7bb4816SSepherosa Ziehau 	/*
501b7bb4816SSepherosa Ziehau 	 * Check events.  Hot path for network and storage I/O data; high rate.
502b7bb4816SSepherosa Ziehau 	 *
503b7bb4816SSepherosa Ziehau 	 * NOTE:
504b7bb4816SSepherosa Ziehau 	 * As recommended by the Windows guest fellows, we check events before
505b7bb4816SSepherosa Ziehau 	 * checking messages.
506b7bb4816SSepherosa Ziehau 	 */
507b7bb4816SSepherosa Ziehau 	sc->vmbus_event_proc(sc, cpu);
508b7bb4816SSepherosa Ziehau 
509b7bb4816SSepherosa Ziehau 	/*
510b7bb4816SSepherosa Ziehau 	 * Check messages.  Mainly management stuffs; ultra low rate.
511b7bb4816SSepherosa Ziehau 	 */
512b7bb4816SSepherosa Ziehau 	msg = msg_base + VMBUS_SINT_MESSAGE;
513*236764b1SSepherosa Ziehau 	if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
514b7bb4816SSepherosa Ziehau 		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
515b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, message_task, cpu));
516b7bb4816SSepherosa Ziehau 	}
517b7bb4816SSepherosa Ziehau 
518b7bb4816SSepherosa Ziehau 	return (FILTER_HANDLED);
519b7bb4816SSepherosa Ziehau }
520b7bb4816SSepherosa Ziehau 
521b7bb4816SSepherosa Ziehau void
522b7bb4816SSepherosa Ziehau vmbus_handle_intr(struct trapframe *trap_frame)
523b7bb4816SSepherosa Ziehau {
524b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = vmbus_get_softc();
525b7bb4816SSepherosa Ziehau 	int cpu = curcpu;
526b7bb4816SSepherosa Ziehau 
527b7bb4816SSepherosa Ziehau 	/*
528b7bb4816SSepherosa Ziehau 	 * Disable preemption.
529b7bb4816SSepherosa Ziehau 	 */
530b7bb4816SSepherosa Ziehau 	critical_enter();
531b7bb4816SSepherosa Ziehau 
532b7bb4816SSepherosa Ziehau 	/*
533b7bb4816SSepherosa Ziehau 	 * Do a little interrupt counting.
534b7bb4816SSepherosa Ziehau 	 */
535b7bb4816SSepherosa Ziehau 	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
536b7bb4816SSepherosa Ziehau 
537b7bb4816SSepherosa Ziehau 	vmbus_handle_intr1(sc, trap_frame, cpu);
538b7bb4816SSepherosa Ziehau 
539b7bb4816SSepherosa Ziehau 	/*
540b7bb4816SSepherosa Ziehau 	 * Enable preemption.
541b7bb4816SSepherosa Ziehau 	 */
542b7bb4816SSepherosa Ziehau 	critical_exit();
543b7bb4816SSepherosa Ziehau }
544b7bb4816SSepherosa Ziehau 
545b7bb4816SSepherosa Ziehau static void
546b7bb4816SSepherosa Ziehau vmbus_synic_setup(void *xsc)
547b7bb4816SSepherosa Ziehau {
548b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = xsc;
549b7bb4816SSepherosa Ziehau 	int cpu = curcpu;
550b7bb4816SSepherosa Ziehau 	uint64_t val, orig;
551b7bb4816SSepherosa Ziehau 	uint32_t sint;
552b7bb4816SSepherosa Ziehau 
553b7bb4816SSepherosa Ziehau 	if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
554b7bb4816SSepherosa Ziehau 		/*
555b7bb4816SSepherosa Ziehau 		 * Save virtual processor id.
556b7bb4816SSepherosa Ziehau 		 */
557b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX);
558b7bb4816SSepherosa Ziehau 	} else {
559b7bb4816SSepherosa Ziehau 		/*
560b7bb4816SSepherosa Ziehau 		 * XXX
561b7bb4816SSepherosa Ziehau 		 * Virtual processoor id is only used by a pretty broken
562b7bb4816SSepherosa Ziehau 		 * channel selection code from storvsc.  It's nothing
563b7bb4816SSepherosa Ziehau 		 * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep
564b7bb4816SSepherosa Ziehau 		 * moving on.
565b7bb4816SSepherosa Ziehau 		 */
566b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, vcpuid, cpu) = cpu;
567b7bb4816SSepherosa Ziehau 	}
568b7bb4816SSepherosa Ziehau 
569b7bb4816SSepherosa Ziehau 	/*
570b7bb4816SSepherosa Ziehau 	 * Setup the SynIC message.
571b7bb4816SSepherosa Ziehau 	 */
572b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIMP);
573b7bb4816SSepherosa Ziehau 	val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
574b7bb4816SSepherosa Ziehau 	    ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) <<
575b7bb4816SSepherosa Ziehau 	     MSR_HV_SIMP_PGSHIFT);
576b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIMP, val);
577b7bb4816SSepherosa Ziehau 
578b7bb4816SSepherosa Ziehau 	/*
579b7bb4816SSepherosa Ziehau 	 * Setup the SynIC event flags.
580b7bb4816SSepherosa Ziehau 	 */
581b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIEFP);
582b7bb4816SSepherosa Ziehau 	val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
583b7bb4816SSepherosa Ziehau 	    ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu)
584b7bb4816SSepherosa Ziehau 	      >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT);
585b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIEFP, val);
586b7bb4816SSepherosa Ziehau 
587b7bb4816SSepherosa Ziehau 
588b7bb4816SSepherosa Ziehau 	/*
589b7bb4816SSepherosa Ziehau 	 * Configure and unmask SINT for message and event flags.
590b7bb4816SSepherosa Ziehau 	 */
591b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
592b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
593b7bb4816SSepherosa Ziehau 	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
594b7bb4816SSepherosa Ziehau 	    (orig & MSR_HV_SINT_RSVD_MASK);
595b7bb4816SSepherosa Ziehau 	wrmsr(sint, val);
596b7bb4816SSepherosa Ziehau 
597b7bb4816SSepherosa Ziehau 	/*
598b7bb4816SSepherosa Ziehau 	 * Configure and unmask SINT for timer.
599b7bb4816SSepherosa Ziehau 	 */
600b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
601b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
602b7bb4816SSepherosa Ziehau 	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
603b7bb4816SSepherosa Ziehau 	    (orig & MSR_HV_SINT_RSVD_MASK);
604b7bb4816SSepherosa Ziehau 	wrmsr(sint, val);
605b7bb4816SSepherosa Ziehau 
606b7bb4816SSepherosa Ziehau 	/*
607b7bb4816SSepherosa Ziehau 	 * All done; enable SynIC.
608b7bb4816SSepherosa Ziehau 	 */
609b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SCONTROL);
610b7bb4816SSepherosa Ziehau 	val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
611b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SCONTROL, val);
612b7bb4816SSepherosa Ziehau }
613b7bb4816SSepherosa Ziehau 
614b7bb4816SSepherosa Ziehau static void
615b7bb4816SSepherosa Ziehau vmbus_synic_teardown(void *arg)
616b7bb4816SSepherosa Ziehau {
617b7bb4816SSepherosa Ziehau 	uint64_t orig;
618b7bb4816SSepherosa Ziehau 	uint32_t sint;
619b7bb4816SSepherosa Ziehau 
620b7bb4816SSepherosa Ziehau 	/*
621b7bb4816SSepherosa Ziehau 	 * Disable SynIC.
622b7bb4816SSepherosa Ziehau 	 */
623b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SCONTROL);
624b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
625b7bb4816SSepherosa Ziehau 
626b7bb4816SSepherosa Ziehau 	/*
627b7bb4816SSepherosa Ziehau 	 * Mask message and event flags SINT.
628b7bb4816SSepherosa Ziehau 	 */
629b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
630b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
631b7bb4816SSepherosa Ziehau 	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
632b7bb4816SSepherosa Ziehau 
633b7bb4816SSepherosa Ziehau 	/*
634b7bb4816SSepherosa Ziehau 	 * Mask timer SINT.
635b7bb4816SSepherosa Ziehau 	 */
636b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
637b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
638b7bb4816SSepherosa Ziehau 	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
639b7bb4816SSepherosa Ziehau 
640b7bb4816SSepherosa Ziehau 	/*
641b7bb4816SSepherosa Ziehau 	 * Teardown SynIC message.
642b7bb4816SSepherosa Ziehau 	 */
643b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIMP);
644b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
645b7bb4816SSepherosa Ziehau 
646b7bb4816SSepherosa Ziehau 	/*
647b7bb4816SSepherosa Ziehau 	 * Teardown SynIC event flags.
648b7bb4816SSepherosa Ziehau 	 */
649b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIEFP);
650b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
651b7bb4816SSepherosa Ziehau }
652b7bb4816SSepherosa Ziehau 
653b7bb4816SSepherosa Ziehau static int
654b7bb4816SSepherosa Ziehau vmbus_dma_alloc(struct vmbus_softc *sc)
655b7bb4816SSepherosa Ziehau {
6566523ab7eSSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
6576523ab7eSSepherosa Ziehau 	uint8_t *evtflags;
658b7bb4816SSepherosa Ziehau 	int cpu;
659b7bb4816SSepherosa Ziehau 
6606523ab7eSSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(sc->vmbus_dev);
661b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
662b7bb4816SSepherosa Ziehau 		void *ptr;
663b7bb4816SSepherosa Ziehau 
664b7bb4816SSepherosa Ziehau 		/*
665b7bb4816SSepherosa Ziehau 		 * Per-cpu messages and event flags.
666b7bb4816SSepherosa Ziehau 		 */
6676523ab7eSSepherosa Ziehau 		ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6686523ab7eSSepherosa Ziehau 		    PAGE_SIZE, VMBUS_PCPU_PTR(sc, message_dma, cpu),
669b7bb4816SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
670b7bb4816SSepherosa Ziehau 		if (ptr == NULL)
671b7bb4816SSepherosa Ziehau 			return ENOMEM;
672b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
673b7bb4816SSepherosa Ziehau 
6746523ab7eSSepherosa Ziehau 		ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6756523ab7eSSepherosa Ziehau 		    PAGE_SIZE, VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
676b7bb4816SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
677b7bb4816SSepherosa Ziehau 		if (ptr == NULL)
678b7bb4816SSepherosa Ziehau 			return ENOMEM;
679b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr;
680b7bb4816SSepherosa Ziehau 	}
6816523ab7eSSepherosa Ziehau 
6826523ab7eSSepherosa Ziehau 	evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6836523ab7eSSepherosa Ziehau 	    PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
6846523ab7eSSepherosa Ziehau 	if (evtflags == NULL)
6856523ab7eSSepherosa Ziehau 		return ENOMEM;
6866523ab7eSSepherosa Ziehau 	sc->vmbus_rx_evtflags = (u_long *)evtflags;
68751a9a000SSepherosa Ziehau 	sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2));
6886523ab7eSSepherosa Ziehau 	sc->vmbus_evtflags = evtflags;
6896523ab7eSSepherosa Ziehau 
6906523ab7eSSepherosa Ziehau 	sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6916523ab7eSSepherosa Ziehau 	    PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
6926523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf1 == NULL)
6936523ab7eSSepherosa Ziehau 		return ENOMEM;
6946523ab7eSSepherosa Ziehau 
6956523ab7eSSepherosa Ziehau 	sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6966523ab7eSSepherosa Ziehau 	    PAGE_SIZE, &sc->vmbus_mnf2_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
6976523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf2 == NULL)
6986523ab7eSSepherosa Ziehau 		return ENOMEM;
6996523ab7eSSepherosa Ziehau 
700b7bb4816SSepherosa Ziehau 	return 0;
701b7bb4816SSepherosa Ziehau }
702b7bb4816SSepherosa Ziehau 
703b7bb4816SSepherosa Ziehau static void
704b7bb4816SSepherosa Ziehau vmbus_dma_free(struct vmbus_softc *sc)
705b7bb4816SSepherosa Ziehau {
706b7bb4816SSepherosa Ziehau 	int cpu;
707b7bb4816SSepherosa Ziehau 
7086523ab7eSSepherosa Ziehau 	if (sc->vmbus_evtflags != NULL) {
7096523ab7eSSepherosa Ziehau 		hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags);
7106523ab7eSSepherosa Ziehau 		sc->vmbus_evtflags = NULL;
7116523ab7eSSepherosa Ziehau 		sc->vmbus_rx_evtflags = NULL;
7126523ab7eSSepherosa Ziehau 		sc->vmbus_tx_evtflags = NULL;
7136523ab7eSSepherosa Ziehau 	}
7146523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf1 != NULL) {
7156523ab7eSSepherosa Ziehau 		hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1);
7166523ab7eSSepherosa Ziehau 		sc->vmbus_mnf1 = NULL;
7176523ab7eSSepherosa Ziehau 	}
7186523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf2 != NULL) {
7196523ab7eSSepherosa Ziehau 		hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2);
7206523ab7eSSepherosa Ziehau 		sc->vmbus_mnf2 = NULL;
7216523ab7eSSepherosa Ziehau 	}
7226523ab7eSSepherosa Ziehau 
723b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
724b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
725b7bb4816SSepherosa Ziehau 			hyperv_dmamem_free(
726b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
727b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_GET(sc, message, cpu));
728b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
729b7bb4816SSepherosa Ziehau 		}
730b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) {
731b7bb4816SSepherosa Ziehau 			hyperv_dmamem_free(
732b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
733b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_GET(sc, event_flags, cpu));
734b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL;
735b7bb4816SSepherosa Ziehau 		}
736b7bb4816SSepherosa Ziehau 	}
737b7bb4816SSepherosa Ziehau }
738b7bb4816SSepherosa Ziehau 
739b7bb4816SSepherosa Ziehau static int
740b7bb4816SSepherosa Ziehau vmbus_intr_setup(struct vmbus_softc *sc)
741b7bb4816SSepherosa Ziehau {
742b7bb4816SSepherosa Ziehau 	int cpu;
743b7bb4816SSepherosa Ziehau 
744b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
745b7bb4816SSepherosa Ziehau 		char buf[MAXCOMLEN + 1];
746b7bb4816SSepherosa Ziehau 		cpuset_t cpu_mask;
747b7bb4816SSepherosa Ziehau 
748b7bb4816SSepherosa Ziehau 		/* Allocate an interrupt counter for Hyper-V interrupt */
749b7bb4816SSepherosa Ziehau 		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
750b7bb4816SSepherosa Ziehau 		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
751b7bb4816SSepherosa Ziehau 
752b7bb4816SSepherosa Ziehau 		/*
753b7bb4816SSepherosa Ziehau 		 * Setup taskqueue to handle events.  Task will be per-
754b7bb4816SSepherosa Ziehau 		 * channel.
755b7bb4816SSepherosa Ziehau 		 */
756b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
757b7bb4816SSepherosa Ziehau 		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
758b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
759b7bb4816SSepherosa Ziehau 		CPU_SETOF(cpu, &cpu_mask);
760b7bb4816SSepherosa Ziehau 		taskqueue_start_threads_cpuset(
761b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
762b7bb4816SSepherosa Ziehau 		    "hvevent%d", cpu);
763b7bb4816SSepherosa Ziehau 
764b7bb4816SSepherosa Ziehau 		/*
765b7bb4816SSepherosa Ziehau 		 * Setup tasks and taskqueues to handle messages.
766b7bb4816SSepherosa Ziehau 		 */
767b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
768b7bb4816SSepherosa Ziehau 		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
769b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
770b7bb4816SSepherosa Ziehau 		CPU_SETOF(cpu, &cpu_mask);
771b7bb4816SSepherosa Ziehau 		taskqueue_start_threads_cpuset(
772b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
773b7bb4816SSepherosa Ziehau 		    "hvmsg%d", cpu);
774b7bb4816SSepherosa Ziehau 		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
775b7bb4816SSepherosa Ziehau 		    vmbus_msg_task, sc);
776b7bb4816SSepherosa Ziehau 	}
777b7bb4816SSepherosa Ziehau 
778b7bb4816SSepherosa Ziehau 	/*
779b7bb4816SSepherosa Ziehau 	 * All Hyper-V ISR required resources are setup, now let's find a
780b7bb4816SSepherosa Ziehau 	 * free IDT vector for Hyper-V ISR and set it up.
781b7bb4816SSepherosa Ziehau 	 */
782b7bb4816SSepherosa Ziehau 	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr));
783b7bb4816SSepherosa Ziehau 	if (sc->vmbus_idtvec < 0) {
784b7bb4816SSepherosa Ziehau 		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
785b7bb4816SSepherosa Ziehau 		return ENXIO;
786b7bb4816SSepherosa Ziehau 	}
787b7bb4816SSepherosa Ziehau 	if(bootverbose) {
788b7bb4816SSepherosa Ziehau 		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
789b7bb4816SSepherosa Ziehau 		    sc->vmbus_idtvec);
790b7bb4816SSepherosa Ziehau 	}
791b7bb4816SSepherosa Ziehau 	return 0;
792b7bb4816SSepherosa Ziehau }
793b7bb4816SSepherosa Ziehau 
794b7bb4816SSepherosa Ziehau static void
795b7bb4816SSepherosa Ziehau vmbus_intr_teardown(struct vmbus_softc *sc)
796b7bb4816SSepherosa Ziehau {
797b7bb4816SSepherosa Ziehau 	int cpu;
798b7bb4816SSepherosa Ziehau 
799b7bb4816SSepherosa Ziehau 	if (sc->vmbus_idtvec >= 0) {
800b7bb4816SSepherosa Ziehau 		lapic_ipi_free(sc->vmbus_idtvec);
801b7bb4816SSepherosa Ziehau 		sc->vmbus_idtvec = -1;
802b7bb4816SSepherosa Ziehau 	}
803b7bb4816SSepherosa Ziehau 
804b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
805b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
806b7bb4816SSepherosa Ziehau 			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
807b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
808b7bb4816SSepherosa Ziehau 		}
809b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
810b7bb4816SSepherosa Ziehau 			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
811b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_PTR(sc, message_task, cpu));
812b7bb4816SSepherosa Ziehau 			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
813b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
814b7bb4816SSepherosa Ziehau 		}
815b7bb4816SSepherosa Ziehau 	}
816b7bb4816SSepherosa Ziehau }
817b7bb4816SSepherosa Ziehau 
818b7bb4816SSepherosa Ziehau static int
819b7bb4816SSepherosa Ziehau vmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
820b7bb4816SSepherosa Ziehau {
821b7bb4816SSepherosa Ziehau 	struct hv_device *child_dev_ctx = device_get_ivars(child);
822b7bb4816SSepherosa Ziehau 
823b7bb4816SSepherosa Ziehau 	switch (index) {
824b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_TYPE:
825b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)&child_dev_ctx->class_id;
826b7bb4816SSepherosa Ziehau 		return (0);
827b7bb4816SSepherosa Ziehau 
828b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_INSTANCE:
829b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)&child_dev_ctx->device_id;
830b7bb4816SSepherosa Ziehau 		return (0);
831b7bb4816SSepherosa Ziehau 
832b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_DEVCTX:
833b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)child_dev_ctx;
834b7bb4816SSepherosa Ziehau 		return (0);
835b7bb4816SSepherosa Ziehau 
836b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_NODE:
837b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)child_dev_ctx->device;
838b7bb4816SSepherosa Ziehau 		return (0);
839b7bb4816SSepherosa Ziehau 	}
840b7bb4816SSepherosa Ziehau 	return (ENOENT);
841b7bb4816SSepherosa Ziehau }
842b7bb4816SSepherosa Ziehau 
843b7bb4816SSepherosa Ziehau static int
844b7bb4816SSepherosa Ziehau vmbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
845b7bb4816SSepherosa Ziehau {
846b7bb4816SSepherosa Ziehau 	switch (index) {
847b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_TYPE:
848b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_INSTANCE:
849b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_DEVCTX:
850b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_NODE:
851b7bb4816SSepherosa Ziehau 		/* read-only */
852b7bb4816SSepherosa Ziehau 		return (EINVAL);
853b7bb4816SSepherosa Ziehau 	}
854b7bb4816SSepherosa Ziehau 	return (ENOENT);
855b7bb4816SSepherosa Ziehau }
856b7bb4816SSepherosa Ziehau 
857b7bb4816SSepherosa Ziehau static int
858b7bb4816SSepherosa Ziehau vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
859b7bb4816SSepherosa Ziehau {
860b7bb4816SSepherosa Ziehau 	struct hv_device *dev_ctx = device_get_ivars(child);
861b7bb4816SSepherosa Ziehau 	char guidbuf[HYPERV_GUID_STRLEN];
862b7bb4816SSepherosa Ziehau 
863b7bb4816SSepherosa Ziehau 	if (dev_ctx == NULL)
864b7bb4816SSepherosa Ziehau 		return (0);
865b7bb4816SSepherosa Ziehau 
866b7bb4816SSepherosa Ziehau 	strlcat(buf, "classid=", buflen);
867b7bb4816SSepherosa Ziehau 	hyperv_guid2str(&dev_ctx->class_id, guidbuf, sizeof(guidbuf));
868b7bb4816SSepherosa Ziehau 	strlcat(buf, guidbuf, buflen);
869b7bb4816SSepherosa Ziehau 
870b7bb4816SSepherosa Ziehau 	strlcat(buf, " deviceid=", buflen);
871b7bb4816SSepherosa Ziehau 	hyperv_guid2str(&dev_ctx->device_id, guidbuf, sizeof(guidbuf));
872b7bb4816SSepherosa Ziehau 	strlcat(buf, guidbuf, buflen);
873b7bb4816SSepherosa Ziehau 
874b7bb4816SSepherosa Ziehau 	return (0);
875b7bb4816SSepherosa Ziehau }
876b7bb4816SSepherosa Ziehau 
877b7bb4816SSepherosa Ziehau struct hv_device *
878b7bb4816SSepherosa Ziehau hv_vmbus_child_device_create(hv_guid type, hv_guid instance,
879b7bb4816SSepherosa Ziehau     hv_vmbus_channel *channel)
880b7bb4816SSepherosa Ziehau {
881b7bb4816SSepherosa Ziehau 	hv_device *child_dev;
882b7bb4816SSepherosa Ziehau 
883b7bb4816SSepherosa Ziehau 	/*
884b7bb4816SSepherosa Ziehau 	 * Allocate the new child device
885b7bb4816SSepherosa Ziehau 	 */
886b7bb4816SSepherosa Ziehau 	child_dev = malloc(sizeof(hv_device), M_DEVBUF, M_WAITOK | M_ZERO);
887b7bb4816SSepherosa Ziehau 
888b7bb4816SSepherosa Ziehau 	child_dev->channel = channel;
889b7bb4816SSepherosa Ziehau 	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
890b7bb4816SSepherosa Ziehau 	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
891b7bb4816SSepherosa Ziehau 
892b7bb4816SSepherosa Ziehau 	return (child_dev);
893b7bb4816SSepherosa Ziehau }
894b7bb4816SSepherosa Ziehau 
895b7bb4816SSepherosa Ziehau int
896b7bb4816SSepherosa Ziehau hv_vmbus_child_device_register(struct hv_device *child_dev)
897b7bb4816SSepherosa Ziehau {
898b7bb4816SSepherosa Ziehau 	device_t child, parent;
899b7bb4816SSepherosa Ziehau 
900b7bb4816SSepherosa Ziehau 	parent = vmbus_get_device();
901b7bb4816SSepherosa Ziehau 	if (bootverbose) {
902b7bb4816SSepherosa Ziehau 		char name[HYPERV_GUID_STRLEN];
903b7bb4816SSepherosa Ziehau 
904b7bb4816SSepherosa Ziehau 		hyperv_guid2str(&child_dev->class_id, name, sizeof(name));
905b7bb4816SSepherosa Ziehau 		device_printf(parent, "add device, classid: %s\n", name);
906b7bb4816SSepherosa Ziehau 	}
907b7bb4816SSepherosa Ziehau 
908b7bb4816SSepherosa Ziehau 	child = device_add_child(parent, NULL, -1);
909b7bb4816SSepherosa Ziehau 	child_dev->device = child;
910b7bb4816SSepherosa Ziehau 	device_set_ivars(child, child_dev);
911b7bb4816SSepherosa Ziehau 
912b7bb4816SSepherosa Ziehau 	return (0);
913b7bb4816SSepherosa Ziehau }
914b7bb4816SSepherosa Ziehau 
915b7bb4816SSepherosa Ziehau int
916b7bb4816SSepherosa Ziehau hv_vmbus_child_device_unregister(struct hv_device *child_dev)
917b7bb4816SSepherosa Ziehau {
918b7bb4816SSepherosa Ziehau 	int ret = 0;
919b7bb4816SSepherosa Ziehau 	/*
920b7bb4816SSepherosa Ziehau 	 * XXXKYS: Ensure that this is the opposite of
921b7bb4816SSepherosa Ziehau 	 * device_add_child()
922b7bb4816SSepherosa Ziehau 	 */
923b7bb4816SSepherosa Ziehau 	mtx_lock(&Giant);
924b7bb4816SSepherosa Ziehau 	ret = device_delete_child(vmbus_get_device(), child_dev->device);
925b7bb4816SSepherosa Ziehau 	mtx_unlock(&Giant);
926b7bb4816SSepherosa Ziehau 	return(ret);
927b7bb4816SSepherosa Ziehau }
928b7bb4816SSepherosa Ziehau 
929b7bb4816SSepherosa Ziehau static int
930b7bb4816SSepherosa Ziehau vmbus_probe(device_t dev)
931b7bb4816SSepherosa Ziehau {
932b7bb4816SSepherosa Ziehau 	char *id[] = { "VMBUS", NULL };
933b7bb4816SSepherosa Ziehau 
934b7bb4816SSepherosa Ziehau 	if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL ||
935b7bb4816SSepherosa Ziehau 	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV ||
936b7bb4816SSepherosa Ziehau 	    (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
937b7bb4816SSepherosa Ziehau 		return (ENXIO);
938b7bb4816SSepherosa Ziehau 
939b7bb4816SSepherosa Ziehau 	device_set_desc(dev, "Hyper-V Vmbus");
940b7bb4816SSepherosa Ziehau 
941b7bb4816SSepherosa Ziehau 	return (BUS_PROBE_DEFAULT);
942b7bb4816SSepherosa Ziehau }
943b7bb4816SSepherosa Ziehau 
944b7bb4816SSepherosa Ziehau /**
945b7bb4816SSepherosa Ziehau  * @brief Main vmbus driver initialization routine.
946b7bb4816SSepherosa Ziehau  *
947b7bb4816SSepherosa Ziehau  * Here, we
948b7bb4816SSepherosa Ziehau  * - initialize the vmbus driver context
949b7bb4816SSepherosa Ziehau  * - setup various driver entry points
950b7bb4816SSepherosa Ziehau  * - invoke the vmbus hv main init routine
951b7bb4816SSepherosa Ziehau  * - get the irq resource
952b7bb4816SSepherosa Ziehau  * - invoke the vmbus to add the vmbus root device
953b7bb4816SSepherosa Ziehau  * - setup the vmbus root device
954b7bb4816SSepherosa Ziehau  * - retrieve the channel offers
955b7bb4816SSepherosa Ziehau  */
956b7bb4816SSepherosa Ziehau static int
957b7bb4816SSepherosa Ziehau vmbus_bus_init(void)
958b7bb4816SSepherosa Ziehau {
959b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = vmbus_get_softc();
960b7bb4816SSepherosa Ziehau 	int ret;
961b7bb4816SSepherosa Ziehau 
962b7bb4816SSepherosa Ziehau 	if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
963b7bb4816SSepherosa Ziehau 		return (0);
964b7bb4816SSepherosa Ziehau 	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
965b7bb4816SSepherosa Ziehau 
966b7bb4816SSepherosa Ziehau 	/*
967*236764b1SSepherosa Ziehau 	 * Create context for "post message" Hypercalls
968*236764b1SSepherosa Ziehau 	 */
969*236764b1SSepherosa Ziehau 	sc->vmbus_msg_hc = vmbus_msghc_ctx_create(
970*236764b1SSepherosa Ziehau 	    bus_get_dma_tag(sc->vmbus_dev));
971*236764b1SSepherosa Ziehau 	if (sc->vmbus_msg_hc == NULL) {
972*236764b1SSepherosa Ziehau 		ret = ENXIO;
973*236764b1SSepherosa Ziehau 		goto cleanup;
974*236764b1SSepherosa Ziehau 	}
975*236764b1SSepherosa Ziehau 
976*236764b1SSepherosa Ziehau 	/*
977b7bb4816SSepherosa Ziehau 	 * Allocate DMA stuffs.
978b7bb4816SSepherosa Ziehau 	 */
979b7bb4816SSepherosa Ziehau 	ret = vmbus_dma_alloc(sc);
980b7bb4816SSepherosa Ziehau 	if (ret != 0)
981b7bb4816SSepherosa Ziehau 		goto cleanup;
982b7bb4816SSepherosa Ziehau 
983b7bb4816SSepherosa Ziehau 	/*
984b7bb4816SSepherosa Ziehau 	 * Setup interrupt.
985b7bb4816SSepherosa Ziehau 	 */
986b7bb4816SSepherosa Ziehau 	ret = vmbus_intr_setup(sc);
987b7bb4816SSepherosa Ziehau 	if (ret != 0)
988b7bb4816SSepherosa Ziehau 		goto cleanup;
989b7bb4816SSepherosa Ziehau 
990b7bb4816SSepherosa Ziehau 	/*
991b7bb4816SSepherosa Ziehau 	 * Setup SynIC.
992b7bb4816SSepherosa Ziehau 	 */
993b7bb4816SSepherosa Ziehau 	if (bootverbose)
994b7bb4816SSepherosa Ziehau 		device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
995b7bb4816SSepherosa Ziehau 	smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc);
996b7bb4816SSepherosa Ziehau 	sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
997b7bb4816SSepherosa Ziehau 
998b7bb4816SSepherosa Ziehau 	/*
999b7bb4816SSepherosa Ziehau 	 * Connect to VMBus in the root partition
1000b7bb4816SSepherosa Ziehau 	 */
10016523ab7eSSepherosa Ziehau 	ret = hv_vmbus_connect(sc);
1002b7bb4816SSepherosa Ziehau 	if (ret != 0)
1003b7bb4816SSepherosa Ziehau 		goto cleanup;
1004b7bb4816SSepherosa Ziehau 
1005*236764b1SSepherosa Ziehau 	ret = vmbus_init(sc);
1006*236764b1SSepherosa Ziehau 	if (ret != 0)
1007*236764b1SSepherosa Ziehau 		goto cleanup;
1008*236764b1SSepherosa Ziehau 
1009b7bb4816SSepherosa Ziehau 	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
1010b7bb4816SSepherosa Ziehau 	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
1011b7bb4816SSepherosa Ziehau 		sc->vmbus_event_proc = vmbus_event_proc_compat;
1012b7bb4816SSepherosa Ziehau 	else
1013b7bb4816SSepherosa Ziehau 		sc->vmbus_event_proc = vmbus_event_proc;
1014b7bb4816SSepherosa Ziehau 
1015b7bb4816SSepherosa Ziehau 	hv_vmbus_request_channel_offers();
1016b7bb4816SSepherosa Ziehau 
1017b7bb4816SSepherosa Ziehau 	vmbus_scan();
1018b7bb4816SSepherosa Ziehau 	bus_generic_attach(sc->vmbus_dev);
1019b7bb4816SSepherosa Ziehau 	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
1020b7bb4816SSepherosa Ziehau 
1021b7bb4816SSepherosa Ziehau 	return (ret);
1022b7bb4816SSepherosa Ziehau 
1023b7bb4816SSepherosa Ziehau cleanup:
1024b7bb4816SSepherosa Ziehau 	vmbus_intr_teardown(sc);
1025b7bb4816SSepherosa Ziehau 	vmbus_dma_free(sc);
1026*236764b1SSepherosa Ziehau 	if (sc->vmbus_msg_hc != NULL) {
1027*236764b1SSepherosa Ziehau 		vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
1028*236764b1SSepherosa Ziehau 		sc->vmbus_msg_hc = NULL;
1029*236764b1SSepherosa Ziehau 	}
1030b7bb4816SSepherosa Ziehau 
1031b7bb4816SSepherosa Ziehau 	return (ret);
1032b7bb4816SSepherosa Ziehau }
1033b7bb4816SSepherosa Ziehau 
1034b7bb4816SSepherosa Ziehau static void
1035b7bb4816SSepherosa Ziehau vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
1036b7bb4816SSepherosa Ziehau {
1037b7bb4816SSepherosa Ziehau }
1038b7bb4816SSepherosa Ziehau 
1039b7bb4816SSepherosa Ziehau static int
1040b7bb4816SSepherosa Ziehau vmbus_attach(device_t dev)
1041b7bb4816SSepherosa Ziehau {
1042b7bb4816SSepherosa Ziehau 	vmbus_sc = device_get_softc(dev);
1043b7bb4816SSepherosa Ziehau 	vmbus_sc->vmbus_dev = dev;
1044b7bb4816SSepherosa Ziehau 	vmbus_sc->vmbus_idtvec = -1;
1045b7bb4816SSepherosa Ziehau 
1046b7bb4816SSepherosa Ziehau 	/*
1047b7bb4816SSepherosa Ziehau 	 * Event processing logic will be configured:
1048b7bb4816SSepherosa Ziehau 	 * - After the vmbus protocol version negotiation.
1049b7bb4816SSepherosa Ziehau 	 * - Before we request channel offers.
1050b7bb4816SSepherosa Ziehau 	 */
1051b7bb4816SSepherosa Ziehau 	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
1052b7bb4816SSepherosa Ziehau 
1053b7bb4816SSepherosa Ziehau #ifndef EARLY_AP_STARTUP
1054b7bb4816SSepherosa Ziehau 	/*
1055b7bb4816SSepherosa Ziehau 	 * If the system has already booted and thread
1056b7bb4816SSepherosa Ziehau 	 * scheduling is possible indicated by the global
1057b7bb4816SSepherosa Ziehau 	 * cold set to zero, we just call the driver
1058b7bb4816SSepherosa Ziehau 	 * initialization directly.
1059b7bb4816SSepherosa Ziehau 	 */
1060b7bb4816SSepherosa Ziehau 	if (!cold)
1061b7bb4816SSepherosa Ziehau #endif
1062b7bb4816SSepherosa Ziehau 		vmbus_bus_init();
1063b7bb4816SSepherosa Ziehau 
1064b7bb4816SSepherosa Ziehau 	bus_generic_probe(dev);
1065b7bb4816SSepherosa Ziehau 	return (0);
1066b7bb4816SSepherosa Ziehau }
1067b7bb4816SSepherosa Ziehau 
1068b7bb4816SSepherosa Ziehau static void
1069b7bb4816SSepherosa Ziehau vmbus_sysinit(void *arg __unused)
1070b7bb4816SSepherosa Ziehau {
1071b7bb4816SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
1072b7bb4816SSepherosa Ziehau 		return;
1073b7bb4816SSepherosa Ziehau 
1074b7bb4816SSepherosa Ziehau #ifndef EARLY_AP_STARTUP
1075b7bb4816SSepherosa Ziehau 	/*
1076b7bb4816SSepherosa Ziehau 	 * If the system has already booted and thread
1077b7bb4816SSepherosa Ziehau 	 * scheduling is possible, as indicated by the
1078b7bb4816SSepherosa Ziehau 	 * global cold set to zero, we just call the driver
1079b7bb4816SSepherosa Ziehau 	 * initialization directly.
1080b7bb4816SSepherosa Ziehau 	 */
1081b7bb4816SSepherosa Ziehau 	if (!cold)
1082b7bb4816SSepherosa Ziehau #endif
1083b7bb4816SSepherosa Ziehau 		vmbus_bus_init();
1084b7bb4816SSepherosa Ziehau }
1085b7bb4816SSepherosa Ziehau 
1086b7bb4816SSepherosa Ziehau static int
1087b7bb4816SSepherosa Ziehau vmbus_detach(device_t dev)
1088b7bb4816SSepherosa Ziehau {
1089b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = device_get_softc(dev);
1090b7bb4816SSepherosa Ziehau 
1091b7bb4816SSepherosa Ziehau 	hv_vmbus_release_unattached_channels();
1092b7bb4816SSepherosa Ziehau 	hv_vmbus_disconnect();
1093b7bb4816SSepherosa Ziehau 
1094b7bb4816SSepherosa Ziehau 	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
1095b7bb4816SSepherosa Ziehau 		sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
1096b7bb4816SSepherosa Ziehau 		smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
1097b7bb4816SSepherosa Ziehau 	}
1098b7bb4816SSepherosa Ziehau 
1099b7bb4816SSepherosa Ziehau 	vmbus_intr_teardown(sc);
1100b7bb4816SSepherosa Ziehau 	vmbus_dma_free(sc);
1101b7bb4816SSepherosa Ziehau 
1102*236764b1SSepherosa Ziehau 	if (sc->vmbus_msg_hc != NULL) {
1103*236764b1SSepherosa Ziehau 		vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
1104*236764b1SSepherosa Ziehau 		sc->vmbus_msg_hc = NULL;
1105*236764b1SSepherosa Ziehau 	}
1106*236764b1SSepherosa Ziehau 
1107b7bb4816SSepherosa Ziehau 	return (0);
1108b7bb4816SSepherosa Ziehau }
1109b7bb4816SSepherosa Ziehau 
1110b7bb4816SSepherosa Ziehau static device_method_t vmbus_methods[] = {
1111b7bb4816SSepherosa Ziehau 	/* Device interface */
1112b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_probe,			vmbus_probe),
1113b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_attach,		vmbus_attach),
1114b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_detach,		vmbus_detach),
1115b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
1116b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_suspend,		bus_generic_suspend),
1117b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_resume,		bus_generic_resume),
1118b7bb4816SSepherosa Ziehau 
1119b7bb4816SSepherosa Ziehau 	/* Bus interface */
1120b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_add_child,		bus_generic_add_child),
1121b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_print_child,		bus_generic_print_child),
1122b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
1123b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
1124b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
1125b7bb4816SSepherosa Ziehau 
1126b7bb4816SSepherosa Ziehau 	DEVMETHOD_END
1127b7bb4816SSepherosa Ziehau };
1128b7bb4816SSepherosa Ziehau 
1129b7bb4816SSepherosa Ziehau static driver_t vmbus_driver = {
1130b7bb4816SSepherosa Ziehau 	"vmbus",
1131b7bb4816SSepherosa Ziehau 	vmbus_methods,
1132b7bb4816SSepherosa Ziehau 	sizeof(struct vmbus_softc)
1133b7bb4816SSepherosa Ziehau };
1134b7bb4816SSepherosa Ziehau 
1135b7bb4816SSepherosa Ziehau static devclass_t vmbus_devclass;
1136b7bb4816SSepherosa Ziehau 
1137b7bb4816SSepherosa Ziehau DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
1138b7bb4816SSepherosa Ziehau MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
1139b7bb4816SSepherosa Ziehau MODULE_VERSION(vmbus, 1);
1140b7bb4816SSepherosa Ziehau 
1141b7bb4816SSepherosa Ziehau #ifndef EARLY_AP_STARTUP
1142b7bb4816SSepherosa Ziehau /*
1143b7bb4816SSepherosa Ziehau  * NOTE:
1144b7bb4816SSepherosa Ziehau  * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
1145b7bb4816SSepherosa Ziehau  * initialized.
1146b7bb4816SSepherosa Ziehau  */
1147b7bb4816SSepherosa Ziehau SYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
1148b7bb4816SSepherosa Ziehau #endif
1149