1250199Sgrehan/*-
2250199Sgrehan * Copyright (c) 2009-2012 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: releng/10.3/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 320912 2017-07-12 08:07:55Z delphij $");
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>
41295789Ssephe#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>
57283280Swhu#include <machine/md_var.h>
58283280Swhu#include <machine/segments.h>
59250199Sgrehan#include <sys/pcpu.h>
60283280Swhu#include <machine/apicvar.h>
61250199Sgrehan
62320912Sdelphij#include <dev/hyperv/include/hyperv.h>
63250199Sgrehan#include "hv_vmbus_priv.h"
64250199Sgrehan
65295789Ssephe#include <contrib/dev/acpica/include/acpi.h>
66295789Ssephe#include "acpi_if.h"
67250199Sgrehan
68250199Sgrehanstatic device_t vmbus_devp;
69250199Sgrehanstatic int vmbus_inited;
70255414Sgrehanstatic hv_setup_args setup_args; /* only CPU 0 supported at this time */
71250199Sgrehan
72295789Ssephestatic char *vmbus_ids[] = { "VMBUS", NULL };
73295789Ssephe
74250199Sgrehan/**
75250199Sgrehan * @brief Software interrupt thread routine to handle channel messages from
76250199Sgrehan * the hypervisor.
77250199Sgrehan */
78250199Sgrehanstatic void
79283280Swhuvmbus_msg_swintr(void *arg)
80250199Sgrehan{
81250199Sgrehan	int 			cpu;
82250199Sgrehan	void*			page_addr;
83293820Sdelphij	hv_vmbus_channel_msg_header	 *hdr;
84293820Sdelphij	hv_vmbus_channel_msg_table_entry *entry;
85293820Sdelphij	hv_vmbus_channel_msg_type msg_type;
86250199Sgrehan	hv_vmbus_message*	msg;
87250199Sgrehan	hv_vmbus_message*	copied;
88293820Sdelphij	static bool warned	= false;
89250199Sgrehan
90283280Swhu	cpu = (int)(long)arg;
91283280Swhu	KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: "
92283280Swhu	    "cpu out of range!"));
93283280Swhu
94250199Sgrehan	page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu];
95250199Sgrehan	msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT;
96250199Sgrehan
97250199Sgrehan	for (;;) {
98293820Sdelphij		if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
99250199Sgrehan			break; /* no message */
100293820Sdelphij
101293820Sdelphij		hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
102293820Sdelphij		msg_type = hdr->message_type;
103293820Sdelphij
104293820Sdelphij		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT && !warned) {
105293820Sdelphij			warned = true;
106293820Sdelphij			printf("VMBUS: unknown message type = %d\n", msg_type);
107293820Sdelphij			goto handled;
108293820Sdelphij		}
109293820Sdelphij
110293820Sdelphij		entry = &g_channel_message_table[msg_type];
111293820Sdelphij
112293820Sdelphij		if (entry->handler_no_sleep)
113293820Sdelphij			entry->messageHandler(hdr);
114293820Sdelphij		else {
115293820Sdelphij
116250199Sgrehan			copied = malloc(sizeof(hv_vmbus_message),
117250199Sgrehan					M_DEVBUF, M_NOWAIT);
118250199Sgrehan			KASSERT(copied != NULL,
119250199Sgrehan				("Error VMBUS: malloc failed to allocate"
120250199Sgrehan					" hv_vmbus_message!"));
121250199Sgrehan			if (copied == NULL)
122250199Sgrehan				continue;
123293820Sdelphij
124250199Sgrehan			memcpy(copied, msg, sizeof(hv_vmbus_message));
125250199Sgrehan			hv_queue_work_item(hv_vmbus_g_connection.work_queue,
126293820Sdelphij					   hv_vmbus_on_channel_message,
127293820Sdelphij					   copied);
128293820Sdelphij		}
129293820Sdelphijhandled:
130250199Sgrehan	    msg->header.message_type = HV_MESSAGE_TYPE_NONE;
131250199Sgrehan
132250199Sgrehan	    /*
133250199Sgrehan	     * Make sure the write to message_type (ie set to
134250199Sgrehan	     * HV_MESSAGE_TYPE_NONE) happens before we read the
135250199Sgrehan	     * message_pending and EOMing. Otherwise, the EOMing will
136250199Sgrehan	     * not deliver any more messages
137250199Sgrehan	     * since there is no empty slot
138250199Sgrehan	     */
139250199Sgrehan	    wmb();
140250199Sgrehan
141256276Sdim	    if (msg->header.message_flags.u.message_pending) {
142250199Sgrehan			/*
143250199Sgrehan			 * This will cause message queue rescan to possibly
144250199Sgrehan			 * deliver another msg from the hypervisor
145250199Sgrehan			 */
146255414Sgrehan			wrmsr(HV_X64_MSR_EOM, 0);
147250199Sgrehan	    }
148250199Sgrehan	}
149250199Sgrehan}
150250199Sgrehan
151250199Sgrehan/**
152250199Sgrehan * @brief Interrupt filter routine for VMBUS.
153250199Sgrehan *
154250199Sgrehan * The purpose of this routine is to determine the type of VMBUS protocol
155250199Sgrehan * message to process - an event or a channel message.
156250199Sgrehan */
157283280Swhustatic inline int
158295789Ssephehv_vmbus_isr(struct trapframe *frame)
159250199Sgrehan{
160250199Sgrehan	int				cpu;
161250199Sgrehan	hv_vmbus_message*		msg;
162250199Sgrehan	hv_vmbus_synic_event_flags*	event;
163250199Sgrehan	void*				page_addr;
164250199Sgrehan
165250199Sgrehan	cpu = PCPU_GET(cpuid);
166250199Sgrehan
167250199Sgrehan	/*
168250199Sgrehan	 * The Windows team has advised that we check for events
169250199Sgrehan	 * before checking for messages. This is the way they do it
170250199Sgrehan	 * in Windows when running as a guest in Hyper-V
171250199Sgrehan	 */
172250199Sgrehan
173250199Sgrehan	page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
174250199Sgrehan	event = (hv_vmbus_synic_event_flags*)
175250199Sgrehan		    page_addr + HV_VMBUS_MESSAGE_SINT;
176250199Sgrehan
177283280Swhu	if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
178283280Swhu	    (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
179283280Swhu		/* Since we are a child, we only need to check bit 0 */
180283280Swhu		if (synch_test_and_clear_bit(0, &event->flags32[0])) {
181283280Swhu			swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0);
182283280Swhu		}
183283280Swhu	} else {
184283280Swhu		/*
185283280Swhu		 * On host with Win8 or above, we can directly look at
186283280Swhu		 * the event page. If bit n is set, we have an interrupt
187283280Swhu		 * on the channel with id n.
188283280Swhu		 * Directly schedule the event software interrupt on
189283280Swhu		 * current cpu.
190283280Swhu		 */
191283280Swhu		swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0);
192250199Sgrehan	}
193250199Sgrehan
194250199Sgrehan	/* Check if there are actual msgs to be process */
195250199Sgrehan	page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu];
196250199Sgrehan	msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT;
197250199Sgrehan
198295789Ssephe	/* we call eventtimer process the message */
199295789Ssephe	if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) {
200295789Ssephe		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
201295789Ssephe
202295789Ssephe		/*
203295789Ssephe		 * Make sure the write to message_type (ie set to
204295789Ssephe		 * HV_MESSAGE_TYPE_NONE) happens before we read the
205295789Ssephe		 * message_pending and EOMing. Otherwise, the EOMing will
206295789Ssephe		 * not deliver any more messages
207295789Ssephe		 * since there is no empty slot
208295789Ssephe		 */
209295789Ssephe		wmb();
210295789Ssephe
211295789Ssephe		if (msg->header.message_flags.u.message_pending) {
212295789Ssephe			/*
213295789Ssephe			 * This will cause message queue rescan to possibly
214295789Ssephe			 * deliver another msg from the hypervisor
215295789Ssephe			 */
216295789Ssephe			wrmsr(HV_X64_MSR_EOM, 0);
217295789Ssephe		}
218295789Ssephe		hv_et_intr(frame);
219295789Ssephe		return (FILTER_HANDLED);
220295789Ssephe	}
221295789Ssephe
222250199Sgrehan	if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) {
223283280Swhu		swi_sched(hv_vmbus_g_context.msg_swintr[cpu], 0);
224250199Sgrehan	}
225250199Sgrehan
226295789Ssephe	return (FILTER_HANDLED);
227250199Sgrehan}
228250199Sgrehan
229283280Swhuuint32_t hv_vmbus_swintr_event_cpu[MAXCPU];
230295789Ssepheu_long *hv_vmbus_intr_cpu[MAXCPU];
231283280Swhu
232283280Swhuvoid
233283280Swhuhv_vector_handler(struct trapframe *trap_frame)
234283280Swhu{
235283280Swhu	int cpu;
236283280Swhu
237283280Swhu	/*
238283280Swhu	 * Disable preemption.
239283280Swhu	 */
240283280Swhu	critical_enter();
241283280Swhu
242283280Swhu	/*
243283280Swhu	 * Do a little interrupt counting.
244283280Swhu	 */
245283280Swhu	cpu = PCPU_GET(cpuid);
246295789Ssephe	(*hv_vmbus_intr_cpu[cpu])++;
247283280Swhu
248295789Ssephe	hv_vmbus_isr(trap_frame);
249283280Swhu
250283280Swhu	/*
251283280Swhu	 * Enable preemption.
252283280Swhu	 */
253283280Swhu	critical_exit();
254283280Swhu}
255283280Swhu
256250199Sgrehanstatic int
257250199Sgrehanvmbus_read_ivar(
258250199Sgrehan	device_t	dev,
259250199Sgrehan	device_t	child,
260250199Sgrehan	int		index,
261250199Sgrehan	uintptr_t*	result)
262250199Sgrehan{
263250199Sgrehan	struct hv_device *child_dev_ctx = device_get_ivars(child);
264250199Sgrehan
265250199Sgrehan	switch (index) {
266250199Sgrehan
267250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
268250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->class_id;
269250199Sgrehan		return (0);
270250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
271250199Sgrehan		*result = (uintptr_t) &child_dev_ctx->device_id;
272250199Sgrehan		return (0);
273250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
274250199Sgrehan		*result = (uintptr_t) child_dev_ctx;
275250199Sgrehan		return (0);
276250199Sgrehan	case HV_VMBUS_IVAR_NODE:
277250199Sgrehan		*result = (uintptr_t) child_dev_ctx->device;
278250199Sgrehan		return (0);
279250199Sgrehan	}
280250199Sgrehan	return (ENOENT);
281250199Sgrehan}
282250199Sgrehan
283250199Sgrehanstatic int
284250199Sgrehanvmbus_write_ivar(
285250199Sgrehan	device_t	dev,
286250199Sgrehan	device_t	child,
287250199Sgrehan	int		index,
288250199Sgrehan	uintptr_t	value)
289250199Sgrehan{
290250199Sgrehan	switch (index) {
291250199Sgrehan
292250199Sgrehan	case HV_VMBUS_IVAR_TYPE:
293250199Sgrehan	case HV_VMBUS_IVAR_INSTANCE:
294250199Sgrehan	case HV_VMBUS_IVAR_DEVCTX:
295250199Sgrehan	case HV_VMBUS_IVAR_NODE:
296250199Sgrehan		/* read-only */
297250199Sgrehan		return (EINVAL);
298250199Sgrehan	}
299250199Sgrehan	return (ENOENT);
300250199Sgrehan}
301250199Sgrehan
302320912Sdelphijstatic int
303320912Sdelphijvmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
304320912Sdelphij{
305320912Sdelphij	char guidbuf[40];
306320912Sdelphij	struct hv_device *dev_ctx = device_get_ivars(child);
307320912Sdelphij
308320912Sdelphij	strlcat(buf, "classid=", buflen);
309320912Sdelphij	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id);
310320912Sdelphij	strlcat(buf, guidbuf, buflen);
311320912Sdelphij
312320912Sdelphij	strlcat(buf, " deviceid=", buflen);
313320912Sdelphij	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id);
314320912Sdelphij	strlcat(buf, guidbuf, buflen);
315320912Sdelphij
316320912Sdelphij	return (0);
317320912Sdelphij}
318320912Sdelphij
319250199Sgrehanstruct hv_device*
320250199Sgrehanhv_vmbus_child_device_create(
321250199Sgrehan	hv_guid		type,
322250199Sgrehan	hv_guid		instance,
323250199Sgrehan	hv_vmbus_channel*	channel)
324250199Sgrehan{
325250199Sgrehan	hv_device* child_dev;
326250199Sgrehan
327250199Sgrehan	/*
328250199Sgrehan	 * Allocate the new child device
329250199Sgrehan	 */
330250199Sgrehan	child_dev = malloc(sizeof(hv_device), M_DEVBUF,
331250199Sgrehan			M_NOWAIT |  M_ZERO);
332250199Sgrehan	KASSERT(child_dev != NULL,
333250199Sgrehan	    ("Error VMBUS: malloc failed to allocate hv_device!"));
334250199Sgrehan
335250199Sgrehan	if (child_dev == NULL)
336250199Sgrehan		return (NULL);
337250199Sgrehan
338250199Sgrehan	child_dev->channel = channel;
339250199Sgrehan	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
340250199Sgrehan	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
341250199Sgrehan
342250199Sgrehan	return (child_dev);
343250199Sgrehan}
344250199Sgrehan
345320912Sdelphijint
346320912Sdelphijsnprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid)
347250199Sgrehan{
348320912Sdelphij	int cnt;
349320912Sdelphij	const unsigned char *d = guid->data;
350320912Sdelphij
351320912Sdelphij	cnt = snprintf(buf, sz,
352320912Sdelphij		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
353320912Sdelphij		d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
354320912Sdelphij		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
355320912Sdelphij	return (cnt);
356250199Sgrehan}
357250199Sgrehan
358250199Sgrehanint
359250199Sgrehanhv_vmbus_child_device_register(struct hv_device *child_dev)
360250199Sgrehan{
361250199Sgrehan	device_t child;
362250199Sgrehan	int ret = 0;
363250199Sgrehan
364320912Sdelphij	if (bootverbose) {
365320912Sdelphij		char name[40];
366320912Sdelphij		snprintf_hv_guid(name, sizeof(name), &child_dev->class_id);
367320912Sdelphij		printf("VMBUS: Class ID: %s\n", name);
368320912Sdelphij	}
369250199Sgrehan
370250199Sgrehan	child = device_add_child(vmbus_devp, NULL, -1);
371250199Sgrehan	child_dev->device = child;
372250199Sgrehan	device_set_ivars(child, child_dev);
373250199Sgrehan
374250199Sgrehan	mtx_lock(&Giant);
375250199Sgrehan	ret = device_probe_and_attach(child);
376250199Sgrehan	mtx_unlock(&Giant);
377250199Sgrehan
378250199Sgrehan	return (0);
379250199Sgrehan}
380250199Sgrehan
381250199Sgrehanint
382250199Sgrehanhv_vmbus_child_device_unregister(struct hv_device *child_dev)
383250199Sgrehan{
384250199Sgrehan	int ret = 0;
385250199Sgrehan	/*
386250199Sgrehan	 * XXXKYS: Ensure that this is the opposite of
387250199Sgrehan	 * device_add_child()
388250199Sgrehan	 */
389250199Sgrehan	mtx_lock(&Giant);
390250199Sgrehan	ret = device_delete_child(vmbus_devp, child_dev->device);
391250199Sgrehan	mtx_unlock(&Giant);
392250199Sgrehan	return(ret);
393250199Sgrehan}
394250199Sgrehan
395250199Sgrehanstatic int
396250199Sgrehanvmbus_probe(device_t dev) {
397295789Ssephe	if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL ||
398295789Ssephe	    device_get_unit(dev) != 0)
399295789Ssephe		return (ENXIO);
400250199Sgrehan
401250199Sgrehan	device_set_desc(dev, "Vmbus Devices");
402250199Sgrehan
403295789Ssephe	return (BUS_PROBE_DEFAULT);
404250199Sgrehan}
405250199Sgrehan
406283280Swhu#ifdef HYPERV
407283280Swhuextern inthand_t IDTVEC(rsvd), IDTVEC(hv_vmbus_callback);
408283280Swhu
409250199Sgrehan/**
410283280Swhu * @brief Find a free IDT slot and setup the interrupt handler.
411283280Swhu */
412283280Swhustatic int
413283280Swhuvmbus_vector_alloc(void)
414283280Swhu{
415283280Swhu	int vector;
416283280Swhu	uintptr_t func;
417283280Swhu	struct gate_descriptor *ip;
418283280Swhu
419283280Swhu	/*
420283280Swhu	 * Search backwards form the highest IDT vector available for use
421283280Swhu	 * as vmbus channel callback vector. We install 'hv_vmbus_callback'
422283280Swhu	 * handler at that vector and use it to interrupt vcpus.
423283280Swhu	 */
424283280Swhu	vector = APIC_SPURIOUS_INT;
425283280Swhu	while (--vector >= APIC_IPI_INTS) {
426283280Swhu		ip = &idt[vector];
427283280Swhu		func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset);
428283280Swhu		if (func == (uintptr_t)&IDTVEC(rsvd)) {
429283280Swhu#ifdef __i386__
430283280Swhu			setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYS386IGT,
431283280Swhu			    SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
432283280Swhu#else
433283280Swhu			setidt(vector , IDTVEC(hv_vmbus_callback), SDT_SYSIGT,
434283280Swhu			    SEL_KPL, 0);
435283280Swhu#endif
436283280Swhu
437283280Swhu			return (vector);
438283280Swhu		}
439283280Swhu	}
440283280Swhu	return (0);
441283280Swhu}
442283280Swhu
443283280Swhu/**
444283280Swhu * @brief Restore the IDT slot to rsvd.
445283280Swhu */
446283280Swhustatic void
447283280Swhuvmbus_vector_free(int vector)
448283280Swhu{
449283280Swhu        uintptr_t func;
450283280Swhu        struct gate_descriptor *ip;
451283280Swhu
452283280Swhu	if (vector == 0)
453283280Swhu		return;
454283280Swhu
455283280Swhu        KASSERT(vector >= APIC_IPI_INTS && vector < APIC_SPURIOUS_INT,
456283280Swhu            ("invalid vector %d", vector));
457283280Swhu
458283280Swhu        ip = &idt[vector];
459283280Swhu        func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset);
460283280Swhu        KASSERT(func == (uintptr_t)&IDTVEC(hv_vmbus_callback),
461283280Swhu            ("invalid vector %d", vector));
462283280Swhu
463283280Swhu        setidt(vector, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0);
464283280Swhu}
465283280Swhu
466283280Swhu#else /* HYPERV */
467283280Swhu
468283280Swhustatic int
469283280Swhuvmbus_vector_alloc(void)
470283280Swhu{
471283280Swhu	return(0);
472283280Swhu}
473283280Swhu
474283280Swhustatic void
475283280Swhuvmbus_vector_free(int vector)
476283280Swhu{
477283280Swhu}
478283280Swhu
479283280Swhu#endif /* HYPERV */
480283280Swhu
481283280Swhu/**
482250199Sgrehan * @brief Main vmbus driver initialization routine.
483250199Sgrehan *
484250199Sgrehan * Here, we
485250199Sgrehan * - initialize the vmbus driver context
486250199Sgrehan * - setup various driver entry points
487250199Sgrehan * - invoke the vmbus hv main init routine
488250199Sgrehan * - get the irq resource
489250199Sgrehan * - invoke the vmbus to add the vmbus root device
490250199Sgrehan * - setup the vmbus root device
491250199Sgrehan * - retrieve the channel offers
492250199Sgrehan */
493250199Sgrehanstatic int
494250199Sgrehanvmbus_bus_init(void)
495250199Sgrehan{
496283280Swhu	int i, j, n, ret;
497295789Ssephe	char buf[MAXCOMLEN + 1];
498250199Sgrehan
499250199Sgrehan	if (vmbus_inited)
500250199Sgrehan		return (0);
501250199Sgrehan
502250199Sgrehan	vmbus_inited = 1;
503250199Sgrehan
504250199Sgrehan	ret = hv_vmbus_init();
505250199Sgrehan
506250199Sgrehan	if (ret) {
507250199Sgrehan		if(bootverbose)
508252645Sgrehan			printf("Error VMBUS: Hypervisor Initialization Failed!\n");
509250199Sgrehan		return (ret);
510250199Sgrehan	}
511250199Sgrehan
512250199Sgrehan	/*
513283280Swhu	 * Find a free IDT slot for vmbus callback.
514250199Sgrehan	 */
515283280Swhu	hv_vmbus_g_context.hv_cb_vector = vmbus_vector_alloc();
516250199Sgrehan
517283280Swhu	if (hv_vmbus_g_context.hv_cb_vector == 0) {
518283280Swhu		if(bootverbose)
519283280Swhu			printf("Error VMBUS: Cannot find free IDT slot for "
520283280Swhu			    "vmbus callback!\n");
521283280Swhu		goto cleanup;
522283280Swhu	}
523250199Sgrehan
524283280Swhu	if(bootverbose)
525283280Swhu		printf("VMBUS: vmbus callback vector %d\n",
526283280Swhu		    hv_vmbus_g_context.hv_cb_vector);
527250199Sgrehan
528283280Swhu	/*
529283280Swhu	 * Notify the hypervisor of our vector.
530283280Swhu	 */
531283280Swhu	setup_args.vector = hv_vmbus_g_context.hv_cb_vector;
532250199Sgrehan
533283280Swhu	CPU_FOREACH(j) {
534283280Swhu		hv_vmbus_swintr_event_cpu[j] = 0;
535283280Swhu		hv_vmbus_g_context.hv_event_intr_event[j] = NULL;
536283280Swhu		hv_vmbus_g_context.hv_msg_intr_event[j] = NULL;
537283280Swhu		hv_vmbus_g_context.event_swintr[j] = NULL;
538283280Swhu		hv_vmbus_g_context.msg_swintr[j] = NULL;
539250199Sgrehan
540295789Ssephe		snprintf(buf, sizeof(buf), "cpu%d:hyperv", j);
541295789Ssephe		intrcnt_add(buf, &hv_vmbus_intr_cpu[j]);
542295789Ssephe
543283280Swhu		for (i = 0; i < 2; i++)
544283280Swhu			setup_args.page_buffers[2 * j + i] = NULL;
545250199Sgrehan	}
546250199Sgrehan
547250199Sgrehan	/*
548283280Swhu	 * Per cpu setup.
549250199Sgrehan	 */
550283280Swhu	CPU_FOREACH(j) {
551283280Swhu		/*
552283280Swhu		 * Setup software interrupt thread and handler for msg handling.
553283280Swhu		 */
554283280Swhu		ret = swi_add(&hv_vmbus_g_context.hv_msg_intr_event[j],
555283280Swhu		    "hv_msg", vmbus_msg_swintr, (void *)(long)j, SWI_CLOCK, 0,
556283280Swhu		    &hv_vmbus_g_context.msg_swintr[j]);
557283280Swhu		if (ret) {
558283280Swhu			if(bootverbose)
559283280Swhu				printf("VMBUS: failed to setup msg swi for "
560283280Swhu				    "cpu %d\n", j);
561283280Swhu			goto cleanup1;
562283280Swhu		}
563250199Sgrehan
564283280Swhu		/*
565283280Swhu		 * Bind the swi thread to the cpu.
566283280Swhu		 */
567283280Swhu		ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j],
568283280Swhu		    j);
569283280Swhu	 	if (ret) {
570283280Swhu			if(bootverbose)
571283280Swhu				printf("VMBUS: failed to bind msg swi thread "
572283280Swhu				    "to cpu %d\n", j);
573283280Swhu			goto cleanup1;
574283280Swhu		}
575250199Sgrehan
576283280Swhu		/*
577283280Swhu		 * Setup software interrupt thread and handler for
578283280Swhu		 * event handling.
579283280Swhu		 */
580283280Swhu		ret = swi_add(&hv_vmbus_g_context.hv_event_intr_event[j],
581283280Swhu		    "hv_event", hv_vmbus_on_events, (void *)(long)j,
582283280Swhu		    SWI_CLOCK, 0, &hv_vmbus_g_context.event_swintr[j]);
583283280Swhu		if (ret) {
584283280Swhu			if(bootverbose)
585283280Swhu				printf("VMBUS: failed to setup event swi for "
586283280Swhu				    "cpu %d\n", j);
587283280Swhu			goto cleanup1;
588283280Swhu		}
589250199Sgrehan
590283280Swhu		/*
591283280Swhu		 * Prepare the per cpu msg and event pages to be called on each cpu.
592283280Swhu		 */
593283280Swhu		for(i = 0; i < 2; i++) {
594283280Swhu			setup_args.page_buffers[2 * j + i] =
595255414Sgrehan				malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO);
596283280Swhu			if (setup_args.page_buffers[2 * j + i] == NULL) {
597283280Swhu				KASSERT(setup_args.page_buffers[2 * j + i] != NULL,
598255414Sgrehan					("Error VMBUS: malloc failed!"));
599283280Swhu				goto cleanup1;
600283280Swhu			}
601255414Sgrehan		}
602255414Sgrehan	}
603250199Sgrehan
604283280Swhu	if (bootverbose)
605283280Swhu		printf("VMBUS: Calling smp_rendezvous, smp_started = %d\n",
606283280Swhu		    smp_started);
607283280Swhu
608255414Sgrehan	smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args);
609250199Sgrehan
610255414Sgrehan	/*
611250199Sgrehan	 * Connect to VMBus in the root partition
612250199Sgrehan	 */
613250199Sgrehan	ret = hv_vmbus_connect();
614250199Sgrehan
615255414Sgrehan	if (ret != 0)
616283280Swhu		goto cleanup1;
617250199Sgrehan
618250199Sgrehan	hv_vmbus_request_channel_offers();
619250199Sgrehan	return (ret);
620250199Sgrehan
621283280Swhu	cleanup1:
622283280Swhu	/*
623283280Swhu	 * Free pages alloc'ed
624283280Swhu	 */
625283280Swhu	for (n = 0; n < 2 * MAXCPU; n++)
626283280Swhu		if (setup_args.page_buffers[n] != NULL)
627283280Swhu			free(setup_args.page_buffers[n], M_DEVBUF);
628250199Sgrehan
629250199Sgrehan	/*
630283280Swhu	 * remove swi and vmbus callback vector;
631250199Sgrehan	 */
632283280Swhu	CPU_FOREACH(j) {
633283280Swhu		if (hv_vmbus_g_context.msg_swintr[j] != NULL)
634283280Swhu			swi_remove(hv_vmbus_g_context.msg_swintr[j]);
635283280Swhu		if (hv_vmbus_g_context.event_swintr[j] != NULL)
636283280Swhu			swi_remove(hv_vmbus_g_context.event_swintr[j]);
637283280Swhu		hv_vmbus_g_context.hv_msg_intr_event[j] = NULL;
638283280Swhu		hv_vmbus_g_context.hv_event_intr_event[j] = NULL;
639283280Swhu	}
640250199Sgrehan
641283280Swhu	vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector);
642250199Sgrehan
643250199Sgrehan	cleanup:
644250199Sgrehan	hv_vmbus_cleanup();
645250199Sgrehan
646250199Sgrehan	return (ret);
647250199Sgrehan}
648250199Sgrehan
649250199Sgrehanstatic int
650250199Sgrehanvmbus_attach(device_t dev)
651250199Sgrehan{
652250199Sgrehan	if(bootverbose)
653250199Sgrehan		device_printf(dev, "VMBUS: attach dev: %p\n", dev);
654250199Sgrehan	vmbus_devp = dev;
655250199Sgrehan
656250199Sgrehan	/*
657250199Sgrehan	 * If the system has already booted and thread
658250199Sgrehan	 * scheduling is possible indicated by the global
659250199Sgrehan	 * cold set to zero, we just call the driver
660250199Sgrehan	 * initialization directly.
661250199Sgrehan	 */
662250199Sgrehan	if (!cold)
663250199Sgrehan		vmbus_bus_init();
664250199Sgrehan
665250199Sgrehan	return (0);
666250199Sgrehan}
667250199Sgrehan
668250199Sgrehanstatic void
669250199Sgrehanvmbus_init(void)
670250199Sgrehan{
671256758Sgibbs	if (vm_guest != VM_GUEST_HV)
672256758Sgibbs		return;
673256758Sgibbs
674250199Sgrehan	/*
675250199Sgrehan	 * If the system has already booted and thread
676256758Sgibbs	 * scheduling is possible, as indicated by the
677256758Sgibbs	 * global cold set to zero, we just call the driver
678250199Sgrehan	 * initialization directly.
679250199Sgrehan	 */
680250199Sgrehan	if (!cold)
681250199Sgrehan		vmbus_bus_init();
682250199Sgrehan}
683250199Sgrehan
684250199Sgrehanstatic void
685250199Sgrehanvmbus_bus_exit(void)
686250199Sgrehan{
687255414Sgrehan	int i;
688255414Sgrehan
689250199Sgrehan	hv_vmbus_release_unattached_channels();
690250199Sgrehan	hv_vmbus_disconnect();
691250199Sgrehan
692250199Sgrehan	smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL);
693250199Sgrehan
694283280Swhu	for(i = 0; i < 2 * MAXCPU; i++) {
695255414Sgrehan		if (setup_args.page_buffers[i] != 0)
696255414Sgrehan			free(setup_args.page_buffers[i], M_DEVBUF);
697255414Sgrehan	}
698255414Sgrehan
699250199Sgrehan	hv_vmbus_cleanup();
700250199Sgrehan
701283280Swhu	/* remove swi */
702283280Swhu	CPU_FOREACH(i) {
703283280Swhu		if (hv_vmbus_g_context.msg_swintr[i] != NULL)
704283280Swhu			swi_remove(hv_vmbus_g_context.msg_swintr[i]);
705283280Swhu		if (hv_vmbus_g_context.event_swintr[i] != NULL)
706283280Swhu			swi_remove(hv_vmbus_g_context.event_swintr[i]);
707283280Swhu		hv_vmbus_g_context.hv_msg_intr_event[i] = NULL;
708283280Swhu		hv_vmbus_g_context.hv_event_intr_event[i] = NULL;
709283280Swhu	}
710250199Sgrehan
711283280Swhu	vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector);
712250199Sgrehan
713250199Sgrehan	return;
714250199Sgrehan}
715250199Sgrehan
716250199Sgrehanstatic void
717250199Sgrehanvmbus_exit(void)
718250199Sgrehan{
719250199Sgrehan	vmbus_bus_exit();
720250199Sgrehan}
721250199Sgrehan
722250199Sgrehanstatic int
723250199Sgrehanvmbus_detach(device_t dev)
724250199Sgrehan{
725250199Sgrehan	vmbus_exit();
726250199Sgrehan	return (0);
727250199Sgrehan}
728250199Sgrehan
729250199Sgrehanstatic void
730250199Sgrehanvmbus_mod_load(void)
731250199Sgrehan{
732250199Sgrehan	if(bootverbose)
733250199Sgrehan		printf("VMBUS: load\n");
734250199Sgrehan}
735250199Sgrehan
736250199Sgrehanstatic void
737250199Sgrehanvmbus_mod_unload(void)
738250199Sgrehan{
739250199Sgrehan	if(bootverbose)
740250199Sgrehan		printf("VMBUS: unload\n");
741250199Sgrehan}
742250199Sgrehan
743250199Sgrehanstatic int
744250199Sgrehanvmbus_modevent(module_t mod, int what, void *arg)
745250199Sgrehan{
746250199Sgrehan	switch (what) {
747250199Sgrehan
748250199Sgrehan	case MOD_LOAD:
749250199Sgrehan		vmbus_mod_load();
750250199Sgrehan		break;
751250199Sgrehan	case MOD_UNLOAD:
752250199Sgrehan		vmbus_mod_unload();
753250199Sgrehan		break;
754250199Sgrehan	}
755250199Sgrehan
756250199Sgrehan	return (0);
757250199Sgrehan}
758250199Sgrehan
759250199Sgrehanstatic device_method_t vmbus_methods[] = {
760250199Sgrehan	/** Device interface */
761250199Sgrehan	DEVMETHOD(device_probe, vmbus_probe),
762250199Sgrehan	DEVMETHOD(device_attach, vmbus_attach),
763250199Sgrehan	DEVMETHOD(device_detach, vmbus_detach),
764250199Sgrehan	DEVMETHOD(device_shutdown, bus_generic_shutdown),
765250199Sgrehan	DEVMETHOD(device_suspend, bus_generic_suspend),
766250199Sgrehan	DEVMETHOD(device_resume, bus_generic_resume),
767250199Sgrehan
768250199Sgrehan	/** Bus interface */
769250199Sgrehan	DEVMETHOD(bus_add_child, bus_generic_add_child),
770250199Sgrehan	DEVMETHOD(bus_print_child, bus_generic_print_child),
771250199Sgrehan	DEVMETHOD(bus_read_ivar, vmbus_read_ivar),
772250199Sgrehan	DEVMETHOD(bus_write_ivar, vmbus_write_ivar),
773320912Sdelphij	DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str),
774250199Sgrehan
775250199Sgrehan	{ 0, 0 } };
776250199Sgrehan
777250199Sgrehanstatic char driver_name[] = "vmbus";
778250199Sgrehanstatic driver_t vmbus_driver = { driver_name, vmbus_methods,0, };
779250199Sgrehan
780250199Sgrehan
781250199Sgrehandevclass_t vmbus_devclass;
782250199Sgrehan
783295789SsepheDRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, vmbus_modevent, 0);
784295789SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1);
785295789SsepheMODULE_VERSION(vmbus, 1);
786250199Sgrehan
787283280Swhu/* We want to be started after SMP is initialized */
788283280SwhuSYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL);
789250199Sgrehan
790