vmbus.c revision 318392
1250199Sgrehan/*-
2298446Ssephe * Copyright (c) 2009-2012,2016 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2012 NetApp Inc.
4250199Sgrehan * Copyright (c) 2012 Citrix Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29250199Sgrehan/*
30250199Sgrehan * VM Bus Driver Implementation
31250199Sgrehan */
32256276Sdim#include <sys/cdefs.h>
33256276Sdim__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/vmbus/vmbus.c 318392 2017-05-17 01:48:44Z sephe $");
34250199Sgrehan
35250199Sgrehan#include <sys/param.h>
36250199Sgrehan#include <sys/bus.h>
37250199Sgrehan#include <sys/kernel.h>
38250199Sgrehan#include <sys/lock.h>
39250199Sgrehan#include <sys/malloc.h>
40250199Sgrehan#include <sys/module.h>
41307466Ssephe#include <sys/mutex.h>
42307466Ssephe#include <sys/smp.h>
43250199Sgrehan#include <sys/sysctl.h>
44250199Sgrehan#include <sys/systm.h>
45250199Sgrehan#include <sys/taskqueue.h>
46250199Sgrehan
47309312Sdexuan#include <machine/bus.h>
48250199Sgrehan#include <machine/intr_machdep.h>
49309312Sdexuan#include <machine/resource.h>
50307466Ssephe#include <x86/include/apicvar.h>
51250199Sgrehan
52307466Ssephe#include <contrib/dev/acpica/include/acpi.h>
53309312Sdexuan#include <dev/acpica/acpivar.h>
54307466Ssephe
55297142Ssephe#include <dev/hyperv/include/hyperv.h>
56307475Ssephe#include <dev/hyperv/include/vmbus_xact.h>
57300654Ssephe#include <dev/hyperv/vmbus/hyperv_reg.h>
58300834Ssephe#include <dev/hyperv/vmbus/hyperv_var.h>
59301019Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h>
60300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
61307463Ssephe#include <dev/hyperv/vmbus/vmbus_chanvar.h>
62250199Sgrehan
63293870Ssephe#include "acpi_if.h"
64309312Sdexuan#include "pcib_if.h"
65307302Ssephe#include "vmbus_if.h"
66250199Sgrehan
67307302Ssephe#define VMBUS_GPADL_START		0xe1e10
68307278Ssephe
69307278Ssephestruct vmbus_msghc {
70307475Ssephe	struct vmbus_xact		*mh_xact;
71307278Ssephe	struct hypercall_postmsg_in	mh_inprm_save;
72307278Ssephe};
73307278Ssephe
74318392Ssephestatic void			vmbus_identify(driver_t *, device_t);
75307466Ssephestatic int			vmbus_probe(device_t);
76307466Ssephestatic int			vmbus_attach(device_t);
77307466Ssephestatic int			vmbus_detach(device_t);
78307466Ssephestatic int			vmbus_read_ivar(device_t, device_t, int,
79307466Ssephe				    uintptr_t *);
80307466Ssephestatic int			vmbus_child_pnpinfo_str(device_t, device_t,
81307466Ssephe				    char *, size_t);
82309312Sdexuanstatic struct resource		*vmbus_alloc_resource(device_t dev,
83309312Sdexuan				    device_t child, int type, int *rid,
84309312Sdexuan				    rman_res_t start, rman_res_t end,
85309312Sdexuan				    rman_res_t count, u_int flags);
86309312Sdexuanstatic int			vmbus_alloc_msi(device_t bus, device_t dev,
87309312Sdexuan				    int count, int maxcount, int *irqs);
88309312Sdexuanstatic int			vmbus_release_msi(device_t bus, device_t dev,
89309312Sdexuan				    int count, int *irqs);
90309312Sdexuanstatic int			vmbus_alloc_msix(device_t bus, device_t dev,
91309312Sdexuan				    int *irq);
92309312Sdexuanstatic int			vmbus_release_msix(device_t bus, device_t dev,
93309312Sdexuan				    int irq);
94309312Sdexuanstatic int			vmbus_map_msi(device_t bus, device_t dev,
95309312Sdexuan				    int irq, uint64_t *addr, uint32_t *data);
96307466Ssephestatic uint32_t			vmbus_get_version_method(device_t, device_t);
97307466Ssephestatic int			vmbus_probe_guid_method(device_t, device_t,
98307466Ssephe				    const struct hyperv_guid *);
99309312Sdexuanstatic uint32_t			vmbus_get_vcpu_id_method(device_t bus,
100309312Sdexuan				    device_t dev, int cpu);
101311368Ssephestatic struct taskqueue		*vmbus_get_eventtq_method(device_t, device_t,
102311368Ssephe				    int);
103310573Ssephe#ifdef EARLY_AP_STARTUP
104310573Ssephestatic void			vmbus_intrhook(void *);
105310573Ssephe#endif
106307466Ssephe
107307278Ssephestatic int			vmbus_init(struct vmbus_softc *);
108307302Ssephestatic int			vmbus_connect(struct vmbus_softc *, uint32_t);
109307291Ssephestatic int			vmbus_req_channels(struct vmbus_softc *sc);
110307302Ssephestatic void			vmbus_disconnect(struct vmbus_softc *);
111307291Ssephestatic int			vmbus_scan(struct vmbus_softc *);
112307599Ssephestatic void			vmbus_scan_teardown(struct vmbus_softc *);
113307451Ssephestatic void			vmbus_scan_done(struct vmbus_softc *,
114307451Ssephe				    const struct vmbus_message *);
115307451Ssephestatic void			vmbus_chanmsg_handle(struct vmbus_softc *,
116307451Ssephe				    const struct vmbus_message *);
117307466Ssephestatic void			vmbus_msg_task(void *, int);
118307466Ssephestatic void			vmbus_synic_setup(void *);
119307466Ssephestatic void			vmbus_synic_teardown(void *);
120307291Ssephestatic int			vmbus_sysctl_version(SYSCTL_HANDLER_ARGS);
121307466Ssephestatic int			vmbus_dma_alloc(struct vmbus_softc *);
122307466Ssephestatic void			vmbus_dma_free(struct vmbus_softc *);
123307466Ssephestatic int			vmbus_intr_setup(struct vmbus_softc *);
124307466Ssephestatic void			vmbus_intr_teardown(struct vmbus_softc *);
125307466Ssephestatic int			vmbus_doattach(struct vmbus_softc *);
126307466Ssephestatic void			vmbus_event_proc_dummy(struct vmbus_softc *,
127307466Ssephe				    int);
128307291Ssephe
129307466Ssephestatic struct vmbus_softc	*vmbus_sc;
130300102Ssephe
131301015Ssepheextern inthand_t IDTVEC(vmbus_isr);
132300574Ssephe
133307278Ssephestatic const uint32_t		vmbus_version[] = {
134307302Ssephe	VMBUS_VERSION_WIN8_1,
135307302Ssephe	VMBUS_VERSION_WIN8,
136307302Ssephe	VMBUS_VERSION_WIN7,
137307302Ssephe	VMBUS_VERSION_WS2008
138307278Ssephe};
139307278Ssephe
140307451Ssephestatic const vmbus_chanmsg_proc_t
141307451Ssephevmbus_chanmsg_handlers[VMBUS_CHANMSG_TYPE_MAX] = {
142307451Ssephe	VMBUS_CHANMSG_PROC(CHOFFER_DONE, vmbus_scan_done),
143307451Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(CONNECT_RESP)
144307451Ssephe};
145307451Ssephe
146307466Ssephestatic device_method_t vmbus_methods[] = {
147307466Ssephe	/* Device interface */
148318392Ssephe	DEVMETHOD(device_identify,		vmbus_identify),
149307466Ssephe	DEVMETHOD(device_probe,			vmbus_probe),
150307466Ssephe	DEVMETHOD(device_attach,		vmbus_attach),
151307466Ssephe	DEVMETHOD(device_detach,		vmbus_detach),
152307466Ssephe	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
153307466Ssephe	DEVMETHOD(device_suspend,		bus_generic_suspend),
154307466Ssephe	DEVMETHOD(device_resume,		bus_generic_resume),
155307466Ssephe
156307466Ssephe	/* Bus interface */
157307466Ssephe	DEVMETHOD(bus_add_child,		bus_generic_add_child),
158307466Ssephe	DEVMETHOD(bus_print_child,		bus_generic_print_child),
159307466Ssephe	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
160307466Ssephe	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
161309312Sdexuan	DEVMETHOD(bus_alloc_resource,		vmbus_alloc_resource),
162309312Sdexuan	DEVMETHOD(bus_release_resource,		bus_generic_release_resource),
163309312Sdexuan	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
164309312Sdexuan	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
165309312Sdexuan	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
166309312Sdexuan	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
167309312Sdexuan#if __FreeBSD_version >= 1100000
168309312Sdexuan	DEVMETHOD(bus_get_cpus,			bus_generic_get_cpus),
169309312Sdexuan#endif
170307466Ssephe
171309312Sdexuan	/* pcib interface */
172309312Sdexuan	DEVMETHOD(pcib_alloc_msi,		vmbus_alloc_msi),
173309312Sdexuan	DEVMETHOD(pcib_release_msi,		vmbus_release_msi),
174309312Sdexuan	DEVMETHOD(pcib_alloc_msix,		vmbus_alloc_msix),
175309312Sdexuan	DEVMETHOD(pcib_release_msix,		vmbus_release_msix),
176309312Sdexuan	DEVMETHOD(pcib_map_msi,			vmbus_map_msi),
177309312Sdexuan
178307466Ssephe	/* Vmbus interface */
179307466Ssephe	DEVMETHOD(vmbus_get_version,		vmbus_get_version_method),
180307466Ssephe	DEVMETHOD(vmbus_probe_guid,		vmbus_probe_guid_method),
181309312Sdexuan	DEVMETHOD(vmbus_get_vcpu_id,		vmbus_get_vcpu_id_method),
182311368Ssephe	DEVMETHOD(vmbus_get_event_taskq,	vmbus_get_eventtq_method),
183307466Ssephe
184307466Ssephe	DEVMETHOD_END
185307466Ssephe};
186307466Ssephe
187307466Ssephestatic driver_t vmbus_driver = {
188307466Ssephe	"vmbus",
189307466Ssephe	vmbus_methods,
190307466Ssephe	sizeof(struct vmbus_softc)
191307466Ssephe};
192307466Ssephe
193307466Ssephestatic devclass_t vmbus_devclass;
194307466Ssephe
195318392SsepheDRIVER_MODULE(vmbus, pcib, vmbus_driver, vmbus_devclass, NULL, NULL);
196318392SsepheDRIVER_MODULE(vmbus, acpi_syscontainer, vmbus_driver, vmbus_devclass,
197318392Ssephe    NULL, NULL);
198318392Ssephe
199307466SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1);
200309312SdexuanMODULE_DEPEND(vmbus, pci, 1, 1, 1);
201307466SsepheMODULE_VERSION(vmbus, 1);
202307466Ssephe
203307466Ssephestatic __inline struct vmbus_softc *
204307466Ssephevmbus_get_softc(void)
205307466Ssephe{
206307466Ssephe	return vmbus_sc;
207307466Ssephe}
208307466Ssephe
209307301Ssephevoid
210307301Ssephevmbus_msghc_reset(struct vmbus_msghc *mh, size_t dsize)
211307301Ssephe{
212307301Ssephe	struct hypercall_postmsg_in *inprm;
213307301Ssephe
214307301Ssephe	if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
215307301Ssephe		panic("invalid data size %zu", dsize);
216307301Ssephe
217307475Ssephe	inprm = vmbus_xact_req_data(mh->mh_xact);
218307301Ssephe	memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE);
219307301Ssephe	inprm->hc_connid = VMBUS_CONNID_MESSAGE;
220307301Ssephe	inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL;
221307301Ssephe	inprm->hc_dsize = dsize;
222307301Ssephe}
223307301Ssephe
224307278Ssephestruct vmbus_msghc *
225307278Ssephevmbus_msghc_get(struct vmbus_softc *sc, size_t dsize)
226307278Ssephe{
227307278Ssephe	struct vmbus_msghc *mh;
228307475Ssephe	struct vmbus_xact *xact;
229307278Ssephe
230307278Ssephe	if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
231307301Ssephe		panic("invalid data size %zu", dsize);
232307278Ssephe
233307475Ssephe	xact = vmbus_xact_get(sc->vmbus_xc,
234307475Ssephe	    dsize + __offsetof(struct hypercall_postmsg_in, hc_data[0]));
235307475Ssephe	if (xact == NULL)
236307475Ssephe		return (NULL);
237307278Ssephe
238307475Ssephe	mh = vmbus_xact_priv(xact, sizeof(*mh));
239307475Ssephe	mh->mh_xact = xact;
240307475Ssephe
241307301Ssephe	vmbus_msghc_reset(mh, dsize);
242307475Ssephe	return (mh);
243307278Ssephe}
244307278Ssephe
245307278Ssephevoid
246307475Ssephevmbus_msghc_put(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh)
247307278Ssephe{
248307278Ssephe
249307475Ssephe	vmbus_xact_put(mh->mh_xact);
250307278Ssephe}
251307278Ssephe
252307278Ssephevoid *
253307278Ssephevmbus_msghc_dataptr(struct vmbus_msghc *mh)
254307278Ssephe{
255307475Ssephe	struct hypercall_postmsg_in *inprm;
256307278Ssephe
257307475Ssephe	inprm = vmbus_xact_req_data(mh->mh_xact);
258307475Ssephe	return (inprm->hc_data);
259307278Ssephe}
260307278Ssephe
261307278Ssepheint
262307278Ssephevmbus_msghc_exec_noresult(struct vmbus_msghc *mh)
263307278Ssephe{
264307278Ssephe	sbintime_t time = SBT_1MS;
265307475Ssephe	struct hypercall_postmsg_in *inprm;
266307475Ssephe	bus_addr_t inprm_paddr;
267307278Ssephe	int i;
268307278Ssephe
269307475Ssephe	inprm = vmbus_xact_req_data(mh->mh_xact);
270307475Ssephe	inprm_paddr = vmbus_xact_req_paddr(mh->mh_xact);
271307475Ssephe
272307278Ssephe	/*
273307278Ssephe	 * Save the input parameter so that we could restore the input
274307278Ssephe	 * parameter if the Hypercall failed.
275307278Ssephe	 *
276307278Ssephe	 * XXX
277307278Ssephe	 * Is this really necessary?!  i.e. Will the Hypercall ever
278307278Ssephe	 * overwrite the input parameter?
279307278Ssephe	 */
280307475Ssephe	memcpy(&mh->mh_inprm_save, inprm, HYPERCALL_POSTMSGIN_SIZE);
281307278Ssephe
282307278Ssephe	/*
283307278Ssephe	 * In order to cope with transient failures, e.g. insufficient
284307278Ssephe	 * resources on host side, we retry the post message Hypercall
285307278Ssephe	 * several times.  20 retries seem sufficient.
286307278Ssephe	 */
287307278Ssephe#define HC_RETRY_MAX	20
288307278Ssephe
289307278Ssephe	for (i = 0; i < HC_RETRY_MAX; ++i) {
290307278Ssephe		uint64_t status;
291307278Ssephe
292307475Ssephe		status = hypercall_post_message(inprm_paddr);
293307278Ssephe		if (status == HYPERCALL_STATUS_SUCCESS)
294307278Ssephe			return 0;
295307278Ssephe
296307278Ssephe		pause_sbt("hcpmsg", time, 0, C_HARDCLOCK);
297307278Ssephe		if (time < SBT_1S * 2)
298307278Ssephe			time *= 2;
299307278Ssephe
300307278Ssephe		/* Restore input parameter and try again */
301307475Ssephe		memcpy(inprm, &mh->mh_inprm_save, HYPERCALL_POSTMSGIN_SIZE);
302307278Ssephe	}
303307278Ssephe
304307278Ssephe#undef HC_RETRY_MAX
305307278Ssephe
306307278Ssephe	return EIO;
307307278Ssephe}
308307278Ssephe
309307278Ssepheint
310307475Ssephevmbus_msghc_exec(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh)
311307278Ssephe{
312307278Ssephe	int error;
313307278Ssephe
314307475Ssephe	vmbus_xact_activate(mh->mh_xact);
315307278Ssephe	error = vmbus_msghc_exec_noresult(mh);
316307475Ssephe	if (error)
317307475Ssephe		vmbus_xact_deactivate(mh->mh_xact);
318307278Ssephe	return error;
319307278Ssephe}
320307278Ssephe
321311367Ssephevoid
322311367Ssephevmbus_msghc_exec_cancel(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh)
323311367Ssephe{
324311367Ssephe
325311367Ssephe	vmbus_xact_deactivate(mh->mh_xact);
326311367Ssephe}
327311367Ssephe
328307278Ssepheconst struct vmbus_message *
329307475Ssephevmbus_msghc_wait_result(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh)
330307278Ssephe{
331307475Ssephe	size_t resp_len;
332307278Ssephe
333307475Ssephe	return (vmbus_xact_wait(mh->mh_xact, &resp_len));
334307278Ssephe}
335307278Ssephe
336311367Ssepheconst struct vmbus_message *
337311367Ssephevmbus_msghc_poll_result(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh)
338311367Ssephe{
339311367Ssephe	size_t resp_len;
340311367Ssephe
341311367Ssephe	return (vmbus_xact_poll(mh->mh_xact, &resp_len));
342311367Ssephe}
343311367Ssephe
344307278Ssephevoid
345307278Ssephevmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg)
346307278Ssephe{
347307278Ssephe
348307475Ssephe	vmbus_xact_ctx_wakeup(sc->vmbus_xc, msg, sizeof(*msg));
349307278Ssephe}
350307278Ssephe
351307302Ssepheuint32_t
352307302Ssephevmbus_gpadl_alloc(struct vmbus_softc *sc)
353307302Ssephe{
354311366Ssephe	uint32_t gpadl;
355311366Ssephe
356311366Ssepheagain:
357311366Ssephe	gpadl = atomic_fetchadd_int(&sc->vmbus_gpadl, 1);
358311366Ssephe	if (gpadl == 0)
359311366Ssephe		goto again;
360311366Ssephe	return (gpadl);
361307302Ssephe}
362307302Ssephe
363307278Ssephestatic int
364307302Ssephevmbus_connect(struct vmbus_softc *sc, uint32_t version)
365307278Ssephe{
366307302Ssephe	struct vmbus_chanmsg_connect *req;
367307278Ssephe	const struct vmbus_message *msg;
368307278Ssephe	struct vmbus_msghc *mh;
369307302Ssephe	int error, done = 0;
370307278Ssephe
371307278Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
372307278Ssephe	if (mh == NULL)
373307278Ssephe		return ENXIO;
374307278Ssephe
375307278Ssephe	req = vmbus_msghc_dataptr(mh);
376307302Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CONNECT;
377307278Ssephe	req->chm_ver = version;
378307278Ssephe	req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr;
379307278Ssephe	req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr;
380307278Ssephe	req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr;
381307278Ssephe
382307278Ssephe	error = vmbus_msghc_exec(sc, mh);
383307278Ssephe	if (error) {
384307278Ssephe		vmbus_msghc_put(sc, mh);
385307278Ssephe		return error;
386307278Ssephe	}
387307278Ssephe
388307278Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
389307302Ssephe	done = ((const struct vmbus_chanmsg_connect_resp *)
390307302Ssephe	    msg->msg_data)->chm_done;
391307278Ssephe
392307278Ssephe	vmbus_msghc_put(sc, mh);
393307278Ssephe
394307302Ssephe	return (done ? 0 : EOPNOTSUPP);
395307278Ssephe}
396307278Ssephe
397307278Ssephestatic int
398307278Ssephevmbus_init(struct vmbus_softc *sc)
399307278Ssephe{
400307278Ssephe	int i;
401307278Ssephe
402307278Ssephe	for (i = 0; i < nitems(vmbus_version); ++i) {
403307278Ssephe		int error;
404307278Ssephe
405307302Ssephe		error = vmbus_connect(sc, vmbus_version[i]);
406307278Ssephe		if (!error) {
407307302Ssephe			sc->vmbus_version = vmbus_version[i];
408307278Ssephe			device_printf(sc->vmbus_dev, "version %u.%u\n",
409307302Ssephe			    VMBUS_VERSION_MAJOR(sc->vmbus_version),
410307302Ssephe			    VMBUS_VERSION_MINOR(sc->vmbus_version));
411307278Ssephe			return 0;
412307278Ssephe		}
413307278Ssephe	}
414307278Ssephe	return ENXIO;
415307278Ssephe}
416307278Ssephe
417307278Ssephestatic void
418307302Ssephevmbus_disconnect(struct vmbus_softc *sc)
419307291Ssephe{
420307302Ssephe	struct vmbus_chanmsg_disconnect *req;
421307291Ssephe	struct vmbus_msghc *mh;
422307291Ssephe	int error;
423307291Ssephe
424307291Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
425307291Ssephe	if (mh == NULL) {
426307291Ssephe		device_printf(sc->vmbus_dev,
427307302Ssephe		    "can not get msg hypercall for disconnect\n");
428307291Ssephe		return;
429307291Ssephe	}
430307291Ssephe
431307291Ssephe	req = vmbus_msghc_dataptr(mh);
432307302Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_DISCONNECT;
433307291Ssephe
434307291Ssephe	error = vmbus_msghc_exec_noresult(mh);
435307291Ssephe	vmbus_msghc_put(sc, mh);
436307291Ssephe
437307291Ssephe	if (error) {
438307291Ssephe		device_printf(sc->vmbus_dev,
439307302Ssephe		    "disconnect msg hypercall failed\n");
440307291Ssephe	}
441307291Ssephe}
442307291Ssephe
443307291Ssephestatic int
444307291Ssephevmbus_req_channels(struct vmbus_softc *sc)
445307291Ssephe{
446307302Ssephe	struct vmbus_chanmsg_chrequest *req;
447307291Ssephe	struct vmbus_msghc *mh;
448307291Ssephe	int error;
449307291Ssephe
450307291Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
451307291Ssephe	if (mh == NULL)
452307291Ssephe		return ENXIO;
453307291Ssephe
454307291Ssephe	req = vmbus_msghc_dataptr(mh);
455307302Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHREQUEST;
456307291Ssephe
457307291Ssephe	error = vmbus_msghc_exec_noresult(mh);
458307291Ssephe	vmbus_msghc_put(sc, mh);
459307291Ssephe
460307291Ssephe	return error;
461307291Ssephe}
462307291Ssephe
463307451Ssephestatic void
464307599Ssephevmbus_scan_done_task(void *xsc, int pending __unused)
465307291Ssephe{
466307599Ssephe	struct vmbus_softc *sc = xsc;
467307599Ssephe
468307599Ssephe	mtx_lock(&Giant);
469307599Ssephe	sc->vmbus_scandone = true;
470307599Ssephe	mtx_unlock(&Giant);
471307599Ssephe	wakeup(&sc->vmbus_scandone);
472307291Ssephe}
473307291Ssephe
474307451Ssephestatic void
475307451Ssephevmbus_scan_done(struct vmbus_softc *sc,
476307451Ssephe    const struct vmbus_message *msg __unused)
477307291Ssephe{
478307291Ssephe
479307599Ssephe	taskqueue_enqueue(sc->vmbus_devtq, &sc->vmbus_scandone_task);
480307291Ssephe}
481307291Ssephe
482307291Ssephestatic int
483307291Ssephevmbus_scan(struct vmbus_softc *sc)
484307291Ssephe{
485307291Ssephe	int error;
486307291Ssephe
487307291Ssephe	/*
488307599Ssephe	 * Identify, probe and attach for non-channel devices.
489307599Ssephe	 */
490307599Ssephe	bus_generic_probe(sc->vmbus_dev);
491307599Ssephe	bus_generic_attach(sc->vmbus_dev);
492307599Ssephe
493307599Ssephe	/*
494307599Ssephe	 * This taskqueue serializes vmbus devices' attach and detach
495307599Ssephe	 * for channel offer and rescind messages.
496307599Ssephe	 */
497307599Ssephe	sc->vmbus_devtq = taskqueue_create("vmbus dev", M_WAITOK,
498307599Ssephe	    taskqueue_thread_enqueue, &sc->vmbus_devtq);
499307599Ssephe	taskqueue_start_threads(&sc->vmbus_devtq, 1, PI_NET, "vmbusdev");
500307599Ssephe	TASK_INIT(&sc->vmbus_scandone_task, 0, vmbus_scan_done_task, sc);
501307599Ssephe
502307599Ssephe	/*
503307599Ssephe	 * This taskqueue handles sub-channel detach, so that vmbus
504307599Ssephe	 * device's detach running in vmbus_devtq can drain its sub-
505307599Ssephe	 * channels.
506307599Ssephe	 */
507307599Ssephe	sc->vmbus_subchtq = taskqueue_create("vmbus subch", M_WAITOK,
508307599Ssephe	    taskqueue_thread_enqueue, &sc->vmbus_subchtq);
509307599Ssephe	taskqueue_start_threads(&sc->vmbus_subchtq, 1, PI_NET, "vmbussch");
510307599Ssephe
511307599Ssephe	/*
512307291Ssephe	 * Start vmbus scanning.
513307291Ssephe	 */
514307291Ssephe	error = vmbus_req_channels(sc);
515307291Ssephe	if (error) {
516307291Ssephe		device_printf(sc->vmbus_dev, "channel request failed: %d\n",
517307291Ssephe		    error);
518307599Ssephe		return (error);
519307291Ssephe	}
520307291Ssephe
521307291Ssephe	/*
522307599Ssephe	 * Wait for all vmbus devices from the initial channel offers to be
523307599Ssephe	 * attached.
524307291Ssephe	 */
525307599Ssephe	GIANT_REQUIRED;
526307599Ssephe	while (!sc->vmbus_scandone)
527307599Ssephe		mtx_sleep(&sc->vmbus_scandone, &Giant, 0, "vmbusdev", 0);
528307291Ssephe
529307291Ssephe	if (bootverbose) {
530307291Ssephe		device_printf(sc->vmbus_dev, "device scan, probe and attach "
531307291Ssephe		    "done\n");
532307291Ssephe	}
533307599Ssephe	return (0);
534307291Ssephe}
535307291Ssephe
536307291Ssephestatic void
537307599Ssephevmbus_scan_teardown(struct vmbus_softc *sc)
538307599Ssephe{
539307599Ssephe
540307599Ssephe	GIANT_REQUIRED;
541307599Ssephe	if (sc->vmbus_devtq != NULL) {
542307599Ssephe		mtx_unlock(&Giant);
543307599Ssephe		taskqueue_free(sc->vmbus_devtq);
544307599Ssephe		mtx_lock(&Giant);
545307599Ssephe		sc->vmbus_devtq = NULL;
546307599Ssephe	}
547307599Ssephe	if (sc->vmbus_subchtq != NULL) {
548307599Ssephe		mtx_unlock(&Giant);
549307599Ssephe		taskqueue_free(sc->vmbus_subchtq);
550307599Ssephe		mtx_lock(&Giant);
551307599Ssephe		sc->vmbus_subchtq = NULL;
552307599Ssephe	}
553307599Ssephe}
554307599Ssephe
555307599Ssephestatic void
556307451Ssephevmbus_chanmsg_handle(struct vmbus_softc *sc, const struct vmbus_message *msg)
557307451Ssephe{
558307451Ssephe	vmbus_chanmsg_proc_t msg_proc;
559307451Ssephe	uint32_t msg_type;
560307451Ssephe
561307451Ssephe	msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
562307451Ssephe	if (msg_type >= VMBUS_CHANMSG_TYPE_MAX) {
563307451Ssephe		device_printf(sc->vmbus_dev, "unknown message type 0x%x\n",
564307451Ssephe		    msg_type);
565307451Ssephe		return;
566307451Ssephe	}
567307451Ssephe
568307451Ssephe	msg_proc = vmbus_chanmsg_handlers[msg_type];
569307451Ssephe	if (msg_proc != NULL)
570307451Ssephe		msg_proc(sc, msg);
571307451Ssephe
572307451Ssephe	/* Channel specific processing */
573307451Ssephe	vmbus_chan_msgproc(sc, msg);
574307451Ssephe}
575307451Ssephe
576307451Ssephestatic void
577300572Ssephevmbus_msg_task(void *xsc, int pending __unused)
578250199Sgrehan{
579300572Ssephe	struct vmbus_softc *sc = xsc;
580301019Ssephe	volatile struct vmbus_message *msg;
581250199Sgrehan
582300988Ssephe	msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE;
583300108Ssephe	for (;;) {
584307278Ssephe		if (msg->msg_type == HYPERV_MSGTYPE_NONE) {
585301484Ssephe			/* No message */
586301484Ssephe			break;
587307278Ssephe		} else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) {
588301487Ssephe			/* Channel message */
589307451Ssephe			vmbus_chanmsg_handle(sc,
590301488Ssephe			    __DEVOLATILE(const struct vmbus_message *, msg));
591301484Ssephe		}
592292861Sdelphij
593307278Ssephe		msg->msg_type = HYPERV_MSGTYPE_NONE;
594300108Ssephe		/*
595301019Ssephe		 * Make sure the write to msg_type (i.e. set to
596307278Ssephe		 * HYPERV_MSGTYPE_NONE) happens before we read the
597301019Ssephe		 * msg_flags and EOMing. Otherwise, the EOMing will
598301019Ssephe		 * not deliver any more messages since there is no
599301019Ssephe		 * empty slot
600300108Ssephe		 *
601300108Ssephe		 * NOTE:
602300108Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
603300108Ssephe		 * will become compiler fence on UP kernel.
604300108Ssephe		 */
605300108Ssephe		mb();
606301019Ssephe		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
607250199Sgrehan			/*
608250199Sgrehan			 * This will cause message queue rescan to possibly
609250199Sgrehan			 * deliver another msg from the hypervisor
610250199Sgrehan			 */
611300830Ssephe			wrmsr(MSR_HV_EOM, 0);
612300108Ssephe		}
613250199Sgrehan	}
614250199Sgrehan}
615250199Sgrehan
616301015Ssephestatic __inline int
617301015Ssephevmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
618250199Sgrehan{
619301019Ssephe	volatile struct vmbus_message *msg;
620301019Ssephe	struct vmbus_message *msg_base;
621250199Sgrehan
622301009Ssephe	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
623301009Ssephe
624250199Sgrehan	/*
625301009Ssephe	 * Check event timer.
626301009Ssephe	 *
627301009Ssephe	 * TODO: move this to independent IDT vector.
628250199Sgrehan	 */
629300988Ssephe	msg = msg_base + VMBUS_SINT_TIMER;
630307278Ssephe	if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) {
631307278Ssephe		msg->msg_type = HYPERV_MSGTYPE_NONE;
632293873Ssephe
633300988Ssephe		vmbus_et_intr(frame);
634297176Ssephe
635293873Ssephe		/*
636301019Ssephe		 * Make sure the write to msg_type (i.e. set to
637307278Ssephe		 * HYPERV_MSGTYPE_NONE) happens before we read the
638301019Ssephe		 * msg_flags and EOMing. Otherwise, the EOMing will
639301019Ssephe		 * not deliver any more messages since there is no
640301019Ssephe		 * empty slot
641297634Ssephe		 *
642297634Ssephe		 * NOTE:
643297634Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
644297636Ssephe		 * will become compiler fence on UP kernel.
645293873Ssephe		 */
646297634Ssephe		mb();
647301019Ssephe		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
648293873Ssephe			/*
649293873Ssephe			 * This will cause message queue rescan to possibly
650293873Ssephe			 * deliver another msg from the hypervisor
651293873Ssephe			 */
652300830Ssephe			wrmsr(MSR_HV_EOM, 0);
653293873Ssephe		}
654293873Ssephe	}
655293873Ssephe
656301009Ssephe	/*
657301009Ssephe	 * Check events.  Hot path for network and storage I/O data; high rate.
658301009Ssephe	 *
659301009Ssephe	 * NOTE:
660301009Ssephe	 * As recommended by the Windows guest fellows, we check events before
661301009Ssephe	 * checking messages.
662301009Ssephe	 */
663301009Ssephe	sc->vmbus_event_proc(sc, cpu);
664301009Ssephe
665301009Ssephe	/*
666301009Ssephe	 * Check messages.  Mainly management stuffs; ultra low rate.
667301009Ssephe	 */
668300988Ssephe	msg = msg_base + VMBUS_SINT_MESSAGE;
669307278Ssephe	if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
670300646Ssephe		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
671300646Ssephe		    VMBUS_PCPU_PTR(sc, message_task, cpu));
672250199Sgrehan	}
673250199Sgrehan
674293873Ssephe	return (FILTER_HANDLED);
675250199Sgrehan}
676250199Sgrehan
677282212Swhuvoid
678301015Ssephevmbus_handle_intr(struct trapframe *trap_frame)
679282212Swhu{
680300565Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
681300565Ssephe	int cpu = curcpu;
682282212Swhu
683282212Swhu	/*
684282212Swhu	 * Disable preemption.
685282212Swhu	 */
686282212Swhu	critical_enter();
687282212Swhu
688282212Swhu	/*
689282212Swhu	 * Do a little interrupt counting.
690282212Swhu	 */
691300573Ssephe	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
692282212Swhu
693301015Ssephe	vmbus_handle_intr1(sc, trap_frame, cpu);
694282212Swhu
695282212Swhu	/*
696282212Swhu	 * Enable preemption.
697282212Swhu	 */
698282212Swhu	critical_exit();
699282212Swhu}
700282212Swhu
701300571Ssephestatic void
702300652Ssephevmbus_synic_setup(void *xsc)
703300571Ssephe{
704300652Ssephe	struct vmbus_softc *sc = xsc;
705300654Ssephe	int cpu = curcpu;
706300654Ssephe	uint64_t val, orig;
707300654Ssephe	uint32_t sint;
708300571Ssephe
709300834Ssephe	if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
710307454Ssephe		/* Save virtual processor id. */
711300834Ssephe		VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX);
712300834Ssephe	} else {
713307454Ssephe		/* Set virtual processor id to 0 for compatibility. */
714307454Ssephe		VMBUS_PCPU_GET(sc, vcpuid, cpu) = 0;
715300834Ssephe	}
716300571Ssephe
717300571Ssephe	/*
718300654Ssephe	 * Setup the SynIC message.
719300571Ssephe	 */
720300654Ssephe	orig = rdmsr(MSR_HV_SIMP);
721300654Ssephe	val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
722300654Ssephe	    ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) <<
723300654Ssephe	     MSR_HV_SIMP_PGSHIFT);
724300654Ssephe	wrmsr(MSR_HV_SIMP, val);
725300571Ssephe
726300571Ssephe	/*
727300654Ssephe	 * Setup the SynIC event flags.
728300571Ssephe	 */
729300654Ssephe	orig = rdmsr(MSR_HV_SIEFP);
730300654Ssephe	val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
731301106Ssephe	    ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu)
732301106Ssephe	      >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT);
733300654Ssephe	wrmsr(MSR_HV_SIEFP, val);
734300571Ssephe
735300571Ssephe
736300654Ssephe	/*
737300654Ssephe	 * Configure and unmask SINT for message and event flags.
738300654Ssephe	 */
739300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
740300654Ssephe	orig = rdmsr(sint);
741300654Ssephe	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
742300654Ssephe	    (orig & MSR_HV_SINT_RSVD_MASK);
743300654Ssephe	wrmsr(sint, val);
744300571Ssephe
745300654Ssephe	/*
746300654Ssephe	 * Configure and unmask SINT for timer.
747300654Ssephe	 */
748300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
749300654Ssephe	orig = rdmsr(sint);
750300654Ssephe	val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI |
751300654Ssephe	    (orig & MSR_HV_SINT_RSVD_MASK);
752300654Ssephe	wrmsr(sint, val);
753300571Ssephe
754300571Ssephe	/*
755300654Ssephe	 * All done; enable SynIC.
756300571Ssephe	 */
757300654Ssephe	orig = rdmsr(MSR_HV_SCONTROL);
758300654Ssephe	val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
759300654Ssephe	wrmsr(MSR_HV_SCONTROL, val);
760300571Ssephe}
761300571Ssephe
762300571Ssephestatic void
763300571Ssephevmbus_synic_teardown(void *arg)
764300571Ssephe{
765300654Ssephe	uint64_t orig;
766300654Ssephe	uint32_t sint;
767300571Ssephe
768300654Ssephe	/*
769300654Ssephe	 * Disable SynIC.
770300654Ssephe	 */
771300654Ssephe	orig = rdmsr(MSR_HV_SCONTROL);
772300654Ssephe	wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
773300571Ssephe
774300654Ssephe	/*
775300654Ssephe	 * Mask message and event flags SINT.
776300654Ssephe	 */
777300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
778300654Ssephe	orig = rdmsr(sint);
779300654Ssephe	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
780300571Ssephe
781300571Ssephe	/*
782300654Ssephe	 * Mask timer SINT.
783300571Ssephe	 */
784300988Ssephe	sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
785300654Ssephe	orig = rdmsr(sint);
786300654Ssephe	wrmsr(sint, orig | MSR_HV_SINT_MASKED);
787300571Ssephe
788300654Ssephe	/*
789300654Ssephe	 * Teardown SynIC message.
790300654Ssephe	 */
791300654Ssephe	orig = rdmsr(MSR_HV_SIMP);
792300654Ssephe	wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
793300571Ssephe
794300571Ssephe	/*
795300654Ssephe	 * Teardown SynIC event flags.
796300571Ssephe	 */
797300654Ssephe	orig = rdmsr(MSR_HV_SIEFP);
798300654Ssephe	wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
799300571Ssephe}
800300571Ssephe
801300644Ssephestatic int
802300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc)
803300572Ssephe{
804301583Ssephe	bus_dma_tag_t parent_dtag;
805301583Ssephe	uint8_t *evtflags;
806300572Ssephe	int cpu;
807300572Ssephe
808301583Ssephe	parent_dtag = bus_get_dma_tag(sc->vmbus_dev);
809300572Ssephe	CPU_FOREACH(cpu) {
810300644Ssephe		void *ptr;
811300644Ssephe
812300572Ssephe		/*
813300572Ssephe		 * Per-cpu messages and event flags.
814300572Ssephe		 */
815301583Ssephe		ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
816301583Ssephe		    PAGE_SIZE, VMBUS_PCPU_PTR(sc, message_dma, cpu),
817300572Ssephe		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
818300644Ssephe		if (ptr == NULL)
819300644Ssephe			return ENOMEM;
820300644Ssephe		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
821300644Ssephe
822301583Ssephe		ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
823301583Ssephe		    PAGE_SIZE, VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
824300572Ssephe		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
825300644Ssephe		if (ptr == NULL)
826300644Ssephe			return ENOMEM;
827301106Ssephe		VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr;
828300572Ssephe	}
829301583Ssephe
830301583Ssephe	evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
831301583Ssephe	    PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
832301583Ssephe	if (evtflags == NULL)
833301583Ssephe		return ENOMEM;
834301583Ssephe	sc->vmbus_rx_evtflags = (u_long *)evtflags;
835301588Ssephe	sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2));
836301583Ssephe	sc->vmbus_evtflags = evtflags;
837301583Ssephe
838301583Ssephe	sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
839301583Ssephe	    PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
840301583Ssephe	if (sc->vmbus_mnf1 == NULL)
841301583Ssephe		return ENOMEM;
842301583Ssephe
843301583Ssephe	sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
844307310Ssephe	    sizeof(struct vmbus_mnf), &sc->vmbus_mnf2_dma,
845307310Ssephe	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
846301583Ssephe	if (sc->vmbus_mnf2 == NULL)
847301583Ssephe		return ENOMEM;
848301583Ssephe
849300644Ssephe	return 0;
850300572Ssephe}
851300572Ssephe
852300572Ssephestatic void
853300572Ssephevmbus_dma_free(struct vmbus_softc *sc)
854300572Ssephe{
855300572Ssephe	int cpu;
856300572Ssephe
857301583Ssephe	if (sc->vmbus_evtflags != NULL) {
858301583Ssephe		hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags);
859301583Ssephe		sc->vmbus_evtflags = NULL;
860301583Ssephe		sc->vmbus_rx_evtflags = NULL;
861301583Ssephe		sc->vmbus_tx_evtflags = NULL;
862301583Ssephe	}
863301583Ssephe	if (sc->vmbus_mnf1 != NULL) {
864301583Ssephe		hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1);
865301583Ssephe		sc->vmbus_mnf1 = NULL;
866301583Ssephe	}
867301583Ssephe	if (sc->vmbus_mnf2 != NULL) {
868301583Ssephe		hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2);
869301583Ssephe		sc->vmbus_mnf2 = NULL;
870301583Ssephe	}
871301583Ssephe
872300572Ssephe	CPU_FOREACH(cpu) {
873300573Ssephe		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
874300572Ssephe			hyperv_dmamem_free(
875300573Ssephe			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
876300573Ssephe			    VMBUS_PCPU_GET(sc, message, cpu));
877300573Ssephe			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
878300572Ssephe		}
879301106Ssephe		if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) {
880300572Ssephe			hyperv_dmamem_free(
881301106Ssephe			    VMBUS_PCPU_PTR(sc, event_flags_dma, cpu),
882301106Ssephe			    VMBUS_PCPU_GET(sc, event_flags, cpu));
883301106Ssephe			VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL;
884300572Ssephe		}
885300572Ssephe	}
886300572Ssephe}
887300572Ssephe
888250199Sgrehanstatic int
889300574Ssephevmbus_intr_setup(struct vmbus_softc *sc)
890300574Ssephe{
891300574Ssephe	int cpu;
892300574Ssephe
893300574Ssephe	CPU_FOREACH(cpu) {
894300574Ssephe		char buf[MAXCOMLEN + 1];
895300645Ssephe		cpuset_t cpu_mask;
896300574Ssephe
897300645Ssephe		/* Allocate an interrupt counter for Hyper-V interrupt */
898300574Ssephe		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
899300574Ssephe		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
900300574Ssephe
901300574Ssephe		/*
902300645Ssephe		 * Setup taskqueue to handle events.  Task will be per-
903300645Ssephe		 * channel.
904300574Ssephe		 */
905300646Ssephe		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
906300646Ssephe		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
907300646Ssephe		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
908300574Ssephe		CPU_SETOF(cpu, &cpu_mask);
909300574Ssephe		taskqueue_start_threads_cpuset(
910300646Ssephe		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
911300646Ssephe		    "hvevent%d", cpu);
912300574Ssephe
913300574Ssephe		/*
914300645Ssephe		 * Setup tasks and taskqueues to handle messages.
915300574Ssephe		 */
916300646Ssephe		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
917300574Ssephe		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
918300646Ssephe		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
919300574Ssephe		CPU_SETOF(cpu, &cpu_mask);
920300574Ssephe		taskqueue_start_threads_cpuset(
921300646Ssephe		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
922300646Ssephe		    "hvmsg%d", cpu);
923300646Ssephe		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
924300574Ssephe		    vmbus_msg_task, sc);
925300574Ssephe	}
926300645Ssephe
927300645Ssephe	/*
928300645Ssephe	 * All Hyper-V ISR required resources are setup, now let's find a
929300645Ssephe	 * free IDT vector for Hyper-V ISR and set it up.
930300645Ssephe	 */
931301015Ssephe	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr));
932300645Ssephe	if (sc->vmbus_idtvec < 0) {
933300645Ssephe		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
934300645Ssephe		return ENXIO;
935300645Ssephe	}
936308621Ssephe	if (bootverbose) {
937300645Ssephe		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
938300645Ssephe		    sc->vmbus_idtvec);
939300645Ssephe	}
940300574Ssephe	return 0;
941300574Ssephe}
942300574Ssephe
943300574Ssephestatic void
944300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc)
945300574Ssephe{
946300574Ssephe	int cpu;
947300574Ssephe
948300645Ssephe	if (sc->vmbus_idtvec >= 0) {
949300645Ssephe		lapic_ipi_free(sc->vmbus_idtvec);
950300645Ssephe		sc->vmbus_idtvec = -1;
951300645Ssephe	}
952300645Ssephe
953300574Ssephe	CPU_FOREACH(cpu) {
954300646Ssephe		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
955300646Ssephe			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
956300646Ssephe			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
957300574Ssephe		}
958300646Ssephe		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
959300646Ssephe			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
960300646Ssephe			    VMBUS_PCPU_PTR(sc, message_task, cpu));
961300646Ssephe			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
962300646Ssephe			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
963300576Ssephe		}
964300574Ssephe	}
965300574Ssephe}
966300574Ssephe
967300574Ssephestatic int
968300651Ssephevmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
969250199Sgrehan{
970250199Sgrehan	return (ENOENT);
971250199Sgrehan}
972250199Sgrehan
973250199Sgrehanstatic int
974297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
975297143Ssephe{
976307461Ssephe	const struct vmbus_channel *chan;
977301021Ssephe	char guidbuf[HYPERV_GUID_STRLEN];
978297143Ssephe
979307307Ssephe	chan = vmbus_get_channel(child);
980307307Ssephe	if (chan == NULL) {
981307307Ssephe		/* Event timer device, which does not belong to a channel */
982298449Ssephe		return (0);
983307307Ssephe	}
984298449Ssephe
985297143Ssephe	strlcat(buf, "classid=", buflen);
986307307Ssephe	hyperv_guid2str(&chan->ch_guid_type, guidbuf, sizeof(guidbuf));
987297143Ssephe	strlcat(buf, guidbuf, buflen);
988297143Ssephe
989297143Ssephe	strlcat(buf, " deviceid=", buflen);
990307307Ssephe	hyperv_guid2str(&chan->ch_guid_inst, guidbuf, sizeof(guidbuf));
991297143Ssephe	strlcat(buf, guidbuf, buflen);
992297143Ssephe
993297143Ssephe	return (0);
994297143Ssephe}
995297143Ssephe
996307307Ssepheint
997307461Ssephevmbus_add_child(struct vmbus_channel *chan)
998250199Sgrehan{
999307461Ssephe	struct vmbus_softc *sc = chan->ch_vmbus;
1000307307Ssephe	device_t parent = sc->vmbus_dev;
1001250199Sgrehan
1002307599Ssephe	mtx_lock(&Giant);
1003307449Ssephe
1004307307Ssephe	chan->ch_dev = device_add_child(parent, NULL, -1);
1005307307Ssephe	if (chan->ch_dev == NULL) {
1006307599Ssephe		mtx_unlock(&Giant);
1007307307Ssephe		device_printf(parent, "device_add_child for chan%u failed\n",
1008307307Ssephe		    chan->ch_id);
1009307599Ssephe		return (ENXIO);
1010307307Ssephe	}
1011307307Ssephe	device_set_ivars(chan->ch_dev, chan);
1012307599Ssephe	device_probe_and_attach(chan->ch_dev);
1013250199Sgrehan
1014307599Ssephe	mtx_unlock(&Giant);
1015307599Ssephe	return (0);
1016250199Sgrehan}
1017250199Sgrehan
1018307307Ssepheint
1019307461Ssephevmbus_delete_child(struct vmbus_channel *chan)
1020250199Sgrehan{
1021307599Ssephe	int error = 0;
1022250199Sgrehan
1023307599Ssephe	mtx_lock(&Giant);
1024307599Ssephe	if (chan->ch_dev != NULL) {
1025307599Ssephe		error = device_delete_child(chan->ch_vmbus->vmbus_dev,
1026307599Ssephe		    chan->ch_dev);
1027308621Ssephe		chan->ch_dev = NULL;
1028297142Ssephe	}
1029250199Sgrehan	mtx_unlock(&Giant);
1030307599Ssephe	return (error);
1031250199Sgrehan}
1032250199Sgrehan
1033250199Sgrehanstatic int
1034307291Ssephevmbus_sysctl_version(SYSCTL_HANDLER_ARGS)
1035307291Ssephe{
1036307302Ssephe	struct vmbus_softc *sc = arg1;
1037307291Ssephe	char verstr[16];
1038307291Ssephe
1039307291Ssephe	snprintf(verstr, sizeof(verstr), "%u.%u",
1040307302Ssephe	    VMBUS_VERSION_MAJOR(sc->vmbus_version),
1041307302Ssephe	    VMBUS_VERSION_MINOR(sc->vmbus_version));
1042307291Ssephe	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
1043307291Ssephe}
1044307291Ssephe
1045309312Sdexuan/*
1046309312Sdexuan * We need the function to make sure the MMIO resource is allocated from the
1047309312Sdexuan * ranges found in _CRS.
1048309312Sdexuan *
1049309312Sdexuan * For the release function, we can use bus_generic_release_resource().
1050309312Sdexuan */
1051309312Sdexuanstatic struct resource *
1052309312Sdexuanvmbus_alloc_resource(device_t dev, device_t child, int type, int *rid,
1053309312Sdexuan    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
1054309312Sdexuan{
1055309312Sdexuan	device_t parent = device_get_parent(dev);
1056309312Sdexuan	struct resource *res;
1057309312Sdexuan
1058309312Sdexuan#ifdef NEW_PCIB
1059309312Sdexuan	if (type == SYS_RES_MEMORY) {
1060309312Sdexuan		struct vmbus_softc *sc = device_get_softc(dev);
1061309312Sdexuan
1062309312Sdexuan		res = pcib_host_res_alloc(&sc->vmbus_mmio_res, child, type,
1063309312Sdexuan		    rid, start, end, count, flags);
1064309312Sdexuan	} else
1065309312Sdexuan#endif
1066309312Sdexuan	{
1067309312Sdexuan		res = BUS_ALLOC_RESOURCE(parent, child, type, rid, start,
1068309312Sdexuan		    end, count, flags);
1069309312Sdexuan	}
1070309312Sdexuan
1071309312Sdexuan	return (res);
1072309312Sdexuan}
1073309312Sdexuan
1074309312Sdexuanstatic int
1075309312Sdexuanvmbus_alloc_msi(device_t bus, device_t dev, int count, int maxcount, int *irqs)
1076309312Sdexuan{
1077318392Ssephe
1078318392Ssephe	return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount,
1079318392Ssephe	    irqs));
1080309312Sdexuan}
1081309312Sdexuan
1082309312Sdexuanstatic int
1083309312Sdexuanvmbus_release_msi(device_t bus, device_t dev, int count, int *irqs)
1084309312Sdexuan{
1085318392Ssephe
1086318392Ssephe	return (PCIB_RELEASE_MSI(device_get_parent(bus), dev, count, irqs));
1087309312Sdexuan}
1088309312Sdexuan
1089309312Sdexuanstatic int
1090309312Sdexuanvmbus_alloc_msix(device_t bus, device_t dev, int *irq)
1091309312Sdexuan{
1092318392Ssephe
1093318392Ssephe	return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq));
1094309312Sdexuan}
1095309312Sdexuan
1096309312Sdexuanstatic int
1097309312Sdexuanvmbus_release_msix(device_t bus, device_t dev, int irq)
1098309312Sdexuan{
1099318392Ssephe
1100318392Ssephe	return (PCIB_RELEASE_MSIX(device_get_parent(bus), dev, irq));
1101309312Sdexuan}
1102309312Sdexuan
1103309312Sdexuanstatic int
1104309312Sdexuanvmbus_map_msi(device_t bus, device_t dev, int irq, uint64_t *addr,
1105309312Sdexuan	uint32_t *data)
1106309312Sdexuan{
1107318392Ssephe
1108318392Ssephe	return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data));
1109309312Sdexuan}
1110309312Sdexuan
1111307302Ssephestatic uint32_t
1112307302Ssephevmbus_get_version_method(device_t bus, device_t dev)
1113307302Ssephe{
1114307302Ssephe	struct vmbus_softc *sc = device_get_softc(bus);
1115307302Ssephe
1116307302Ssephe	return sc->vmbus_version;
1117307302Ssephe}
1118307302Ssephe
1119307291Ssephestatic int
1120307448Ssephevmbus_probe_guid_method(device_t bus, device_t dev,
1121307448Ssephe    const struct hyperv_guid *guid)
1122307307Ssephe{
1123307461Ssephe	const struct vmbus_channel *chan = vmbus_get_channel(dev);
1124307307Ssephe
1125307448Ssephe	if (memcmp(&chan->ch_guid_type, guid, sizeof(struct hyperv_guid)) == 0)
1126307307Ssephe		return 0;
1127307307Ssephe	return ENXIO;
1128307307Ssephe}
1129307307Ssephe
1130309312Sdexuanstatic uint32_t
1131309312Sdexuanvmbus_get_vcpu_id_method(device_t bus, device_t dev, int cpu)
1132309312Sdexuan{
1133309312Sdexuan	const struct vmbus_softc *sc = device_get_softc(bus);
1134309312Sdexuan
1135309312Sdexuan	return (VMBUS_PCPU_GET(sc, vcpuid, cpu));
1136309312Sdexuan}
1137309312Sdexuan
1138311368Ssephestatic struct taskqueue *
1139311368Ssephevmbus_get_eventtq_method(device_t bus, device_t dev __unused, int cpu)
1140311368Ssephe{
1141311368Ssephe	const struct vmbus_softc *sc = device_get_softc(bus);
1142311368Ssephe
1143311368Ssephe	KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu%d", cpu));
1144311368Ssephe	return (VMBUS_PCPU_GET(sc, event_tq, cpu));
1145311368Ssephe}
1146311368Ssephe
1147309312Sdexuan#ifdef NEW_PCIB
1148309312Sdexuan#define VTPM_BASE_ADDR 0xfed40000
1149309312Sdexuan#define FOUR_GB (1ULL << 32)
1150309312Sdexuan
1151309312Sdexuanenum parse_pass { parse_64, parse_32 };
1152309312Sdexuan
1153309312Sdexuanstruct parse_context {
1154309312Sdexuan	device_t vmbus_dev;
1155309312Sdexuan	enum parse_pass pass;
1156309312Sdexuan};
1157309312Sdexuan
1158309312Sdexuanstatic ACPI_STATUS
1159309312Sdexuanparse_crs(ACPI_RESOURCE *res, void *ctx)
1160309312Sdexuan{
1161309312Sdexuan	const struct parse_context *pc = ctx;
1162309312Sdexuan	device_t vmbus_dev = pc->vmbus_dev;
1163309312Sdexuan
1164309312Sdexuan	struct vmbus_softc *sc = device_get_softc(vmbus_dev);
1165309312Sdexuan	UINT64 start, end;
1166309312Sdexuan
1167309312Sdexuan	switch (res->Type) {
1168309312Sdexuan	case ACPI_RESOURCE_TYPE_ADDRESS32:
1169309312Sdexuan		start = res->Data.Address32.Address.Minimum;
1170309312Sdexuan		end = res->Data.Address32.Address.Maximum;
1171309312Sdexuan		break;
1172309312Sdexuan
1173309312Sdexuan	case ACPI_RESOURCE_TYPE_ADDRESS64:
1174309312Sdexuan		start = res->Data.Address64.Address.Minimum;
1175309312Sdexuan		end = res->Data.Address64.Address.Maximum;
1176309312Sdexuan		break;
1177309312Sdexuan
1178309312Sdexuan	default:
1179309312Sdexuan		/* Unused types. */
1180309312Sdexuan		return (AE_OK);
1181309312Sdexuan	}
1182309312Sdexuan
1183309312Sdexuan	/*
1184309312Sdexuan	 * We don't use <1MB addresses.
1185309312Sdexuan	 */
1186309312Sdexuan	if (end < 0x100000)
1187309312Sdexuan		return (AE_OK);
1188309312Sdexuan
1189309312Sdexuan	/* Don't conflict with vTPM. */
1190309312Sdexuan	if (end >= VTPM_BASE_ADDR && start < VTPM_BASE_ADDR)
1191309312Sdexuan		end = VTPM_BASE_ADDR - 1;
1192309312Sdexuan
1193309312Sdexuan	if ((pc->pass == parse_32 && start < FOUR_GB) ||
1194309312Sdexuan	    (pc->pass == parse_64 && start >= FOUR_GB))
1195309312Sdexuan		pcib_host_res_decodes(&sc->vmbus_mmio_res, SYS_RES_MEMORY,
1196309312Sdexuan		    start, end, 0);
1197309312Sdexuan
1198309312Sdexuan	return (AE_OK);
1199309312Sdexuan}
1200309312Sdexuan
1201309312Sdexuanstatic void
1202309312Sdexuanvmbus_get_crs(device_t dev, device_t vmbus_dev, enum parse_pass pass)
1203309312Sdexuan{
1204309312Sdexuan	struct parse_context pc;
1205309312Sdexuan	ACPI_STATUS status;
1206309312Sdexuan
1207309312Sdexuan	if (bootverbose)
1208309312Sdexuan		device_printf(dev, "walking _CRS, pass=%d\n", pass);
1209309312Sdexuan
1210309312Sdexuan	pc.vmbus_dev = vmbus_dev;
1211309312Sdexuan	pc.pass = pass;
1212309312Sdexuan	status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
1213309312Sdexuan			parse_crs, &pc);
1214309312Sdexuan
1215309312Sdexuan	if (bootverbose && ACPI_FAILURE(status))
1216309312Sdexuan		device_printf(dev, "_CRS: not found, pass=%d\n", pass);
1217309312Sdexuan}
1218309312Sdexuan
1219309312Sdexuanstatic void
1220309312Sdexuanvmbus_get_mmio_res_pass(device_t dev, enum parse_pass pass)
1221309312Sdexuan{
1222318392Ssephe	device_t acpi0, parent;
1223309312Sdexuan
1224318392Ssephe	parent = device_get_parent(dev);
1225309312Sdexuan
1226318392Ssephe	acpi0 = device_get_parent(parent);
1227318392Ssephe	if (strcmp("acpi0", device_get_nameunit(acpi0)) == 0) {
1228318392Ssephe		device_t *children;
1229318392Ssephe		int count;
1230309312Sdexuan
1231318392Ssephe		/*
1232318392Ssephe		 * Try to locate VMBUS resources and find _CRS on them.
1233318392Ssephe		 */
1234318392Ssephe		if (device_get_children(acpi0, &children, &count) == 0) {
1235318392Ssephe			int i;
1236309312Sdexuan
1237318392Ssephe			for (i = 0; i < count; ++i) {
1238318392Ssephe				if (!device_is_attached(children[i]))
1239318392Ssephe					continue;
1240309312Sdexuan
1241318392Ssephe				if (strcmp("vmbus_res",
1242318392Ssephe				    device_get_name(children[i])) == 0)
1243318392Ssephe					vmbus_get_crs(children[i], dev, pass);
1244318392Ssephe			}
1245318392Ssephe			free(children, M_TEMP);
1246318392Ssephe		}
1247309312Sdexuan
1248318392Ssephe		/*
1249318392Ssephe		 * Try to find _CRS on acpi.
1250318392Ssephe		 */
1251318392Ssephe		vmbus_get_crs(acpi0, dev, pass);
1252318392Ssephe	} else {
1253318392Ssephe		device_printf(dev, "not grandchild of acpi\n");
1254309312Sdexuan	}
1255309312Sdexuan
1256318392Ssephe	/*
1257318392Ssephe	 * Try to find _CRS on parent.
1258318392Ssephe	 */
1259318392Ssephe	vmbus_get_crs(parent, dev, pass);
1260309312Sdexuan}
1261309312Sdexuan
1262309312Sdexuanstatic void
1263309312Sdexuanvmbus_get_mmio_res(device_t dev)
1264309312Sdexuan{
1265309312Sdexuan	struct vmbus_softc *sc = device_get_softc(dev);
1266309312Sdexuan	/*
1267309312Sdexuan	 * We walk the resources twice to make sure that: in the resource
1268309312Sdexuan	 * list, the 32-bit resources appear behind the 64-bit resources.
1269309312Sdexuan	 * NB: resource_list_add() uses INSERT_TAIL. This way, when we
1270309312Sdexuan	 * iterate through the list to find a range for a 64-bit BAR in
1271309312Sdexuan	 * vmbus_alloc_resource(), we can make sure we try to use >4GB
1272309312Sdexuan	 * ranges first.
1273309312Sdexuan	 */
1274309312Sdexuan	pcib_host_res_init(dev, &sc->vmbus_mmio_res);
1275309312Sdexuan
1276309312Sdexuan	vmbus_get_mmio_res_pass(dev, parse_64);
1277309312Sdexuan	vmbus_get_mmio_res_pass(dev, parse_32);
1278309312Sdexuan}
1279309312Sdexuan
1280309312Sdexuanstatic void
1281309312Sdexuanvmbus_free_mmio_res(device_t dev)
1282309312Sdexuan{
1283309312Sdexuan	struct vmbus_softc *sc = device_get_softc(dev);
1284309312Sdexuan
1285309312Sdexuan	pcib_host_res_free(dev, &sc->vmbus_mmio_res);
1286309312Sdexuan}
1287309312Sdexuan#endif	/* NEW_PCIB */
1288309312Sdexuan
1289318392Ssephestatic void
1290318392Ssephevmbus_identify(driver_t *driver, device_t parent)
1291318392Ssephe{
1292318392Ssephe
1293318392Ssephe	if (device_get_unit(parent) != 0 || vm_guest != VM_GUEST_HV ||
1294318392Ssephe	    (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
1295318392Ssephe		return;
1296318392Ssephe	device_add_child(parent, "vmbus", -1);
1297318392Ssephe}
1298318392Ssephe
1299307307Ssephestatic int
1300300127Ssephevmbus_probe(device_t dev)
1301300127Ssephe{
1302301018Ssephe
1303318392Ssephe	if (device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV ||
1304300834Ssephe	    (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
1305293870Ssephe		return (ENXIO);
1306250199Sgrehan
1307300129Ssephe	device_set_desc(dev, "Hyper-V Vmbus");
1308293870Ssephe	return (BUS_PROBE_DEFAULT);
1309250199Sgrehan}
1310250199Sgrehan
1311282212Swhu/**
1312250199Sgrehan * @brief Main vmbus driver initialization routine.
1313250199Sgrehan *
1314250199Sgrehan * Here, we
1315250199Sgrehan * - initialize the vmbus driver context
1316250199Sgrehan * - setup various driver entry points
1317250199Sgrehan * - invoke the vmbus hv main init routine
1318250199Sgrehan * - get the irq resource
1319250199Sgrehan * - invoke the vmbus to add the vmbus root device
1320250199Sgrehan * - setup the vmbus root device
1321250199Sgrehan * - retrieve the channel offers
1322250199Sgrehan */
1323250199Sgrehanstatic int
1324307291Ssephevmbus_doattach(struct vmbus_softc *sc)
1325250199Sgrehan{
1326307291Ssephe	struct sysctl_oid_list *child;
1327307291Ssephe	struct sysctl_ctx_list *ctx;
1328300574Ssephe	int ret;
1329250199Sgrehan
1330300650Ssephe	if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
1331250199Sgrehan		return (0);
1332309312Sdexuan
1333309312Sdexuan#ifdef NEW_PCIB
1334309312Sdexuan	vmbus_get_mmio_res(sc->vmbus_dev);
1335309312Sdexuan#endif
1336309312Sdexuan
1337300650Ssephe	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
1338250199Sgrehan
1339307302Ssephe	sc->vmbus_gpadl = VMBUS_GPADL_START;
1340307450Ssephe	mtx_init(&sc->vmbus_prichan_lock, "vmbus prichan", NULL, MTX_DEF);
1341307450Ssephe	TAILQ_INIT(&sc->vmbus_prichans);
1342307599Ssephe	mtx_init(&sc->vmbus_chan_lock, "vmbus channel", NULL, MTX_DEF);
1343307599Ssephe	TAILQ_INIT(&sc->vmbus_chans);
1344307304Ssephe	sc->vmbus_chmap = malloc(
1345307461Ssephe	    sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX, M_DEVBUF,
1346307304Ssephe	    M_WAITOK | M_ZERO);
1347307291Ssephe
1348250199Sgrehan	/*
1349307278Ssephe	 * Create context for "post message" Hypercalls
1350307278Ssephe	 */
1351307475Ssephe	sc->vmbus_xc = vmbus_xact_ctx_create(bus_get_dma_tag(sc->vmbus_dev),
1352307475Ssephe	    HYPERCALL_POSTMSGIN_SIZE, VMBUS_MSG_SIZE,
1353307475Ssephe	    sizeof(struct vmbus_msghc));
1354307475Ssephe	if (sc->vmbus_xc == NULL) {
1355307278Ssephe		ret = ENXIO;
1356307278Ssephe		goto cleanup;
1357307278Ssephe	}
1358307278Ssephe
1359307278Ssephe	/*
1360300645Ssephe	 * Allocate DMA stuffs.
1361250199Sgrehan	 */
1362300645Ssephe	ret = vmbus_dma_alloc(sc);
1363300574Ssephe	if (ret != 0)
1364282212Swhu		goto cleanup;
1365250199Sgrehan
1366250199Sgrehan	/*
1367300645Ssephe	 * Setup interrupt.
1368250199Sgrehan	 */
1369300645Ssephe	ret = vmbus_intr_setup(sc);
1370300644Ssephe	if (ret != 0)
1371300644Ssephe		goto cleanup;
1372300572Ssephe
1373300650Ssephe	/*
1374300650Ssephe	 * Setup SynIC.
1375300650Ssephe	 */
1376282212Swhu	if (bootverbose)
1377300650Ssephe		device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
1378300652Ssephe	smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc);
1379300650Ssephe	sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
1380282212Swhu
1381255414Sgrehan	/*
1382307304Ssephe	 * Initialize vmbus, e.g. connect to Hypervisor.
1383250199Sgrehan	 */
1384307278Ssephe	ret = vmbus_init(sc);
1385307278Ssephe	if (ret != 0)
1386307278Ssephe		goto cleanup;
1387307278Ssephe
1388307302Ssephe	if (sc->vmbus_version == VMBUS_VERSION_WS2008 ||
1389307302Ssephe	    sc->vmbus_version == VMBUS_VERSION_WIN7)
1390300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc_compat;
1391300107Ssephe	else
1392300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc;
1393300107Ssephe
1394307291Ssephe	ret = vmbus_scan(sc);
1395307291Ssephe	if (ret != 0)
1396307291Ssephe		goto cleanup;
1397298260Ssephe
1398307291Ssephe	ctx = device_get_sysctl_ctx(sc->vmbus_dev);
1399307291Ssephe	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vmbus_dev));
1400307291Ssephe	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "version",
1401307302Ssephe	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
1402307291Ssephe	    vmbus_sysctl_version, "A", "vmbus version");
1403298260Ssephe
1404250199Sgrehan	return (ret);
1405250199Sgrehan
1406300574Ssephecleanup:
1407307599Ssephe	vmbus_scan_teardown(sc);
1408300645Ssephe	vmbus_intr_teardown(sc);
1409300572Ssephe	vmbus_dma_free(sc);
1410307475Ssephe	if (sc->vmbus_xc != NULL) {
1411307475Ssephe		vmbus_xact_ctx_destroy(sc->vmbus_xc);
1412307475Ssephe		sc->vmbus_xc = NULL;
1413307278Ssephe	}
1414311389Ssephe	free(__DEVOLATILE(void *, sc->vmbus_chmap), M_DEVBUF);
1415307450Ssephe	mtx_destroy(&sc->vmbus_prichan_lock);
1416307599Ssephe	mtx_destroy(&sc->vmbus_chan_lock);
1417250199Sgrehan
1418250199Sgrehan	return (ret);
1419250199Sgrehan}
1420250199Sgrehan
1421300107Ssephestatic void
1422300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
1423300107Ssephe{
1424300107Ssephe}
1425300107Ssephe
1426310573Ssephe#ifdef EARLY_AP_STARTUP
1427310573Ssephe
1428310573Ssephestatic void
1429310573Ssephevmbus_intrhook(void *xsc)
1430310573Ssephe{
1431310573Ssephe	struct vmbus_softc *sc = xsc;
1432310573Ssephe
1433310573Ssephe	if (bootverbose)
1434310573Ssephe		device_printf(sc->vmbus_dev, "intrhook\n");
1435310573Ssephe	vmbus_doattach(sc);
1436310573Ssephe	config_intrhook_disestablish(&sc->vmbus_intrhook);
1437310573Ssephe}
1438310573Ssephe
1439310573Ssephe#endif	/* EARLY_AP_STARTUP */
1440310573Ssephe
1441250199Sgrehanstatic int
1442250199Sgrehanvmbus_attach(device_t dev)
1443250199Sgrehan{
1444300102Ssephe	vmbus_sc = device_get_softc(dev);
1445300486Ssephe	vmbus_sc->vmbus_dev = dev;
1446300574Ssephe	vmbus_sc->vmbus_idtvec = -1;
1447250199Sgrehan
1448300107Ssephe	/*
1449300107Ssephe	 * Event processing logic will be configured:
1450300107Ssephe	 * - After the vmbus protocol version negotiation.
1451300107Ssephe	 * - Before we request channel offers.
1452300107Ssephe	 */
1453300107Ssephe	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
1454300107Ssephe
1455310573Ssephe#ifdef EARLY_AP_STARTUP
1456310573Ssephe	/*
1457310573Ssephe	 * Defer the real attach until the pause(9) works as expected.
1458310573Ssephe	 */
1459310573Ssephe	vmbus_sc->vmbus_intrhook.ich_func = vmbus_intrhook;
1460310573Ssephe	vmbus_sc->vmbus_intrhook.ich_arg = vmbus_sc;
1461310573Ssephe	config_intrhook_establish(&vmbus_sc->vmbus_intrhook);
1462310573Ssephe#else	/* !EARLY_AP_STARTUP */
1463250199Sgrehan	/*
1464250199Sgrehan	 * If the system has already booted and thread
1465250199Sgrehan	 * scheduling is possible indicated by the global
1466250199Sgrehan	 * cold set to zero, we just call the driver
1467250199Sgrehan	 * initialization directly.
1468250199Sgrehan	 */
1469250199Sgrehan	if (!cold)
1470307291Ssephe		vmbus_doattach(vmbus_sc);
1471310573Ssephe#endif	/* EARLY_AP_STARTUP */
1472250199Sgrehan
1473250199Sgrehan	return (0);
1474250199Sgrehan}
1475250199Sgrehan
1476300121Ssephestatic int
1477300121Ssephevmbus_detach(device_t dev)
1478250199Sgrehan{
1479300487Ssephe	struct vmbus_softc *sc = device_get_softc(dev);
1480255414Sgrehan
1481307599Ssephe	bus_generic_detach(dev);
1482307450Ssephe	vmbus_chan_destroy_all(sc);
1483307291Ssephe
1484307599Ssephe	vmbus_scan_teardown(sc);
1485307599Ssephe
1486307302Ssephe	vmbus_disconnect(sc);
1487250199Sgrehan
1488300650Ssephe	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
1489300650Ssephe		sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
1490300650Ssephe		smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
1491300650Ssephe	}
1492250199Sgrehan
1493300645Ssephe	vmbus_intr_teardown(sc);
1494300572Ssephe	vmbus_dma_free(sc);
1495255414Sgrehan
1496307475Ssephe	if (sc->vmbus_xc != NULL) {
1497307475Ssephe		vmbus_xact_ctx_destroy(sc->vmbus_xc);
1498307475Ssephe		sc->vmbus_xc = NULL;
1499307278Ssephe	}
1500307278Ssephe
1501311389Ssephe	free(__DEVOLATILE(void *, sc->vmbus_chmap), M_DEVBUF);
1502307450Ssephe	mtx_destroy(&sc->vmbus_prichan_lock);
1503307599Ssephe	mtx_destroy(&sc->vmbus_chan_lock);
1504307304Ssephe
1505309312Sdexuan#ifdef NEW_PCIB
1506309312Sdexuan	vmbus_free_mmio_res(dev);
1507309312Sdexuan#endif
1508309312Sdexuan
1509250199Sgrehan	return (0);
1510250199Sgrehan}
1511250199Sgrehan
1512307466Ssephe#ifndef EARLY_AP_STARTUP
1513250199Sgrehan
1514307466Ssephestatic void
1515307466Ssephevmbus_sysinit(void *arg __unused)
1516307466Ssephe{
1517307466Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
1518250199Sgrehan
1519307466Ssephe	if (vm_guest != VM_GUEST_HV || sc == NULL)
1520307466Ssephe		return;
1521307302Ssephe
1522307466Ssephe	/*
1523307466Ssephe	 * If the system has already booted and thread
1524307466Ssephe	 * scheduling is possible, as indicated by the
1525307466Ssephe	 * global cold set to zero, we just call the driver
1526307466Ssephe	 * initialization directly.
1527307466Ssephe	 */
1528307466Ssephe	if (!cold)
1529307466Ssephe		vmbus_doattach(sc);
1530307466Ssephe}
1531300126Ssephe/*
1532300126Ssephe * NOTE:
1533300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
1534300126Ssephe * initialized.
1535300126Ssephe */
1536300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
1537307466Ssephe
1538307466Ssephe#endif	/* !EARLY_AP_STARTUP */
1539