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