xref: /freebsd/sys/dev/hyperv/vmbus/vmbus.c (revision dd012a57a7c85d27264df795038b8cf46ecb1028)
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 
72236764b1SSepherosa Ziehau /*
73236764b1SSepherosa Ziehau  * NOTE: DO NOT CHANGE THESE
74236764b1SSepherosa Ziehau  */
75236764b1SSepherosa Ziehau #define VMBUS_CONNID_MESSAGE		1
76236764b1SSepherosa Ziehau #define VMBUS_CONNID_EVENT		2
77236764b1SSepherosa Ziehau 
78236764b1SSepherosa Ziehau struct vmbus_msghc {
79236764b1SSepherosa Ziehau 	struct hypercall_postmsg_in	*mh_inprm;
80236764b1SSepherosa Ziehau 	struct hypercall_postmsg_in	mh_inprm_save;
81236764b1SSepherosa Ziehau 	struct hyperv_dma		mh_inprm_dma;
82236764b1SSepherosa Ziehau 
83236764b1SSepherosa Ziehau 	struct vmbus_message		*mh_resp;
84236764b1SSepherosa Ziehau 	struct vmbus_message		mh_resp0;
85236764b1SSepherosa Ziehau };
86236764b1SSepherosa Ziehau 
87236764b1SSepherosa Ziehau struct vmbus_msghc_ctx {
88236764b1SSepherosa Ziehau 	struct vmbus_msghc		*mhc_free;
89236764b1SSepherosa Ziehau 	struct mtx			mhc_free_lock;
90236764b1SSepherosa Ziehau 	uint32_t			mhc_flags;
91236764b1SSepherosa Ziehau 
92236764b1SSepherosa Ziehau 	struct vmbus_msghc		*mhc_active;
93236764b1SSepherosa Ziehau 	struct mtx			mhc_active_lock;
94236764b1SSepherosa Ziehau };
95236764b1SSepherosa Ziehau 
96236764b1SSepherosa Ziehau #define VMBUS_MSGHC_CTXF_DESTROY	0x0001
97236764b1SSepherosa Ziehau 
98236764b1SSepherosa Ziehau static int			vmbus_init(struct vmbus_softc *);
99236764b1SSepherosa Ziehau static int			vmbus_init_contact(struct vmbus_softc *,
100236764b1SSepherosa Ziehau 				    uint32_t);
101c1cc5bdfSSepherosa Ziehau static int			vmbus_req_channels(struct vmbus_softc *sc);
102236764b1SSepherosa Ziehau 
103236764b1SSepherosa Ziehau static struct vmbus_msghc_ctx	*vmbus_msghc_ctx_create(bus_dma_tag_t);
104236764b1SSepherosa Ziehau static void			vmbus_msghc_ctx_destroy(
105236764b1SSepherosa Ziehau 				    struct vmbus_msghc_ctx *);
106236764b1SSepherosa Ziehau static void			vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *);
107236764b1SSepherosa Ziehau static struct vmbus_msghc	*vmbus_msghc_alloc(bus_dma_tag_t);
108236764b1SSepherosa Ziehau static void			vmbus_msghc_free(struct vmbus_msghc *);
109236764b1SSepherosa Ziehau static struct vmbus_msghc	*vmbus_msghc_get1(struct vmbus_msghc_ctx *,
110236764b1SSepherosa Ziehau 				    uint32_t);
111236764b1SSepherosa Ziehau 
112b7bb4816SSepherosa Ziehau struct vmbus_softc	*vmbus_sc;
113b7bb4816SSepherosa Ziehau 
114b7bb4816SSepherosa Ziehau extern inthand_t IDTVEC(vmbus_isr);
115b7bb4816SSepherosa Ziehau 
116236764b1SSepherosa Ziehau static const uint32_t		vmbus_version[] = {
117236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WIN8_1,
118236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WIN8,
119236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WIN7,
120236764b1SSepherosa Ziehau 	HV_VMBUS_VERSION_WS2008
121236764b1SSepherosa Ziehau };
122236764b1SSepherosa Ziehau 
123236764b1SSepherosa Ziehau static struct vmbus_msghc *
124236764b1SSepherosa Ziehau vmbus_msghc_alloc(bus_dma_tag_t parent_dtag)
125236764b1SSepherosa Ziehau {
126236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
127236764b1SSepherosa Ziehau 
128236764b1SSepherosa Ziehau 	mh = malloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO);
129236764b1SSepherosa Ziehau 
130236764b1SSepherosa Ziehau 	mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag,
131236764b1SSepherosa Ziehau 	    HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE,
132236764b1SSepherosa Ziehau 	    &mh->mh_inprm_dma, BUS_DMA_WAITOK);
133236764b1SSepherosa Ziehau 	if (mh->mh_inprm == NULL) {
134236764b1SSepherosa Ziehau 		free(mh, M_DEVBUF);
135236764b1SSepherosa Ziehau 		return NULL;
136236764b1SSepherosa Ziehau 	}
137236764b1SSepherosa Ziehau 	return mh;
138236764b1SSepherosa Ziehau }
139236764b1SSepherosa Ziehau 
140236764b1SSepherosa Ziehau static void
141236764b1SSepherosa Ziehau vmbus_msghc_free(struct vmbus_msghc *mh)
142236764b1SSepherosa Ziehau {
143236764b1SSepherosa Ziehau 	hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm);
144236764b1SSepherosa Ziehau 	free(mh, M_DEVBUF);
145236764b1SSepherosa Ziehau }
146236764b1SSepherosa Ziehau 
147236764b1SSepherosa Ziehau static void
148236764b1SSepherosa Ziehau vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc)
149236764b1SSepherosa Ziehau {
150236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall"));
151236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg"));
152236764b1SSepherosa Ziehau 
153236764b1SSepherosa Ziehau 	mtx_destroy(&mhc->mhc_free_lock);
154236764b1SSepherosa Ziehau 	mtx_destroy(&mhc->mhc_active_lock);
155236764b1SSepherosa Ziehau 	free(mhc, M_DEVBUF);
156236764b1SSepherosa Ziehau }
157236764b1SSepherosa Ziehau 
158236764b1SSepherosa Ziehau static struct vmbus_msghc_ctx *
159236764b1SSepherosa Ziehau vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag)
160236764b1SSepherosa Ziehau {
161236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc;
162236764b1SSepherosa Ziehau 
163236764b1SSepherosa Ziehau 	mhc = malloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO);
164236764b1SSepherosa Ziehau 	mtx_init(&mhc->mhc_free_lock, "vmbus msghc free", NULL, MTX_DEF);
165236764b1SSepherosa Ziehau 	mtx_init(&mhc->mhc_active_lock, "vmbus msghc act", NULL, MTX_DEF);
166236764b1SSepherosa Ziehau 
167236764b1SSepherosa Ziehau 	mhc->mhc_free = vmbus_msghc_alloc(parent_dtag);
168236764b1SSepherosa Ziehau 	if (mhc->mhc_free == NULL) {
169236764b1SSepherosa Ziehau 		vmbus_msghc_ctx_free(mhc);
170236764b1SSepherosa Ziehau 		return NULL;
171236764b1SSepherosa Ziehau 	}
172236764b1SSepherosa Ziehau 	return mhc;
173236764b1SSepherosa Ziehau }
174236764b1SSepherosa Ziehau 
175236764b1SSepherosa Ziehau static struct vmbus_msghc *
176236764b1SSepherosa Ziehau vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag)
177236764b1SSepherosa Ziehau {
178236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
179236764b1SSepherosa Ziehau 
180236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_free_lock);
181236764b1SSepherosa Ziehau 
182236764b1SSepherosa Ziehau 	while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL) {
183236764b1SSepherosa Ziehau 		mtx_sleep(&mhc->mhc_free, &mhc->mhc_free_lock, 0,
184236764b1SSepherosa Ziehau 		    "gmsghc", 0);
185236764b1SSepherosa Ziehau 	}
186236764b1SSepherosa Ziehau 	if (mhc->mhc_flags & dtor_flag) {
187236764b1SSepherosa Ziehau 		/* Being destroyed */
188236764b1SSepherosa Ziehau 		mh = NULL;
189236764b1SSepherosa Ziehau 	} else {
190236764b1SSepherosa Ziehau 		mh = mhc->mhc_free;
191236764b1SSepherosa Ziehau 		KASSERT(mh != NULL, ("no free hypercall msg"));
192236764b1SSepherosa Ziehau 		KASSERT(mh->mh_resp == NULL,
193236764b1SSepherosa Ziehau 		    ("hypercall msg has pending response"));
194236764b1SSepherosa Ziehau 		mhc->mhc_free = NULL;
195236764b1SSepherosa Ziehau 	}
196236764b1SSepherosa Ziehau 
197236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_free_lock);
198236764b1SSepherosa Ziehau 
199236764b1SSepherosa Ziehau 	return mh;
200236764b1SSepherosa Ziehau }
201236764b1SSepherosa Ziehau 
202236764b1SSepherosa Ziehau struct vmbus_msghc *
203236764b1SSepherosa Ziehau vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize)
204236764b1SSepherosa Ziehau {
205236764b1SSepherosa Ziehau 	struct hypercall_postmsg_in *inprm;
206236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
207236764b1SSepherosa Ziehau 
208236764b1SSepherosa Ziehau 	if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
209236764b1SSepherosa Ziehau 		return NULL;
210236764b1SSepherosa Ziehau 
211236764b1SSepherosa Ziehau 	mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY);
212236764b1SSepherosa Ziehau 	if (mh == NULL)
213236764b1SSepherosa Ziehau 		return NULL;
214236764b1SSepherosa Ziehau 
215236764b1SSepherosa Ziehau 	inprm = mh->mh_inprm;
216236764b1SSepherosa Ziehau 	memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE);
217236764b1SSepherosa Ziehau 	inprm->hc_connid = VMBUS_CONNID_MESSAGE;
218236764b1SSepherosa Ziehau 	inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL;
219236764b1SSepherosa Ziehau 	inprm->hc_dsize = dsize;
220236764b1SSepherosa Ziehau 
221236764b1SSepherosa Ziehau 	return mh;
222236764b1SSepherosa Ziehau }
223236764b1SSepherosa Ziehau 
224236764b1SSepherosa Ziehau void
225236764b1SSepherosa Ziehau vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh)
226236764b1SSepherosa Ziehau {
227236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
228236764b1SSepherosa Ziehau 
229236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active"));
230236764b1SSepherosa Ziehau 	mh->mh_resp = NULL;
231236764b1SSepherosa Ziehau 
232236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_free_lock);
233236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg"));
234236764b1SSepherosa Ziehau 	mhc->mhc_free = mh;
235236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_free_lock);
236236764b1SSepherosa Ziehau 	wakeup(&mhc->mhc_free);
237236764b1SSepherosa Ziehau }
238236764b1SSepherosa Ziehau 
239236764b1SSepherosa Ziehau void *
240236764b1SSepherosa Ziehau vmbus_msghc_dataptr(struct vmbus_msghc *mh)
241236764b1SSepherosa Ziehau {
242236764b1SSepherosa Ziehau 	return mh->mh_inprm->hc_data;
243236764b1SSepherosa Ziehau }
244236764b1SSepherosa Ziehau 
245236764b1SSepherosa Ziehau static void
246236764b1SSepherosa Ziehau vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc)
247236764b1SSepherosa Ziehau {
248236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
249236764b1SSepherosa Ziehau 
250236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_free_lock);
251236764b1SSepherosa Ziehau 	mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY;
252236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_free_lock);
253236764b1SSepherosa Ziehau 	wakeup(&mhc->mhc_free);
254236764b1SSepherosa Ziehau 
255236764b1SSepherosa Ziehau 	mh = vmbus_msghc_get1(mhc, 0);
256236764b1SSepherosa Ziehau 	if (mh == NULL)
257236764b1SSepherosa Ziehau 		panic("can't get msghc");
258236764b1SSepherosa Ziehau 
259236764b1SSepherosa Ziehau 	vmbus_msghc_free(mh);
260236764b1SSepherosa Ziehau 	vmbus_msghc_ctx_free(mhc);
261236764b1SSepherosa Ziehau }
262236764b1SSepherosa Ziehau 
263236764b1SSepherosa Ziehau int
264236764b1SSepherosa Ziehau vmbus_msghc_exec_noresult(struct vmbus_msghc *mh)
265236764b1SSepherosa Ziehau {
266236764b1SSepherosa Ziehau 	sbintime_t time = SBT_1MS;
267236764b1SSepherosa Ziehau 	int i;
268236764b1SSepherosa Ziehau 
269236764b1SSepherosa Ziehau 	/*
270236764b1SSepherosa Ziehau 	 * Save the input parameter so that we could restore the input
271236764b1SSepherosa Ziehau 	 * parameter if the Hypercall failed.
272236764b1SSepherosa Ziehau 	 *
273236764b1SSepherosa Ziehau 	 * XXX
274236764b1SSepherosa Ziehau 	 * Is this really necessary?!  i.e. Will the Hypercall ever
275236764b1SSepherosa Ziehau 	 * overwrite the input parameter?
276236764b1SSepherosa Ziehau 	 */
277236764b1SSepherosa Ziehau 	memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE);
278236764b1SSepherosa Ziehau 
279236764b1SSepherosa Ziehau 	/*
280236764b1SSepherosa Ziehau 	 * In order to cope with transient failures, e.g. insufficient
281236764b1SSepherosa Ziehau 	 * resources on host side, we retry the post message Hypercall
282236764b1SSepherosa Ziehau 	 * several times.  20 retries seem sufficient.
283236764b1SSepherosa Ziehau 	 */
284236764b1SSepherosa Ziehau #define HC_RETRY_MAX	20
285236764b1SSepherosa Ziehau 
286236764b1SSepherosa Ziehau 	for (i = 0; i < HC_RETRY_MAX; ++i) {
287236764b1SSepherosa Ziehau 		uint64_t status;
288236764b1SSepherosa Ziehau 
289236764b1SSepherosa Ziehau 		status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr);
290236764b1SSepherosa Ziehau 		if (status == HYPERCALL_STATUS_SUCCESS)
291236764b1SSepherosa Ziehau 			return 0;
292236764b1SSepherosa Ziehau 
293236764b1SSepherosa Ziehau 		pause_sbt("hcpmsg", time, 0, C_HARDCLOCK);
294236764b1SSepherosa Ziehau 		if (time < SBT_1S * 2)
295236764b1SSepherosa Ziehau 			time *= 2;
296236764b1SSepherosa Ziehau 
297236764b1SSepherosa Ziehau 		/* Restore input parameter and try again */
298236764b1SSepherosa Ziehau 		memcpy(mh->mh_inprm, &mh->mh_inprm_save,
299236764b1SSepherosa Ziehau 		    HYPERCALL_POSTMSGIN_SIZE);
300236764b1SSepherosa Ziehau 	}
301236764b1SSepherosa Ziehau 
302236764b1SSepherosa Ziehau #undef HC_RETRY_MAX
303236764b1SSepherosa Ziehau 
304236764b1SSepherosa Ziehau 	return EIO;
305236764b1SSepherosa Ziehau }
306236764b1SSepherosa Ziehau 
307236764b1SSepherosa Ziehau int
308236764b1SSepherosa Ziehau vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh)
309236764b1SSepherosa Ziehau {
310236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
311236764b1SSepherosa Ziehau 	int error;
312236764b1SSepherosa Ziehau 
313236764b1SSepherosa Ziehau 	KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response"));
314236764b1SSepherosa Ziehau 
315236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_active_lock);
316236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall"));
317236764b1SSepherosa Ziehau 	mhc->mhc_active = mh;
318236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_active_lock);
319236764b1SSepherosa Ziehau 
320236764b1SSepherosa Ziehau 	error = vmbus_msghc_exec_noresult(mh);
321236764b1SSepherosa Ziehau 	if (error) {
322236764b1SSepherosa Ziehau 		mtx_lock(&mhc->mhc_active_lock);
323236764b1SSepherosa Ziehau 		KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
324236764b1SSepherosa Ziehau 		mhc->mhc_active = NULL;
325236764b1SSepherosa Ziehau 		mtx_unlock(&mhc->mhc_active_lock);
326236764b1SSepherosa Ziehau 	}
327236764b1SSepherosa Ziehau 	return error;
328236764b1SSepherosa Ziehau }
329236764b1SSepherosa Ziehau 
330236764b1SSepherosa Ziehau const struct vmbus_message *
331236764b1SSepherosa Ziehau vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh)
332236764b1SSepherosa Ziehau {
333236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
334236764b1SSepherosa Ziehau 
335236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_active_lock);
336236764b1SSepherosa Ziehau 
337236764b1SSepherosa Ziehau 	KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
338236764b1SSepherosa Ziehau 	while (mh->mh_resp == NULL) {
339236764b1SSepherosa Ziehau 		mtx_sleep(&mhc->mhc_active, &mhc->mhc_active_lock, 0,
340236764b1SSepherosa Ziehau 		    "wmsghc", 0);
341236764b1SSepherosa Ziehau 	}
342236764b1SSepherosa Ziehau 	mhc->mhc_active = NULL;
343236764b1SSepherosa Ziehau 
344236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_active_lock);
345236764b1SSepherosa Ziehau 
346236764b1SSepherosa Ziehau 	return mh->mh_resp;
347236764b1SSepherosa Ziehau }
348236764b1SSepherosa Ziehau 
349236764b1SSepherosa Ziehau void
350236764b1SSepherosa Ziehau vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg)
351236764b1SSepherosa Ziehau {
352236764b1SSepherosa Ziehau 	struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
353236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
354236764b1SSepherosa Ziehau 
355236764b1SSepherosa Ziehau 	mtx_lock(&mhc->mhc_active_lock);
356236764b1SSepherosa Ziehau 
357236764b1SSepherosa Ziehau 	mh = mhc->mhc_active;
358236764b1SSepherosa Ziehau 	KASSERT(mh != NULL, ("no pending msg hypercall"));
359236764b1SSepherosa Ziehau 	memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0));
360236764b1SSepherosa Ziehau 	mh->mh_resp = &mh->mh_resp0;
361236764b1SSepherosa Ziehau 
362236764b1SSepherosa Ziehau 	mtx_unlock(&mhc->mhc_active_lock);
363236764b1SSepherosa Ziehau 	wakeup(&mhc->mhc_active);
364236764b1SSepherosa Ziehau }
365236764b1SSepherosa Ziehau 
366236764b1SSepherosa Ziehau static int
367236764b1SSepherosa Ziehau vmbus_init_contact(struct vmbus_softc *sc, uint32_t version)
368236764b1SSepherosa Ziehau {
369236764b1SSepherosa Ziehau 	struct vmbus_chanmsg_init_contact *req;
370236764b1SSepherosa Ziehau 	const struct vmbus_chanmsg_version_resp *resp;
371236764b1SSepherosa Ziehau 	const struct vmbus_message *msg;
372236764b1SSepherosa Ziehau 	struct vmbus_msghc *mh;
373236764b1SSepherosa Ziehau 	int error, supp = 0;
374236764b1SSepherosa Ziehau 
375236764b1SSepherosa Ziehau 	mh = vmbus_msghc_get(sc, sizeof(*req));
376236764b1SSepherosa Ziehau 	if (mh == NULL)
377236764b1SSepherosa Ziehau 		return ENXIO;
378236764b1SSepherosa Ziehau 
379236764b1SSepherosa Ziehau 	req = vmbus_msghc_dataptr(mh);
380236764b1SSepherosa Ziehau 	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT;
381236764b1SSepherosa Ziehau 	req->chm_ver = version;
382236764b1SSepherosa Ziehau 	req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr;
383236764b1SSepherosa Ziehau 	req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr;
384236764b1SSepherosa Ziehau 	req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr;
385236764b1SSepherosa Ziehau 
386236764b1SSepherosa Ziehau 	error = vmbus_msghc_exec(sc, mh);
387236764b1SSepherosa Ziehau 	if (error) {
388236764b1SSepherosa Ziehau 		vmbus_msghc_put(sc, mh);
389236764b1SSepherosa Ziehau 		return error;
390236764b1SSepherosa Ziehau 	}
391236764b1SSepherosa Ziehau 
392236764b1SSepherosa Ziehau 	msg = vmbus_msghc_wait_result(sc, mh);
393236764b1SSepherosa Ziehau 	resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data;
394236764b1SSepherosa Ziehau 	supp = resp->chm_supp;
395236764b1SSepherosa Ziehau 
396236764b1SSepherosa Ziehau 	vmbus_msghc_put(sc, mh);
397236764b1SSepherosa Ziehau 
398236764b1SSepherosa Ziehau 	return (supp ? 0 : EOPNOTSUPP);
399236764b1SSepherosa Ziehau }
400236764b1SSepherosa Ziehau 
401236764b1SSepherosa Ziehau static int
402236764b1SSepherosa Ziehau vmbus_init(struct vmbus_softc *sc)
403236764b1SSepherosa Ziehau {
404236764b1SSepherosa Ziehau 	int i;
405236764b1SSepherosa Ziehau 
406236764b1SSepherosa Ziehau 	for (i = 0; i < nitems(vmbus_version); ++i) {
407236764b1SSepherosa Ziehau 		int error;
408236764b1SSepherosa Ziehau 
409236764b1SSepherosa Ziehau 		error = vmbus_init_contact(sc, vmbus_version[i]);
410236764b1SSepherosa Ziehau 		if (!error) {
411236764b1SSepherosa Ziehau 			hv_vmbus_protocal_version = vmbus_version[i];
412236764b1SSepherosa Ziehau 			device_printf(sc->vmbus_dev, "version %u.%u\n",
413236764b1SSepherosa Ziehau 			    (hv_vmbus_protocal_version >> 16),
414236764b1SSepherosa Ziehau 			    (hv_vmbus_protocal_version & 0xffff));
415236764b1SSepherosa Ziehau 			return 0;
416236764b1SSepherosa Ziehau 		}
417236764b1SSepherosa Ziehau 	}
418236764b1SSepherosa Ziehau 	return ENXIO;
419236764b1SSepherosa Ziehau }
420236764b1SSepherosa Ziehau 
421c1cc5bdfSSepherosa Ziehau static int
422c1cc5bdfSSepherosa Ziehau vmbus_req_channels(struct vmbus_softc *sc)
423c1cc5bdfSSepherosa Ziehau {
424c1cc5bdfSSepherosa Ziehau 	struct vmbus_chanmsg_channel_req *req;
425c1cc5bdfSSepherosa Ziehau 	struct vmbus_msghc *mh;
426c1cc5bdfSSepherosa Ziehau 	int error;
427c1cc5bdfSSepherosa Ziehau 
428c1cc5bdfSSepherosa Ziehau 	mh = vmbus_msghc_get(sc, sizeof(*req));
429c1cc5bdfSSepherosa Ziehau 	if (mh == NULL)
430c1cc5bdfSSepherosa Ziehau 		return ENXIO;
431c1cc5bdfSSepherosa Ziehau 
432c1cc5bdfSSepherosa Ziehau 	req = vmbus_msghc_dataptr(mh);
433c1cc5bdfSSepherosa Ziehau 	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHANNEL_REQ;
434c1cc5bdfSSepherosa Ziehau 
435c1cc5bdfSSepherosa Ziehau 	error = vmbus_msghc_exec_noresult(mh);
436c1cc5bdfSSepherosa Ziehau 	vmbus_msghc_put(sc, mh);
437c1cc5bdfSSepherosa Ziehau 
438c1cc5bdfSSepherosa Ziehau 	return error;
439c1cc5bdfSSepherosa Ziehau }
440c1cc5bdfSSepherosa Ziehau 
441b7bb4816SSepherosa Ziehau static void
442b7bb4816SSepherosa Ziehau vmbus_msg_task(void *xsc, int pending __unused)
443b7bb4816SSepherosa Ziehau {
444b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = xsc;
445b7bb4816SSepherosa Ziehau 	volatile struct vmbus_message *msg;
446b7bb4816SSepherosa Ziehau 
447b7bb4816SSepherosa Ziehau 	msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE;
448b7bb4816SSepherosa Ziehau 	for (;;) {
449236764b1SSepherosa Ziehau 		if (msg->msg_type == HYPERV_MSGTYPE_NONE) {
450fa4828b0SSepherosa Ziehau 			/* No message */
451fa4828b0SSepherosa Ziehau 			break;
452236764b1SSepherosa Ziehau 		} else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) {
45312ea25aeSSepherosa Ziehau 			/* Channel message */
45443926ba1SSepherosa Ziehau 			vmbus_chan_msgproc(sc,
45543926ba1SSepherosa Ziehau 			    __DEVOLATILE(const struct vmbus_message *, msg));
456fa4828b0SSepherosa Ziehau 		}
457b7bb4816SSepherosa Ziehau 
458236764b1SSepherosa Ziehau 		msg->msg_type = HYPERV_MSGTYPE_NONE;
459b7bb4816SSepherosa Ziehau 		/*
460b7bb4816SSepherosa Ziehau 		 * Make sure the write to msg_type (i.e. set to
461236764b1SSepherosa Ziehau 		 * HYPERV_MSGTYPE_NONE) happens before we read the
462b7bb4816SSepherosa Ziehau 		 * msg_flags and EOMing. Otherwise, the EOMing will
463b7bb4816SSepherosa Ziehau 		 * not deliver any more messages since there is no
464b7bb4816SSepherosa Ziehau 		 * empty slot
465b7bb4816SSepherosa Ziehau 		 *
466b7bb4816SSepherosa Ziehau 		 * NOTE:
467b7bb4816SSepherosa Ziehau 		 * mb() is used here, since atomic_thread_fence_seq_cst()
468b7bb4816SSepherosa Ziehau 		 * will become compiler fence on UP kernel.
469b7bb4816SSepherosa Ziehau 		 */
470b7bb4816SSepherosa Ziehau 		mb();
471b7bb4816SSepherosa Ziehau 		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
472b7bb4816SSepherosa Ziehau 			/*
473b7bb4816SSepherosa Ziehau 			 * This will cause message queue rescan to possibly
474b7bb4816SSepherosa Ziehau 			 * deliver another msg from the hypervisor
475b7bb4816SSepherosa Ziehau 			 */
476b7bb4816SSepherosa Ziehau 			wrmsr(MSR_HV_EOM, 0);
477b7bb4816SSepherosa Ziehau 		}
478b7bb4816SSepherosa Ziehau 	}
479b7bb4816SSepherosa Ziehau }
480b7bb4816SSepherosa Ziehau 
481b7bb4816SSepherosa Ziehau static __inline int
482b7bb4816SSepherosa Ziehau vmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
483b7bb4816SSepherosa Ziehau {
484b7bb4816SSepherosa Ziehau 	volatile struct vmbus_message *msg;
485b7bb4816SSepherosa Ziehau 	struct vmbus_message *msg_base;
486b7bb4816SSepherosa Ziehau 
487b7bb4816SSepherosa Ziehau 	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
488b7bb4816SSepherosa Ziehau 
489b7bb4816SSepherosa Ziehau 	/*
490b7bb4816SSepherosa Ziehau 	 * Check event timer.
491b7bb4816SSepherosa Ziehau 	 *
492b7bb4816SSepherosa Ziehau 	 * TODO: move this to independent IDT vector.
493b7bb4816SSepherosa Ziehau 	 */
494b7bb4816SSepherosa Ziehau 	msg = msg_base + VMBUS_SINT_TIMER;
495236764b1SSepherosa Ziehau 	if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) {
496236764b1SSepherosa Ziehau 		msg->msg_type = HYPERV_MSGTYPE_NONE;
497b7bb4816SSepherosa Ziehau 
498b7bb4816SSepherosa Ziehau 		vmbus_et_intr(frame);
499b7bb4816SSepherosa Ziehau 
500b7bb4816SSepherosa Ziehau 		/*
501b7bb4816SSepherosa Ziehau 		 * Make sure the write to msg_type (i.e. set to
502236764b1SSepherosa Ziehau 		 * HYPERV_MSGTYPE_NONE) happens before we read the
503b7bb4816SSepherosa Ziehau 		 * msg_flags and EOMing. Otherwise, the EOMing will
504b7bb4816SSepherosa Ziehau 		 * not deliver any more messages since there is no
505b7bb4816SSepherosa Ziehau 		 * empty slot
506b7bb4816SSepherosa Ziehau 		 *
507b7bb4816SSepherosa Ziehau 		 * NOTE:
508b7bb4816SSepherosa Ziehau 		 * mb() is used here, since atomic_thread_fence_seq_cst()
509b7bb4816SSepherosa Ziehau 		 * will become compiler fence on UP kernel.
510b7bb4816SSepherosa Ziehau 		 */
511b7bb4816SSepherosa Ziehau 		mb();
512b7bb4816SSepherosa Ziehau 		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
513b7bb4816SSepherosa Ziehau 			/*
514b7bb4816SSepherosa Ziehau 			 * This will cause message queue rescan to possibly
515b7bb4816SSepherosa Ziehau 			 * deliver another msg from the hypervisor
516b7bb4816SSepherosa Ziehau 			 */
517b7bb4816SSepherosa Ziehau 			wrmsr(MSR_HV_EOM, 0);
518b7bb4816SSepherosa Ziehau 		}
519b7bb4816SSepherosa Ziehau 	}
520b7bb4816SSepherosa Ziehau 
521b7bb4816SSepherosa Ziehau 	/*
522b7bb4816SSepherosa Ziehau 	 * Check events.  Hot path for network and storage I/O data; high rate.
523b7bb4816SSepherosa Ziehau 	 *
524b7bb4816SSepherosa Ziehau 	 * NOTE:
525b7bb4816SSepherosa Ziehau 	 * As recommended by the Windows guest fellows, we check events before
526b7bb4816SSepherosa Ziehau 	 * checking messages.
527b7bb4816SSepherosa Ziehau 	 */
528b7bb4816SSepherosa Ziehau 	sc->vmbus_event_proc(sc, cpu);
529b7bb4816SSepherosa Ziehau 
530b7bb4816SSepherosa Ziehau 	/*
531b7bb4816SSepherosa Ziehau 	 * Check messages.  Mainly management stuffs; ultra low rate.
532b7bb4816SSepherosa Ziehau 	 */
533b7bb4816SSepherosa Ziehau 	msg = msg_base + VMBUS_SINT_MESSAGE;
534236764b1SSepherosa Ziehau 	if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
535b7bb4816SSepherosa Ziehau 		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
536b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, message_task, cpu));
537b7bb4816SSepherosa Ziehau 	}
538b7bb4816SSepherosa Ziehau 
539b7bb4816SSepherosa Ziehau 	return (FILTER_HANDLED);
540b7bb4816SSepherosa Ziehau }
541b7bb4816SSepherosa Ziehau 
542b7bb4816SSepherosa Ziehau void
543b7bb4816SSepherosa Ziehau vmbus_handle_intr(struct trapframe *trap_frame)
544b7bb4816SSepherosa Ziehau {
545b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = vmbus_get_softc();
546b7bb4816SSepherosa Ziehau 	int cpu = curcpu;
547b7bb4816SSepherosa Ziehau 
548b7bb4816SSepherosa Ziehau 	/*
549b7bb4816SSepherosa Ziehau 	 * Disable preemption.
550b7bb4816SSepherosa Ziehau 	 */
551b7bb4816SSepherosa Ziehau 	critical_enter();
552b7bb4816SSepherosa Ziehau 
553b7bb4816SSepherosa Ziehau 	/*
554b7bb4816SSepherosa Ziehau 	 * Do a little interrupt counting.
555b7bb4816SSepherosa Ziehau 	 */
556b7bb4816SSepherosa Ziehau 	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
557b7bb4816SSepherosa Ziehau 
558b7bb4816SSepherosa Ziehau 	vmbus_handle_intr1(sc, trap_frame, cpu);
559b7bb4816SSepherosa Ziehau 
560b7bb4816SSepherosa Ziehau 	/*
561b7bb4816SSepherosa Ziehau 	 * Enable preemption.
562b7bb4816SSepherosa Ziehau 	 */
563b7bb4816SSepherosa Ziehau 	critical_exit();
564b7bb4816SSepherosa Ziehau }
565b7bb4816SSepherosa Ziehau 
566b7bb4816SSepherosa Ziehau static void
567b7bb4816SSepherosa Ziehau vmbus_synic_setup(void *xsc)
568b7bb4816SSepherosa Ziehau {
569b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = xsc;
570b7bb4816SSepherosa Ziehau 	int cpu = curcpu;
571b7bb4816SSepherosa Ziehau 	uint64_t val, orig;
572b7bb4816SSepherosa Ziehau 	uint32_t sint;
573b7bb4816SSepherosa Ziehau 
574b7bb4816SSepherosa Ziehau 	if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
575b7bb4816SSepherosa Ziehau 		/*
576b7bb4816SSepherosa Ziehau 		 * Save virtual processor id.
577b7bb4816SSepherosa Ziehau 		 */
578b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX);
579b7bb4816SSepherosa Ziehau 	} else {
580b7bb4816SSepherosa Ziehau 		/*
581b7bb4816SSepherosa Ziehau 		 * XXX
582b7bb4816SSepherosa Ziehau 		 * Virtual processoor id is only used by a pretty broken
583b7bb4816SSepherosa Ziehau 		 * channel selection code from storvsc.  It's nothing
584b7bb4816SSepherosa Ziehau 		 * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep
585b7bb4816SSepherosa Ziehau 		 * moving on.
586b7bb4816SSepherosa Ziehau 		 */
587b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, vcpuid, cpu) = cpu;
588b7bb4816SSepherosa Ziehau 	}
589b7bb4816SSepherosa Ziehau 
590b7bb4816SSepherosa Ziehau 	/*
591b7bb4816SSepherosa Ziehau 	 * Setup the SynIC message.
592b7bb4816SSepherosa Ziehau 	 */
593b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIMP);
594b7bb4816SSepherosa Ziehau 	val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
595b7bb4816SSepherosa Ziehau 	    ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) <<
596b7bb4816SSepherosa Ziehau 	     MSR_HV_SIMP_PGSHIFT);
597b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIMP, val);
598b7bb4816SSepherosa Ziehau 
599b7bb4816SSepherosa Ziehau 	/*
600b7bb4816SSepherosa Ziehau 	 * Setup the SynIC event flags.
601b7bb4816SSepherosa Ziehau 	 */
602b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIEFP);
603b7bb4816SSepherosa Ziehau 	val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
604b7bb4816SSepherosa Ziehau 	    ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu)
605b7bb4816SSepherosa Ziehau 	      >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT);
606b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIEFP, val);
607b7bb4816SSepherosa Ziehau 
608b7bb4816SSepherosa Ziehau 
609b7bb4816SSepherosa Ziehau 	/*
610b7bb4816SSepherosa Ziehau 	 * Configure and unmask SINT for message and event flags.
611b7bb4816SSepherosa Ziehau 	 */
612b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
613b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
614b7bb4816SSepherosa Ziehau 	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
615b7bb4816SSepherosa Ziehau 	    (orig & MSR_HV_SINT_RSVD_MASK);
616b7bb4816SSepherosa Ziehau 	wrmsr(sint, val);
617b7bb4816SSepherosa Ziehau 
618b7bb4816SSepherosa Ziehau 	/*
619b7bb4816SSepherosa Ziehau 	 * Configure and unmask SINT for timer.
620b7bb4816SSepherosa Ziehau 	 */
621b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
622b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
623b7bb4816SSepherosa Ziehau 	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
624b7bb4816SSepherosa Ziehau 	    (orig & MSR_HV_SINT_RSVD_MASK);
625b7bb4816SSepherosa Ziehau 	wrmsr(sint, val);
626b7bb4816SSepherosa Ziehau 
627b7bb4816SSepherosa Ziehau 	/*
628b7bb4816SSepherosa Ziehau 	 * All done; enable SynIC.
629b7bb4816SSepherosa Ziehau 	 */
630b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SCONTROL);
631b7bb4816SSepherosa Ziehau 	val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
632b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SCONTROL, val);
633b7bb4816SSepherosa Ziehau }
634b7bb4816SSepherosa Ziehau 
635b7bb4816SSepherosa Ziehau static void
636b7bb4816SSepherosa Ziehau vmbus_synic_teardown(void *arg)
637b7bb4816SSepherosa Ziehau {
638b7bb4816SSepherosa Ziehau 	uint64_t orig;
639b7bb4816SSepherosa Ziehau 	uint32_t sint;
640b7bb4816SSepherosa Ziehau 
641b7bb4816SSepherosa Ziehau 	/*
642b7bb4816SSepherosa Ziehau 	 * Disable SynIC.
643b7bb4816SSepherosa Ziehau 	 */
644b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SCONTROL);
645b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
646b7bb4816SSepherosa Ziehau 
647b7bb4816SSepherosa Ziehau 	/*
648b7bb4816SSepherosa Ziehau 	 * Mask message and event flags SINT.
649b7bb4816SSepherosa Ziehau 	 */
650b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
651b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
652b7bb4816SSepherosa Ziehau 	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
653b7bb4816SSepherosa Ziehau 
654b7bb4816SSepherosa Ziehau 	/*
655b7bb4816SSepherosa Ziehau 	 * Mask timer SINT.
656b7bb4816SSepherosa Ziehau 	 */
657b7bb4816SSepherosa Ziehau 	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
658b7bb4816SSepherosa Ziehau 	orig = rdmsr(sint);
659b7bb4816SSepherosa Ziehau 	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
660b7bb4816SSepherosa Ziehau 
661b7bb4816SSepherosa Ziehau 	/*
662b7bb4816SSepherosa Ziehau 	 * Teardown SynIC message.
663b7bb4816SSepherosa Ziehau 	 */
664b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIMP);
665b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
666b7bb4816SSepherosa Ziehau 
667b7bb4816SSepherosa Ziehau 	/*
668b7bb4816SSepherosa Ziehau 	 * Teardown SynIC event flags.
669b7bb4816SSepherosa Ziehau 	 */
670b7bb4816SSepherosa Ziehau 	orig = rdmsr(MSR_HV_SIEFP);
671b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
672b7bb4816SSepherosa Ziehau }
673b7bb4816SSepherosa Ziehau 
674b7bb4816SSepherosa Ziehau static int
675b7bb4816SSepherosa Ziehau vmbus_dma_alloc(struct vmbus_softc *sc)
676b7bb4816SSepherosa Ziehau {
6776523ab7eSSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
6786523ab7eSSepherosa Ziehau 	uint8_t *evtflags;
679b7bb4816SSepherosa Ziehau 	int cpu;
680b7bb4816SSepherosa Ziehau 
6816523ab7eSSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(sc->vmbus_dev);
682b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
683b7bb4816SSepherosa Ziehau 		void *ptr;
684b7bb4816SSepherosa Ziehau 
685b7bb4816SSepherosa Ziehau 		/*
686b7bb4816SSepherosa Ziehau 		 * Per-cpu messages and event flags.
687b7bb4816SSepherosa Ziehau 		 */
6886523ab7eSSepherosa Ziehau 		ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6896523ab7eSSepherosa Ziehau 		    PAGE_SIZE, VMBUS_PCPU_PTR(sc, message_dma, cpu),
690b7bb4816SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
691b7bb4816SSepherosa Ziehau 		if (ptr == NULL)
692b7bb4816SSepherosa Ziehau 			return ENOMEM;
693b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
694b7bb4816SSepherosa Ziehau 
6956523ab7eSSepherosa Ziehau 		ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
6966523ab7eSSepherosa Ziehau 		    PAGE_SIZE, VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
697b7bb4816SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
698b7bb4816SSepherosa Ziehau 		if (ptr == NULL)
699b7bb4816SSepherosa Ziehau 			return ENOMEM;
700b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr;
701b7bb4816SSepherosa Ziehau 	}
7026523ab7eSSepherosa Ziehau 
7036523ab7eSSepherosa Ziehau 	evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
7046523ab7eSSepherosa Ziehau 	    PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
7056523ab7eSSepherosa Ziehau 	if (evtflags == NULL)
7066523ab7eSSepherosa Ziehau 		return ENOMEM;
7076523ab7eSSepherosa Ziehau 	sc->vmbus_rx_evtflags = (u_long *)evtflags;
70851a9a000SSepherosa Ziehau 	sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2));
7096523ab7eSSepherosa Ziehau 	sc->vmbus_evtflags = evtflags;
7106523ab7eSSepherosa Ziehau 
7116523ab7eSSepherosa Ziehau 	sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
7126523ab7eSSepherosa Ziehau 	    PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
7136523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf1 == NULL)
7146523ab7eSSepherosa Ziehau 		return ENOMEM;
7156523ab7eSSepherosa Ziehau 
7166523ab7eSSepherosa Ziehau 	sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
7176523ab7eSSepherosa Ziehau 	    PAGE_SIZE, &sc->vmbus_mnf2_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
7186523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf2 == NULL)
7196523ab7eSSepherosa Ziehau 		return ENOMEM;
7206523ab7eSSepherosa Ziehau 
721b7bb4816SSepherosa Ziehau 	return 0;
722b7bb4816SSepherosa Ziehau }
723b7bb4816SSepherosa Ziehau 
724b7bb4816SSepherosa Ziehau static void
725b7bb4816SSepherosa Ziehau vmbus_dma_free(struct vmbus_softc *sc)
726b7bb4816SSepherosa Ziehau {
727b7bb4816SSepherosa Ziehau 	int cpu;
728b7bb4816SSepherosa Ziehau 
7296523ab7eSSepherosa Ziehau 	if (sc->vmbus_evtflags != NULL) {
7306523ab7eSSepherosa Ziehau 		hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags);
7316523ab7eSSepherosa Ziehau 		sc->vmbus_evtflags = NULL;
7326523ab7eSSepherosa Ziehau 		sc->vmbus_rx_evtflags = NULL;
7336523ab7eSSepherosa Ziehau 		sc->vmbus_tx_evtflags = NULL;
7346523ab7eSSepherosa Ziehau 	}
7356523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf1 != NULL) {
7366523ab7eSSepherosa Ziehau 		hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1);
7376523ab7eSSepherosa Ziehau 		sc->vmbus_mnf1 = NULL;
7386523ab7eSSepherosa Ziehau 	}
7396523ab7eSSepherosa Ziehau 	if (sc->vmbus_mnf2 != NULL) {
7406523ab7eSSepherosa Ziehau 		hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2);
7416523ab7eSSepherosa Ziehau 		sc->vmbus_mnf2 = NULL;
7426523ab7eSSepherosa Ziehau 	}
7436523ab7eSSepherosa Ziehau 
744b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
745b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
746b7bb4816SSepherosa Ziehau 			hyperv_dmamem_free(
747b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
748b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_GET(sc, message, cpu));
749b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
750b7bb4816SSepherosa Ziehau 		}
751b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) {
752b7bb4816SSepherosa Ziehau 			hyperv_dmamem_free(
753b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
754b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_GET(sc, event_flags, cpu));
755b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL;
756b7bb4816SSepherosa Ziehau 		}
757b7bb4816SSepherosa Ziehau 	}
758b7bb4816SSepherosa Ziehau }
759b7bb4816SSepherosa Ziehau 
760b7bb4816SSepherosa Ziehau static int
761b7bb4816SSepherosa Ziehau vmbus_intr_setup(struct vmbus_softc *sc)
762b7bb4816SSepherosa Ziehau {
763b7bb4816SSepherosa Ziehau 	int cpu;
764b7bb4816SSepherosa Ziehau 
765b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
766b7bb4816SSepherosa Ziehau 		char buf[MAXCOMLEN + 1];
767b7bb4816SSepherosa Ziehau 		cpuset_t cpu_mask;
768b7bb4816SSepherosa Ziehau 
769b7bb4816SSepherosa Ziehau 		/* Allocate an interrupt counter for Hyper-V interrupt */
770b7bb4816SSepherosa Ziehau 		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
771b7bb4816SSepherosa Ziehau 		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
772b7bb4816SSepherosa Ziehau 
773b7bb4816SSepherosa Ziehau 		/*
774b7bb4816SSepherosa Ziehau 		 * Setup taskqueue to handle events.  Task will be per-
775b7bb4816SSepherosa Ziehau 		 * channel.
776b7bb4816SSepherosa Ziehau 		 */
777b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
778b7bb4816SSepherosa Ziehau 		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
779b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
780b7bb4816SSepherosa Ziehau 		CPU_SETOF(cpu, &cpu_mask);
781b7bb4816SSepherosa Ziehau 		taskqueue_start_threads_cpuset(
782b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
783b7bb4816SSepherosa Ziehau 		    "hvevent%d", cpu);
784b7bb4816SSepherosa Ziehau 
785b7bb4816SSepherosa Ziehau 		/*
786b7bb4816SSepherosa Ziehau 		 * Setup tasks and taskqueues to handle messages.
787b7bb4816SSepherosa Ziehau 		 */
788b7bb4816SSepherosa Ziehau 		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
789b7bb4816SSepherosa Ziehau 		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
790b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
791b7bb4816SSepherosa Ziehau 		CPU_SETOF(cpu, &cpu_mask);
792b7bb4816SSepherosa Ziehau 		taskqueue_start_threads_cpuset(
793b7bb4816SSepherosa Ziehau 		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
794b7bb4816SSepherosa Ziehau 		    "hvmsg%d", cpu);
795b7bb4816SSepherosa Ziehau 		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
796b7bb4816SSepherosa Ziehau 		    vmbus_msg_task, sc);
797b7bb4816SSepherosa Ziehau 	}
798b7bb4816SSepherosa Ziehau 
799b7bb4816SSepherosa Ziehau 	/*
800b7bb4816SSepherosa Ziehau 	 * All Hyper-V ISR required resources are setup, now let's find a
801b7bb4816SSepherosa Ziehau 	 * free IDT vector for Hyper-V ISR and set it up.
802b7bb4816SSepherosa Ziehau 	 */
803b7bb4816SSepherosa Ziehau 	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr));
804b7bb4816SSepherosa Ziehau 	if (sc->vmbus_idtvec < 0) {
805b7bb4816SSepherosa Ziehau 		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
806b7bb4816SSepherosa Ziehau 		return ENXIO;
807b7bb4816SSepherosa Ziehau 	}
808b7bb4816SSepherosa Ziehau 	if(bootverbose) {
809b7bb4816SSepherosa Ziehau 		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
810b7bb4816SSepherosa Ziehau 		    sc->vmbus_idtvec);
811b7bb4816SSepherosa Ziehau 	}
812b7bb4816SSepherosa Ziehau 	return 0;
813b7bb4816SSepherosa Ziehau }
814b7bb4816SSepherosa Ziehau 
815b7bb4816SSepherosa Ziehau static void
816b7bb4816SSepherosa Ziehau vmbus_intr_teardown(struct vmbus_softc *sc)
817b7bb4816SSepherosa Ziehau {
818b7bb4816SSepherosa Ziehau 	int cpu;
819b7bb4816SSepherosa Ziehau 
820b7bb4816SSepherosa Ziehau 	if (sc->vmbus_idtvec >= 0) {
821b7bb4816SSepherosa Ziehau 		lapic_ipi_free(sc->vmbus_idtvec);
822b7bb4816SSepherosa Ziehau 		sc->vmbus_idtvec = -1;
823b7bb4816SSepherosa Ziehau 	}
824b7bb4816SSepherosa Ziehau 
825b7bb4816SSepherosa Ziehau 	CPU_FOREACH(cpu) {
826b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
827b7bb4816SSepherosa Ziehau 			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
828b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
829b7bb4816SSepherosa Ziehau 		}
830b7bb4816SSepherosa Ziehau 		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
831b7bb4816SSepherosa Ziehau 			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
832b7bb4816SSepherosa Ziehau 			    VMBUS_PCPU_PTR(sc, message_task, cpu));
833b7bb4816SSepherosa Ziehau 			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
834b7bb4816SSepherosa Ziehau 			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
835b7bb4816SSepherosa Ziehau 		}
836b7bb4816SSepherosa Ziehau 	}
837b7bb4816SSepherosa Ziehau }
838b7bb4816SSepherosa Ziehau 
839b7bb4816SSepherosa Ziehau static int
840b7bb4816SSepherosa Ziehau vmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
841b7bb4816SSepherosa Ziehau {
842b7bb4816SSepherosa Ziehau 	struct hv_device *child_dev_ctx = device_get_ivars(child);
843b7bb4816SSepherosa Ziehau 
844b7bb4816SSepherosa Ziehau 	switch (index) {
845b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_TYPE:
846b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)&child_dev_ctx->class_id;
847b7bb4816SSepherosa Ziehau 		return (0);
848b7bb4816SSepherosa Ziehau 
849b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_INSTANCE:
850b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)&child_dev_ctx->device_id;
851b7bb4816SSepherosa Ziehau 		return (0);
852b7bb4816SSepherosa Ziehau 
853b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_DEVCTX:
854b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)child_dev_ctx;
855b7bb4816SSepherosa Ziehau 		return (0);
856b7bb4816SSepherosa Ziehau 
857b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_NODE:
858b7bb4816SSepherosa Ziehau 		*result = (uintptr_t)child_dev_ctx->device;
859b7bb4816SSepherosa Ziehau 		return (0);
860b7bb4816SSepherosa Ziehau 	}
861b7bb4816SSepherosa Ziehau 	return (ENOENT);
862b7bb4816SSepherosa Ziehau }
863b7bb4816SSepherosa Ziehau 
864b7bb4816SSepherosa Ziehau static int
865b7bb4816SSepherosa Ziehau vmbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
866b7bb4816SSepherosa Ziehau {
867b7bb4816SSepherosa Ziehau 	switch (index) {
868b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_TYPE:
869b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_INSTANCE:
870b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_DEVCTX:
871b7bb4816SSepherosa Ziehau 	case HV_VMBUS_IVAR_NODE:
872b7bb4816SSepherosa Ziehau 		/* read-only */
873b7bb4816SSepherosa Ziehau 		return (EINVAL);
874b7bb4816SSepherosa Ziehau 	}
875b7bb4816SSepherosa Ziehau 	return (ENOENT);
876b7bb4816SSepherosa Ziehau }
877b7bb4816SSepherosa Ziehau 
878b7bb4816SSepherosa Ziehau static int
879b7bb4816SSepherosa Ziehau vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
880b7bb4816SSepherosa Ziehau {
881b7bb4816SSepherosa Ziehau 	struct hv_device *dev_ctx = device_get_ivars(child);
882b7bb4816SSepherosa Ziehau 	char guidbuf[HYPERV_GUID_STRLEN];
883b7bb4816SSepherosa Ziehau 
884b7bb4816SSepherosa Ziehau 	if (dev_ctx == NULL)
885b7bb4816SSepherosa Ziehau 		return (0);
886b7bb4816SSepherosa Ziehau 
887b7bb4816SSepherosa Ziehau 	strlcat(buf, "classid=", buflen);
888b7bb4816SSepherosa Ziehau 	hyperv_guid2str(&dev_ctx->class_id, guidbuf, sizeof(guidbuf));
889b7bb4816SSepherosa Ziehau 	strlcat(buf, guidbuf, buflen);
890b7bb4816SSepherosa Ziehau 
891b7bb4816SSepherosa Ziehau 	strlcat(buf, " deviceid=", buflen);
892b7bb4816SSepherosa Ziehau 	hyperv_guid2str(&dev_ctx->device_id, guidbuf, sizeof(guidbuf));
893b7bb4816SSepherosa Ziehau 	strlcat(buf, guidbuf, buflen);
894b7bb4816SSepherosa Ziehau 
895b7bb4816SSepherosa Ziehau 	return (0);
896b7bb4816SSepherosa Ziehau }
897b7bb4816SSepherosa Ziehau 
898b7bb4816SSepherosa Ziehau struct hv_device *
899b7bb4816SSepherosa Ziehau hv_vmbus_child_device_create(hv_guid type, hv_guid instance,
900b7bb4816SSepherosa Ziehau     hv_vmbus_channel *channel)
901b7bb4816SSepherosa Ziehau {
902b7bb4816SSepherosa Ziehau 	hv_device *child_dev;
903b7bb4816SSepherosa Ziehau 
904b7bb4816SSepherosa Ziehau 	/*
905b7bb4816SSepherosa Ziehau 	 * Allocate the new child device
906b7bb4816SSepherosa Ziehau 	 */
907b7bb4816SSepherosa Ziehau 	child_dev = malloc(sizeof(hv_device), M_DEVBUF, M_WAITOK | M_ZERO);
908b7bb4816SSepherosa Ziehau 
909b7bb4816SSepherosa Ziehau 	child_dev->channel = channel;
910b7bb4816SSepherosa Ziehau 	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
911b7bb4816SSepherosa Ziehau 	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
912b7bb4816SSepherosa Ziehau 
913b7bb4816SSepherosa Ziehau 	return (child_dev);
914b7bb4816SSepherosa Ziehau }
915b7bb4816SSepherosa Ziehau 
916b7bb4816SSepherosa Ziehau int
917b7bb4816SSepherosa Ziehau hv_vmbus_child_device_register(struct hv_device *child_dev)
918b7bb4816SSepherosa Ziehau {
919b7bb4816SSepherosa Ziehau 	device_t child, parent;
920b7bb4816SSepherosa Ziehau 
921b7bb4816SSepherosa Ziehau 	parent = vmbus_get_device();
922b7bb4816SSepherosa Ziehau 	if (bootverbose) {
923b7bb4816SSepherosa Ziehau 		char name[HYPERV_GUID_STRLEN];
924b7bb4816SSepherosa Ziehau 
925b7bb4816SSepherosa Ziehau 		hyperv_guid2str(&child_dev->class_id, name, sizeof(name));
926b7bb4816SSepherosa Ziehau 		device_printf(parent, "add device, classid: %s\n", name);
927b7bb4816SSepherosa Ziehau 	}
928b7bb4816SSepherosa Ziehau 
929b7bb4816SSepherosa Ziehau 	child = device_add_child(parent, NULL, -1);
930b7bb4816SSepherosa Ziehau 	child_dev->device = child;
931b7bb4816SSepherosa Ziehau 	device_set_ivars(child, child_dev);
932b7bb4816SSepherosa Ziehau 
933b7bb4816SSepherosa Ziehau 	return (0);
934b7bb4816SSepherosa Ziehau }
935b7bb4816SSepherosa Ziehau 
936b7bb4816SSepherosa Ziehau int
937b7bb4816SSepherosa Ziehau hv_vmbus_child_device_unregister(struct hv_device *child_dev)
938b7bb4816SSepherosa Ziehau {
939b7bb4816SSepherosa Ziehau 	int ret = 0;
940b7bb4816SSepherosa Ziehau 	/*
941b7bb4816SSepherosa Ziehau 	 * XXXKYS: Ensure that this is the opposite of
942b7bb4816SSepherosa Ziehau 	 * device_add_child()
943b7bb4816SSepherosa Ziehau 	 */
944b7bb4816SSepherosa Ziehau 	mtx_lock(&Giant);
945b7bb4816SSepherosa Ziehau 	ret = device_delete_child(vmbus_get_device(), child_dev->device);
946b7bb4816SSepherosa Ziehau 	mtx_unlock(&Giant);
947b7bb4816SSepherosa Ziehau 	return(ret);
948b7bb4816SSepherosa Ziehau }
949b7bb4816SSepherosa Ziehau 
950b7bb4816SSepherosa Ziehau static int
951b7bb4816SSepherosa Ziehau vmbus_probe(device_t dev)
952b7bb4816SSepherosa Ziehau {
953b7bb4816SSepherosa Ziehau 	char *id[] = { "VMBUS", NULL };
954b7bb4816SSepherosa Ziehau 
955b7bb4816SSepherosa Ziehau 	if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL ||
956b7bb4816SSepherosa Ziehau 	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV ||
957b7bb4816SSepherosa Ziehau 	    (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
958b7bb4816SSepherosa Ziehau 		return (ENXIO);
959b7bb4816SSepherosa Ziehau 
960b7bb4816SSepherosa Ziehau 	device_set_desc(dev, "Hyper-V Vmbus");
961b7bb4816SSepherosa Ziehau 
962b7bb4816SSepherosa Ziehau 	return (BUS_PROBE_DEFAULT);
963b7bb4816SSepherosa Ziehau }
964b7bb4816SSepherosa Ziehau 
965b7bb4816SSepherosa Ziehau /**
966b7bb4816SSepherosa Ziehau  * @brief Main vmbus driver initialization routine.
967b7bb4816SSepherosa Ziehau  *
968b7bb4816SSepherosa Ziehau  * Here, we
969b7bb4816SSepherosa Ziehau  * - initialize the vmbus driver context
970b7bb4816SSepherosa Ziehau  * - setup various driver entry points
971b7bb4816SSepherosa Ziehau  * - invoke the vmbus hv main init routine
972b7bb4816SSepherosa Ziehau  * - get the irq resource
973b7bb4816SSepherosa Ziehau  * - invoke the vmbus to add the vmbus root device
974b7bb4816SSepherosa Ziehau  * - setup the vmbus root device
975b7bb4816SSepherosa Ziehau  * - retrieve the channel offers
976b7bb4816SSepherosa Ziehau  */
977b7bb4816SSepherosa Ziehau static int
978*dd012a57SSepherosa Ziehau vmbus_doattach(struct vmbus_softc *sc)
979b7bb4816SSepherosa Ziehau {
980b7bb4816SSepherosa Ziehau 	int ret;
981b7bb4816SSepherosa Ziehau 
982b7bb4816SSepherosa Ziehau 	if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
983b7bb4816SSepherosa Ziehau 		return (0);
984b7bb4816SSepherosa Ziehau 	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
985b7bb4816SSepherosa Ziehau 
986b7bb4816SSepherosa Ziehau 	/*
987236764b1SSepherosa Ziehau 	 * Create context for "post message" Hypercalls
988236764b1SSepherosa Ziehau 	 */
989236764b1SSepherosa Ziehau 	sc->vmbus_msg_hc = vmbus_msghc_ctx_create(
990236764b1SSepherosa Ziehau 	    bus_get_dma_tag(sc->vmbus_dev));
991236764b1SSepherosa Ziehau 	if (sc->vmbus_msg_hc == NULL) {
992236764b1SSepherosa Ziehau 		ret = ENXIO;
993236764b1SSepherosa Ziehau 		goto cleanup;
994236764b1SSepherosa Ziehau 	}
995236764b1SSepherosa Ziehau 
996236764b1SSepherosa Ziehau 	/*
997b7bb4816SSepherosa Ziehau 	 * Allocate DMA stuffs.
998b7bb4816SSepherosa Ziehau 	 */
999b7bb4816SSepherosa Ziehau 	ret = vmbus_dma_alloc(sc);
1000b7bb4816SSepherosa Ziehau 	if (ret != 0)
1001b7bb4816SSepherosa Ziehau 		goto cleanup;
1002b7bb4816SSepherosa Ziehau 
1003b7bb4816SSepherosa Ziehau 	/*
1004b7bb4816SSepherosa Ziehau 	 * Setup interrupt.
1005b7bb4816SSepherosa Ziehau 	 */
1006b7bb4816SSepherosa Ziehau 	ret = vmbus_intr_setup(sc);
1007b7bb4816SSepherosa Ziehau 	if (ret != 0)
1008b7bb4816SSepherosa Ziehau 		goto cleanup;
1009b7bb4816SSepherosa Ziehau 
1010b7bb4816SSepherosa Ziehau 	/*
1011b7bb4816SSepherosa Ziehau 	 * Setup SynIC.
1012b7bb4816SSepherosa Ziehau 	 */
1013b7bb4816SSepherosa Ziehau 	if (bootverbose)
1014b7bb4816SSepherosa Ziehau 		device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
1015b7bb4816SSepherosa Ziehau 	smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc);
1016b7bb4816SSepherosa Ziehau 	sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
1017b7bb4816SSepherosa Ziehau 
1018b7bb4816SSepherosa Ziehau 	/*
1019b7bb4816SSepherosa Ziehau 	 * Connect to VMBus in the root partition
1020b7bb4816SSepherosa Ziehau 	 */
10216523ab7eSSepherosa Ziehau 	ret = hv_vmbus_connect(sc);
1022b7bb4816SSepherosa Ziehau 	if (ret != 0)
1023b7bb4816SSepherosa Ziehau 		goto cleanup;
1024b7bb4816SSepherosa Ziehau 
1025236764b1SSepherosa Ziehau 	ret = vmbus_init(sc);
1026236764b1SSepherosa Ziehau 	if (ret != 0)
1027236764b1SSepherosa Ziehau 		goto cleanup;
1028236764b1SSepherosa Ziehau 
1029b7bb4816SSepherosa Ziehau 	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
1030b7bb4816SSepherosa Ziehau 	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
1031b7bb4816SSepherosa Ziehau 		sc->vmbus_event_proc = vmbus_event_proc_compat;
1032b7bb4816SSepherosa Ziehau 	else
1033b7bb4816SSepherosa Ziehau 		sc->vmbus_event_proc = vmbus_event_proc;
1034b7bb4816SSepherosa Ziehau 
1035c1cc5bdfSSepherosa Ziehau 	ret = vmbus_req_channels(sc);
1036c1cc5bdfSSepherosa Ziehau 	if (ret != 0)
1037c1cc5bdfSSepherosa Ziehau 		goto cleanup;
1038b7bb4816SSepherosa Ziehau 
1039b7bb4816SSepherosa Ziehau 	vmbus_scan();
1040b7bb4816SSepherosa Ziehau 	bus_generic_attach(sc->vmbus_dev);
1041b7bb4816SSepherosa Ziehau 	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
1042b7bb4816SSepherosa Ziehau 
1043b7bb4816SSepherosa Ziehau 	return (ret);
1044b7bb4816SSepherosa Ziehau 
1045b7bb4816SSepherosa Ziehau cleanup:
1046b7bb4816SSepherosa Ziehau 	vmbus_intr_teardown(sc);
1047b7bb4816SSepherosa Ziehau 	vmbus_dma_free(sc);
1048236764b1SSepherosa Ziehau 	if (sc->vmbus_msg_hc != NULL) {
1049236764b1SSepherosa Ziehau 		vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
1050236764b1SSepherosa Ziehau 		sc->vmbus_msg_hc = NULL;
1051236764b1SSepherosa Ziehau 	}
1052b7bb4816SSepherosa Ziehau 
1053b7bb4816SSepherosa Ziehau 	return (ret);
1054b7bb4816SSepherosa Ziehau }
1055b7bb4816SSepherosa Ziehau 
1056b7bb4816SSepherosa Ziehau static void
1057b7bb4816SSepherosa Ziehau vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
1058b7bb4816SSepherosa Ziehau {
1059b7bb4816SSepherosa Ziehau }
1060b7bb4816SSepherosa Ziehau 
1061b7bb4816SSepherosa Ziehau static int
1062b7bb4816SSepherosa Ziehau vmbus_attach(device_t dev)
1063b7bb4816SSepherosa Ziehau {
1064b7bb4816SSepherosa Ziehau 	vmbus_sc = device_get_softc(dev);
1065b7bb4816SSepherosa Ziehau 	vmbus_sc->vmbus_dev = dev;
1066b7bb4816SSepherosa Ziehau 	vmbus_sc->vmbus_idtvec = -1;
1067b7bb4816SSepherosa Ziehau 
1068b7bb4816SSepherosa Ziehau 	/*
1069b7bb4816SSepherosa Ziehau 	 * Event processing logic will be configured:
1070b7bb4816SSepherosa Ziehau 	 * - After the vmbus protocol version negotiation.
1071b7bb4816SSepherosa Ziehau 	 * - Before we request channel offers.
1072b7bb4816SSepherosa Ziehau 	 */
1073b7bb4816SSepherosa Ziehau 	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
1074b7bb4816SSepherosa Ziehau 
1075b7bb4816SSepherosa Ziehau #ifndef EARLY_AP_STARTUP
1076b7bb4816SSepherosa Ziehau 	/*
1077b7bb4816SSepherosa Ziehau 	 * If the system has already booted and thread
1078b7bb4816SSepherosa Ziehau 	 * scheduling is possible indicated by the global
1079b7bb4816SSepherosa Ziehau 	 * cold set to zero, we just call the driver
1080b7bb4816SSepherosa Ziehau 	 * initialization directly.
1081b7bb4816SSepherosa Ziehau 	 */
1082b7bb4816SSepherosa Ziehau 	if (!cold)
1083b7bb4816SSepherosa Ziehau #endif
1084*dd012a57SSepherosa Ziehau 		vmbus_doattach(vmbus_sc);
1085b7bb4816SSepherosa Ziehau 
1086b7bb4816SSepherosa Ziehau 	bus_generic_probe(dev);
1087b7bb4816SSepherosa Ziehau 	return (0);
1088b7bb4816SSepherosa Ziehau }
1089b7bb4816SSepherosa Ziehau 
1090b7bb4816SSepherosa Ziehau static void
1091b7bb4816SSepherosa Ziehau vmbus_sysinit(void *arg __unused)
1092b7bb4816SSepherosa Ziehau {
1093*dd012a57SSepherosa Ziehau 	struct vmbus_softc *sc = vmbus_get_softc();
1094*dd012a57SSepherosa Ziehau 
1095*dd012a57SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV || sc == NULL)
1096b7bb4816SSepherosa Ziehau 		return;
1097b7bb4816SSepherosa Ziehau 
1098b7bb4816SSepherosa Ziehau #ifndef EARLY_AP_STARTUP
1099b7bb4816SSepherosa Ziehau 	/*
1100b7bb4816SSepherosa Ziehau 	 * If the system has already booted and thread
1101b7bb4816SSepherosa Ziehau 	 * scheduling is possible, as indicated by the
1102b7bb4816SSepherosa Ziehau 	 * global cold set to zero, we just call the driver
1103b7bb4816SSepherosa Ziehau 	 * initialization directly.
1104b7bb4816SSepherosa Ziehau 	 */
1105b7bb4816SSepherosa Ziehau 	if (!cold)
1106b7bb4816SSepherosa Ziehau #endif
1107*dd012a57SSepherosa Ziehau 		vmbus_doattach(sc);
1108b7bb4816SSepherosa Ziehau }
1109b7bb4816SSepherosa Ziehau 
1110b7bb4816SSepherosa Ziehau static int
1111b7bb4816SSepherosa Ziehau vmbus_detach(device_t dev)
1112b7bb4816SSepherosa Ziehau {
1113b7bb4816SSepherosa Ziehau 	struct vmbus_softc *sc = device_get_softc(dev);
1114b7bb4816SSepherosa Ziehau 
1115b7bb4816SSepherosa Ziehau 	hv_vmbus_release_unattached_channels();
1116b7bb4816SSepherosa Ziehau 	hv_vmbus_disconnect();
1117b7bb4816SSepherosa Ziehau 
1118b7bb4816SSepherosa Ziehau 	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
1119b7bb4816SSepherosa Ziehau 		sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
1120b7bb4816SSepherosa Ziehau 		smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
1121b7bb4816SSepherosa Ziehau 	}
1122b7bb4816SSepherosa Ziehau 
1123b7bb4816SSepherosa Ziehau 	vmbus_intr_teardown(sc);
1124b7bb4816SSepherosa Ziehau 	vmbus_dma_free(sc);
1125b7bb4816SSepherosa Ziehau 
1126236764b1SSepherosa Ziehau 	if (sc->vmbus_msg_hc != NULL) {
1127236764b1SSepherosa Ziehau 		vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
1128236764b1SSepherosa Ziehau 		sc->vmbus_msg_hc = NULL;
1129236764b1SSepherosa Ziehau 	}
1130236764b1SSepherosa Ziehau 
1131b7bb4816SSepherosa Ziehau 	return (0);
1132b7bb4816SSepherosa Ziehau }
1133b7bb4816SSepherosa Ziehau 
1134b7bb4816SSepherosa Ziehau static device_method_t vmbus_methods[] = {
1135b7bb4816SSepherosa Ziehau 	/* Device interface */
1136b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_probe,			vmbus_probe),
1137b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_attach,		vmbus_attach),
1138b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_detach,		vmbus_detach),
1139b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
1140b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_suspend,		bus_generic_suspend),
1141b7bb4816SSepherosa Ziehau 	DEVMETHOD(device_resume,		bus_generic_resume),
1142b7bb4816SSepherosa Ziehau 
1143b7bb4816SSepherosa Ziehau 	/* Bus interface */
1144b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_add_child,		bus_generic_add_child),
1145b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_print_child,		bus_generic_print_child),
1146b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
1147b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
1148b7bb4816SSepherosa Ziehau 	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
1149b7bb4816SSepherosa Ziehau 
1150b7bb4816SSepherosa Ziehau 	DEVMETHOD_END
1151b7bb4816SSepherosa Ziehau };
1152b7bb4816SSepherosa Ziehau 
1153b7bb4816SSepherosa Ziehau static driver_t vmbus_driver = {
1154b7bb4816SSepherosa Ziehau 	"vmbus",
1155b7bb4816SSepherosa Ziehau 	vmbus_methods,
1156b7bb4816SSepherosa Ziehau 	sizeof(struct vmbus_softc)
1157b7bb4816SSepherosa Ziehau };
1158b7bb4816SSepherosa Ziehau 
1159b7bb4816SSepherosa Ziehau static devclass_t vmbus_devclass;
1160b7bb4816SSepherosa Ziehau 
1161b7bb4816SSepherosa Ziehau DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
1162b7bb4816SSepherosa Ziehau MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
1163b7bb4816SSepherosa Ziehau MODULE_VERSION(vmbus, 1);
1164b7bb4816SSepherosa Ziehau 
1165b7bb4816SSepherosa Ziehau #ifndef EARLY_AP_STARTUP
1166b7bb4816SSepherosa Ziehau /*
1167b7bb4816SSepherosa Ziehau  * NOTE:
1168b7bb4816SSepherosa Ziehau  * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
1169b7bb4816SSepherosa Ziehau  * initialized.
1170b7bb4816SSepherosa Ziehau  */
1171b7bb4816SSepherosa Ziehau SYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
1172b7bb4816SSepherosa Ziehau #endif
1173