vmbus.c revision 300107
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 300107 2016-05-18 03:41:37Z 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 device_t vmbus_devp;
72250199Sgrehanstatic int vmbus_inited;
73255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */
74250199Sgrehan
75293870Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL };
76293870Ssephe
77250199Sgrehan/**
78250199Sgrehan * @brief Software interrupt thread routine to handle channel messages from
79250199Sgrehan * the hypervisor.
80250199Sgrehan */
81250199Sgrehanstatic void
82297177Ssephevmbus_msg_swintr(void *arg, int pending __unused)
83250199Sgrehan{
84250199Sgrehan	int 			cpu;
85250199Sgrehan	void*			page_addr;
86292861Sdelphij	hv_vmbus_channel_msg_header	 *hdr;
87292861Sdelphij	hv_vmbus_channel_msg_table_entry *entry;
88292861Sdelphij	hv_vmbus_channel_msg_type msg_type;
89250199Sgrehan	hv_vmbus_message*	msg;
90250199Sgrehan
91282212Swhu	cpu = (int)(long)arg;
92282212Swhu	KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: "
93282212Swhu	    "cpu out of range!"));
94282212Swhu
95250199Sgrehan	page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu];
96250199Sgrehan	msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT;
97250199Sgrehan
98250199Sgrehan	for (;;) {
99292861Sdelphij		if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
100250199Sgrehan			break; /* no message */
101292861Sdelphij
102292861Sdelphij		hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
103292861Sdelphij		msg_type = hdr->message_type;
104292861Sdelphij
105295307Ssephe		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
106292861Sdelphij			printf("VMBUS: unknown message type = %d\n", msg_type);
107292861Sdelphij			goto handled;
108292861Sdelphij		}
109292861Sdelphij
110292861Sdelphij		entry = &g_channel_message_table[msg_type];
111292861Sdelphij
112295307Ssephe		if (entry->messageHandler)
113292861Sdelphij			entry->messageHandler(hdr);
114292861Sdelphijhandled:
115250199Sgrehan	    msg->header.message_type = HV_MESSAGE_TYPE_NONE;
116250199Sgrehan
117250199Sgrehan	    /*
118250199Sgrehan	     * Make sure the write to message_type (ie set to
119250199Sgrehan	     * HV_MESSAGE_TYPE_NONE) happens before we read the
120250199Sgrehan	     * message_pending and EOMing. Otherwise, the EOMing will
121250199Sgrehan	     * not deliver any more messages
122250199Sgrehan	     * since there is no empty slot
123297634Ssephe	     *
124297634Ssephe	     * NOTE:
125297634Ssephe	     * mb() is used here, since atomic_thread_fence_seq_cst()
126297636Ssephe	     * will become compiler fence on UP kernel.
127250199Sgrehan	     */
128297634Ssephe	    mb();
129250199Sgrehan
130256276Sdim	    if (msg->header.message_flags.u.message_pending) {
131250199Sgrehan			/*
132250199Sgrehan			 * This will cause message queue rescan to possibly
133250199Sgrehan			 * deliver another msg from the hypervisor
134250199Sgrehan			 */
135255414Sgrehan			wrmsr(HV_X64_MSR_EOM, 0);
136250199Sgrehan	    }
137250199Sgrehan	}
138250199Sgrehan}
139250199Sgrehan
140250199Sgrehan/**
141250199Sgrehan * @brief Interrupt filter routine for VMBUS.
142250199Sgrehan *
143250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol
144250199Sgrehan * message to process - an event or a channel message.
145250199Sgrehan */
146282212Swhustatic inline int
147293873Ssephehv_vmbus_isr(struct trapframe *frame)
148250199Sgrehan{
149300107Ssephe	struct vmbus_softc *sc = vmbus_get_softc();
150250199Sgrehan	int				cpu;
151250199Sgrehan	hv_vmbus_message*		msg;
152250199Sgrehan	void*				page_addr;
153250199Sgrehan
154250199Sgrehan	cpu = PCPU_GET(cpuid);
155250199Sgrehan
156250199Sgrehan	/*
157250199Sgrehan	 * The Windows team has advised that we check for events
158250199Sgrehan	 * before checking for messages. This is the way they do it
159250199Sgrehan	 * in Windows when running as a guest in Hyper-V
160250199Sgrehan	 */
161300107Ssephe	sc->vmbus_event_proc(sc, cpu);
162250199Sgrehan
163250199Sgrehan	/* Check if there are actual msgs to be process */
164250199Sgrehan	page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu];
165297176Ssephe	msg = (hv_vmbus_message*) page_addr + HV_VMBUS_TIMER_SINT;
166250199Sgrehan
167293873Ssephe	/* we call eventtimer process the message */
168293873Ssephe	if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) {
169293873Ssephe		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
170293873Ssephe
171297176Ssephe		/* call intrrupt handler of event timer */
172297176Ssephe		hv_et_intr(frame);
173297176Ssephe
174293873Ssephe		/*
175293873Ssephe		 * Make sure the write to message_type (ie set to
176293873Ssephe		 * HV_MESSAGE_TYPE_NONE) happens before we read the
177293873Ssephe		 * message_pending and EOMing. Otherwise, the EOMing will
178293873Ssephe		 * not deliver any more messages
179293873Ssephe		 * since there is no empty slot
180297634Ssephe		 *
181297634Ssephe		 * NOTE:
182297634Ssephe		 * mb() is used here, since atomic_thread_fence_seq_cst()
183297636Ssephe		 * will become compiler fence on UP kernel.
184293873Ssephe		 */
185297634Ssephe		mb();
186293873Ssephe
187293873Ssephe		if (msg->header.message_flags.u.message_pending) {
188293873Ssephe			/*
189293873Ssephe			 * This will cause message queue rescan to possibly
190293873Ssephe			 * deliver another msg from the hypervisor
191293873Ssephe			 */
192293873Ssephe			wrmsr(HV_X64_MSR_EOM, 0);
193293873Ssephe		}
194293873Ssephe	}
195293873Ssephe
196297176Ssephe	msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT;
197250199Sgrehan	if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) {
198297221Ssephe		taskqueue_enqueue(hv_vmbus_g_context.hv_msg_tq[cpu],
199297221Ssephe		    &hv_vmbus_g_context.hv_msg_task[cpu]);
200250199Sgrehan	}
201250199Sgrehan
202293873Ssephe	return (FILTER_HANDLED);
203250199Sgrehan}
204250199Sgrehan
205293874Ssepheu_long *hv_vmbus_intr_cpu[MAXCPU];
206282212Swhu
207282212Swhuvoid
208282212Swhuhv_vector_handler(struct trapframe *trap_frame)
209282212Swhu{
210282212Swhu	int cpu;
211282212Swhu
212282212Swhu	/*
213282212Swhu	 * Disable preemption.
214282212Swhu	 */
215282212Swhu	critical_enter();
216282212Swhu
217282212Swhu	/*
218282212Swhu	 * Do a little interrupt counting.
219282212Swhu	 */
220282212Swhu	cpu = PCPU_GET(cpuid);
221293874Ssephe	(*hv_vmbus_intr_cpu[cpu])++;
222282212Swhu
223293873Ssephe	hv_vmbus_isr(trap_frame);
224282212Swhu
225282212Swhu	/*
226282212Swhu	 * Enable preemption.
227282212Swhu	 */
228282212Swhu	critical_exit();
229282212Swhu}
230282212Swhu
231250199Sgrehanstatic int
232250199Sgrehanvmbus_read_ivar(
233250199Sgrehan	device_t	dev,
234250199Sgrehan	device_t	child,
235250199Sgrehan	int		index,
236250199Sgrehan	uintptr_t*	result)
237250199Sgrehan{
238250199Sgrehan	struct hv_device *child_dev_ctx = device_get_ivars(child);
239250199Sgrehan
240250199Sgrehan	switch (index) {
241250199Sgrehan
242250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
243250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->class_id;
244250199Sgrehan		return (0);
245250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
246250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->device_id;
247250199Sgrehan		return (0);
248250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
249250199Sgrehan		*result = (uintptr_t) child_dev_ctx;
250250199Sgrehan		return (0);
251250199Sgrehan	case HV_VMBUS_IVAR_NODE:
252250199Sgrehan		*result = (uintptr_t) child_dev_ctx->device;
253250199Sgrehan		return (0);
254250199Sgrehan	}
255250199Sgrehan	return (ENOENT);
256250199Sgrehan}
257250199Sgrehan
258250199Sgrehanstatic int
259250199Sgrehanvmbus_write_ivar(
260250199Sgrehan	device_t	dev,
261250199Sgrehan	device_t	child,
262250199Sgrehan	int		index,
263250199Sgrehan	uintptr_t	value)
264250199Sgrehan{
265250199Sgrehan	switch (index) {
266250199Sgrehan
267250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
268250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
269250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
270250199Sgrehan	case HV_VMBUS_IVAR_NODE:
271250199Sgrehan		/* read-only */
272250199Sgrehan		return (EINVAL);
273250199Sgrehan	}
274250199Sgrehan	return (ENOENT);
275250199Sgrehan}
276250199Sgrehan
277297143Ssephestatic int
278297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
279297143Ssephe{
280297143Ssephe	char guidbuf[40];
281297143Ssephe	struct hv_device *dev_ctx = device_get_ivars(child);
282297143Ssephe
283298449Ssephe	if (dev_ctx == NULL)
284298449Ssephe		return (0);
285298449Ssephe
286297143Ssephe	strlcat(buf, "classid=", buflen);
287297143Ssephe	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id);
288297143Ssephe	strlcat(buf, guidbuf, buflen);
289297143Ssephe
290297143Ssephe	strlcat(buf, " deviceid=", buflen);
291297143Ssephe	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id);
292297143Ssephe	strlcat(buf, guidbuf, buflen);
293297143Ssephe
294297143Ssephe	return (0);
295297143Ssephe}
296297143Ssephe
297250199Sgrehanstruct hv_device*
298250199Sgrehanhv_vmbus_child_device_create(
299250199Sgrehan	hv_guid		type,
300250199Sgrehan	hv_guid		instance,
301250199Sgrehan	hv_vmbus_channel*	channel)
302250199Sgrehan{
303250199Sgrehan	hv_device* child_dev;
304250199Sgrehan
305250199Sgrehan	/*
306250199Sgrehan	 * Allocate the new child device
307250199Sgrehan	 */
308250199Sgrehan	child_dev = malloc(sizeof(hv_device), M_DEVBUF,
309295308Ssephe			M_WAITOK |  M_ZERO);
310250199Sgrehan
311250199Sgrehan	child_dev->channel = channel;
312250199Sgrehan	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
313250199Sgrehan	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
314250199Sgrehan
315250199Sgrehan	return (child_dev);
316250199Sgrehan}
317250199Sgrehan
318297142Ssepheint
319297142Ssephesnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid)
320250199Sgrehan{
321297142Ssephe	int cnt;
322297142Ssephe	const unsigned char *d = guid->data;
323297142Ssephe
324297142Ssephe	cnt = snprintf(buf, sz,
325297142Ssephe		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
326297142Ssephe		d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
327297142Ssephe		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
328297142Ssephe	return (cnt);
329250199Sgrehan}
330250199Sgrehan
331250199Sgrehanint
332250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev)
333250199Sgrehan{
334250199Sgrehan	device_t child;
335250199Sgrehan
336297142Ssephe	if (bootverbose) {
337297142Ssephe		char name[40];
338297142Ssephe		snprintf_hv_guid(name, sizeof(name), &child_dev->class_id);
339297142Ssephe		printf("VMBUS: Class ID: %s\n", name);
340297142Ssephe	}
341250199Sgrehan
342250199Sgrehan	child = device_add_child(vmbus_devp, NULL, -1);
343250199Sgrehan	child_dev->device = child;
344250199Sgrehan	device_set_ivars(child, child_dev);
345250199Sgrehan
346250199Sgrehan	return (0);
347250199Sgrehan}
348250199Sgrehan
349250199Sgrehanint
350250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev)
351250199Sgrehan{
352250199Sgrehan	int ret = 0;
353250199Sgrehan	/*
354250199Sgrehan	 * XXXKYS: Ensure that this is the opposite of
355250199Sgrehan	 * device_add_child()
356250199Sgrehan	 */
357250199Sgrehan	mtx_lock(&Giant);
358250199Sgrehan	ret = device_delete_child(vmbus_devp, child_dev->device);
359250199Sgrehan	mtx_unlock(&Giant);
360250199Sgrehan	return(ret);
361250199Sgrehan}
362250199Sgrehan
363250199Sgrehanstatic int
364250199Sgrehanvmbus_probe(device_t dev) {
365293870Ssephe	if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL ||
366293870Ssephe	    device_get_unit(dev) != 0)
367293870Ssephe		return (ENXIO);
368250199Sgrehan
369250199Sgrehan	device_set_desc(dev, "Vmbus Devices");
370250199Sgrehan
371293870Ssephe	return (BUS_PROBE_DEFAULT);
372250199Sgrehan}
373250199Sgrehan
374297641Ssepheextern inthand_t IDTVEC(hv_vmbus_callback);
375282212Swhu
376282212Swhu/**
377250199Sgrehan * @brief Main vmbus driver initialization routine.
378250199Sgrehan *
379250199Sgrehan * Here, we
380250199Sgrehan * - initialize the vmbus driver context
381250199Sgrehan * - setup various driver entry points
382250199Sgrehan * - invoke the vmbus hv main init routine
383250199Sgrehan * - get the irq resource
384250199Sgrehan * - invoke the vmbus to add the vmbus root device
385250199Sgrehan * - setup the vmbus root device
386250199Sgrehan * - retrieve the channel offers
387250199Sgrehan */
388250199Sgrehanstatic int
389250199Sgrehanvmbus_bus_init(void)
390250199Sgrehan{
391300107Ssephe	struct vmbus_softc *sc;
392282212Swhu	int i, j, n, ret;
393293874Ssephe	char buf[MAXCOMLEN + 1];
394294886Ssephe	cpuset_t cpu_mask;
395250199Sgrehan
396250199Sgrehan	if (vmbus_inited)
397250199Sgrehan		return (0);
398250199Sgrehan
399250199Sgrehan	vmbus_inited = 1;
400300107Ssephe	sc = vmbus_get_softc();
401250199Sgrehan
402250199Sgrehan	ret = hv_vmbus_init();
403250199Sgrehan
404250199Sgrehan	if (ret) {
405250199Sgrehan		if(bootverbose)
406252645Sgrehan			printf("Error VMBUS: Hypervisor Initialization Failed!\n");
407250199Sgrehan		return (ret);
408250199Sgrehan	}
409250199Sgrehan
410250199Sgrehan	/*
411282212Swhu	 * Find a free IDT slot for vmbus callback.
412250199Sgrehan	 */
413297641Ssephe	hv_vmbus_g_context.hv_cb_vector = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback));
414297641Ssephe	if (hv_vmbus_g_context.hv_cb_vector < 0) {
415282212Swhu		if(bootverbose)
416282212Swhu			printf("Error VMBUS: Cannot find free IDT slot for "
417282212Swhu			    "vmbus callback!\n");
418282212Swhu		goto cleanup;
419282212Swhu	}
420250199Sgrehan
421282212Swhu	if(bootverbose)
422282212Swhu		printf("VMBUS: vmbus callback vector %d\n",
423282212Swhu		    hv_vmbus_g_context.hv_cb_vector);
424250199Sgrehan
425282212Swhu	/*
426282212Swhu	 * Notify the hypervisor of our vector.
427282212Swhu	 */
428282212Swhu	setup_args.vector = hv_vmbus_g_context.hv_cb_vector;
429250199Sgrehan
430282212Swhu	CPU_FOREACH(j) {
431293874Ssephe		snprintf(buf, sizeof(buf), "cpu%d:hyperv", j);
432293874Ssephe		intrcnt_add(buf, &hv_vmbus_intr_cpu[j]);
433293874Ssephe
434282212Swhu		for (i = 0; i < 2; i++)
435282212Swhu			setup_args.page_buffers[2 * j + i] = NULL;
436250199Sgrehan	}
437250199Sgrehan
438250199Sgrehan	/*
439282212Swhu	 * Per cpu setup.
440250199Sgrehan	 */
441282212Swhu	CPU_FOREACH(j) {
442282212Swhu		/*
443294886Ssephe		 * Setup taskqueue to handle events
444294886Ssephe		 */
445294886Ssephe		hv_vmbus_g_context.hv_event_queue[j] = taskqueue_create_fast("hyperv event", M_WAITOK,
446294886Ssephe			taskqueue_thread_enqueue, &hv_vmbus_g_context.hv_event_queue[j]);
447294886Ssephe		CPU_SETOF(j, &cpu_mask);
448294886Ssephe		taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_event_queue[j], 1, PI_NET, &cpu_mask,
449294886Ssephe			"hvevent%d", j);
450294886Ssephe
451294886Ssephe		/*
452297221Ssephe		 * Setup per-cpu tasks and taskqueues to handle msg.
453282212Swhu		 */
454297221Ssephe		hv_vmbus_g_context.hv_msg_tq[j] = taskqueue_create_fast(
455297221Ssephe		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
456297221Ssephe		    &hv_vmbus_g_context.hv_msg_tq[j]);
457297221Ssephe		CPU_SETOF(j, &cpu_mask);
458297221Ssephe		taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_msg_tq[j],
459297221Ssephe		    1, PI_NET, &cpu_mask, "hvmsg%d", j);
460297221Ssephe		TASK_INIT(&hv_vmbus_g_context.hv_msg_task[j], 0,
461297221Ssephe		    vmbus_msg_swintr, (void *)(long)j);
462297221Ssephe
463282212Swhu		/*
464282212Swhu		 * Prepare the per cpu msg and event pages to be called on each cpu.
465282212Swhu		 */
466282212Swhu		for(i = 0; i < 2; i++) {
467282212Swhu			setup_args.page_buffers[2 * j + i] =
468295308Ssephe				malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
469255414Sgrehan		}
470255414Sgrehan	}
471250199Sgrehan
472282212Swhu	if (bootverbose)
473282212Swhu		printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n",
474282212Swhu		    smp_started);
475282212Swhu
476255414Sgrehan	smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args);
477250199Sgrehan
478255414Sgrehan	/*
479250199Sgrehan	 * Connect to VMBus in the root partition
480250199Sgrehan	 */
481250199Sgrehan	ret = hv_vmbus_connect();
482250199Sgrehan
483255414Sgrehan	if (ret != 0)
484282212Swhu		goto cleanup1;
485250199Sgrehan
486300107Ssephe	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
487300107Ssephe	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
488300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc_compat;
489300107Ssephe	else
490300107Ssephe		sc->vmbus_event_proc = vmbus_event_proc;
491300107Ssephe
492250199Sgrehan	hv_vmbus_request_channel_offers();
493298260Ssephe
494298260Ssephe	vmbus_scan();
495298260Ssephe	bus_generic_attach(vmbus_devp);
496298260Ssephe	device_printf(vmbus_devp, "device scan, probe and attach done\n");
497298260Ssephe
498250199Sgrehan	return (ret);
499250199Sgrehan
500282212Swhu	cleanup1:
501282212Swhu	/*
502282212Swhu	 * Free pages alloc'ed
503282212Swhu	 */
504282212Swhu	for (n = 0; n < 2 * MAXCPU; n++)
505282212Swhu		if (setup_args.page_buffers[n] != NULL)
506282212Swhu			free(setup_args.page_buffers[n], M_DEVBUF);
507250199Sgrehan
508250199Sgrehan	/*
509282212Swhu	 * remove swi and vmbus callback vector;
510250199Sgrehan	 */
511282212Swhu	CPU_FOREACH(j) {
512297177Ssephe		if (hv_vmbus_g_context.hv_event_queue[j] != NULL) {
513294886Ssephe			taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]);
514297177Ssephe			hv_vmbus_g_context.hv_event_queue[j] = NULL;
515297177Ssephe		}
516282212Swhu	}
517250199Sgrehan
518297641Ssephe	lapic_ipi_free(hv_vmbus_g_context.hv_cb_vector);
519250199Sgrehan
520250199Sgrehan	cleanup:
521250199Sgrehan	hv_vmbus_cleanup();
522250199Sgrehan
523250199Sgrehan	return (ret);
524250199Sgrehan}
525250199Sgrehan
526300107Ssephestatic void
527300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
528300107Ssephe{
529300107Ssephe}
530300107Ssephe
531250199Sgrehanstatic int
532250199Sgrehanvmbus_attach(device_t dev)
533250199Sgrehan{
534250199Sgrehan	if(bootverbose)
535250199Sgrehan		device_printf(dev, "VMBUS: attach dev: %p\n", dev);
536300102Ssephe
537250199Sgrehan	vmbus_devp = dev;
538300102Ssephe	vmbus_sc = device_get_softc(dev);
539250199Sgrehan
540300107Ssephe	/*
541300107Ssephe	 * Event processing logic will be configured:
542300107Ssephe	 * - After the vmbus protocol version negotiation.
543300107Ssephe	 * - Before we request channel offers.
544300107Ssephe	 */
545300107Ssephe	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
546300107Ssephe
547299746Sjhb#ifndef EARLY_AP_STARTUP
548250199Sgrehan	/*
549250199Sgrehan	 * If the system has already booted and thread
550250199Sgrehan	 * scheduling is possible indicated by the global
551250199Sgrehan	 * cold set to zero, we just call the driver
552250199Sgrehan	 * initialization directly.
553250199Sgrehan	 */
554250199Sgrehan	if (!cold)
555299746Sjhb#endif
556250199Sgrehan		vmbus_bus_init();
557250199Sgrehan
558298449Ssephe	bus_generic_probe(dev);
559250199Sgrehan	return (0);
560250199Sgrehan}
561250199Sgrehan
562250199Sgrehanstatic void
563250199Sgrehanvmbus_init(void)
564250199Sgrehan{
565300102Ssephe	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
566256425Sgibbs		return;
567256425Sgibbs
568299746Sjhb#ifndef EARLY_AP_STARTUP
569250199Sgrehan	/*
570250199Sgrehan	 * If the system has already booted and thread
571256425Sgibbs	 * scheduling is possible, as indicated by the
572256425Sgibbs	 * global cold set to zero, we just call the driver
573250199Sgrehan	 * initialization directly.
574250199Sgrehan	 */
575250199Sgrehan	if (!cold)
576299746Sjhb#endif
577250199Sgrehan		vmbus_bus_init();
578250199Sgrehan}
579250199Sgrehan
580250199Sgrehanstatic void
581250199Sgrehanvmbus_bus_exit(void)
582250199Sgrehan{
583255414Sgrehan	int i;
584255414Sgrehan
585250199Sgrehan	hv_vmbus_release_unattached_channels();
586250199Sgrehan	hv_vmbus_disconnect();
587250199Sgrehan
588250199Sgrehan	smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL);
589250199Sgrehan
590282212Swhu	for(i = 0; i < 2 * MAXCPU; i++) {
591297841Ssephe		if (setup_args.page_buffers[i] != NULL)
592255414Sgrehan			free(setup_args.page_buffers[i], M_DEVBUF);
593255414Sgrehan	}
594255414Sgrehan
595250199Sgrehan	hv_vmbus_cleanup();
596250199Sgrehan
597282212Swhu	/* remove swi */
598282212Swhu	CPU_FOREACH(i) {
599297177Ssephe		if (hv_vmbus_g_context.hv_event_queue[i] != NULL) {
600294886Ssephe			taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]);
601297177Ssephe			hv_vmbus_g_context.hv_event_queue[i] = NULL;
602297177Ssephe		}
603282212Swhu	}
604250199Sgrehan
605297641Ssephe	lapic_ipi_free(hv_vmbus_g_context.hv_cb_vector);
606250199Sgrehan
607250199Sgrehan	return;
608250199Sgrehan}
609250199Sgrehan
610250199Sgrehanstatic void
611250199Sgrehanvmbus_exit(void)
612250199Sgrehan{
613250199Sgrehan	vmbus_bus_exit();
614250199Sgrehan}
615250199Sgrehan
616250199Sgrehanstatic int
617250199Sgrehanvmbus_detach(device_t dev)
618250199Sgrehan{
619250199Sgrehan	vmbus_exit();
620250199Sgrehan	return (0);
621250199Sgrehan}
622250199Sgrehan
623250199Sgrehanstatic void
624250199Sgrehanvmbus_mod_load(void)
625250199Sgrehan{
626250199Sgrehan	if(bootverbose)
627250199Sgrehan		printf("VMBUS: load\n");
628250199Sgrehan}
629250199Sgrehan
630250199Sgrehanstatic void
631250199Sgrehanvmbus_mod_unload(void)
632250199Sgrehan{
633250199Sgrehan	if(bootverbose)
634250199Sgrehan		printf("VMBUS: unload\n");
635250199Sgrehan}
636250199Sgrehan
637250199Sgrehanstatic int
638250199Sgrehanvmbus_modevent(module_t mod, int what, void *arg)
639250199Sgrehan{
640250199Sgrehan	switch (what) {
641250199Sgrehan
642250199Sgrehan	case MOD_LOAD:
643299746Sjhb#ifdef EARLY_AP_STARTUP
644299746Sjhb		vmbus_init();
645299746Sjhb#endif
646250199Sgrehan		vmbus_mod_load();
647250199Sgrehan		break;
648250199Sgrehan	case MOD_UNLOAD:
649250199Sgrehan		vmbus_mod_unload();
650250199Sgrehan		break;
651250199Sgrehan	}
652250199Sgrehan
653250199Sgrehan	return (0);
654250199Sgrehan}
655250199Sgrehan
656250199Sgrehanstatic device_method_t vmbus_methods[] = {
657250199Sgrehan	/** Device interface */
658250199Sgrehan	DEVMETHOD(device_probe, vmbus_probe),
659250199Sgrehan	DEVMETHOD(device_attach, vmbus_attach),
660250199Sgrehan	DEVMETHOD(device_detach, vmbus_detach),
661250199Sgrehan	DEVMETHOD(device_shutdown, bus_generic_shutdown),
662250199Sgrehan	DEVMETHOD(device_suspend, bus_generic_suspend),
663250199Sgrehan	DEVMETHOD(device_resume, bus_generic_resume),
664250199Sgrehan
665250199Sgrehan	/** Bus interface */
666250199Sgrehan	DEVMETHOD(bus_add_child, bus_generic_add_child),
667250199Sgrehan	DEVMETHOD(bus_print_child, bus_generic_print_child),
668250199Sgrehan	DEVMETHOD(bus_read_ivar, vmbus_read_ivar),
669250199Sgrehan	DEVMETHOD(bus_write_ivar, vmbus_write_ivar),
670297143Ssephe	DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str),
671250199Sgrehan
672250199Sgrehan	{ 0, 0 } };
673250199Sgrehan
674300102Ssephestatic driver_t vmbus_driver = {
675300102Ssephe	"vmbus",
676300102Ssephe	vmbus_methods,
677300102Ssephe	sizeof(struct vmbus_softc)
678300102Ssephe};
679250199Sgrehan
680250199Sgrehandevclass_t vmbus_devclass;
681250199Sgrehan
682293870SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, vmbus_modevent, 0);
683293870SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1);
684293870SsepheMODULE_VERSION(vmbus, 1);
685250199Sgrehan
686299746Sjhb#ifndef EARLY_AP_STARTUP
687282212Swhu/* We want to be started after SMP is initialized */
688282212SwhuSYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL);
689299746Sjhb#endif
690