vmbus.c revision 300653
175295Sdes/*-
275295Sdes * Copyright (c) 2009-2012,2016 Microsoft Corp.
375295Sdes * Copyright (c) 2012 NetApp Inc.
475295Sdes * Copyright (c) 2012 Citrix Inc.
575295Sdes * All rights reserved.
675295Sdes *
775295Sdes * Redistribution and use in source and binary forms, with or without
875295Sdes * modification, are permitted provided that the following conditions
975295Sdes * are met:
1075295Sdes * 1. Redistributions of source code must retain the above copyright
1175295Sdes *    notice unmodified, this list of conditions, and the following
1275295Sdes *    disclaimer.
1375295Sdes * 2. Redistributions in binary form must reproduce the above copyright
1475295Sdes *    notice, this list of conditions and the following disclaimer in the
1575295Sdes *    documentation and/or other materials provided with the distribution.
1675295Sdes *
1775295Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1875295Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1975295Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2075295Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2175295Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2275295Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2375295Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2475295Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2575295Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2675295Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2775295Sdes */
2875295Sdes
2975295Sdes/*
3075295Sdes * VM Bus Driver Implementation
3175295Sdes */
3275295Sdes#include <sys/cdefs.h>
3375295Sdes__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c 300653 2016-05-25 05:45:43Z sephe $");
3478073Sdes
3575295Sdes#include <sys/param.h>
3677965Sdes#include <sys/bus.h>
3784246Sdes#include <sys/kernel.h>
3875295Sdes#include <sys/lock.h>
3975295Sdes#include <sys/malloc.h>
4075295Sdes#include <sys/module.h>
4175295Sdes#include <sys/proc.h>
4275295Sdes#include <sys/sysctl.h>
4375295Sdes#include <sys/syslog.h>
4477998Sdes#include <sys/systm.h>
4575295Sdes#include <sys/rtprio.h>
4675295Sdes#include <sys/interrupt.h>
4789071Smsmith#include <sys/sx.h>
4884246Sdes#include <sys/taskqueue.h>
4975295Sdes#include <sys/mutex.h>
5075295Sdes#include <sys/smp.h>
5175295Sdes
5275295Sdes#include <machine/resource.h>
5384246Sdes#include <sys/rman.h>
5484246Sdes
5584246Sdes#include <machine/stdarg.h>
5684246Sdes#include <machine/intr_machdep.h>
5784246Sdes#include <machine/md_var.h>
5884246Sdes#include <machine/segments.h>
5984246Sdes#include <sys/pcpu.h>
6084246Sdes#include <x86/apicvar.h>
6184246Sdes
6284246Sdes#include <dev/hyperv/include/hyperv.h>
6375295Sdes#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
6484246Sdes#include <dev/hyperv/vmbus/vmbus_var.h>
6584246Sdes
6675295Sdes#include <contrib/dev/acpica/include/acpi.h>
6775295Sdes#include "acpi_if.h"
6875295Sdes
6984246Sdesstruct vmbus_softc	*vmbus_sc;
7084246Sdes
7175295Sdesstatic char *vmbus_ids[] = { "VMBUS", NULL };
7275295Sdes
7375295Sdesextern inthand_t IDTVEC(hv_vmbus_callback);
7475295Sdes
7575295Sdesstatic void
7675295Sdesvmbus_msg_task(void *xsc, int pending __unused)
7775295Sdes{
7875295Sdes	struct vmbus_softc *sc = xsc;
7975295Sdes	hv_vmbus_message *msg;
8075295Sdes
8193818Sjhb	msg = VMBUS_PCPU_GET(sc, message, curcpu) + HV_VMBUS_MESSAGE_SINT;
8293818Sjhb	for (;;) {
8384246Sdes		const hv_vmbus_channel_msg_table_entry *entry;
8484246Sdes		hv_vmbus_channel_msg_header *hdr;
8575295Sdes		hv_vmbus_channel_msg_type msg_type;
8675295Sdes
8775295Sdes		if (msg->header.message_type == HV_MESSAGE_TYPE_NONE)
8875295Sdes			break; /* no message */
8975295Sdes
9075295Sdes		hdr = (hv_vmbus_channel_msg_header *)msg->u.payload;
9175295Sdes		msg_type = hdr->message_type;
9275295Sdes
9384246Sdes		if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
9484386Sdes			printf("VMBUS: unknown message type = %d\n", msg_type);
9584386Sdes			goto handled;
9684386Sdes		}
9775295Sdes
9875295Sdes		entry = &g_channel_message_table[msg_type];
9975295Sdes		if (entry->messageHandler)
10075295Sdes			entry->messageHandler(hdr);
10175295Sdeshandled:
10275295Sdes		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
10375295Sdes		/*
10477998Sdes		 * Make sure the write to message_type (ie set to
10577998Sdes		 * HV_MESSAGE_TYPE_NONE) happens before we read the
10675295Sdes		 * message_pending and EOMing. Otherwise, the EOMing will
10777998Sdes		 * not deliver any more messages
10875295Sdes		 * since there is no empty slot
10988234Sdillon		 *
11088234Sdillon		 * NOTE:
11197940Sdes		 * mb() is used here, since atomic_thread_fence_seq_cst()
11288234Sdillon		 * will become compiler fence on UP kernel.
11388234Sdillon		 */
11475295Sdes		mb();
11584246Sdes		if (msg->header.message_flags.u.message_pending) {
116109969Stjr			/*
117109969Stjr			 * This will cause message queue rescan to possibly
11884246Sdes			 * deliver another msg from the hypervisor
11975295Sdes			 */
12084246Sdes			wrmsr(HV_X64_MSR_EOM, 0);
12175295Sdes		}
12288234Sdillon	}
12388234Sdillon}
12499566Sjeff
12599566Sjeff/**
12675295Sdes * @brief Interrupt filter routine for VMBUS.
12775295Sdes *
12877998Sdes * The purpose of this routine is to determine the type of VMBUS protocol
12977998Sdes * message to process - an event or a channel message.
13077998Sdes */
13177998Sdesstatic inline int
13277998Sdeshv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
13375295Sdes{
13475295Sdes	hv_vmbus_message *msg, *msg_base;
13575295Sdes
136111119Simp	/*
13784246Sdes	 * The Windows team has advised that we check for events
13884246Sdes	 * before checking for messages. This is the way they do it
139103314Snjl	 * in Windows when running as a guest in Hyper-V
140105165Sphk	 */
141105165Sphk	sc->vmbus_event_proc(sc, cpu);
14275295Sdes
143105165Sphk	/* Check if there are actual msgs to be process */
14477998Sdes	msg_base = VMBUS_PCPU_GET(sc, message, cpu);
14577998Sdes	msg = msg_base + HV_VMBUS_TIMER_SINT;
14677998Sdes
14775295Sdes	/* we call eventtimer process the message */
14875295Sdes	if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) {
149101308Sjeff		msg->header.message_type = HV_MESSAGE_TYPE_NONE;
15075295Sdes
15175295Sdes		/* call intrrupt handler of event timer */
15275295Sdes		hv_et_intr(frame);
15384246Sdes
15475295Sdes		/*
15575295Sdes		 * Make sure the write to message_type (ie set to
15675295Sdes		 * HV_MESSAGE_TYPE_NONE) happens before we read the
15777998Sdes		 * message_pending and EOMing. Otherwise, the EOMing will
15875295Sdes		 * not deliver any more messages
15975295Sdes		 * since there is no empty slot
16075295Sdes		 *
16175295Sdes		 * NOTE:
16275295Sdes		 * mb() is used here, since atomic_thread_fence_seq_cst()
16375295Sdes		 * will become compiler fence on UP kernel.
16475295Sdes		 */
16575295Sdes		mb();
16677998Sdes
16777998Sdes		if (msg->header.message_flags.u.message_pending) {
16875295Sdes			/*
16975295Sdes			 * This will cause message queue rescan to possibly
17075295Sdes			 * deliver another msg from the hypervisor
171103314Snjl			 */
172103314Snjl			wrmsr(HV_X64_MSR_EOM, 0);
173103314Snjl		}
174103314Snjl	}
175103314Snjl
176103314Snjl	msg = msg_base + HV_VMBUS_MESSAGE_SINT;
17784246Sdes	if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) {
17877998Sdes		taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
17984246Sdes		    VMBUS_PCPU_PTR(sc, message_task, cpu));
18084246Sdes	}
18184246Sdes
18284246Sdes	return (FILTER_HANDLED);
18384246Sdes}
18475295Sdes
185105077Smckusickvoid
18699566Sjeffhv_vector_handler(struct trapframe *trap_frame)
18775295Sdes{
18875295Sdes	struct vmbus_softc *sc = vmbus_get_softc();
18975295Sdes	int cpu = curcpu;
19075295Sdes
19175295Sdes	/*
19275295Sdes	 * Disable preemption.
19375295Sdes	 */
19475295Sdes	critical_enter();
19575295Sdes
19677998Sdes	/*
19788234Sdillon	 * Do a little interrupt counting.
19888234Sdillon	 */
19997940Sdes	(*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++;
20075295Sdes
20184246Sdes	hv_vmbus_isr(sc, trap_frame, cpu);
20284246Sdes
20384246Sdes	/*
20484246Sdes	 * Enable preemption.
20584246Sdes	 */
20684246Sdes	critical_exit();
20777998Sdes}
20884246Sdes
20977998Sdesstatic void
21084246Sdesvmbus_synic_setup(void *xsc)
21184246Sdes{
21277998Sdes	struct vmbus_softc *sc = xsc;
21375295Sdes	int			cpu;
21475295Sdes	hv_vmbus_synic_simp	simp;
21575295Sdes	hv_vmbus_synic_siefp	siefp;
21684246Sdes	hv_vmbus_synic_scontrol sctrl;
21784246Sdes	hv_vmbus_synic_sint	shared_sint;
21884246Sdes
21984246Sdes	cpu = PCPU_GET(cpuid);
22084246Sdes
22184246Sdes	/*
22284246Sdes	 * Setup the Synic's message page
22384246Sdes	 */
22484246Sdes	simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
22584246Sdes	simp.u.simp_enabled = 1;
22684246Sdes	simp.u.base_simp_gpa =
22784246Sdes	    VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT;
22884246Sdes
22984246Sdes	wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
23084246Sdes
23184246Sdes	/*
23284246Sdes	 * Setup the Synic's event page
23384246Sdes	 */
23484246Sdes	siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
23584246Sdes	siefp.u.siefp_enabled = 1;
23684246Sdes	siefp.u.base_siefp_gpa =
23784246Sdes	    VMBUS_PCPU_GET(sc, event_flag_dma.hv_paddr, cpu) >> PAGE_SHIFT;
23888868Stanimura
23988868Stanimura	wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
24084246Sdes
24184246Sdes	/*HV_SHARED_SINT_IDT_VECTOR + 0x20; */
24284246Sdes	shared_sint.as_uint64_t = 0;
24384386Sdes	shared_sint.u.vector = sc->vmbus_idtvec;
24484386Sdes	shared_sint.u.masked = FALSE;
24584386Sdes	shared_sint.u.auto_eoi = TRUE;
24684386Sdes
24784386Sdes	wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
24884386Sdes	    shared_sint.as_uint64_t);
24984386Sdes
25084386Sdes	wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
25197940Sdes	    shared_sint.as_uint64_t);
25284386Sdes
25384386Sdes	/* Enable the global synic bit */
25484386Sdes	sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL);
25584386Sdes	sctrl.u.enable = 1;
25684386Sdes
25784386Sdes	wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t);
25884386Sdes
25984386Sdes	/*
26084386Sdes	 * Set up the cpuid mapping from Hyper-V to FreeBSD.
26184386Sdes	 * The array is indexed using FreeBSD cpuid.
26284386Sdes	 */
26384386Sdes	VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(HV_X64_MSR_VP_INDEX);
26488868Stanimura}
26588868Stanimura
26684386Sdesstatic void
26784386Sdesvmbus_synic_teardown(void *arg)
26884386Sdes{
26984386Sdes	hv_vmbus_synic_sint	shared_sint;
27084386Sdes	hv_vmbus_synic_simp	simp;
27184386Sdes	hv_vmbus_synic_siefp	siefp;
27284386Sdes
27384386Sdes	shared_sint.as_uint64_t = rdmsr(
27484386Sdes	    HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT);
27584386Sdes
27684386Sdes	shared_sint.u.masked = 1;
27784386Sdes
27884386Sdes	/*
27984386Sdes	 * Disable the interrupt 0
280	 */
281	wrmsr(
282	    HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
283	    shared_sint.as_uint64_t);
284
285	shared_sint.as_uint64_t = rdmsr(
286	    HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT);
287
288	shared_sint.u.masked = 1;
289
290	/*
291	 * Disable the interrupt 1
292	 */
293	wrmsr(
294	    HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
295	    shared_sint.as_uint64_t);
296	simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
297	simp.u.simp_enabled = 0;
298	simp.u.base_simp_gpa = 0;
299
300	wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
301
302	siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
303	siefp.u.siefp_enabled = 0;
304	siefp.u.base_siefp_gpa = 0;
305
306	wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
307}
308
309static int
310vmbus_dma_alloc(struct vmbus_softc *sc)
311{
312	int cpu;
313
314	CPU_FOREACH(cpu) {
315		void *ptr;
316
317		/*
318		 * Per-cpu messages and event flags.
319		 */
320		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
321		    PAGE_SIZE, 0, PAGE_SIZE,
322		    VMBUS_PCPU_PTR(sc, message_dma, cpu),
323		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
324		if (ptr == NULL)
325			return ENOMEM;
326		VMBUS_PCPU_GET(sc, message, cpu) = ptr;
327
328		ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
329		    PAGE_SIZE, 0, PAGE_SIZE,
330		    VMBUS_PCPU_PTR(sc, event_flag_dma, cpu),
331		    BUS_DMA_WAITOK | BUS_DMA_ZERO);
332		if (ptr == NULL)
333			return ENOMEM;
334		VMBUS_PCPU_GET(sc, event_flag, cpu) = ptr;
335	}
336	return 0;
337}
338
339static void
340vmbus_dma_free(struct vmbus_softc *sc)
341{
342	int cpu;
343
344	CPU_FOREACH(cpu) {
345		if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) {
346			hyperv_dmamem_free(
347			    VMBUS_PCPU_PTR(sc, message_dma, cpu),
348			    VMBUS_PCPU_GET(sc, message, cpu));
349			VMBUS_PCPU_GET(sc, message, cpu) = NULL;
350		}
351		if (VMBUS_PCPU_GET(sc, event_flag, cpu) != NULL) {
352			hyperv_dmamem_free(
353			    VMBUS_PCPU_PTR(sc, event_flag_dma, cpu),
354			    VMBUS_PCPU_GET(sc, event_flag, cpu));
355			VMBUS_PCPU_GET(sc, event_flag, cpu) = NULL;
356		}
357	}
358}
359
360static int
361vmbus_intr_setup(struct vmbus_softc *sc)
362{
363	int cpu;
364
365	CPU_FOREACH(cpu) {
366		char buf[MAXCOMLEN + 1];
367		cpuset_t cpu_mask;
368
369		/* Allocate an interrupt counter for Hyper-V interrupt */
370		snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu);
371		intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu));
372
373		/*
374		 * Setup taskqueue to handle events.  Task will be per-
375		 * channel.
376		 */
377		VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast(
378		    "hyperv event", M_WAITOK, taskqueue_thread_enqueue,
379		    VMBUS_PCPU_PTR(sc, event_tq, cpu));
380		CPU_SETOF(cpu, &cpu_mask);
381		taskqueue_start_threads_cpuset(
382		    VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask,
383		    "hvevent%d", cpu);
384
385		/*
386		 * Setup tasks and taskqueues to handle messages.
387		 */
388		VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast(
389		    "hyperv msg", M_WAITOK, taskqueue_thread_enqueue,
390		    VMBUS_PCPU_PTR(sc, message_tq, cpu));
391		CPU_SETOF(cpu, &cpu_mask);
392		taskqueue_start_threads_cpuset(
393		    VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask,
394		    "hvmsg%d", cpu);
395		TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0,
396		    vmbus_msg_task, sc);
397	}
398
399	/*
400	 * All Hyper-V ISR required resources are setup, now let's find a
401	 * free IDT vector for Hyper-V ISR and set it up.
402	 */
403	sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(hv_vmbus_callback));
404	if (sc->vmbus_idtvec < 0) {
405		device_printf(sc->vmbus_dev, "cannot find free IDT vector\n");
406		return ENXIO;
407	}
408	if(bootverbose) {
409		device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n",
410		    sc->vmbus_idtvec);
411	}
412	return 0;
413}
414
415static void
416vmbus_intr_teardown(struct vmbus_softc *sc)
417{
418	int cpu;
419
420	if (sc->vmbus_idtvec >= 0) {
421		lapic_ipi_free(sc->vmbus_idtvec);
422		sc->vmbus_idtvec = -1;
423	}
424
425	CPU_FOREACH(cpu) {
426		if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) {
427			taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu));
428			VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL;
429		}
430		if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) {
431			taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu),
432			    VMBUS_PCPU_PTR(sc, message_task, cpu));
433			taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu));
434			VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL;
435		}
436	}
437}
438
439static int
440vmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
441{
442	struct hv_device *child_dev_ctx = device_get_ivars(child);
443
444	switch (index) {
445	case HV_VMBUS_IVAR_TYPE:
446		*result = (uintptr_t) &child_dev_ctx->class_id;
447		return (0);
448	case HV_VMBUS_IVAR_INSTANCE:
449		*result = (uintptr_t) &child_dev_ctx->device_id;
450		return (0);
451	case HV_VMBUS_IVAR_DEVCTX:
452		*result = (uintptr_t) child_dev_ctx;
453		return (0);
454	case HV_VMBUS_IVAR_NODE:
455		*result = (uintptr_t) child_dev_ctx->device;
456		return (0);
457	}
458	return (ENOENT);
459}
460
461static int
462vmbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
463{
464	switch (index) {
465	case HV_VMBUS_IVAR_TYPE:
466	case HV_VMBUS_IVAR_INSTANCE:
467	case HV_VMBUS_IVAR_DEVCTX:
468	case HV_VMBUS_IVAR_NODE:
469		/* read-only */
470		return (EINVAL);
471	}
472	return (ENOENT);
473}
474
475static int
476vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen)
477{
478	char guidbuf[40];
479	struct hv_device *dev_ctx = device_get_ivars(child);
480
481	if (dev_ctx == NULL)
482		return (0);
483
484	strlcat(buf, "classid=", buflen);
485	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id);
486	strlcat(buf, guidbuf, buflen);
487
488	strlcat(buf, " deviceid=", buflen);
489	snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id);
490	strlcat(buf, guidbuf, buflen);
491
492	return (0);
493}
494
495struct hv_device *
496hv_vmbus_child_device_create(hv_guid type, hv_guid instance,
497    hv_vmbus_channel *channel)
498{
499	hv_device *child_dev;
500
501	/*
502	 * Allocate the new child device
503	 */
504	child_dev = malloc(sizeof(hv_device), M_DEVBUF, M_WAITOK | M_ZERO);
505
506	child_dev->channel = channel;
507	memcpy(&child_dev->class_id, &type, sizeof(hv_guid));
508	memcpy(&child_dev->device_id, &instance, sizeof(hv_guid));
509
510	return (child_dev);
511}
512
513int
514snprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid)
515{
516	int cnt;
517	const unsigned char *d = guid->data;
518
519	cnt = snprintf(buf, sz,
520		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
521		d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
522		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
523	return (cnt);
524}
525
526int
527hv_vmbus_child_device_register(struct hv_device *child_dev)
528{
529	device_t child;
530
531	if (bootverbose) {
532		char name[40];
533		snprintf_hv_guid(name, sizeof(name), &child_dev->class_id);
534		printf("VMBUS: Class ID: %s\n", name);
535	}
536
537	child = device_add_child(vmbus_get_device(), NULL, -1);
538	child_dev->device = child;
539	device_set_ivars(child, child_dev);
540
541	return (0);
542}
543
544int
545hv_vmbus_child_device_unregister(struct hv_device *child_dev)
546{
547	int ret = 0;
548	/*
549	 * XXXKYS: Ensure that this is the opposite of
550	 * device_add_child()
551	 */
552	mtx_lock(&Giant);
553	ret = device_delete_child(vmbus_get_device(), child_dev->device);
554	mtx_unlock(&Giant);
555	return(ret);
556}
557
558static int
559vmbus_probe(device_t dev)
560{
561	if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL ||
562	    device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV)
563		return (ENXIO);
564
565	device_set_desc(dev, "Hyper-V Vmbus");
566
567	return (BUS_PROBE_DEFAULT);
568}
569
570/**
571 * @brief Main vmbus driver initialization routine.
572 *
573 * Here, we
574 * - initialize the vmbus driver context
575 * - setup various driver entry points
576 * - invoke the vmbus hv main init routine
577 * - get the irq resource
578 * - invoke the vmbus to add the vmbus root device
579 * - setup the vmbus root device
580 * - retrieve the channel offers
581 */
582static int
583vmbus_bus_init(void)
584{
585	struct vmbus_softc *sc = vmbus_get_softc();
586	int ret;
587
588	if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED)
589		return (0);
590	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
591
592	/*
593	 * Allocate DMA stuffs.
594	 */
595	ret = vmbus_dma_alloc(sc);
596	if (ret != 0)
597		goto cleanup;
598
599	/*
600	 * Setup interrupt.
601	 */
602	ret = vmbus_intr_setup(sc);
603	if (ret != 0)
604		goto cleanup;
605
606	/*
607	 * Setup SynIC.
608	 */
609	if (bootverbose)
610		device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started);
611	smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc);
612	sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
613
614	/*
615	 * Connect to VMBus in the root partition
616	 */
617	ret = hv_vmbus_connect();
618
619	if (ret != 0)
620		goto cleanup;
621
622	if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
623	    hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
624		sc->vmbus_event_proc = vmbus_event_proc_compat;
625	else
626		sc->vmbus_event_proc = vmbus_event_proc;
627
628	hv_vmbus_request_channel_offers();
629
630	vmbus_scan();
631	bus_generic_attach(sc->vmbus_dev);
632	device_printf(sc->vmbus_dev, "device scan, probe and attach done\n");
633
634	return (ret);
635
636cleanup:
637	vmbus_intr_teardown(sc);
638	vmbus_dma_free(sc);
639
640	return (ret);
641}
642
643static void
644vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
645{
646}
647
648static int
649vmbus_attach(device_t dev)
650{
651	vmbus_sc = device_get_softc(dev);
652	vmbus_sc->vmbus_dev = dev;
653	vmbus_sc->vmbus_idtvec = -1;
654
655	/*
656	 * Event processing logic will be configured:
657	 * - After the vmbus protocol version negotiation.
658	 * - Before we request channel offers.
659	 */
660	vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
661
662#ifndef EARLY_AP_STARTUP
663	/*
664	 * If the system has already booted and thread
665	 * scheduling is possible indicated by the global
666	 * cold set to zero, we just call the driver
667	 * initialization directly.
668	 */
669	if (!cold)
670#endif
671		vmbus_bus_init();
672
673	bus_generic_probe(dev);
674	return (0);
675}
676
677static void
678vmbus_sysinit(void *arg __unused)
679{
680	if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
681		return;
682
683#ifndef EARLY_AP_STARTUP
684	/*
685	 * If the system has already booted and thread
686	 * scheduling is possible, as indicated by the
687	 * global cold set to zero, we just call the driver
688	 * initialization directly.
689	 */
690	if (!cold)
691#endif
692		vmbus_bus_init();
693}
694
695static int
696vmbus_detach(device_t dev)
697{
698	struct vmbus_softc *sc = device_get_softc(dev);
699
700	hv_vmbus_release_unattached_channels();
701	hv_vmbus_disconnect();
702
703	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
704		sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC;
705		smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL);
706	}
707
708	vmbus_intr_teardown(sc);
709	vmbus_dma_free(sc);
710
711	return (0);
712}
713
714static device_method_t vmbus_methods[] = {
715	/* Device interface */
716	DEVMETHOD(device_probe,			vmbus_probe),
717	DEVMETHOD(device_attach,		vmbus_attach),
718	DEVMETHOD(device_detach,		vmbus_detach),
719	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
720	DEVMETHOD(device_suspend,		bus_generic_suspend),
721	DEVMETHOD(device_resume,		bus_generic_resume),
722
723	/* Bus interface */
724	DEVMETHOD(bus_add_child,		bus_generic_add_child),
725	DEVMETHOD(bus_print_child,		bus_generic_print_child),
726	DEVMETHOD(bus_read_ivar,		vmbus_read_ivar),
727	DEVMETHOD(bus_write_ivar,		vmbus_write_ivar),
728	DEVMETHOD(bus_child_pnpinfo_str,	vmbus_child_pnpinfo_str),
729
730	DEVMETHOD_END
731};
732
733static driver_t vmbus_driver = {
734	"vmbus",
735	vmbus_methods,
736	sizeof(struct vmbus_softc)
737};
738
739static devclass_t vmbus_devclass;
740
741DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
742MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
743MODULE_VERSION(vmbus, 1);
744
745#ifndef EARLY_AP_STARTUP
746/*
747 * NOTE:
748 * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is
749 * initialized.
750 */
751SYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL);
752#endif
753