vmbus.c revision 300567
1250199Sgrehan/*-
2298446Ssephe * Copyright (c) 2009-2012,2016 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2012 NetApp Inc.
4250199Sgrehan * Copyright (c) 2012 Citrix Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29250199Sgrehan/*
30250199Sgrehan * VM Bus Driver Implementation
31250199Sgrehan */
32256276Sdim#include <sys/cdefs.h>
33256276Sdim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 300567 2016-05-24 05:18:26Z sephe $");
34250199Sgrehan
35250199Sgrehan#include <sys/param.h>
36250199Sgrehan#include <sys/bus.h>
37250199Sgrehan#include <sys/kernel.h>
38250199Sgrehan#include <sys/lock.h>
39250199Sgrehan#include <sys/malloc.h>
40250199Sgrehan#include <sys/module.h>
41293873Ssephe#include <sys/proc.h>
42250199Sgrehan#include <sys/sysctl.h>
43250199Sgrehan#include <sys/syslog.h>
44250199Sgrehan#include <sys/systm.h>
45250199Sgrehan#include <sys/rtprio.h>
46250199Sgrehan#include <sys/interrupt.h>
47250199Sgrehan#include <sys/sx.h>
48250199Sgrehan#include <sys/taskqueue.h>
49250199Sgrehan#include <sys/mutex.h>
50250199Sgrehan#include <sys/smp.h>
51250199Sgrehan
52250199Sgrehan#include <machine/resource.h>
53250199Sgrehan#include <sys/rman.h>
54250199Sgrehan
55250199Sgrehan#include <machine/stdarg.h>
56250199Sgrehan#include <machine/intr_machdep.h>
57282212Swhu#include <machine/md_var.h>
58282212Swhu#include <machine/segments.h>
59250199Sgrehan#include <sys/pcpu.h>
60282212Swhu#include <x86/apicvar.h>
61250199Sgrehan
62297142Ssephe#include <dev/hyperv/include/hyperv.h>
63300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
64300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
65250199Sgrehan
66293870Ssephe#include <contrib/dev/acpica/include/acpi.h>
67293870Ssephe#include "acpi_if.h"
68250199Sgrehan
69300102Ssephestruct vmbus_softc	*vmbus_sc;
70300102Ssephe
71250199Sgrehanstatic int vmbus_inited;
72255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */
73250199Sgrehan
74293870Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL };
75293870Ssephe
76250199Sgrehanstatic void
77300112Ssephevmbus_msg_task(void *arg __unused, int pending __unused)
78250199Sgrehan{
79300108Ssephe	hv_vmbus_message *msg;
80250199Sgrehan
81300481Ssephe	msg = hv_vmbus_g_context.syn_ic_msg_page[curcpu] +
82300108Ssephe	    HV_VMBUS_MESSAGE_SINT;
83300481Ssephe
84300108Ssephe	for (;;) {
85300108Ssephe		const hv_vmbus_channel_msg_table_entry *entry;
86300108Ssephe		hv_vmbus_channel_msg_header *hdr;
87300108Ssephe		hv_vmbus_channel_msg_type msg_type;
88282212Swhu
89292861Sdelphij		if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
90250199Sgrehan			break; /* no message */
91292861Sdelphij
92292861Sdelphij		hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
93292861Sdelphij		msg_type = hdr->message_type;
94292861Sdelphij
95295307Ssephe		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
96292861Sdelphij			printf("VMBUS: unknown message type = %d\n", msg_type);
97292861Sdelphij			goto handled;
98292861Sdelphij		}
99292861Sdelphij
100292861Sdelphij		entry = &g_channel_message_table[msg_type];
101295307Ssephe		if (entry->messageHandler)
102292861Sdelphij			entry->messageHandler(hdr);
103292861Sdelphijhandled:
104300108Ssephe		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
105300108Ssephe		/*
106300108Ssephe		 * Make sure the write to message_type (ie set to
107300108Ssephe		 * HV_MESSAGE_TYPE_NONE) happens before we read the
108300108Ssephe		 * message_pending and EOMing. Otherwise, the EOMing will
109300108Ssephe		 * not deliver any more messages
110300108Ssephe		 * since there is no empty slot
111300108Ssephe		 *
112300108Ssephe		 * NOTE:
113300108Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
114300108Ssephe		 * will become compiler fence on UP kernel.
115300108Ssephe		 */
116300108Ssephe		mb();
117300108Ssephe		if (msg->header.message_flags.u.message_pending) {
118250199Sgrehan			/*
119250199Sgrehan			 * This will cause message queue rescan to possibly
120250199Sgrehan			 * deliver another msg from the hypervisor
121250199Sgrehan			 */
122255414Sgrehan			wrmsr(HV_X64_MSR_EOM, 0);
123300108Ssephe		}
124250199Sgrehan	}
125250199Sgrehan}
126250199Sgrehan
127250199Sgrehan/**
128250199Sgrehan * @brief Interrupt filter routine for VMBUS.
129250199Sgrehan *
130250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol
131250199Sgrehan * message to process - an event or a channel message.
132250199Sgrehan */
133282212Swhustatic inline int
134300567Ssephehv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
135250199Sgrehan{
136300481Ssephe	hv_vmbus_message *msg, *msg_base;
137250199Sgrehan
138250199Sgrehan	/*
139250199Sgrehan	 * The Windows team has advised that we check for events
140250199Sgrehan	 * before checking for messages. This is the way they do it
141250199Sgrehan	 * in Windows when running as a guest in Hyper-V
142250199Sgrehan	 */
143300107Ssephe	sc->vmbus_event_proc(sc, cpu);
144250199Sgrehan
145250199Sgrehan	/* Check if there are actual msgs to be process */
146300481Ssephe	msg_base = hv_vmbus_g_context.syn_ic_msg_page[cpu];
147300481Ssephe	msg = msg_base + HV_VMBUS_TIMER_SINT;
148250199Sgrehan
149293873Ssephe	/* we call eventtimer process the message */
150293873Ssephe	if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) {
151293873Ssephe		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
152293873Ssephe
153297176Ssephe		/* call intrrupt handler of event timer */
154297176Ssephe		hv_et_intr(frame);
155297176Ssephe
156293873Ssephe		/*
157293873Ssephe		 * Make sure the write to message_type (ie set to
158293873Ssephe		 * HV_MESSAGE_TYPE_NONE) happens before we read the
159293873Ssephe		 * message_pending and EOMing. Otherwise, the EOMing will
160293873Ssephe		 * not deliver any more messages
161293873Ssephe		 * since there is no empty slot
162297634Ssephe		 *
163297634Ssephe		 * NOTE:
164297634Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
165297636Ssephe		 * will become compiler fence on UP kernel.
166293873Ssephe		 */
167297634Ssephe		mb();
168293873Ssephe
169293873Ssephe		if (msg->header.message_flags.u.message_pending) {
170293873Ssephe			/*
171293873Ssephe			 * This will cause message queue rescan to possibly
172293873Ssephe			 * deliver another msg from the hypervisor
173293873Ssephe			 */
174293873Ssephe			wrmsr(HV_X64_MSR_EOM, 0);
175293873Ssephe		}
176293873Ssephe	}
177293873Ssephe
178300481Ssephe	msg = msg_base + HV_VMBUS_MESSAGE_SINT;
179250199Sgrehan	if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) {
180297221Ssephe		taskqueue_enqueue(hv_vmbus_g_context.hv_msg_tq[cpu],
181297221Ssephe		    &hv_vmbus_g_context.hv_msg_task[cpu]);
182250199Sgrehan	}
183250199Sgrehan
184293873Ssephe	return (FILTER_HANDLED);
185250199Sgrehan}
186250199Sgrehan
187282212Swhuvoid
188282212Swhuhv_vector_handler(struct trapframe *trap_frame)
189282212Swhu{
190300565Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
191300565Ssephe	int cpu = curcpu;
192282212Swhu
193282212Swhu	/*
194282212Swhu	 * Disable preemption.
195282212Swhu	 */
196282212Swhu	critical_enter();
197282212Swhu
198282212Swhu	/*
199282212Swhu	 * Do a little interrupt counting.
200282212Swhu	 */
201300565Ssephe	(*VMBUS_SC_PCPU_GET(sc, intr_cnt, cpu))++;
202282212Swhu
203300567Ssephe	hv_vmbus_isr(sc, trap_frame, cpu);
204282212Swhu
205282212Swhu	/*
206282212Swhu	 * Enable preemption.
207282212Swhu	 */
208282212Swhu	critical_exit();
209282212Swhu}
210282212Swhu
211250199Sgrehanstatic int
212250199Sgrehanvmbus_read_ivar(
213250199Sgrehan	device_t	dev,
214250199Sgrehan	device_t	child,
215250199Sgrehan	int		index,
216250199Sgrehan	uintptr_t*	result)
217250199Sgrehan{
218250199Sgrehan	struct hv_device *child_dev_ctx = device_get_ivars(child);
219250199Sgrehan
220250199Sgrehan	switch (index) {
221250199Sgrehan
222250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
223250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->class_id;
224250199Sgrehan		return (0);
225250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
226250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->device_id;
227250199Sgrehan		return (0);
228250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
229250199Sgrehan		*result = (uintptr_t) child_dev_ctx;
230250199Sgrehan		return (0);
231250199Sgrehan	case HV_VMBUS_IVAR_NODE:
232250199Sgrehan		*result = (uintptr_t) child_dev_ctx->device;
233250199Sgrehan		return (0);
234250199Sgrehan	}
235250199Sgrehan	return (ENOENT);
236250199Sgrehan}
237250199Sgrehan
238250199Sgrehanstatic int
239250199Sgrehanvmbus_write_ivar(
240250199Sgrehan	device_t	dev,
241250199Sgrehan	device_t	child,
242250199Sgrehan	int		index,
243250199Sgrehan	uintptr_t	value)
244250199Sgrehan{
245250199Sgrehan	switch (index) {
246250199Sgrehan
247250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
248250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
249250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
250250199Sgrehan	case HV_VMBUS_IVAR_NODE:
251250199Sgrehan		/* read-only */
252250199Sgrehan		return (EINVAL);
253250199Sgrehan	}
254250199Sgrehan	return (ENOENT);
255250199Sgrehan}
256250199Sgrehan
257297143Ssephestatic int
258297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
259297143Ssephe{
260297143Ssephe	char guidbuf[40];
261297143Ssephe	struct hv_device *dev_ctx = device_get_ivars(child);
262297143Ssephe
263298449Ssephe	if (dev_ctx == NULL)
264298449Ssephe		return (0);
265298449Ssephe
266297143Ssephe	strlcat(buf, "classid=", buflen);
267297143Ssephe	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id);
268297143Ssephe	strlcat(buf, guidbuf, buflen);
269297143Ssephe
270297143Ssephe	strlcat(buf, " deviceid=", buflen);
271297143Ssephe	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id);
272297143Ssephe	strlcat(buf, guidbuf, buflen);
273297143Ssephe
274297143Ssephe	return (0);
275297143Ssephe}
276297143Ssephe
277250199Sgrehanstruct hv_device*
278250199Sgrehanhv_vmbus_child_device_create(
279250199Sgrehan	hv_guid		type,
280250199Sgrehan	hv_guid		instance,
281250199Sgrehan	hv_vmbus_channel*	channel)
282250199Sgrehan{
283250199Sgrehan	hv_device* child_dev;
284250199Sgrehan
285250199Sgrehan	/*
286250199Sgrehan	 * Allocate the new child device
287250199Sgrehan	 */
288250199Sgrehan	child_dev = malloc(sizeof(hv_device), M_DEVBUF,
289295308Ssephe			M_WAITOK |  M_ZERO);
290250199Sgrehan
291250199Sgrehan	child_dev->channel = channel;
292250199Sgrehan	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
293250199Sgrehan	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
294250199Sgrehan
295250199Sgrehan	return (child_dev);
296250199Sgrehan}
297250199Sgrehan
298297142Ssepheint
299297142Ssephesnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid)
300250199Sgrehan{
301297142Ssephe	int cnt;
302297142Ssephe	const unsigned char *d = guid->data;
303297142Ssephe
304297142Ssephe	cnt = snprintf(buf, sz,
305297142Ssephe		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
306297142Ssephe		d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
307297142Ssephe		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
308297142Ssephe	return (cnt);
309250199Sgrehan}
310250199Sgrehan
311250199Sgrehanint
312250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev)
313250199Sgrehan{
314250199Sgrehan	device_t child;
315250199Sgrehan
316297142Ssephe	if (bootverbose) {
317297142Ssephe		char name[40];
318297142Ssephe		snprintf_hv_guid(name, sizeof(name), &child_dev->class_id);
319297142Ssephe		printf("VMBUS: Class ID: %s\n", name);
320297142Ssephe	}
321250199Sgrehan
322300486Ssephe	child = device_add_child(vmbus_get_device(), NULL, -1);
323250199Sgrehan	child_dev->device = child;
324250199Sgrehan	device_set_ivars(child, child_dev);
325250199Sgrehan
326250199Sgrehan	return (0);
327250199Sgrehan}
328250199Sgrehan
329250199Sgrehanint
330250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev)
331250199Sgrehan{
332250199Sgrehan	int ret = 0;
333250199Sgrehan	/*
334250199Sgrehan	 * XXXKYS: Ensure that this is the opposite of
335250199Sgrehan	 * device_add_child()
336250199Sgrehan	 */
337250199Sgrehan	mtx_lock(&Giant);
338300486Ssephe	ret = device_delete_child(vmbus_get_device(), child_dev->device);
339250199Sgrehan	mtx_unlock(&Giant);
340250199Sgrehan	return(ret);
341250199Sgrehan}
342250199Sgrehan
343250199Sgrehanstatic int
344300127Ssephevmbus_probe(device_t dev)
345300127Ssephe{
346293870Ssephe	if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL ||
347300480Ssephe	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV)
348293870Ssephe		return (ENXIO);
349250199Sgrehan
350300129Ssephe	device_set_desc(dev, "Hyper-V Vmbus");
351250199Sgrehan
352293870Ssephe	return (BUS_PROBE_DEFAULT);
353250199Sgrehan}
354250199Sgrehan
355297641Ssepheextern inthand_t IDTVEC(hv_vmbus_callback);
356282212Swhu
357282212Swhu/**
358250199Sgrehan * @brief Main vmbus driver initialization routine.
359250199Sgrehan *
360250199Sgrehan * Here, we
361250199Sgrehan * - initialize the vmbus driver context
362250199Sgrehan * - setup various driver entry points
363250199Sgrehan * - invoke the vmbus hv main init routine
364250199Sgrehan * - get the irq resource
365250199Sgrehan * - invoke the vmbus to add the vmbus root device
366250199Sgrehan * - setup the vmbus root device
367250199Sgrehan * - retrieve the channel offers
368250199Sgrehan */
369250199Sgrehanstatic int
370250199Sgrehanvmbus_bus_init(void)
371250199Sgrehan{
372300107Ssephe	struct vmbus_softc *sc;
373282212Swhu	int i, j, n, ret;
374293874Ssephe	char buf[MAXCOMLEN + 1];
375294886Ssephe	cpuset_t cpu_mask;
376250199Sgrehan
377250199Sgrehan	if (vmbus_inited)
378250199Sgrehan		return (0);
379250199Sgrehan
380250199Sgrehan	vmbus_inited = 1;
381300107Ssephe	sc = vmbus_get_softc();
382250199Sgrehan
383250199Sgrehan	/*
384300487Ssephe	 * Find a free IDT vector for vmbus messages/events.
385250199Sgrehan	 */
386300487Ssephe	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback));
387300487Ssephe	if (sc->vmbus_idtvec < 0) {
388300487Ssephe		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
389300480Ssephe		ret = ENXIO;
390282212Swhu		goto cleanup;
391282212Swhu	}
392300487Ssephe	if(bootverbose) {
393300487Ssephe		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
394300487Ssephe		    sc->vmbus_idtvec);
395300487Ssephe	}
396250199Sgrehan
397282212Swhu	CPU_FOREACH(j) {
398293874Ssephe		snprintf(buf, sizeof(buf), "cpu%d:hyperv", j);
399300565Ssephe		intrcnt_add(buf, VMBUS_SC_PCPU_PTR(sc, intr_cnt, j));
400293874Ssephe
401282212Swhu		for (i = 0; i < 2; i++)
402282212Swhu			setup_args.page_buffers[2 * j + i] = NULL;
403250199Sgrehan	}
404250199Sgrehan
405250199Sgrehan	/*
406282212Swhu	 * Per cpu setup.
407250199Sgrehan	 */
408282212Swhu	CPU_FOREACH(j) {
409282212Swhu		/*
410294886Ssephe		 * Setup taskqueue to handle events
411294886Ssephe		 */
412294886Ssephe		hv_vmbus_g_context.hv_event_queue[j] = taskqueue_create_fast("hyperv event", M_WAITOK,
413294886Ssephe			taskqueue_thread_enqueue, &hv_vmbus_g_context.hv_event_queue[j]);
414294886Ssephe		CPU_SETOF(j, &cpu_mask);
415294886Ssephe		taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_event_queue[j], 1, PI_NET, &cpu_mask,
416294886Ssephe			"hvevent%d", j);
417294886Ssephe
418294886Ssephe		/*
419297221Ssephe		 * Setup per-cpu tasks and taskqueues to handle msg.
420282212Swhu		 */
421297221Ssephe		hv_vmbus_g_context.hv_msg_tq[j] = taskqueue_create_fast(
422297221Ssephe		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
423297221Ssephe		    &hv_vmbus_g_context.hv_msg_tq[j]);
424297221Ssephe		CPU_SETOF(j, &cpu_mask);
425297221Ssephe		taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_msg_tq[j],
426297221Ssephe		    1, PI_NET, &cpu_mask, "hvmsg%d", j);
427297221Ssephe		TASK_INIT(&hv_vmbus_g_context.hv_msg_task[j], 0,
428300112Ssephe		    vmbus_msg_task, NULL);
429297221Ssephe
430282212Swhu		/*
431282212Swhu		 * Prepare the per cpu msg and event pages to be called on each cpu.
432282212Swhu		 */
433282212Swhu		for(i = 0; i < 2; i++) {
434282212Swhu			setup_args.page_buffers[2 * j + i] =
435295308Ssephe				malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
436255414Sgrehan		}
437255414Sgrehan	}
438250199Sgrehan
439282212Swhu	if (bootverbose)
440282212Swhu		printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n",
441282212Swhu		    smp_started);
442282212Swhu
443255414Sgrehan	smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args);
444250199Sgrehan
445255414Sgrehan	/*
446250199Sgrehan	 * Connect to VMBus in the root partition
447250199Sgrehan	 */
448250199Sgrehan	ret = hv_vmbus_connect();
449250199Sgrehan
450255414Sgrehan	if (ret != 0)
451282212Swhu		goto cleanup1;
452250199Sgrehan
453300107Ssephe	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
454300107Ssephe	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
455300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc_compat;
456300107Ssephe	else
457300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc;
458300107Ssephe
459250199Sgrehan	hv_vmbus_request_channel_offers();
460298260Ssephe
461298260Ssephe	vmbus_scan();
462300486Ssephe	bus_generic_attach(sc->vmbus_dev);
463300486Ssephe	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
464298260Ssephe
465250199Sgrehan	return (ret);
466250199Sgrehan
467282212Swhu	cleanup1:
468282212Swhu	/*
469282212Swhu	 * Free pages alloc'ed
470282212Swhu	 */
471282212Swhu	for (n = 0; n < 2 * MAXCPU; n++)
472282212Swhu		if (setup_args.page_buffers[n] != NULL)
473282212Swhu			free(setup_args.page_buffers[n], M_DEVBUF);
474250199Sgrehan
475250199Sgrehan	/*
476282212Swhu	 * remove swi and vmbus callback vector;
477250199Sgrehan	 */
478282212Swhu	CPU_FOREACH(j) {
479297177Ssephe		if (hv_vmbus_g_context.hv_event_queue[j] != NULL) {
480294886Ssephe			taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]);
481297177Ssephe			hv_vmbus_g_context.hv_event_queue[j] = NULL;
482297177Ssephe		}
483282212Swhu	}
484250199Sgrehan
485300487Ssephe	lapic_ipi_free(sc->vmbus_idtvec);
486250199Sgrehan
487250199Sgrehan	cleanup:
488250199Sgrehan	return (ret);
489250199Sgrehan}
490250199Sgrehan
491300107Ssephestatic void
492300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
493300107Ssephe{
494300107Ssephe}
495300107Ssephe
496250199Sgrehanstatic int
497250199Sgrehanvmbus_attach(device_t dev)
498250199Sgrehan{
499300102Ssephe	vmbus_sc = device_get_softc(dev);
500300486Ssephe	vmbus_sc->vmbus_dev = dev;
501250199Sgrehan
502300107Ssephe	/*
503300107Ssephe	 * Event processing logic will be configured:
504300107Ssephe	 * - After the vmbus protocol version negotiation.
505300107Ssephe	 * - Before we request channel offers.
506300107Ssephe	 */
507300107Ssephe	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
508300107Ssephe
509299746Sjhb#ifndef EARLY_AP_STARTUP
510250199Sgrehan	/*
511250199Sgrehan	 * If the system has already booted and thread
512250199Sgrehan	 * scheduling is possible indicated by the global
513250199Sgrehan	 * cold set to zero, we just call the driver
514250199Sgrehan	 * initialization directly.
515250199Sgrehan	 */
516250199Sgrehan	if (!cold)
517299746Sjhb#endif
518250199Sgrehan		vmbus_bus_init();
519250199Sgrehan
520298449Ssephe	bus_generic_probe(dev);
521250199Sgrehan	return (0);
522250199Sgrehan}
523250199Sgrehan
524250199Sgrehanstatic void
525300126Ssephevmbus_sysinit(void *arg __unused)
526250199Sgrehan{
527300102Ssephe	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
528256425Sgibbs		return;
529256425Sgibbs
530299746Sjhb#ifndef EARLY_AP_STARTUP
531250199Sgrehan	/*
532250199Sgrehan	 * If the system has already booted and thread
533256425Sgibbs	 * scheduling is possible, as indicated by the
534256425Sgibbs	 * global cold set to zero, we just call the driver
535250199Sgrehan	 * initialization directly.
536250199Sgrehan	 */
537250199Sgrehan	if (!cold)
538299746Sjhb#endif
539250199Sgrehan		vmbus_bus_init();
540250199Sgrehan}
541250199Sgrehan
542300121Ssephestatic int
543300121Ssephevmbus_detach(device_t dev)
544250199Sgrehan{
545300487Ssephe	struct vmbus_softc *sc = device_get_softc(dev);
546255414Sgrehan	int i;
547255414Sgrehan
548250199Sgrehan	hv_vmbus_release_unattached_channels();
549250199Sgrehan	hv_vmbus_disconnect();
550250199Sgrehan
551250199Sgrehan	smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL);
552250199Sgrehan
553282212Swhu	for(i = 0; i < 2 * MAXCPU; i++) {
554297841Ssephe		if (setup_args.page_buffers[i] != NULL)
555255414Sgrehan			free(setup_args.page_buffers[i], M_DEVBUF);
556255414Sgrehan	}
557255414Sgrehan
558282212Swhu	/* remove swi */
559282212Swhu	CPU_FOREACH(i) {
560297177Ssephe		if (hv_vmbus_g_context.hv_event_queue[i] != NULL) {
561294886Ssephe			taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]);
562297177Ssephe			hv_vmbus_g_context.hv_event_queue[i] = NULL;
563297177Ssephe		}
564282212Swhu	}
565250199Sgrehan
566300487Ssephe	lapic_ipi_free(sc->vmbus_idtvec);
567250199Sgrehan
568250199Sgrehan	return (0);
569250199Sgrehan}
570250199Sgrehan
571250199Sgrehanstatic device_method_t vmbus_methods[] = {
572300124Ssephe	/* Device interface */
573300124Ssephe	DEVMETHOD(device_probe,			vmbus_probe),
574300124Ssephe	DEVMETHOD(device_attach,		vmbus_attach),
575300124Ssephe	DEVMETHOD(device_detach,		vmbus_detach),
576300124Ssephe	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
577300124Ssephe	DEVMETHOD(device_suspend,		bus_generic_suspend),
578300124Ssephe	DEVMETHOD(device_resume,		bus_generic_resume),
579250199Sgrehan
580300124Ssephe	/* Bus interface */
581300124Ssephe	DEVMETHOD(bus_add_child,		bus_generic_add_child),
582300124Ssephe	DEVMETHOD(bus_print_child,		bus_generic_print_child),
583300124Ssephe	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
584300124Ssephe	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
585300124Ssephe	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
586250199Sgrehan
587300124Ssephe	DEVMETHOD_END
588300124Ssephe};
589250199Sgrehan
590300102Ssephestatic driver_t vmbus_driver = {
591300102Ssephe	"vmbus",
592300102Ssephe	vmbus_methods,
593300102Ssephe	sizeof(struct vmbus_softc)
594300102Ssephe};
595250199Sgrehan
596300123Ssephestatic devclass_t vmbus_devclass;
597250199Sgrehan
598300120SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
599293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1);
600293870SsepheMODULE_VERSION(vmbus, 1);
601250199Sgrehan
602299746Sjhb#ifndef EARLY_AP_STARTUP
603300126Ssephe/*
604300126Ssephe * NOTE:
605300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
606300126Ssephe * initialized.
607300126Ssephe */
608300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
609299746Sjhb#endif
610