From 155d1eca37c9f7e3c6328bd25836f6618a78bfa3 Mon Sep 17 00:00:00 2001
From: Kimihiro Nonaka <nonakap@gmail.com>
Date: Fri, 23 Mar 2018 20:00:29 +0900
Subject: [PATCH 2/5] Hyper-V SynIC (WIP)

---
 sys/arch/amd64/conf/GENERIC    |   3 +
 sys/arch/x86/conf/files.x86    |  10 +-
 sys/arch/x86/x86/hvheartbeat.c | 199 +++++++++++++++++++++
 sys/arch/x86/x86/hvshutdown.c  | 234 ++++++++++++++++++++++++
 sys/arch/x86/x86/hvtimesync.c  | 316 +++++++++++++++++++++++++++++++++
 sys/arch/x86/x86/vmbusic.c     | 272 ++++++++++++++++++++++++++++
 sys/arch/x86/x86/vmbusicreg.h  | 248 ++++++++++++++++++++++++++
 sys/arch/x86/x86/vmbusicvar.h  |  70 ++++++++
 8 files changed, 1347 insertions(+), 5 deletions(-)
 create mode 100644 sys/arch/x86/x86/hvheartbeat.c
 create mode 100644 sys/arch/x86/x86/hvshutdown.c
 create mode 100644 sys/arch/x86/x86/hvtimesync.c
 create mode 100644 sys/arch/x86/x86/vmbusic.c
 create mode 100644 sys/arch/x86/x86/vmbusicreg.h
 create mode 100644 sys/arch/x86/x86/vmbusicvar.h

diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index 4272ae800aa..7a877e335b9 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1252,6 +1252,9 @@ vioscsi* at virtio?			# Virtio SCSI device
 vmbus*		at acpi?		# Hyper-V VMBus
 #hvn*		at vmbus?		# Hyper-V NetVSC
 #hvs*		at vmbus?		# Hyper-V StorVSC
+hvheartbeat*	at vmbus?		# Hyper-V Heartbeat
+hvshutdown*	at vmbus?		# Hyper-V Shutdown
+hvtimesync*	at vmbus?		# Hyper-V Timesync
 
 # Pull in optional local configuration
 cinclude "arch/amd64/conf/GENERIC.local"
diff --git a/sys/arch/x86/conf/files.x86 b/sys/arch/x86/conf/files.x86
index 061308304c9..f4ae0dfc39c 100644
--- a/sys/arch/x86/conf/files.x86
+++ b/sys/arch/x86/conf/files.x86
@@ -83,19 +83,19 @@ file	arch/x86/x86/vmbus.c		vmbus needs-flag
 
 device	hvheartbeat
 attach	hvheartbeat at hypervvmbus
-file	arch/x86/x86/hvheartbeat_vmbus.c hvheartbeat
+file	arch/x86/x86/hvheartbeat.c	hvheartbeat
 
 device	hvkvp
 attach	hvkvp at hypervvmbus
-file	arch/x86/x86/hvkvp_vmbus.c	hvkvp
+file	arch/x86/x86/hvkvp.c		hvkvp
 
-device	hvshutdown
+device	hvshutdown: sysmon_power, sysmon_taskq
 attach	hvshutdown at hypervvmbus
-file	arch/x86/x86/hvshutdown_vmbus.c	hvshutdown
+file	arch/x86/x86/hvshutdown.c	hvshutdown
 
 device	hvtimesync
 attach	hvtimesync at hypervvmbus
-file	arch/x86/x86/hvtimesync_vmbus.c	hvtimesync
+file	arch/x86/x86/hvtimesync.c	hvtimesync
 
 file	arch/x86/x86/vmbusic.c		hvheartbeat | hvkvp | hvshutdown |
 					hvtimesync
diff --git a/sys/arch/x86/x86/hvheartbeat.c b/sys/arch/x86/x86/hvheartbeat.c
new file mode 100644
index 00000000000..5d7d30a3514
--- /dev/null
+++ b/sys/arch/x86/x86/hvheartbeat.c
@@ -0,0 +1,199 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2014,2016 Microsoft Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __KERNEL_RCSID
+__KERNEL_RCSID(0, "$NetBSD$");
+#endif
+#ifdef __FBSDID
+__FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_heartbeat.c 310324 2016-12-20 09:46:14Z sephe $");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/pmf.h>
+
+#include <x86/x86/hypervreg.h>
+#include <x86/x86/vmbusvar.h>
+#include <x86/x86/vmbusicreg.h>
+#include <x86/x86/vmbusicvar.h>
+
+#define VMBUS_HEARTBEAT_FWVER_MAJOR	3
+#define VMBUS_HEARTBEAT_FWVER		\
+	    VMBUS_IC_VERSION(VMBUS_HEARTBEAT_FWVER_MAJOR, 0)
+
+#define VMBUS_HEARTBEAT_MSGVER_MAJOR	3
+#define VMBUS_HEARTBEAT_MSGVER		\
+	    VMBUS_IC_VERSION(VMBUS_HEARTBEAT_MSGVER_MAJOR, 0)
+
+static int	hvheartbeat_match(device_t, cfdata_t, void *);
+static void	hvheartbeat_attach(device_t, device_t, void *);
+static int	hvheartbeat_detach(device_t, int);
+
+static void	hvheartbeat_channel_cb(void *);
+
+struct hvheartbeat_softc {
+	struct vmbusic_softc	sc_vmbusic;
+};
+
+CFATTACH_DECL_NEW(hvheartbeat, sizeof(struct hvheartbeat_softc),
+    hvheartbeat_match, hvheartbeat_attach, hvheartbeat_detach, NULL);
+
+static int
+hvheartbeat_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct vmbus_attach_args *aa = aux;
+
+	return vmbusic_probe(aa, &hyperv_guid_heartbeat);
+}
+
+static void
+hvheartbeat_attach(device_t parent, device_t self, void *aux)
+{
+	struct vmbus_attach_args *aa = aux;
+	int error;
+
+	aprint_naive("\n");
+	aprint_normal(": Hyper-V Heartbeat\n");
+
+	error = vmbusic_attach(self, aa, hvheartbeat_channel_cb);
+	if (error)
+		return;
+
+	(void) pmf_device_register(self, NULL, NULL);
+}
+
+static int
+hvheartbeat_detach(device_t self, int flags)
+{
+	int error;
+
+	error = vmbusic_detach(self, flags);
+	if (error)
+		return error;
+
+	pmf_device_deregister(self);
+
+	return 0;
+}
+
+static void
+hvheartbeat_channel_cb(void *arg)
+{
+	struct hvheartbeat_softc *sc = arg;
+	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
+	struct vmbus_channel *ch = vsc->sc_chan;
+	struct vmbus_icmsg_hdr *hdr;
+	struct vmbus_icmsg_heartbeat *msg;
+	uint64_t rid;
+	uint32_t rlen;
+	int error;
+
+	error = vmbus_channel_recv(ch, vsc->sc_buf, vsc->sc_buflen,
+	    &rlen, &rid, 0);
+	if (error || rlen == 0) {
+		if (error != EAGAIN) {
+			DPRINTF("%s: heartbeat error=%d len=%u\n",
+			    device_xname(vsc->sc_dev), error, rlen);
+		}
+		return;
+	}
+	if (rlen < sizeof(*hdr)) {
+		DPRINTF("%s: heartbeat short read len=%u\n",
+		    device_xname(vsc->sc_dev), rlen);
+		return;
+	}
+
+	hdr = (struct vmbus_icmsg_hdr *)vsc->sc_buf;
+	switch (hdr->ic_type) {
+	case VMBUS_ICMSG_TYPE_NEGOTIATE:
+		error = vmbusic_negotiate(vsc, hdr, &rlen,
+		    VMBUS_HEARTBEAT_FWVER, VMBUS_HEARTBEAT_MSGVER);
+		if (error)
+			return;
+		break;
+
+	case VMBUS_ICMSG_TYPE_HEARTBEAT:
+		if (rlen < VMBUS_ICMSG_HEARTBEAT_SIZE_MIN) {
+			DPRINTF("%s: invalid heartbeat len=%u\n",
+			    device_xname(vsc->sc_dev), rlen);
+			return;
+		}
+
+		msg = (struct vmbus_icmsg_heartbeat *)hdr;
+		msg->ic_seq++;
+		break;
+
+	default:
+		aprint_error_dev(vsc->sc_dev,
+		    "unhandled heartbeat message type %u\n", hdr->ic_type);
+		return;
+	}
+
+	(void) vmbusic_sendresp(vsc, ch, vsc->sc_buf, rlen, rid);
+}
+
+MODULE(MODULE_CLASS_DRIVER, hvheartbeat, "vmbus");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+hvheartbeat_modcmd(modcmd_t cmd, void *aux)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+#ifdef _MODULE
+		error = config_init_component(cfdriver_ioconf_hvheartbeat,
+		    cfattach_ioconf_hvheartbeat, cfdata_ioconf_hvheartbeat);
+#endif
+		break;
+
+	case MODULE_CMD_FINI:
+#ifdef _MODULE
+		error = config_fini_component(cfdriver_ioconf_hvheartbeat,
+		    cfattach_ioconf_hvheartbeat, cfdata_ioconf_hvheartbeat);
+#endif
+		break;
+
+	case MODULE_CMD_AUTOUNLOAD:
+		error = EBUSY;
+		break;
+
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	return error;
+}
diff --git a/sys/arch/x86/x86/hvshutdown.c b/sys/arch/x86/x86/hvshutdown.c
new file mode 100644
index 00000000000..c7eeb3d7efa
--- /dev/null
+++ b/sys/arch/x86/x86/hvshutdown.c
@@ -0,0 +1,234 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2014,2016 Microsoft Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __KERNEL_RCSID
+__KERNEL_RCSID(0, "$NetBSD$");
+#endif
+#ifdef __FBSDID
+__FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_shutdown.c 310324 2016-12-20 09:46:14Z sephe $");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/pmf.h>
+
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/sysmon/sysmon_taskq.h>
+
+#include <x86/x86/hypervreg.h>
+#include <x86/x86/vmbusvar.h>
+#include <x86/x86/vmbusicreg.h>
+#include <x86/x86/vmbusicvar.h>
+
+#define VMBUS_SHUTDOWN_FWVER_MAJOR	3
+#define VMBUS_SHUTDOWN_FWVER		\
+	    VMBUS_IC_VERSION(VMBUS_SHUTDOWN_FWVER_MAJOR, 0)
+
+#define VMBUS_SHUTDOWN_MSGVER_MAJOR	3
+#define VMBUS_SHUTDOWN_MSGVER		\
+	    VMBUS_IC_VERSION(VMBUS_SHUTDOWN_MSGVER_MAJOR, 0)
+
+static int	hvshutdown_match(device_t, cfdata_t, void *);
+static void	hvshutdown_attach(device_t, device_t, void *);
+static int	hvshutdown_detach(device_t, int);
+
+static void	hvshutdown_channel_cb(void *);
+
+struct hvshutdown_softc {
+	struct vmbusic_softc	sc_vmbusic;
+
+	struct sysmon_pswitch	sc_smpsw;
+};
+
+CFATTACH_DECL_NEW(hvshutdown, sizeof(struct hvshutdown_softc),
+    hvshutdown_match, hvshutdown_attach, hvshutdown_detach, NULL);
+
+static int
+hvshutdown_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct vmbus_attach_args *aa = aux;
+
+	return vmbusic_probe(aa, &hyperv_guid_shutdown);
+}
+
+static void
+hvshutdown_attach(device_t parent, device_t self, void *aux)
+{
+	struct hvshutdown_softc *sc = device_private(self);
+	struct vmbus_attach_args *aa = aux;
+	int error;
+
+	aprint_naive("\n");
+	aprint_normal(": Hyper-V Shutdown\n");
+
+	error = vmbusic_attach(self, aa, hvshutdown_channel_cb);
+	if (error)
+		return;
+
+	(void) pmf_device_register(self, NULL, NULL);
+
+	sysmon_task_queue_init();
+
+	sc->sc_smpsw.smpsw_name = device_xname(self);
+	sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
+	(void) sysmon_pswitch_register(&sc->sc_smpsw);
+}
+
+static int
+hvshutdown_detach(device_t self, int flags)
+{
+	struct hvshutdown_softc *sc = device_private(self);
+	int error;
+
+	error = vmbusic_detach(self, flags);
+	if (error)
+		return error;
+
+	pmf_device_deregister(self);
+	sysmon_pswitch_unregister(&sc->sc_smpsw);
+
+	return 0;
+}
+
+static void
+hvshutdown_do_shutdown(void *arg)
+{
+	struct hvshutdown_softc *sc = arg;
+
+	sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED);
+}
+
+static void
+hvshutdown_channel_cb(void *arg)
+{
+	struct hvshutdown_softc *sc = arg;
+	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
+	struct vmbus_channel *ch = vsc->sc_chan;
+	struct vmbus_icmsg_hdr *hdr;
+	struct vmbus_icmsg_shutdown *msg;
+	uint64_t rid;
+	uint32_t rlen;
+	int error;
+	bool do_shutdown = false;
+
+	error = vmbus_channel_recv(ch, vsc->sc_buf, vsc->sc_buflen,
+	    &rlen, &rid, 0);
+	if (error || rlen == 0) {
+		if (error != EAGAIN) {
+			DPRINTF("%s: shutdown error=%d len=%u\n",
+			    device_xname(vsc->sc_dev), error, rlen);
+		}
+		return;
+	}
+	if (rlen < sizeof(*hdr)) {
+		DPRINTF("%s: shutdown short read len=%u\n",
+		    device_xname(vsc->sc_dev), rlen);
+		return;
+	}
+
+	hdr = (struct vmbus_icmsg_hdr *)vsc->sc_buf;
+	switch (hdr->ic_type) {
+	case VMBUS_ICMSG_TYPE_NEGOTIATE:
+		error = vmbusic_negotiate(vsc, hdr, &rlen, VMBUS_SHUTDOWN_FWVER,
+		    VMBUS_SHUTDOWN_MSGVER);
+		if (error)
+			return;
+		break;
+
+	case VMBUS_ICMSG_TYPE_SHUTDOWN:
+		if (rlen < VMBUS_ICMSG_SHUTDOWN_SIZE_MIN) {
+			DPRINTF("%s: invalid shutdown len=%u\n",
+			    device_xname(vsc->sc_dev), rlen);
+			return;
+		}
+
+		msg = (struct vmbus_icmsg_shutdown *)hdr;
+		if (msg->ic_haltflags == 0 || msg->ic_haltflags == 1) {
+			aprint_normal_dev(vsc->sc_dev, "shutdown requested\n");
+			hdr->ic_status = VMBUS_ICMSG_STATUS_OK;
+			do_shutdown = true;
+		} else {
+			aprint_error_dev(vsc->sc_dev,
+			    "unknown shutdown flags 0x%08x\n",
+			    msg->ic_haltflags);
+			hdr->ic_status = VMBUS_ICMSG_STATUS_FAIL;
+		}
+		break;
+
+	default:
+		aprint_error_dev(vsc->sc_dev,
+		    "unhandled shutdown message type %u\n", hdr->ic_type);
+		return;
+	}
+
+	(void) vmbusic_sendresp(vsc, ch, vsc->sc_buf, rlen, rid);
+
+	if (do_shutdown)
+		sysmon_task_queue_sched(0, hvshutdown_do_shutdown, sc);
+}
+
+MODULE(MODULE_CLASS_DRIVER, hvshutdown, "vmbus");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+hvshutdown_modcmd(modcmd_t cmd, void *aux)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+#ifdef _MODULE
+		error = config_init_component(cfdriver_ioconf_hvshutdown,
+		    cfattach_ioconf_hvshutdown, cfdata_ioconf_hvshutdown);
+#endif
+		break;
+
+	case MODULE_CMD_FINI:
+#ifdef _MODULE
+		error = config_fini_component(cfdriver_ioconf_hvshutdown,
+		    cfattach_ioconf_hvshutdown, cfdata_ioconf_hvshutdown);
+#endif
+		break;
+
+	case MODULE_CMD_AUTOUNLOAD:
+		error = EBUSY;
+		break;
+
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	return error;
+}
diff --git a/sys/arch/x86/x86/hvtimesync.c b/sys/arch/x86/x86/hvtimesync.c
new file mode 100644
index 00000000000..a5645e0e7c8
--- /dev/null
+++ b/sys/arch/x86/x86/hvtimesync.c
@@ -0,0 +1,316 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2014,2016-2017 Microsoft Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __KERNEL_RCSID
+__KERNEL_RCSID(0, "$NetBSD$");
+#endif
+#ifdef __FBSDID
+__FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_timesync.c 322488 2017-08-14 06:00:50Z sephe $");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/pmf.h>
+#include <sys/sysctl.h>
+#include <sys/timetc.h>
+
+#include <x86/x86/hypervreg.h>
+#include <x86/x86/vmbusvar.h>
+#include <x86/x86/vmbusicreg.h>
+#include <x86/x86/vmbusicvar.h>
+
+#define VMBUS_TIMESYNC_FWVER_MAJOR	3
+#define VMBUS_TIMESYNC_FWVER		\
+	    VMBUS_IC_VERSION(VMBUS_TIMESYNC_FWVER_MAJOR, 0)
+
+#define VMBUS_TIMESYNC_MSGVER_MAJOR	4
+#define VMBUS_TIMESYNC_MSGVER		\
+	    VMBUS_IC_VERSION(VMBUS_TIMESYNC_MSGVER_MAJOR, 0)
+
+#define VMBUS_TIMESYNC_MSGVER4(sc)	\
+	    VMBUS_ICVER_LE(VMBUS_IC_VERSION(4, 0), (sc)->sc_vmbusic.sc_msgver)
+
+#define VMBUS_TIMESYNC_DORTT(sc)	\
+	    (VMBUS_TIMESYNC_MSGVER4((sc)) && (hyperv_tc64 != NULL))
+
+static int	hvtimesync_match(device_t, cfdata_t, void *);
+static void	hvtimesync_attach(device_t, device_t, void *);
+static int	hvtimesync_detach(device_t, int);
+
+static void	hvtimesync_channel_cb(void *);
+static int	hvtimesync_sysctl_setup(device_t);
+
+struct hvtimesync_softc {
+	struct vmbusic_softc	sc_vmbusic;
+};
+
+CFATTACH_DECL_NEW(hvtimesync, sizeof(struct hvtimesync_softc),
+    hvtimesync_match, hvtimesync_attach, hvtimesync_detach, NULL);
+
+static int hvtimesync_ignore_sync;
+static int hvtimesnyc_sample_verbose;
+static int hvtimesync_sample_thresh = -1;
+
+static int
+hvtimesync_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct vmbus_attach_args *aa = aux;
+
+	return vmbusic_probe(aa, &hyperv_guid_timesync);
+}
+
+static void
+hvtimesync_attach(device_t parent, device_t self, void *aux)
+{
+	struct vmbus_attach_args *aa = aux;
+	int error;
+
+	aprint_naive("\n");
+	aprint_normal(": Hyper-V Timesync\n");
+
+	error = vmbusic_attach(self, aa, hvtimesync_channel_cb);
+	if (error)
+		return;
+
+	(void) pmf_device_register(self, NULL, NULL);
+
+	(void) hvtimesync_sysctl_setup(self);
+}
+
+static int
+hvtimesync_detach(device_t self, int flags)
+{
+	int error;
+
+	error = vmbusic_detach(self, flags);
+	if (error)
+		return error;
+
+	pmf_device_deregister(self);
+
+	return 0;
+}
+
+static void
+do_timesync(struct hvtimesync_softc *sc, uint64_t hvtime, uint64_t sent_tc,
+    uint8_t tsflags)
+{
+	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
+	struct timespec vm_ts, hv_ts;
+	uint64_t hv_ns, vm_ns, rtt = 0;
+	int64_t diff;
+
+	if (VMBUS_TIMESYNC_DORTT(sc))
+		rtt = hyperv_tc64() - sent_tc;
+
+	hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE + rtt) * HYPERV_TIMER_NS_FACTOR;
+	nanotime(&vm_ts);
+	vm_ns = (vm_ts.tv_sec * NANOSECOND) + vm_ts.tv_nsec;
+
+	if ((tsflags & VMBUS_ICMSG_TS_FLAG_SYNC) && !hvtimesync_ignore_sync) {
+		aprint_verbose_dev(vsc->sc_dev,
+		    "apply sync request, hv: %ju, vm: %ju\n",
+		    (uintmax_t)hv_ns, (uintmax_t)vm_ns);
+		hv_ts.tv_sec = hv_ns / NANOSECOND;
+		hv_ts.tv_nsec = hv_ns % NANOSECOND;
+		tc_setclock(&hv_ts);
+		/* Done! */
+		return;
+	}
+
+	if ((tsflags & VMBUS_ICMSG_TS_FLAG_SAMPLE) &&
+	    hvtimesync_sample_thresh >= 0) {
+		if (hvtimesnyc_sample_verbose) {
+			aprint_normal_dev(vsc->sc_dev,
+			    "sample request, hv: %ju, vm: %ju\n",
+			    (uintmax_t)hv_ns, (uintmax_t)vm_ns);
+		}
+
+		if (hv_ns > vm_ns)
+			diff = hv_ns - vm_ns;
+		else
+			diff = vm_ns - hv_ns;
+		/* nanosec -> millisec */
+		diff /= 1000000;
+
+		if (diff > hvtimesync_sample_thresh) {
+			aprint_normal_dev(vsc->sc_dev,
+			    "apply sample request, hv: %ju, vm: %ju\n",
+			    (uintmax_t)hv_ns, (uintmax_t)vm_ns);
+			hv_ts.tv_sec = hv_ns / NANOSECOND;
+			hv_ts.tv_nsec = hv_ns % NANOSECOND;
+			tc_setclock(&hv_ts);
+		}
+		/* Done */
+		return;
+	}
+}
+
+static void
+hvtimesync_channel_cb(void *arg)
+{
+	struct hvtimesync_softc *sc = arg;
+	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
+	struct vmbus_channel *ch = vsc->sc_chan;
+	struct vmbus_icmsg_hdr *hdr;
+	uint64_t rid;
+	uint32_t rlen;
+	int error;
+
+	error = vmbus_channel_recv(ch, vsc->sc_buf, vsc->sc_buflen,
+	    &rlen, &rid, 0);
+	if (error || rlen == 0) {
+		if (error != EAGAIN) {
+			DPRINTF("%s: timesync error=%d len=%u\n",
+			    device_xname(vsc->sc_dev), error, rlen);
+		}
+		return;
+	}
+	if (rlen < sizeof(*hdr)) {
+		DPRINTF("%s: hvtimesync short read len=%u\n",
+		    device_xname(vsc->sc_dev), rlen);
+		return;
+	}
+
+	hdr = (struct vmbus_icmsg_hdr *)vsc->sc_buf;
+	switch (hdr->ic_type) {
+	case VMBUS_ICMSG_TYPE_NEGOTIATE:
+		error = vmbusic_negotiate(vsc, hdr, &rlen, VMBUS_TIMESYNC_FWVER,
+		    VMBUS_TIMESYNC_MSGVER);
+		if (error)
+			return;
+		if (VMBUS_TIMESYNC_DORTT(sc)) {
+			DPRINTF("%s: RTT\n", device_xname(vsc->sc_dev));
+		}
+		break;
+
+	case VMBUS_ICMSG_TYPE_TIMESYNC:
+		if (VMBUS_TIMESYNC_MSGVER4(sc)) {
+			struct vmbus_icmsg_timesync4 *msg4;
+
+			if (rlen < sizeof(*msg4)) {
+				DPRINTF("%s: invalid timesync4 len=%u\n",
+				    device_xname(vsc->sc_dev), rlen);
+				return;
+			}
+
+			msg4 = (struct vmbus_icmsg_timesync4 *)hdr;
+			do_timesync(sc, msg4->ic_hvtime, msg4->ic_sent_tc,
+			    msg4->ic_tsflags);
+		} else {
+			struct vmbus_icmsg_timesync *msg;
+
+			if (rlen < sizeof(*msg)) {
+				DPRINTF("%s: invalid timesync len=%u\n",
+				    device_xname(vsc->sc_dev), rlen);
+				return;
+			}
+
+			msg = (struct vmbus_icmsg_timesync *)hdr;
+			do_timesync(sc, msg->ic_hvtime, 0, msg->ic_tsflags);
+		}
+		break;
+
+	default:
+		aprint_error_dev(vsc->sc_dev,
+		    "unhandled _timesync message type %u\n", hdr->ic_type);
+		return;
+	}
+
+	(void) vmbusic_sendresp(vsc, ch, vsc->sc_buf, rlen, rid);
+}
+
+static int
+hvtimesync_sysctl_setup(device_t self)
+{
+	const struct sysctlnode *node;
+	struct hvtimesync_softc *sc = device_private(self);
+	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
+	int error;
+
+	if (hyperv_sysctl_node == NULL)
+		return ENXIO;
+
+	error = sysctl_createv(&vsc->sc_log, 0, &hyperv_sysctl_node, &node,
+	    0, CTLTYPE_NODE, "timesync", NULL,
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto fail;
+
+	/* XXX sysctl hvtimesync_ignore_sync */
+	/* XXX sysctl hvtimesnyc_sample_verbose */
+	/* XXX sysctl hvtimesync_sample_thresh */
+
+	return 0;
+
+fail:
+	sysctl_teardown(&vsc->sc_log);
+	vsc->sc_log = NULL;
+	return error;
+}
+
+MODULE(MODULE_CLASS_DRIVER, hvtimesync, "vmbus");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+hvtimesync_modcmd(modcmd_t cmd, void *aux)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+#ifdef _MODULE
+		error = config_init_component(cfdriver_ioconf_hvtimesync,
+		    cfattach_ioconf_hvtimesync, cfdata_ioconf_hvtimesync);
+#endif
+		break;
+
+	case MODULE_CMD_FINI:
+#ifdef _MODULE
+		error = config_fini_component(cfdriver_ioconf_hvtimesync,
+		    cfattach_ioconf_hvtimesync, cfdata_ioconf_hvtimesync);
+#endif
+		break;
+
+	case MODULE_CMD_AUTOUNLOAD:
+		error = EBUSY;
+		break;
+
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	return error;
+}
diff --git a/sys/arch/x86/x86/vmbusic.c b/sys/arch/x86/x86/vmbusic.c
new file mode 100644
index 00000000000..59bbb9d3662
--- /dev/null
+++ b/sys/arch/x86/x86/vmbusic.c
@@ -0,0 +1,272 @@
+/*	$NetBSD$	*/
+/*-
+ * Copyright (c) 2014,2016 Microsoft Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __KERNEL_RCSID
+__KERNEL_RCSID(0, "$NetBSD$");
+#endif
+#ifdef __FBSDID
+__FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_ic.c 310317 2016-12-20 07:14:24Z sephe $");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/reboot.h>
+
+#include <x86/x86/hypervreg.h>
+#include <x86/x86/vmbusvar.h>
+#include <x86/x86/vmbusicreg.h>
+#include <x86/x86/vmbusicvar.h>
+
+#define VMBUS_IC_BRSIZE		(4 * PAGE_SIZE)
+
+#define VMBUS_IC_VERCNT         2
+#define VMBUS_IC_NEGOSZ		\
+    offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT])
+__CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE);
+
+
+int
+vmbusic_probe(struct vmbus_attach_args *aa, const struct hyperv_guid *guid)
+{
+
+	if (memcmp(aa->aa_type, guid, sizeof(*aa->aa_type)) != 0)
+		return 0;
+	return 1;
+}
+
+int
+vmbusic_attach(device_t dv, struct vmbus_attach_args *aa,
+    vmbus_channel_callback_t cb)
+{
+	struct vmbusic_softc *sc = device_private(dv);
+
+	sc->sc_dev = dv;
+	sc->sc_chan = aa->aa_chan;
+
+	sc->sc_buflen = VMBUS_IC_BRSIZE;
+	sc->sc_buf = kmem_alloc(sc->sc_buflen, KM_SLEEP);
+
+	/*
+	 * These services are not performance critical and do not need
+	 * batched reading. Furthermore, some services such as KVP can
+	 * only handle one message from the host at a time.
+	 * Turn off batched reading for all util drivers before we open the
+	 * channel.
+	 */
+	sc->sc_chan->ch_flags &= ~CHF_BATCHED;
+
+	if (vmbus_channel_open(sc->sc_chan, sc->sc_buflen, NULL, 0, cb, sc)) {
+		aprint_error_dev(dv, "failed to open channel\n");
+		kmem_free(sc->sc_buf, sc->sc_buflen);
+		sc->sc_buf = NULL;
+		return ENXIO;
+	}
+
+	return 0;
+}
+
+int
+vmbusic_detach(device_t dv, int flags)
+{
+	struct vmbusic_softc *sc = device_private(dv);
+	int error;
+
+	error = vmbus_channel_close(sc->sc_chan);
+	if (error != 0)
+		return error;
+
+	if (sc->sc_buf != NULL) {
+		kmem_free(sc->sc_buf, sc->sc_buflen);
+		sc->sc_buf = NULL;
+	}
+
+	if (sc->sc_log != NULL) {
+		sysctl_teardown(&sc->sc_log);
+		sc->sc_log = NULL;
+	}
+
+	return 0;
+}
+
+int
+vmbusic_negotiate(struct vmbusic_softc *sc, void *data, uint32_t *dlen0,
+    uint32_t fw_ver, uint32_t msg_ver)
+{
+	struct vmbus_icmsg_negotiate *nego;
+	uint32_t sel_fw_ver = 0, sel_msg_ver = 0;
+	int i, cnt, dlen = *dlen0, error;
+	bool has_fw_ver, has_msg_ver = false;
+
+	/*
+	 * Preliminary message verification.
+	 */
+	if (dlen < sizeof(*nego)) {
+		aprint_error_dev(sc->sc_dev, "truncated ic negotiate, len %d\n",
+		    dlen);
+		return EINVAL;
+	}
+	nego = data;
+
+	if (nego->ic_fwver_cnt == 0) {
+		aprint_error_dev(sc->sc_dev, "ic negotiate does not contain "
+		    "framework version %u\n", nego->ic_fwver_cnt);
+		return EINVAL;
+	}
+	if (nego->ic_msgver_cnt == 0) {
+		aprint_error_dev(sc->sc_dev, "ic negotiate does not contain "
+		    "message version %u\n", nego->ic_msgver_cnt);
+		return EINVAL;
+	}
+
+	cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt;
+	if (dlen < offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) {
+		aprint_error_dev(sc->sc_dev, "ic negotiate does not contain "
+		    "versions %d\n", dlen);
+		return EINVAL;
+	}
+
+	error = EOPNOTSUPP;
+
+	/*
+	 * Find the best match framework version.
+	 */
+	has_fw_ver = false;
+	for (i = 0; i < nego->ic_fwver_cnt; ++i) {
+		if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) {
+			if (!has_fw_ver) {
+				sel_fw_ver = nego->ic_ver[i];
+				has_fw_ver = true;
+			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
+			    sel_fw_ver)) {
+				sel_fw_ver = nego->ic_ver[i];
+			}
+		}
+	}
+	if (!has_fw_ver) {
+		aprint_error_dev(sc->sc_dev, "failed to select framework "
+		    "version\n");
+		goto done;
+	}
+
+	/*
+	 * Fine the best match message version.
+	 */
+	has_msg_ver = false;
+	for (i = nego->ic_fwver_cnt;
+	    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) {
+		if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) {
+			if (!has_msg_ver) {
+				sel_msg_ver = nego->ic_ver[i];
+				has_msg_ver = true;
+			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
+			    sel_msg_ver)) {
+				sel_msg_ver = nego->ic_ver[i];
+			}
+		}
+	}
+	if (!has_msg_ver) {
+		aprint_error_dev(sc->sc_dev, "failed to select message "
+		    "version\n");
+		goto done;
+	}
+
+	error = 0;
+done:
+	if (bootverbose || !has_fw_ver || !has_msg_ver) {
+		if (has_fw_ver) {
+			aprint_verbose_dev(sc->sc_dev,
+			    "sel framework version: %u.%u\n",
+			    VMBUS_ICVER_MAJOR(sel_fw_ver),
+			    VMBUS_ICVER_MINOR(sel_fw_ver));
+		}
+		for (i = 0; i < nego->ic_fwver_cnt; i++) {
+			aprint_error_dev(sc->sc_dev,
+			    "supp framework version: %u.%u\n",
+			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
+			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
+		}
+
+		if (has_msg_ver) {
+			aprint_verbose_dev(sc->sc_dev,
+			    "sel message version: %u.%u\n",
+			    VMBUS_ICVER_MAJOR(sel_msg_ver),
+			    VMBUS_ICVER_MINOR(sel_msg_ver));
+		}
+		for (i = nego->ic_fwver_cnt;
+		    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) {
+			aprint_error_dev(sc->sc_dev,
+			    "supp message version: %u.%u\n",
+			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
+			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
+		}
+	}
+	if (error)
+		return error;
+
+	/* Record the selected versions. */
+	sc->sc_fwver = sel_fw_ver;
+	sc->sc_msgver = sel_msg_ver;
+
+	/* One framework version. */
+	nego->ic_fwver_cnt = 1;
+	nego->ic_ver[0] = sel_fw_ver;
+
+	/* One message version. */
+	nego->ic_msgver_cnt = 1;
+	nego->ic_ver[1] = sel_msg_ver;
+
+	/* Update data size. */
+	nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ -
+	    sizeof(struct vmbus_icmsg_hdr);
+
+	/* Update total size, if necessary. */
+	if (dlen < VMBUS_IC_NEGOSZ)
+		*dlen0 = VMBUS_IC_NEGOSZ;
+
+	return 0;
+}
+
+int
+vmbusic_sendresp(struct vmbusic_softc *sc, struct vmbus_channel *chan,
+    void *data, uint32_t dlen, uint64_t rid)
+{
+	struct vmbus_icmsg_hdr *hdr;
+	int error;
+
+	KASSERTMSG(dlen >= sizeof(*hdr), "invalid data length %d", dlen);
+	hdr = data;
+
+	hdr->ic_flags = VMBUS_ICMSG_FLAG_TRANSACTION|VMBUS_ICMSG_FLAG_RESPONSE;
+	error = vmbus_channel_send(chan, data, dlen, rid,
+	    VMBUS_CHANPKT_TYPE_INBAND, 0);
+	if (error != 0)
+		aprint_error_dev(sc->sc_dev, "resp send failed: %d\n", error);
+	return error;
+}
diff --git a/sys/arch/x86/x86/vmbusicreg.h b/sys/arch/x86/x86/vmbusicreg.h
new file mode 100644
index 00000000000..6c9f00e81d0
--- /dev/null
+++ b/sys/arch/x86/x86/vmbusicreg.h
@@ -0,0 +1,248 @@
+/*	$NetBSD$	*/
+/*	$OpenBSD: hypervicreg.h,v 1.6 2017/11/07 16:49:42 mikeb Exp $	*/
+
+/*-
+ * Copyright (c) 2016 Microsoft Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/sys/dev/hyperv/utilities/vmbus_icreg.h 305281 2016-09-02 06:23:28Z sephe $
+ */
+
+#ifndef _VMBUSICREG_H_
+#define _VMBUSICREG_H_
+
+#define VMBUS_IC_BUFRINGSIZE		(4 * PAGE_SIZE)
+
+#define VMBUS_ICMSG_TYPE_NEGOTIATE	0
+#define VMBUS_ICMSG_TYPE_HEARTBEAT	1
+#define VMBUS_ICMSG_TYPE_KVP		2
+#define VMBUS_ICMSG_TYPE_SHUTDOWN	3
+#define VMBUS_ICMSG_TYPE_TIMESYNC	4
+#define VMBUS_ICMSG_TYPE_VSS		5
+
+#define VMBUS_ICMSG_STATUS_OK		0x00000000
+#define VMBUS_ICMSG_STATUS_FAIL		0x80004005
+
+#define VMBUS_IC_VERSION(major, minor)	((major) | (((uint32_t)(minor)) << 16))
+#define VMBUS_ICVER_MAJOR(ver)		((ver) & 0xffff)
+#define VMBUS_ICVER_MINOR(ver)		(((ver) & 0xffff0000) >> 16)
+#define VMBUS_ICVER_SWAP(ver)		\
+            ((VMBUS_ICVER_MAJOR((ver)) << 16) | VMBUS_ICVER_MINOR((ver)))
+#define VMBUS_ICVER_LE(v1, v2)		\
+            (VMBUS_ICVER_SWAP((v1)) <= VMBUS_ICVER_SWAP((v2)))
+#define VMBUS_ICVER_GT(v1, v2)		\
+            (VMBUS_ICVER_SWAP((v1)) > VMBUS_ICVER_SWAP((v2)))
+
+struct vmbus_pipe_hdr {
+	uint32_t		ph_flags;
+	uint32_t		ph_msgsz;
+} __packed;
+
+struct vmbus_icmsg_hdr {
+	struct vmbus_pipe_hdr	ic_pipe;
+	uint32_t		ic_fwver;	/* framework version */
+	uint16_t		ic_type;
+	uint32_t		ic_msgver;	/* message version */
+	uint16_t		ic_dsize;	/* data size */
+	uint32_t		ic_status;	/* VMBUS_ICMSG_STATUS_ */
+	uint8_t			ic_tid;
+	uint8_t			ic_flags;	/* VMBUS_ICMSG_FLAG_ */
+	uint8_t			ic_rsvd[2];
+} __packed;
+
+#define VMBUS_ICMSG_FLAG_TRANSACTION	1
+#define VMBUS_ICMSG_FLAG_REQUEST	2
+#define VMBUS_ICMSG_FLAG_RESPONSE	4
+
+/* VMBUS_ICMSG_TYPE_NEGOTIATE */
+struct vmbus_icmsg_negotiate {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	uint16_t		ic_fwver_cnt;
+	uint16_t		ic_msgver_cnt;
+	uint32_t		ic_rsvd;
+	/*
+	 * This version array contains two set of supported
+	 * versions:
+	 * - The first set consists of #ic_fwver_cnt supported framework
+	 *   versions.
+	 * - The second set consists of #ic_msgver_cnt supported message
+	 *   versions.
+	 */
+	uint32_t		ic_ver[0];
+} __packed;
+
+/* VMBUS_ICMSG_TYPE_HEARTBEAT */
+struct vmbus_icmsg_heartbeat {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	uint64_t		ic_seq;
+	uint32_t		ic_rsvd[8];
+} __packed;
+
+#define VMBUS_ICMSG_HEARTBEAT_SIZE_MIN	\
+	    offsetof(struct vmbus_icmsg_heartbeat, ic_rsvd[0])
+
+/* VMBUS_ICMSG_TYPE_SHUTDOWN */
+struct vmbus_icmsg_shutdown {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	uint32_t		ic_code;
+	uint32_t		ic_timeo;
+	uint32_t 		ic_haltflags;
+	uint8_t			ic_msg[2048];
+} __packed;
+
+#define VMBUS_ICMSG_SHUTDOWN_SIZE_MIN	\
+	    offsetof(struct vmbus_icmsg_shutdown, ic_msg[0])
+
+/* VMBUS_ICMSG_TYPE_TIMESYNC */
+struct vmbus_icmsg_timesync {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	uint64_t		ic_hvtime;
+	uint64_t		ic_vmtime;
+	uint64_t		ic_rtt;
+	uint8_t			ic_tsflags;	/* VMBUS_ICMSG_TS_FLAG_ */
+} __packed;
+
+/* VMBUS_ICMSG_TYPE_TIMESYNC, MSGVER4 */
+struct vmbus_icmsg_timesync4 {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	uint64_t		ic_hvtime;
+	uint64_t		ic_sent_tc;
+	uint8_t			ic_tsflags;	/* VMBUS_ICMSG_TS_FLAG_ */
+	uint8_t			ic_rsvd[5];
+} __packed;
+
+#define VMBUS_ICMSG_TS_FLAG_SYNC	0x01
+#define VMBUS_ICMSG_TS_FLAG_SAMPLE	0x02
+
+#define VMBUS_ICMSG_TS_BASE		116444736000000000ULL
+
+/* Registry value types */
+#define VMBUS_KVP_REG_SZ		1
+#define VMBUS_KVP_REG_U32		4
+#define VMBUS_KVP_REG_U64		8
+
+/* Hyper-V status codes */
+#define VMBUS_KVP_S_OK			0x00000000
+#define VMBUS_KVP_E_FAIL		0x80004005
+#define VMBUS_KVP_S_CONT		0x80070103
+
+#define VMBUS_KVP_MAX_VAL_SIZE		2048
+#define VMBUS_KVP_MAX_KEY_SIZE		512
+
+enum vmbus_kvp_op {
+	VMBUS_KVP_OP_GET = 0,
+	VMBUS_KVP_OP_SET,
+	VMBUS_KVP_OP_DELETE,
+	VMBUS_KVP_OP_ENUMERATE,
+	VMBUS_KVP_OP_GET_IP_INFO,
+	VMBUS_KVP_OP_SET_IP_INFO,
+	VMBUS_KVP_OP_COUNT
+};
+
+enum vmbus_kvp_pool {
+	VMBUS_KVP_POOL_EXTERNAL = 0,
+	VMBUS_KVP_POOL_GUEST,
+	VMBUS_KVP_POOL_AUTO,
+	VMBUS_KVP_POOL_AUTO_EXTERNAL,
+	VMBUS_KVP_POOL_COUNT
+};
+
+union vmbus_kvp_hdr {
+	struct {
+		uint8_t		kvu_op;
+		uint8_t		kvu_pool;
+		uint16_t	kvu_pad;
+	} req;
+	struct {
+		uint32_t	kvu_err;
+	} rsp;
+#define kvh_op			req.kvu_op
+#define kvh_pool		req.kvu_pool
+#define kvh_err			rsp.kvu_err
+} __packed;
+
+struct vmbus_kvp_msg_val {
+	uint32_t		kvm_valtype;
+	uint32_t		kvm_keylen;
+	uint32_t		kvm_vallen;
+	uint8_t			kvm_key[VMBUS_KVP_MAX_KEY_SIZE];
+	uint8_t			kvm_val[VMBUS_KVP_MAX_VAL_SIZE];
+} __packed;
+
+struct vmbus_kvp_msg_enum {
+	uint32_t		kvm_index;
+	uint32_t		kvm_valtype;
+	uint32_t		kvm_keylen;
+	uint32_t		kvm_vallen;
+	uint8_t			kvm_key[VMBUS_KVP_MAX_KEY_SIZE];
+	uint8_t			kvm_val[VMBUS_KVP_MAX_VAL_SIZE];
+} __packed;
+
+struct vmbus_kvp_msg_del {
+	uint32_t		kvm_keylen;
+	uint8_t			kvm_key[VMBUS_KVP_MAX_KEY_SIZE];
+} __packed;
+
+#define ADDR_FAMILY_NONE	0x00
+#define ADDR_FAMILY_IPV4	0x01
+#define ADDR_FAMILY_IPV6	0x02
+
+#define MAX_MAC_ADDR_SIZE	256
+#define MAX_IP_ADDR_SIZE	2048
+#define MAX_GATEWAY_SIZE	1024
+
+struct vmbus_kvp_msg_addr {
+	uint8_t			kvm_mac[MAX_MAC_ADDR_SIZE];
+	uint8_t			kvm_family;
+	uint8_t			kvm_dhcp;
+	uint8_t			kvm_addr[MAX_IP_ADDR_SIZE];
+	uint8_t			kvm_netmask[MAX_IP_ADDR_SIZE];
+	uint8_t			kvm_gateway[MAX_GATEWAY_SIZE];
+	uint8_t			kvm_dns[MAX_IP_ADDR_SIZE];
+} __packed;
+
+union vmbus_kvp_msg {
+	struct vmbus_kvp_msg_val	kvm_val;
+	struct vmbus_kvp_msg_enum	kvm_enum;
+	struct vmbus_kvp_msg_del	kvm_del;
+};
+
+struct vmbus_icmsg_kvp {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	union vmbus_kvp_hdr	ic_kvh;
+	union vmbus_kvp_msg	ic_kvm;
+} __packed;
+
+struct vmbus_icmsg_kvp_addr {
+	struct vmbus_icmsg_hdr	ic_hdr;
+	struct {
+		struct {
+			uint8_t	kvu_op;
+			uint8_t	kvu_pool;
+		} req;
+	}			ic_kvh;
+	struct vmbus_kvp_msg_addr ic_kvm;
+} __packed;
+
+#endif	/* _VMBUSICREG_H_ */
diff --git a/sys/arch/x86/x86/vmbusicvar.h b/sys/arch/x86/x86/vmbusicvar.h
new file mode 100644
index 00000000000..a3d451fd044
--- /dev/null
+++ b/sys/arch/x86/x86/vmbusicvar.h
@@ -0,0 +1,70 @@
+/*	$NetBSD$	*/
+/*	$OpenBSD: hypervic.c,v 1.13 2017/08/10 15:25:57 mikeb Exp $	*/
+
+/*-
+ * Copyright (c) 2009-2016 Microsoft Corp.
+ * Copyright (c) 2012 NetApp Inc.
+ * Copyright (c) 2012 Citrix Inc.
+ * Copyright (c) 2016 Mike Belopuhov <mike@esdenera.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The OpenBSD port was done under funding by Esdenera Networks GmbH.
+ */
+
+#ifndef	_VMBUSICVAR_H_
+#define	_VMBUSICVAR_H_
+
+#include <sys/sysctl.h>
+
+#include <x86/x86/hypervreg.h>
+#include <x86/x86/hypervvar.h>
+#include <x86/x86/vmbusvar.h>
+
+struct vmbusic_softc {
+	device_t		sc_dev;
+
+	struct vmbus_channel	*sc_chan;
+
+	void			*sc_buf;
+	size_t			sc_buflen;
+
+	uint32_t		sc_fwver;	/* framework version */
+	uint32_t		sc_msgver;	/* message version */
+
+	struct sysctllog	*sc_log;
+};
+
+int	vmbusic_probe(struct vmbus_attach_args *, const struct hyperv_guid *);
+int	vmbusic_attach(device_t, struct vmbus_attach_args *,
+	    vmbus_channel_callback_t);
+int	vmbusic_detach(device_t, int);
+
+int	vmbusic_negotiate(struct vmbusic_softc *, void *, uint32_t *, uint32_t,
+	    uint32_t);
+int	vmbusic_sendresp(struct vmbusic_softc *, struct vmbus_channel *,
+	    void *, uint32_t, uint64_t);
+
+#endif	/* _VMBUSICVAR_H_ */
-- 
2.17.0

