Module Name:    src
Committed By:   ryo
Date:           Tue Oct 27 08:57:11 UTC 2020

Modified Files:
        src/distrib/sets/lists/man: mi
        src/share/man/man4: Makefile
        src/share/man/man4/man4.x86: Makefile
        src/sys/arch/evbarm/conf: GENERIC64
        src/sys/arch/x86/conf: files.x86
        src/sys/arch/x86/x86: identcpu.c lapic.c vmt.c
        src/sys/conf: files
        src/sys/dev/fdt: files.fdt
        src/sys/modules/vmt: Makefile
Added Files:
        src/share/man/man4: vmt.4
        src/sys/dev/vmt: files.vmt vmt_subr.c vmtreg.h vmtvar.h
Removed Files:
        src/share/man/man4/man4.x86: vmt.4
        src/sys/arch/x86/x86: vmtreg.h vmtvar.h

Log Message:
move vmt(4) from MD to MI, and add support vmt on aarch64. tested on ESXi-Arm 
Fling

- move from sys/arch/x86/x86/{vmt.c,vmtreg.h,vmtvar.h} to 
sys/dev/vmt/{vmt_subr.c,vmtreg.h,vmtvar.h},
  and split the attach part of the cpufeaturebus and fdt
- add aarch64 vmware backdoor op
- add include guard to vmt{reg,var}.h
- Yet there is still some little-endian dependency. it needs to be fixed in 
order to work properly on aarch64eb


To generate a diff of this commit:
cvs rdiff -u -r1.1706 -r1.1707 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.710 -r1.711 src/share/man/man4/Makefile
cvs rdiff -u -r0 -r1.3 src/share/man/man4/vmt.4
cvs rdiff -u -r1.20 -r1.21 src/share/man/man4/man4.x86/Makefile
cvs rdiff -u -r1.6 -r0 src/share/man/man4/man4.x86/vmt.4
cvs rdiff -u -r1.163 -r1.164 src/sys/arch/evbarm/conf/GENERIC64
cvs rdiff -u -r1.119 -r1.120 src/sys/arch/x86/conf/files.x86
cvs rdiff -u -r1.117 -r1.118 src/sys/arch/x86/x86/identcpu.c
cvs rdiff -u -r1.84 -r1.85 src/sys/arch/x86/x86/lapic.c
cvs rdiff -u -r1.20 -r1.21 src/sys/arch/x86/x86/vmt.c
cvs rdiff -u -r1.2 -r0 src/sys/arch/x86/x86/vmtreg.h
cvs rdiff -u -r1.1 -r0 src/sys/arch/x86/x86/vmtvar.h
cvs rdiff -u -r1.1276 -r1.1277 src/sys/conf/files
cvs rdiff -u -r1.54 -r1.55 src/sys/dev/fdt/files.fdt
cvs rdiff -u -r0 -r1.1 src/sys/dev/vmt/files.vmt src/sys/dev/vmt/vmt_subr.c \
    src/sys/dev/vmt/vmtreg.h src/sys/dev/vmt/vmtvar.h
cvs rdiff -u -r1.3 -r1.4 src/sys/modules/vmt/Makefile

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1706 src/distrib/sets/lists/man/mi:1.1707
--- src/distrib/sets/lists/man/mi:1.1706	Fri Oct 16 15:40:40 2020
+++ src/distrib/sets/lists/man/mi	Tue Oct 27 08:57:10 2020
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1706 2020/10/16 15:40:40 ryo Exp $
+# $NetBSD: mi,v 1.1707 2020/10/27 08:57:10 ryo Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2024,6 +2024,7 @@
 ./usr/share/man/cat4/vlan.0			man-sys-catman		.cat
 ./usr/share/man/cat4/vmmon.0			man-sys-catman		.cat
 ./usr/share/man/cat4/vmnet.0			man-sys-catman		.cat
+./usr/share/man/cat4/vmt.0			man-sys-catman		.cat
 ./usr/share/man/cat4/vmx.0			man-sys-catman		.cat
 ./usr/share/man/cat4/vnd.0			man-sys-catman		.cat
 ./usr/share/man/cat4/voodoofb.0			man-sys-catman		.cat
@@ -2089,7 +2090,7 @@
 ./usr/share/man/cat4/x86/tco.0			man-sys-catman		.cat
 ./usr/share/man/cat4/x86/tprof_amdpmi.0		man-obsolete		obsolete
 ./usr/share/man/cat4/x86/tprof_pmi.0		man-obsolete		obsolete
-./usr/share/man/cat4/x86/vmt.0			man-sys-catman		.cat
+./usr/share/man/cat4/x86/vmt.0			man-obsolete		obsolete
 ./usr/share/man/cat4/x86/vmx.0			man-obsolete		obsolete
 ./usr/share/man/cat4/xbd.0			man-sys-catman		.cat
 ./usr/share/man/cat4/xbdback.0			man-sys-catman		.cat
@@ -5164,6 +5165,7 @@
 ./usr/share/man/html4/vlan.html			man-sys-htmlman		html
 ./usr/share/man/html4/vmmon.html		man-sys-htmlman		html
 ./usr/share/man/html4/vmnet.html		man-sys-htmlman		html
+./usr/share/man/html4/vmt.html			man-sys-htmlman		html
 ./usr/share/man/html4/vmx.html			man-sys-htmlman		html
 ./usr/share/man/html4/vnd.html			man-sys-htmlman		html
 ./usr/share/man/html4/voodoofb.html		man-sys-htmlman		html
@@ -5229,7 +5231,7 @@
 ./usr/share/man/html4/x86/tco.html		man-sys-htmlman		html
 ./usr/share/man/html4/x86/tprof_amdpmi.html	man-obsolete		obsolete
 ./usr/share/man/html4/x86/tprof_pmi.html	man-obsolete		obsolete
-./usr/share/man/html4/x86/vmt.html		man-sys-htmlman		html
+./usr/share/man/html4/x86/vmt.html		man-obsolete		obsolete
 ./usr/share/man/html4/x86/vmx.html		man-obsolete		obsolete
 ./usr/share/man/html4/xbd.html			man-sys-htmlman		html
 ./usr/share/man/html4/xbdback.html		man-sys-htmlman		html
@@ -8236,6 +8238,7 @@
 ./usr/share/man/man4/vlan.4			man-sys-man		.man
 ./usr/share/man/man4/vmmon.4			man-sys-man		.man
 ./usr/share/man/man4/vmnet.4			man-sys-man		.man
+./usr/share/man/man4/vmt.4			man-sys-man		.man
 ./usr/share/man/man4/vmx.4			man-sys-man		.man
 ./usr/share/man/man4/vnd.4			man-sys-man		.man
 ./usr/share/man/man4/voodoofb.4			man-sys-man		.man
@@ -8301,7 +8304,7 @@
 ./usr/share/man/man4/x86/tco.4			man-sys-man		.man
 ./usr/share/man/man4/x86/tprof_amdpmi.4		man-obsolete		obsolete
 ./usr/share/man/man4/x86/tprof_pmi.4		man-obsolete		obsolete
-./usr/share/man/man4/x86/vmt.4			man-sys-man		.man
+./usr/share/man/man4/x86/vmt.4			man-obsolete		obsolete
 ./usr/share/man/man4/x86/vmx.4			man-obsolete		obsolete
 ./usr/share/man/man4/xbd.4			man-sys-man		.man
 ./usr/share/man/man4/xbdback.4			man-sys-man		.man

Index: src/share/man/man4/Makefile
diff -u src/share/man/man4/Makefile:1.710 src/share/man/man4/Makefile:1.711
--- src/share/man/man4/Makefile:1.710	Wed Oct 14 10:19:11 2020
+++ src/share/man/man4/Makefile	Tue Oct 27 08:57:10 2020
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.710 2020/10/14 10:19:11 ryo Exp $
+#	$NetBSD: Makefile,v 1.711 2020/10/27 08:57:10 ryo Exp $
 #	@(#)Makefile	8.1 (Berkeley) 6/18/93
 
 MAN=	aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@@ -69,7 +69,8 @@ MAN=	aac.4 ac97.4 acardide.4 aceride.4 a
 	unix.4 userconf.4 \
 	vald.4 valz.4 veriexec.4 vga.4 vge.4 viaide.4 video.4 \
 	vio9p.4 vioif.4 viomb.4 viornd.4 vioscsi.4 virt.4 virtio.4 \
-	vether.4 vlan.4 vmmon.4 vmnet.4 vmx.4 vnd.4 voodoofb.4 vr.4 vte.4 \
+	vether.4 vlan.4 vmmon.4 vmnet.4 vmt.4 vmx.4 vnd.4 voodoofb.4 vr.4 \
+	vte.4 \
 	wapbl.4 wb.4 wbsio.4 wd.4 wdc.4 wg.4 wi.4 wm.4 wpi.4 \
 	wsbell.4 wscons.4 wsdisplay.4 wsfont.4 wskbd.4 wsmouse.4 wsmux.4 \
 	xbox.4 xge.4 \

Index: src/share/man/man4/man4.x86/Makefile
diff -u src/share/man/man4/man4.x86/Makefile:1.20 src/share/man/man4/man4.x86/Makefile:1.21
--- src/share/man/man4/man4.x86/Makefile:1.20	Wed Oct 14 10:19:11 2020
+++ src/share/man/man4/man4.x86/Makefile	Tue Oct 27 08:57:10 2020
@@ -1,8 +1,8 @@
-#	$NetBSD: Makefile,v 1.20 2020/10/14 10:19:11 ryo Exp $
+#	$NetBSD: Makefile,v 1.21 2020/10/27 08:57:10 ryo Exp $
 
 MAN=	amdpcib.4 apic.4 balloon.4 coretemp.4 est.4 fdc.4 \
 	fwhrng.4 hpet.4 ichlpcib.4 imcsmb.4 lpt.4 mem.4 odcm.4 powernow.4 \
-	soekrisgpio.4 tco.4 vmt.4 \
+	soekrisgpio.4 tco.4 \
 	amdsmn.4 amdzentemp.4
 
 MLINKS+=apic.4 ioapic.4 \

Index: src/sys/arch/evbarm/conf/GENERIC64
diff -u src/sys/arch/evbarm/conf/GENERIC64:1.163 src/sys/arch/evbarm/conf/GENERIC64:1.164
--- src/sys/arch/evbarm/conf/GENERIC64:1.163	Thu Oct 15 04:39:24 2020
+++ src/sys/arch/evbarm/conf/GENERIC64	Tue Oct 27 08:57:10 2020
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: GENERIC64,v 1.163 2020/10/15 04:39:24 ryo Exp $
+#	$NetBSD: GENERIC64,v 1.164 2020/10/27 08:57:10 ryo Exp $
 #
 #	GENERIC ARM (aarch64) kernel
 #
@@ -100,6 +100,8 @@ cpus*		at fdt? pass 0
 cpu*		at fdt? pass 0
 cpu*		at acpi?
 
+vmt*		at fdt?			# VMware Tools
+
 # Performance monitors
 armpmu*		at fdt?
 pseudo-device	tprof

Index: src/sys/arch/x86/conf/files.x86
diff -u src/sys/arch/x86/conf/files.x86:1.119 src/sys/arch/x86/conf/files.x86:1.120
--- src/sys/arch/x86/conf/files.x86:1.119	Sat Aug  1 12:15:40 2020
+++ src/sys/arch/x86/conf/files.x86	Tue Oct 27 08:57:11 2020
@@ -1,4 +1,4 @@
-#	$NetBSD: files.x86,v 1.119 2020/08/01 12:15:40 jdolecek Exp $
+#	$NetBSD: files.x86,v 1.120 2020/10/27 08:57:11 ryo Exp $
 
 # options for MP configuration through the MP spec
 defflag opt_mpbios.h MPBIOS MPDEBUG MPBIOS_SCANPCI
@@ -74,7 +74,6 @@ device	viac7temp: sysmon_envsys
 attach	viac7temp at cpufeaturebus
 file	arch/x86/x86/viac7temp.c	viac7temp
 
-device	vmt: sysmon_power, sysmon_taskq
 attach	vmt at cpufeaturebus
 file	arch/x86/x86/vmt.c		vmt
 

Index: src/sys/arch/x86/x86/identcpu.c
diff -u src/sys/arch/x86/x86/identcpu.c:1.117 src/sys/arch/x86/x86/identcpu.c:1.118
--- src/sys/arch/x86/x86/identcpu.c:1.117	Sat Sep  5 07:45:44 2020
+++ src/sys/arch/x86/x86/identcpu.c	Tue Oct 27 08:57:11 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: identcpu.c,v 1.117 2020/09/05 07:45:44 maxv Exp $	*/
+/*	$NetBSD: identcpu.c,v 1.118 2020/10/27 08:57:11 ryo Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.117 2020/09/05 07:45:44 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.118 2020/10/27 08:57:11 ryo Exp $");
 
 #include "opt_xen.h"
 
@@ -58,8 +58,8 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu.c,v
 #include <x86/cpuvar.h>
 #include <x86/fpu.h>
 
-#include <x86/x86/vmtreg.h>	/* for vmt_hvcall() */
-#include <x86/x86/vmtvar.h>	/* for vmt_hvcall() */
+#include <dev/vmt/vmtreg.h>	/* for vmt_hvcall() */
+#include <dev/vmt/vmtvar.h>	/* for vmt_hvcall() */
 
 #ifndef XENPV
 #include "hyperv.h"

Index: src/sys/arch/x86/x86/lapic.c
diff -u src/sys/arch/x86/x86/lapic.c:1.84 src/sys/arch/x86/x86/lapic.c:1.85
--- src/sys/arch/x86/x86/lapic.c:1.84	Tue Jul 14 00:45:53 2020
+++ src/sys/arch/x86/x86/lapic.c	Tue Oct 27 08:57:11 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: lapic.c,v 1.84 2020/07/14 00:45:53 yamaguchi Exp $	*/
+/*	$NetBSD: lapic.c,v 1.85 2020/10/27 08:57:11 ryo Exp $	*/
 
 /*-
  * Copyright (c) 2000, 2008, 2020 The NetBSD Foundation, Inc.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.84 2020/07/14 00:45:53 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.85 2020/10/27 08:57:11 ryo Exp $");
 
 #include "acpica.h"
 #include "ioapic.h"
@@ -90,8 +90,8 @@ extern int ddb_vec;
 #endif
 #endif
 
-#include <x86/x86/vmtreg.h>	/* for vmt_hvcall() */
-#include <x86/x86/vmtvar.h>	/* for vmt_hvcall() */
+#include <dev/vmt/vmtreg.h>	/* for vmt_hvcall() */
+#include <dev/vmt/vmtvar.h>	/* for vmt_hvcall() */
 
 /* Referenced from vector.S */
 void		lapic_clockintr(void *, struct intrframe *);

Index: src/sys/arch/x86/x86/vmt.c
diff -u src/sys/arch/x86/x86/vmt.c:1.20 src/sys/arch/x86/x86/vmt.c:1.21
--- src/sys/arch/x86/x86/vmt.c:1.20	Fri Dec 29 07:53:33 2017
+++ src/sys/arch/x86/x86/vmt.c	Tue Oct 27 08:57:11 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: vmt.c,v 1.20 2017/12/29 07:53:33 nakayama Exp $ */
+/* $NetBSD: vmt.c,v 1.21 2020/10/27 08:57:11 ryo Exp $ */
 /* $OpenBSD: vmt.c,v 1.11 2011/01/27 21:29:25 dtucker Exp $ */
 
 /*
@@ -24,140 +24,22 @@
  */
 
 #include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/device.h>
 #include <sys/types.h>
-#include <sys/kmem.h>
-#include <sys/callout.h>
-#include <sys/reboot.h>
-#include <sys/syslog.h>
-#include <sys/proc.h>
-#include <sys/socket.h>
-#include <sys/timetc.h>
+#include <sys/device.h>
 #include <sys/module.h>
-#include <sys/sysctl.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-
 #include <machine/cpuvar.h>
 
 #include <dev/sysmon/sysmonvar.h>
-#include <dev/sysmon/sysmon_taskq.h>
-
-#include <x86/x86/vmtreg.h>
-#include <x86/x86/vmtvar.h>
-
-/* #define VMT_DEBUG */
+#include <dev/vmt/vmtreg.h>
+#include <dev/vmt/vmtvar.h>
 
 static int	vmt_match(device_t, cfdata_t, void *);
 static void	vmt_attach(device_t, device_t, void *);
 static int	vmt_detach(device_t, int);
 
-struct vmt_event {
-	struct sysmon_pswitch	ev_smpsw;
-	int			ev_code;
-};
-
-struct vmt_softc {
-	device_t		sc_dev;
-
-	struct sysctllog	*sc_log;
-	struct vm_rpc		sc_tclo_rpc;
-	bool			sc_tclo_rpc_open;
-	char			*sc_rpc_buf;
-	int			sc_rpc_error;
-	int			sc_tclo_ping;
-	int			sc_set_guest_os;
-#define VMT_RPC_BUFLEN			256
-
-	struct callout		sc_tick;
-	struct callout		sc_tclo_tick;
-
-#define VMT_CLOCK_SYNC_PERIOD_SECONDS 60
-	int			sc_clock_sync_period_seconds;
-	struct callout		sc_clock_sync_tick;
-
-	struct vmt_event	sc_ev_power;
-	struct vmt_event	sc_ev_reset;
-	struct vmt_event	sc_ev_sleep;
-	bool			sc_smpsw_valid;
-
-	char			sc_hostname[MAXHOSTNAMELEN];
-};
-
 CFATTACH_DECL_NEW(vmt, sizeof(struct vmt_softc),
 	vmt_match, vmt_attach, vmt_detach, NULL);
 
-static int vmt_sysctl_setup_root(device_t);
-static int vmt_sysctl_setup_clock_sync(device_t, const struct sysctlnode *);
-static int vmt_sysctl_update_clock_sync_period(SYSCTLFN_PROTO);
-
-static void vm_cmd(struct vm_backdoor *);
-static void vm_ins(struct vm_backdoor *);
-static void vm_outs(struct vm_backdoor *);
-
-/* Functions for communicating with the VM Host. */
-static int vm_rpc_open(struct vm_rpc *, uint32_t);
-static int vm_rpc_close(struct vm_rpc *);
-static int vm_rpc_send(const struct vm_rpc *, const uint8_t *, uint32_t);
-static int vm_rpc_send_str(const struct vm_rpc *, const uint8_t *);
-static int vm_rpc_get_length(const struct vm_rpc *, uint32_t *, uint16_t *);
-static int vm_rpc_get_data(const struct vm_rpc *, char *, uint32_t, uint16_t);
-static int vm_rpc_send_rpci_tx_buf(struct vmt_softc *, const uint8_t *, uint32_t);
-static int vm_rpc_send_rpci_tx(struct vmt_softc *, const char *, ...)
-    __printflike(2, 3);
-static int vm_rpci_response_successful(struct vmt_softc *);
-
-static void vmt_tclo_state_change_success(struct vmt_softc *, int, char);
-static void vmt_do_reboot(struct vmt_softc *);
-static void vmt_do_shutdown(struct vmt_softc *);
-
-static void vmt_update_guest_info(struct vmt_softc *);
-static void vmt_update_guest_uptime(struct vmt_softc *);
-static void vmt_sync_guest_clock(struct vmt_softc *);
-
-static void vmt_tick(void *);
-static void vmt_tclo_tick(void *);
-static void vmt_clock_sync_tick(void *);
-static bool vmt_shutdown(device_t, int);
-static void vmt_pswitch_event(void *);
-
-extern char hostname[MAXHOSTNAMELEN];
-
-static void
-vmt_probe_cmd(struct vm_backdoor *frame, uint16_t cmd)
-{
-	memset(frame, 0, sizeof(*frame));
-
-	(frame->eax).word = VM_MAGIC;
-	(frame->ebx).word = ~VM_MAGIC;
-	(frame->ecx).part.low = cmd;
-	(frame->ecx).part.high = 0xffff;
-	(frame->edx).part.low  = VM_PORT_CMD;
-	(frame->edx).part.high = 0;
-
-	vm_cmd(frame);
-}
-
-static bool
-vmt_probe(void)
-{
-	struct vm_backdoor frame;
-
-	vmt_probe_cmd(&frame, VM_CMD_GET_VERSION);
-	if (frame.eax.word == 0xffffffff ||
-	    frame.ebx.word != VM_MAGIC)
-		return false;
-
-	vmt_probe_cmd(&frame, VM_CMD_GET_SPEED);
-	if (frame.eax.word == VM_MAGIC)
-		return false;
-
-	return true;
-}
-
 static int
 vmt_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -175,81 +57,13 @@ vmt_match(device_t parent, cfdata_t matc
 static void
 vmt_attach(device_t parent, device_t self, void *aux)
 {
-	int rv;
 	struct vmt_softc *sc = device_private(self);
 
 	aprint_naive("\n");
 	aprint_normal("\n");
 
 	sc->sc_dev = self;
-	sc->sc_log = NULL;
-
-	callout_init(&sc->sc_tick, 0);
-	callout_init(&sc->sc_tclo_tick, 0);
-	callout_init(&sc->sc_clock_sync_tick, 0);
-
-	sc->sc_clock_sync_period_seconds = VMT_CLOCK_SYNC_PERIOD_SECONDS;
-
-	rv = vmt_sysctl_setup_root(self);
-	if (rv != 0) {
-		aprint_error_dev(self, "failed to initialize sysctl "
-		    "(err %d)\n", rv);
-		goto free;
-	}
-
-	sc->sc_rpc_buf = kmem_alloc(VMT_RPC_BUFLEN, KM_SLEEP);
-
-	if (vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO) != 0) {
-		aprint_error_dev(self, "failed to open backdoor RPC channel (TCLO protocol)\n");
-		goto free;
-	}
-	sc->sc_tclo_rpc_open = true;
-
-	/* don't know if this is important at all yet */
-	if (vm_rpc_send_rpci_tx(sc, "tools.capability.hgfs_server toolbox 1") != 0) {
-		aprint_error_dev(self, "failed to set HGFS server capability\n");
-		goto free;
-	}
-
-	pmf_device_register1(self, NULL, NULL, vmt_shutdown);
-
-	sysmon_task_queue_init();
-
-	sc->sc_ev_power.ev_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
-	sc->sc_ev_power.ev_smpsw.smpsw_name = device_xname(self);
-	sc->sc_ev_power.ev_code = PSWITCH_EVENT_PRESSED;
-	sysmon_pswitch_register(&sc->sc_ev_power.ev_smpsw);
-	sc->sc_ev_reset.ev_smpsw.smpsw_type = PSWITCH_TYPE_RESET;
-	sc->sc_ev_reset.ev_smpsw.smpsw_name = device_xname(self);
-	sc->sc_ev_reset.ev_code = PSWITCH_EVENT_PRESSED;
-	sysmon_pswitch_register(&sc->sc_ev_reset.ev_smpsw);
-	sc->sc_ev_sleep.ev_smpsw.smpsw_type = PSWITCH_TYPE_SLEEP;
-	sc->sc_ev_sleep.ev_smpsw.smpsw_name = device_xname(self);
-	sc->sc_ev_sleep.ev_code = PSWITCH_EVENT_RELEASED;
-	sysmon_pswitch_register(&sc->sc_ev_sleep.ev_smpsw);
-	sc->sc_smpsw_valid = true;
-
-	callout_setfunc(&sc->sc_tick, vmt_tick, sc);
-	callout_schedule(&sc->sc_tick, hz);
-
-	callout_setfunc(&sc->sc_tclo_tick, vmt_tclo_tick, sc);
-	callout_schedule(&sc->sc_tclo_tick, hz);
-	sc->sc_tclo_ping = 1;
-
-	callout_setfunc(&sc->sc_clock_sync_tick, vmt_clock_sync_tick, sc);
-	callout_schedule(&sc->sc_clock_sync_tick,
-	    mstohz(sc->sc_clock_sync_period_seconds * 1000));
-
-	vmt_sync_guest_clock(sc);
-
-	return;
-
-free:
-	if (sc->sc_rpc_buf)
-		kmem_free(sc->sc_rpc_buf, VMT_RPC_BUFLEN);
-	pmf_device_register(self, NULL, NULL);
-	if (sc->sc_log)
-		sysctl_teardown(&sc->sc_log);
+	vmt_common_attach(sc);
 }
 
 static int
@@ -257,864 +71,9 @@ vmt_detach(device_t self, int flags)
 {
 	struct vmt_softc *sc = device_private(self);
 
-	if (sc->sc_tclo_rpc_open)
-		vm_rpc_close(&sc->sc_tclo_rpc);
-
-	if (sc->sc_smpsw_valid) {
-		sysmon_pswitch_unregister(&sc->sc_ev_sleep.ev_smpsw);
-		sysmon_pswitch_unregister(&sc->sc_ev_reset.ev_smpsw);
-		sysmon_pswitch_unregister(&sc->sc_ev_power.ev_smpsw);
-	}
-
-	callout_halt(&sc->sc_tick, NULL);
-	callout_destroy(&sc->sc_tick);
-
-	callout_halt(&sc->sc_tclo_tick, NULL);
-	callout_destroy(&sc->sc_tclo_tick);
-
-	callout_halt(&sc->sc_clock_sync_tick, NULL);
-	callout_destroy(&sc->sc_clock_sync_tick);
-
-	if (sc->sc_rpc_buf)
-		kmem_free(sc->sc_rpc_buf, VMT_RPC_BUFLEN);
-
-	if (sc->sc_log) {
-		sysctl_teardown(&sc->sc_log);
-		sc->sc_log = NULL;
-	}
-
-	return 0;
-}
-
-static int
-vmt_sysctl_setup_root(device_t self)
-{
-	const struct sysctlnode *machdep_node, *vmt_node;
-	struct vmt_softc *sc = device_private(self);
-	int rv;
-
-	rv = sysctl_createv(&sc->sc_log, 0, NULL, &machdep_node,
-	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
-	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &machdep_node, &vmt_node,
-	    0, CTLTYPE_NODE, device_xname(self), NULL,
-	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-
-	rv = vmt_sysctl_setup_clock_sync(self, vmt_node);
-	if (rv != 0)
-		goto fail;
-
-	return 0;
-
-fail:
-	sysctl_teardown(&sc->sc_log);
-	sc->sc_log = NULL;
-
-	return rv;
-}
-
-static int
-vmt_sysctl_setup_clock_sync(device_t self, const struct sysctlnode *root_node)
-{
-	const struct sysctlnode *node, *period_node;
-	struct vmt_softc *sc = device_private(self);
-	int rv;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &root_node, &node,
-	    0, CTLTYPE_NODE, "clock_sync", NULL,
-	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		return rv;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &node, &period_node,
-	    CTLFLAG_READWRITE, CTLTYPE_INT, "period",
-	    SYSCTL_DESCR("Period, in seconds, at which to update the "
-	        "guest's clock"),
-	    vmt_sysctl_update_clock_sync_period, 0, (void *)sc, 0,
-	    CTL_CREATE, CTL_EOL);
-	return rv;
-}
-
-static int
-vmt_sysctl_update_clock_sync_period(SYSCTLFN_ARGS)
-{
-	int error, period;
-	struct sysctlnode node;
-	struct vmt_softc *sc;
-
-	node = *rnode;
-	sc = (struct vmt_softc *)node.sysctl_data;
-
-	period = sc->sc_clock_sync_period_seconds;
-	node.sysctl_data = &period;
-	error = sysctl_lookup(SYSCTLFN_CALL(&node));
-	if (error || newp == NULL)
-		return error;
-
-	if (sc->sc_clock_sync_period_seconds != period) {
-		callout_halt(&sc->sc_clock_sync_tick, NULL);
-		sc->sc_clock_sync_period_seconds = period;
-		if (sc->sc_clock_sync_period_seconds > 0)
-			callout_schedule(&sc->sc_clock_sync_tick,
-			    mstohz(sc->sc_clock_sync_period_seconds * 1000));
-	}
-	return 0;
-}
-
-static void
-vmt_clock_sync_tick(void *xarg)
-{
-	struct vmt_softc *sc = xarg;
-
-	vmt_sync_guest_clock(sc);
-
-	callout_schedule(&sc->sc_clock_sync_tick,
-	    mstohz(sc->sc_clock_sync_period_seconds * 1000));
-}
-
-static void
-vmt_update_guest_uptime(struct vmt_softc *sc)
-{
-	/* host wants uptime in hundredths of a second */
-	if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %" PRId64 "00",
-	    VM_GUEST_INFO_UPTIME, time_uptime) != 0) {
-		device_printf(sc->sc_dev, "unable to set guest uptime\n");
-		sc->sc_rpc_error = 1;
-	}
-}
-
-static void
-vmt_update_guest_info(struct vmt_softc *sc)
-{
-	if (strncmp(sc->sc_hostname, hostname, sizeof(sc->sc_hostname)) != 0) {
-		strlcpy(sc->sc_hostname, hostname, sizeof(sc->sc_hostname));
-
-		if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s",
-		    VM_GUEST_INFO_DNS_NAME, sc->sc_hostname) != 0) {
-			device_printf(sc->sc_dev, "unable to set hostname\n");
-			sc->sc_rpc_error = 1;
-		}
-	}
-
-	/*
-	 * we're supposed to pass the full network address information back here,
-	 * but that involves xdr (sunrpc) data encoding, which seems a bit unreasonable.
-	 */
-
-	if (sc->sc_set_guest_os == 0) {
-		if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s %s %s",
-		    VM_GUEST_INFO_OS_NAME_FULL, ostype, osrelease, machine_arch) != 0) {
-			device_printf(sc->sc_dev, "unable to set full guest OS\n");
-			sc->sc_rpc_error = 1;
-		}
-
-		/*
-		 * host doesn't like it if we send an OS name it doesn't recognise,
-		 * so use "other" for i386 and "other-64" for amd64
-		 */
-		if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s",
-		    VM_GUEST_INFO_OS_NAME, VM_OS_NAME) != 0) {
-			device_printf(sc->sc_dev, "unable to set guest OS\n");
-			sc->sc_rpc_error = 1;
-		}
-
-		sc->sc_set_guest_os = 1;
-	}
-}
-
-static void
-vmt_sync_guest_clock(struct vmt_softc *sc)
-{
-	struct vm_backdoor frame;
-	struct timespec ts;
-
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word = VM_MAGIC;
-	frame.ecx.part.low = VM_CMD_GET_TIME_FULL;
-	frame.edx.part.low  = VM_PORT_CMD;
-	vm_cmd(&frame);
-
-	if (frame.eax.word != 0xffffffff) {
-		ts.tv_sec = ((uint64_t)frame.esi.word << 32) | frame.edx.word;
-		ts.tv_nsec = frame.ebx.word * 1000;
-		tc_setclock(&ts);
-	}
-}
-
-static void
-vmt_tick(void *xarg)
-{
-	struct vmt_softc *sc = xarg;
-
-	vmt_update_guest_info(sc);
-	vmt_update_guest_uptime(sc);
-
-	callout_schedule(&sc->sc_tick, hz * 15);
-}
-
-static void
-vmt_tclo_state_change_success(struct vmt_softc *sc, int success, char state)
-{
-	if (vm_rpc_send_rpci_tx(sc, "tools.os.statechange.status %d %d",
-	    success, state) != 0) {
-		device_printf(sc->sc_dev, "unable to send state change result\n");
-		sc->sc_rpc_error = 1;
-	}
-}
-
-static void
-vmt_do_shutdown(struct vmt_softc *sc)
-{
-	vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_HALT);
-	vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK);
-
-	device_printf(sc->sc_dev, "host requested shutdown\n");
-	sysmon_task_queue_sched(0, vmt_pswitch_event, &sc->sc_ev_power);
-}
-
-static void
-vmt_do_reboot(struct vmt_softc *sc)
-{
-	vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_REBOOT);
-	vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK);
-
-	device_printf(sc->sc_dev, "host requested reboot\n");
-	sysmon_task_queue_sched(0, vmt_pswitch_event, &sc->sc_ev_reset);
-}
-
-static void
-vmt_do_resume(struct vmt_softc *sc)
-{
-	device_printf(sc->sc_dev, "guest resuming from suspended state\n");
-
-	vmt_sync_guest_clock(sc);
-
-	/* force guest info update */
-	sc->sc_hostname[0] = '\0';
-	sc->sc_set_guest_os = 0;
-	vmt_update_guest_info(sc);
-
-	vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_RESUME);
-	if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
-		device_printf(sc->sc_dev, "error sending resume response\n");
-		sc->sc_rpc_error = 1;
-	}
-
-	sysmon_task_queue_sched(0, vmt_pswitch_event, &sc->sc_ev_sleep);
-}
-
-static bool
-vmt_shutdown(device_t self, int flags)
-{
-	struct vmt_softc *sc = device_private(self);
-
-	if (vm_rpc_send_rpci_tx(sc, "tools.capability.hgfs_server toolbox 0") != 0) {
-		device_printf(sc->sc_dev, "failed to disable hgfs server capability\n");
-	}
-
-	if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0) {
-		device_printf(sc->sc_dev, "failed to send shutdown ping\n");
-	}
-
-	vm_rpc_close(&sc->sc_tclo_rpc);
-
-	return true;
-}
-
-static void
-vmt_pswitch_event(void *xarg)
-{
-	struct vmt_event *ev = xarg;
-
-	sysmon_pswitch_event(&ev->ev_smpsw, ev->ev_code);
+	return vmt_common_detach(sc);
 }
 
-static void
-vmt_tclo_tick(void *xarg)
-{
-	struct vmt_softc *sc = xarg;
-	u_int32_t rlen;
-	u_int16_t ack;
-
-	/* reopen tclo channel if it's currently closed */
-	if (sc->sc_tclo_rpc.channel == 0 &&
-	    sc->sc_tclo_rpc.cookie1 == 0 &&
-	    sc->sc_tclo_rpc.cookie2 == 0) {
-		if (vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO) != 0) {
-			device_printf(sc->sc_dev, "unable to reopen TCLO channel\n");
-			callout_schedule(&sc->sc_tclo_tick, hz * 15);
-			return;
-		}
-
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_RESET_REPLY) != 0) {
-			device_printf(sc->sc_dev, "failed to send reset reply\n");
-			sc->sc_rpc_error = 1;
-			goto out;
-		} else {
-			sc->sc_rpc_error = 0;
-		}
-	}
-
-	if (sc->sc_tclo_ping) {
-		if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0) {
-			device_printf(sc->sc_dev, "failed to send TCLO outgoing ping\n");
-			sc->sc_rpc_error = 1;
-			goto out;
-		}
-	}
-
-	if (vm_rpc_get_length(&sc->sc_tclo_rpc, &rlen, &ack) != 0) {
-		device_printf(sc->sc_dev, "failed to get length of incoming TCLO data\n");
-		sc->sc_rpc_error = 1;
-		goto out;
-	}
-
-	if (rlen == 0) {
-		sc->sc_tclo_ping = 1;
-		goto out;
-	}
-
-	if (rlen >= VMT_RPC_BUFLEN) {
-		rlen = VMT_RPC_BUFLEN - 1;
-	}
-	if (vm_rpc_get_data(&sc->sc_tclo_rpc, sc->sc_rpc_buf, rlen, ack) != 0) {
-		device_printf(sc->sc_dev, "failed to get incoming TCLO data\n");
-		sc->sc_rpc_error = 1;
-		goto out;
-	}
-	sc->sc_tclo_ping = 0;
-
-#ifdef VMT_DEBUG
-	printf("vmware: received message '%s'\n", sc->sc_rpc_buf);
-#endif
-
-	if (strcmp(sc->sc_rpc_buf, "reset") == 0) {
-
-		if (sc->sc_rpc_error != 0) {
-			device_printf(sc->sc_dev, "resetting rpc\n");
-			vm_rpc_close(&sc->sc_tclo_rpc);
-			/* reopen and send the reset reply next time around */
-			goto out;
-		}
-
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_RESET_REPLY) != 0) {
-			device_printf(sc->sc_dev, "failed to send reset reply\n");
-			sc->sc_rpc_error = 1;
-		}
-
-	} else if (strcmp(sc->sc_rpc_buf, "ping") == 0) {
-
-		vmt_update_guest_info(sc);
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
-			device_printf(sc->sc_dev, "error sending ping response\n");
-			sc->sc_rpc_error = 1;
-		}
-
-	} else if (strcmp(sc->sc_rpc_buf, "OS_Halt") == 0) {
-		vmt_do_shutdown(sc);
-	} else if (strcmp(sc->sc_rpc_buf, "OS_Reboot") == 0) {
-		vmt_do_reboot(sc);
-	} else if (strcmp(sc->sc_rpc_buf, "OS_PowerOn") == 0) {
-		vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_POWERON);
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
-			device_printf(sc->sc_dev, "error sending poweron response\n");
-			sc->sc_rpc_error = 1;
-		}
-	} else if (strcmp(sc->sc_rpc_buf, "OS_Suspend") == 0) {
-		log(LOG_KERN | LOG_NOTICE, "VMware guest entering suspended state\n");
-
-		vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_SUSPEND);
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
-			device_printf(sc->sc_dev, "error sending suspend response\n");
-			sc->sc_rpc_error = 1;
-		}
-	} else if (strcmp(sc->sc_rpc_buf, "OS_Resume") == 0) {
-		vmt_do_resume(sc);
-	} else if (strcmp(sc->sc_rpc_buf, "Capabilities_Register") == 0) {
-
-		/* don't know if this is important at all */
-		if (vm_rpc_send_rpci_tx(sc, "vmx.capability.unified_loop toolbox") != 0) {
-			device_printf(sc->sc_dev, "unable to set unified loop\n");
-			sc->sc_rpc_error = 1;
-		}
-		if (vm_rpci_response_successful(sc) == 0) {
-			device_printf(sc->sc_dev, "host rejected unified loop setting\n");
-		}
-
-		/* the trailing space is apparently important here */
-		if (vm_rpc_send_rpci_tx(sc, "tools.capability.statechange ") != 0) {
-			device_printf(sc->sc_dev, "unable to send statechange capability\n");
-			sc->sc_rpc_error = 1;
-		}
-		if (vm_rpci_response_successful(sc) == 0) {
-			device_printf(sc->sc_dev, "host rejected statechange capability\n");
-		}
-
-		if (vm_rpc_send_rpci_tx(sc, "tools.set.version %u", VM_VERSION_UNMANAGED) != 0) {
-			device_printf(sc->sc_dev, "unable to set tools version\n");
-			sc->sc_rpc_error = 1;
-		}
-
-		vmt_update_guest_uptime(sc);
-
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
-			device_printf(sc->sc_dev, "error sending capabilities_register response\n");
-			sc->sc_rpc_error = 1;
-		}
-	} else if (strcmp(sc->sc_rpc_buf, "Set_Option broadcastIP 1") == 0) {
-		struct ifaddr *iface_addr = NULL;
-		struct ifnet *iface;
-		struct sockaddr_in *guest_ip;
-		int s;
-		struct psref psref;
-
-		/* find first available ipv4 address */
-		guest_ip = NULL;
-		s = pserialize_read_enter();
-		IFNET_READER_FOREACH(iface) {
-
-			/* skip loopback */
-			if (strncmp(iface->if_xname, "lo", 2) == 0 &&
-			    iface->if_xname[2] >= '0' && iface->if_xname[2] <= '9') {
-				continue;
-			}
-
-			IFADDR_READER_FOREACH(iface_addr, iface) {
-				if (iface_addr->ifa_addr->sa_family != AF_INET) {
-					continue;
-				}
-
-				guest_ip = satosin(iface_addr->ifa_addr);
-				ifa_acquire(iface_addr, &psref);
-				goto got;
-			}
-		}
-	got:
-		pserialize_read_exit(s);
-
-		if (guest_ip != NULL) {
-			if (vm_rpc_send_rpci_tx(sc, "info-set guestinfo.ip %s",
-			    inet_ntoa(guest_ip->sin_addr)) != 0) {
-				device_printf(sc->sc_dev, "unable to send guest IP address\n");
-				sc->sc_rpc_error = 1;
-			}
-			ifa_release(iface_addr, &psref);
-
-			if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
-				device_printf(sc->sc_dev, "error sending broadcastIP response\n");
-				sc->sc_rpc_error = 1;
-			}
-		} else {
-			if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_ERROR_IP_ADDR) != 0) {
-				device_printf(sc->sc_dev,
-				    "error sending broadcastIP error response\n");
-				sc->sc_rpc_error = 1;
-			}
-		}
-	} else {
-		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_ERROR) != 0) {
-			device_printf(sc->sc_dev, "error sending unknown command reply\n");
-			sc->sc_rpc_error = 1;
-		}
-	}
-
-out:
-	callout_schedule(&sc->sc_tclo_tick, sc->sc_tclo_ping ? hz : 1);
-}
-
-static void
-vm_cmd(struct vm_backdoor *frame)
-{
-	BACKDOOR_OP("inl %%dx, %%eax;", frame);
-}
-
-static void
-vm_ins(struct vm_backdoor *frame)
-{
-	BACKDOOR_OP("cld;\n\trep insb;", frame);
-}
-
-static void
-vm_outs(struct vm_backdoor *frame)
-{
-	BACKDOOR_OP("cld;\n\trep outsb;", frame);
-}
-
-static int
-vm_rpc_open(struct vm_rpc *rpc, uint32_t proto)
-{
-	struct vm_backdoor frame;
-
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word      = VM_MAGIC;
-	frame.ebx.word      = proto | VM_RPC_FLAG_COOKIE;
-	frame.ecx.part.low  = VM_CMD_RPC;
-	frame.ecx.part.high = VM_RPC_OPEN;
-	frame.edx.part.low  = VM_PORT_CMD;
-	frame.edx.part.high = 0;
-
-	vm_cmd(&frame);
-
-	if (frame.ecx.part.high != 1 || frame.edx.part.low != 0) {
-		/* open-vm-tools retries without VM_RPC_FLAG_COOKIE here.. */
-		printf("vmware: open failed, eax=%08x, ecx=%08x, edx=%08x\n",
-			frame.eax.word, frame.ecx.word, frame.edx.word);
-		return EIO;
-	}
-
-	rpc->channel = frame.edx.part.high;
-	rpc->cookie1 = frame.esi.word;
-	rpc->cookie2 = frame.edi.word;
-
-	return 0;
-}
-
-static int
-vm_rpc_close(struct vm_rpc *rpc)
-{
-	struct vm_backdoor frame;
-
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word      = VM_MAGIC;
-	frame.ebx.word      = 0;
-	frame.ecx.part.low  = VM_CMD_RPC;
-	frame.ecx.part.high = VM_RPC_CLOSE;
-	frame.edx.part.low  = VM_PORT_CMD;
-	frame.edx.part.high = rpc->channel;
-	frame.edi.word      = rpc->cookie2;
-	frame.esi.word      = rpc->cookie1;
-
-	vm_cmd(&frame);
-
-	if (frame.ecx.part.high == 0 || frame.ecx.part.low != 0) {
-		printf("vmware: close failed, eax=%08x, ecx=%08x\n",
-				frame.eax.word, frame.ecx.word);
-		return EIO;
-	}
-
-	rpc->channel = 0;
-	rpc->cookie1 = 0;
-	rpc->cookie2 = 0;
-
-	return 0;
-}
-
-static int
-vm_rpc_send(const struct vm_rpc *rpc, const uint8_t *buf, uint32_t length)
-{
-	struct vm_backdoor frame;
-
-	/* Send the length of the command. */
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word = VM_MAGIC;
-	frame.ebx.word = length;
-	frame.ecx.part.low  = VM_CMD_RPC;
-	frame.ecx.part.high = VM_RPC_SET_LENGTH;
-	frame.edx.part.low  = VM_PORT_CMD;
-	frame.edx.part.high = rpc->channel;
-	frame.esi.word = rpc->cookie1;
-	frame.edi.word = rpc->cookie2;
-
-	vm_cmd(&frame);
-
-	if ((frame.ecx.part.high & VM_RPC_REPLY_SUCCESS) == 0) {
-		printf("vmware: sending length failed, eax=%08x, ecx=%08x\n",
-				frame.eax.word, frame.ecx.word);
-		return EIO;
-	}
-
-	if (length == 0)
-		return 0; /* Only need to poke once if command is null. */
-
-	/* Send the command using enhanced RPC. */
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word = VM_MAGIC;
-	frame.ebx.word = VM_RPC_ENH_DATA;
-	frame.ecx.word = length;
-	frame.edx.part.low  = VM_PORT_RPC;
-	frame.edx.part.high = rpc->channel;
-	frame.ebp.word = rpc->cookie1;
-	frame.edi.word = rpc->cookie2;
-#ifdef __amd64__
-	frame.esi.quad = (uint64_t)buf;
-#else
-	frame.esi.word = (uint32_t)buf;
-#endif
-
-	vm_outs(&frame);
-
-	if (frame.ebx.word != VM_RPC_ENH_DATA) {
-		/* open-vm-tools retries on VM_RPC_REPLY_CHECKPOINT */
-		printf("vmware: send failed, ebx=%08x\n", frame.ebx.word);
-		return EIO;
-	}
-
-	return 0;
-}
-
-static int
-vm_rpc_send_str(const struct vm_rpc *rpc, const uint8_t *str)
-{
-	return vm_rpc_send(rpc, str, strlen(str));
-}
-
-static int
-vm_rpc_get_data(const struct vm_rpc *rpc, char *data, uint32_t length,
-    uint16_t dataid)
-{
-	struct vm_backdoor frame;
-
-	/* Get data using enhanced RPC. */
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word      = VM_MAGIC;
-	frame.ebx.word      = VM_RPC_ENH_DATA;
-	frame.ecx.word      = length;
-	frame.edx.part.low  = VM_PORT_RPC;
-	frame.edx.part.high = rpc->channel;
-	frame.esi.word      = rpc->cookie1;
-#ifdef __amd64__
-	frame.edi.quad      = (uint64_t)data;
-#else
-	frame.edi.word      = (uint32_t)data;
-#endif
-	frame.ebp.word      = rpc->cookie2;
-
-	vm_ins(&frame);
-
-	/* NUL-terminate the data */
-	data[length] = '\0';
-
-	if (frame.ebx.word != VM_RPC_ENH_DATA) {
-		printf("vmware: get data failed, ebx=%08x\n",
-				frame.ebx.word);
-		return EIO;
-	}
-
-	/* Acknowledge data received. */
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word      = VM_MAGIC;
-	frame.ebx.word      = dataid;
-	frame.ecx.part.low  = VM_CMD_RPC;
-	frame.ecx.part.high = VM_RPC_GET_END;
-	frame.edx.part.low  = VM_PORT_CMD;
-	frame.edx.part.high = rpc->channel;
-	frame.esi.word      = rpc->cookie1;
-	frame.edi.word      = rpc->cookie2;
-
-	vm_cmd(&frame);
-
-	if (frame.ecx.part.high == 0) {
-		printf("vmware: ack data failed, eax=%08x, ecx=%08x\n",
-				frame.eax.word, frame.ecx.word);
-		return EIO;
-	}
-
-	return 0;
-}
-
-static int
-vm_rpc_get_length(const struct vm_rpc *rpc, uint32_t *length, uint16_t *dataid)
-{
-	struct vm_backdoor frame;
-
-	memset(&frame, 0, sizeof(frame));
-	frame.eax.word      = VM_MAGIC;
-	frame.ebx.word      = 0;
-	frame.ecx.part.low  = VM_CMD_RPC;
-	frame.ecx.part.high = VM_RPC_GET_LENGTH;
-	frame.edx.part.low  = VM_PORT_CMD;
-	frame.edx.part.high = rpc->channel;
-	frame.esi.word      = rpc->cookie1;
-	frame.edi.word      = rpc->cookie2;
-
-	vm_cmd(&frame);
-
-	if ((frame.ecx.part.high & VM_RPC_REPLY_SUCCESS) == 0) {
-		printf("vmware: get length failed, eax=%08x, ecx=%08x\n",
-				frame.eax.word, frame.ecx.word);
-		return EIO;
-	}
-	if ((frame.ecx.part.high & VM_RPC_REPLY_DORECV) == 0) {
-		*length = 0;
-		*dataid = 0;
-	} else {
-		*length = frame.ebx.word;
-		*dataid = frame.edx.part.high;
-	}
-
-	return 0;
-}
-
-static int
-vm_rpci_response_successful(struct vmt_softc *sc)
-{
-	return (sc->sc_rpc_buf[0] == '1' && sc->sc_rpc_buf[1] == ' ');
-}
-
-static int
-vm_rpc_send_rpci_tx_buf(struct vmt_softc *sc, const uint8_t *buf, uint32_t length)
-{
-	struct vm_rpc rpci;
-	u_int32_t rlen;
-	u_int16_t ack;
-	int result = 0;
-
-	if (vm_rpc_open(&rpci, VM_RPC_OPEN_RPCI) != 0) {
-		device_printf(sc->sc_dev, "rpci channel open failed\n");
-		return EIO;
-	}
-
-	if (vm_rpc_send(&rpci, sc->sc_rpc_buf, length) != 0) {
-		device_printf(sc->sc_dev, "unable to send rpci command\n");
-		result = EIO;
-		goto out;
-	}
-
-	if (vm_rpc_get_length(&rpci, &rlen, &ack) != 0) {
-		device_printf(sc->sc_dev, "failed to get length of rpci response data\n");
-		result = EIO;
-		goto out;
-	}
-
-	if (rlen > 0) {
-		if (rlen >= VMT_RPC_BUFLEN) {
-			rlen = VMT_RPC_BUFLEN - 1;
-		}
-
-		if (vm_rpc_get_data(&rpci, sc->sc_rpc_buf, rlen, ack) != 0) {
-			device_printf(sc->sc_dev, "failed to get rpci response data\n");
-			result = EIO;
-			goto out;
-		}
-	}
-
-out:
-	if (vm_rpc_close(&rpci) != 0) {
-		device_printf(sc->sc_dev, "unable to close rpci channel\n");
-	}
-
-	return result;
-}
-
-static int
-vm_rpc_send_rpci_tx(struct vmt_softc *sc, const char *fmt, ...)
-{
-	va_list args;
-	int len;
-
-	va_start(args, fmt);
-	len = vsnprintf(sc->sc_rpc_buf, VMT_RPC_BUFLEN, fmt, args);
-	va_end(args);
-
-	if (len >= VMT_RPC_BUFLEN) {
-		device_printf(sc->sc_dev, "rpci command didn't fit in buffer\n");
-		return EIO;
-	}
-
-	return vm_rpc_send_rpci_tx_buf(sc, sc->sc_rpc_buf, len);
-}
-
-#if 0
-	struct vm_backdoor frame;
-
-	memset(&frame, 0, sizeof(frame));
-
-	frame.eax.word = VM_MAGIC;
-	frame.ecx.part.low = VM_CMD_GET_VERSION;
-	frame.edx.part.low  = VM_PORT_CMD;
-
-	printf("\n");
-	printf("eax 0x%08x\n", frame.eax.word);
-	printf("ebx 0x%08x\n", frame.ebx.word);
-	printf("ecx 0x%08x\n", frame.ecx.word);
-	printf("edx 0x%08x\n", frame.edx.word);
-	printf("ebp 0x%08x\n", frame.ebp.word);
-	printf("edi 0x%08x\n", frame.edi.word);
-	printf("esi 0x%08x\n", frame.esi.word);
-
-	vm_cmd(&frame);
-
-	printf("-\n");
-	printf("eax 0x%08x\n", frame.eax.word);
-	printf("ebx 0x%08x\n", frame.ebx.word);
-	printf("ecx 0x%08x\n", frame.ecx.word);
-	printf("edx 0x%08x\n", frame.edx.word);
-	printf("ebp 0x%08x\n", frame.ebp.word);
-	printf("edi 0x%08x\n", frame.edi.word);
-	printf("esi 0x%08x\n", frame.esi.word);
-#endif
-
-/*
- * Notes on tracing backdoor activity in vmware-guestd:
- *
- * - Find the addresses of the inl / rep insb / rep outsb
- *   instructions used to perform backdoor operations.
- *   One way to do this is to disassemble vmware-guestd:
- *
- *   $ objdump -S /emul/freebsd/sbin/vmware-guestd > vmware-guestd.S
- *
- *   and search for '<tab>in ' in the resulting file.  The rep insb and
- *   rep outsb code is directly below that.
- *
- * - Run vmware-guestd under gdb, setting up breakpoints as follows:
- *   (the addresses shown here are the ones from VMware-server-1.0.10-203137,
- *   the last version that actually works in FreeBSD emulation on OpenBSD)
- *
- * break *0x805497b   (address of 'in' instruction)
- * commands 1
- * silent
- * echo INOUT\n
- * print/x $ecx
- * print/x $ebx
- * print/x $edx
- * continue
- * end
- * break *0x805497c   (address of instruction after 'in')
- * commands 2
- * silent
- * echo ===\n
- * print/x $ecx
- * print/x $ebx
- * print/x $edx
- * echo \n
- * continue
- * end
- * break *0x80549b7   (address of instruction before 'rep insb')
- * commands 3
- * silent
- * set variable $inaddr = $edi
- * set variable $incount = $ecx
- * continue
- * end
- * break *0x80549ba   (address of instruction after 'rep insb')
- * commands 4
- * silent
- * echo IN\n
- * print $incount
- * x/s $inaddr
- * echo \n
- * continue
- * end
- * break *0x80549fb    (address of instruction before 'rep outsb')
- * commands 5
- * silent
- * echo OUT\n
- * print $ecx
- * x/s $esi
- * echo \n
- * continue
- * end
- *
- * This will produce a log of the backdoor operations, including the
- * data sent and received and the relevant register values.  You can then
- * match the register values to the various constants in this file.
- */
-
 MODULE(MODULE_CLASS_DRIVER, vmt, "sysmon_power,sysmon_taskq");
 
 #ifdef _MODULE

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1276 src/sys/conf/files:1.1277
--- src/sys/conf/files:1.1276	Sun Sep 27 19:08:11 2020
+++ src/sys/conf/files	Tue Oct 27 08:57:11 2020
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1276 2020/09/27 19:08:11 roy Exp $
+#	$NetBSD: files,v 1.1277 2020/10/27 08:57:11 ryo Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20171118
@@ -1494,6 +1494,9 @@ file	dev/ic/anx_dp.c			anxdp
 device	genet: arp, ether, ifnet, mii
 file	dev/ic/bcmgenet.c		genet
 
+# VMware Tools driver
+include "dev/vmt/files.vmt"
+
 #
 # File systems
 #

Index: src/sys/dev/fdt/files.fdt
diff -u src/sys/dev/fdt/files.fdt:1.54 src/sys/dev/fdt/files.fdt:1.55
--- src/sys/dev/fdt/files.fdt:1.54	Thu Jul 16 16:39:18 2020
+++ src/sys/dev/fdt/files.fdt	Tue Oct 27 08:57:11 2020
@@ -1,4 +1,4 @@
-# $NetBSD: files.fdt,v 1.54 2020/07/16 16:39:18 jmcneill Exp $
+# $NetBSD: files.fdt,v 1.55 2020/10/27 08:57:11 ryo Exp $
 
 include	"external/bsd/libfdt/conf/files.libfdt"
 
@@ -187,3 +187,7 @@ file	dev/fdt/simple_amplifier.c		simplea
 # Broadcom GENET v5
 attach	genet at fdt with genet_fdt
 file	dev/fdt/genet_fdt.c			genet_fdt
+
+# VMware Tools driver
+attach	vmt at fdt with vmt_fdt
+file	dev/fdt/vmt_fdt.c			vmt_fdt

Index: src/sys/modules/vmt/Makefile
diff -u src/sys/modules/vmt/Makefile:1.3 src/sys/modules/vmt/Makefile:1.4
--- src/sys/modules/vmt/Makefile:1.3	Sun Feb 17 04:06:00 2019
+++ src/sys/modules/vmt/Makefile	Tue Oct 27 08:57:11 2020
@@ -1,11 +1,13 @@
-#	$NetBSD: Makefile,v 1.3 2019/02/17 04:06:00 rin Exp $
+#	$NetBSD: Makefile,v 1.4 2020/10/27 08:57:11 ryo Exp $
 
 .include "../Makefile.inc"
 
 .PATH:	${S}/arch/x86/x86
+.PATH:	${S}/dev/vmt
 
 KMOD=	vmt	
 IOCONF=	vmt.ioconf
-SRCS=	vmt.c
+SRCS=	vmt.c \
+	vmt_subr.c
 
 .include <bsd.kmodule.mk>

Added files:

Index: src/share/man/man4/vmt.4
diff -u /dev/null src/share/man/man4/vmt.4:1.3
--- /dev/null	Tue Oct 27 08:57:12 2020
+++ src/share/man/man4/vmt.4	Tue Oct 27 08:57:10 2020
@@ -0,0 +1,81 @@
+.\"	$NetBSD: vmt.4,v 1.3 2020/10/27 08:57:10 ryo Exp $
+.\"	$OpenBSD: vmt.4,v 1.4 2010/10/26 05:07:31 jmc Exp $
+.\"
+.\" Copyright (c) 2008 Marco Peereboom <ma...@openbsd.org>
+.\" Text was heavily borrowed from the IPMI spec V1.5
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.Dd October 6, 2013
+.Dt VMT 4 x86
+.Os
+.Sh NAME
+.Nm vmt
+.Nd VMware Tools driver
+.Sh SYNOPSIS
+.Cd "vmt0 at cpu0"
+.Cd "vmt0 at fdt?"
+.Sh DESCRIPTION
+The
+.Nm
+driver is a kernel level implementation of VMware Tools.
+VMware Tools are intended to provide better support for operating systems
+running inside virtual machines.
+.Pp
+.Nm
+handles shutdown, reboot, resume requests from the host by sending
+events using
+.Xr sysmon_pswitch 9
+of type PSWITCH_TYPE_POWER, PSWITCH_TYPE_RESET, and PSWITCH_TYPE_SLEEP that
+can be handled by
+.Xr powerd 8 .
+.Nm
+will log notifications that the guest has been suspended or resumed by the
+host.
+.\" It also provides access to the host machine's clock as a timedelta sensor.
+.Pp
+.Nm
+reports the guest's hostname and first non-loopback IP address to the host.
+.Ss Clock synchronization
+The
+.Nm
+driver synchronizes the virtual machine's clock with the host clock in the
+following situations:
+.Bl -bullet
+.It
+When the virtual machine resumes after having been suspended.
+.It
+Periodically with the interval indicated by the
+.Va machdep.vmt0.clock_sync.period
+.Xr sysctl 8
+variable.
+This is done so that the virtual machine can keep its clock synchronized
+when the host is suspended, because in this case the
+.Nm
+driver receives no notification of such an event.
+Setting this tunable to zero disables clock synchronization.
+.El
+.Sh SEE ALSO
+.\" .Xr cpu 4 ,
+.Xr powerd 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 4.4
+and was then ported to
+.Nx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An David Gwynne Aq Mt d...@openbsd.org .

Index: src/sys/dev/vmt/files.vmt
diff -u /dev/null src/sys/dev/vmt/files.vmt:1.1
--- /dev/null	Tue Oct 27 08:57:12 2020
+++ src/sys/dev/vmt/files.vmt	Tue Oct 27 08:57:11 2020
@@ -0,0 +1,2 @@
+device vmt: sysmon_power, sysmon_taskq
+file	dev/vmt/vmt_subr.c		vmt
Index: src/sys/dev/vmt/vmt_subr.c
diff -u /dev/null src/sys/dev/vmt/vmt_subr.c:1.1
--- /dev/null	Tue Oct 27 08:57:12 2020
+++ src/sys/dev/vmt/vmt_subr.c	Tue Oct 27 08:57:11 2020
@@ -0,0 +1,1103 @@
+/* $NetBSD: vmt_subr.c,v 1.1 2020/10/27 08:57:11 ryo Exp $ */
+/* $OpenBSD: vmt.c,v 1.11 2011/01/27 21:29:25 dtucker Exp $ */
+
+/*
+ * Copyright (c) 2007 David Crawshaw <da...@zentus.com>
+ * Copyright (c) 2008 David Gwynne <d...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Protocol reverse engineered by Ken Kato:
+ * https://sites.google.com/site/chitchatvmback/backdoor
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/callout.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/timetc.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/sysmon/sysmon_taskq.h>
+#include <dev/vmt/vmtreg.h>
+#include <dev/vmt/vmtvar.h>
+
+/* #define VMT_DEBUG */
+
+static int vmt_sysctl_setup_root(device_t);
+static int vmt_sysctl_setup_clock_sync(device_t, const struct sysctlnode *);
+static int vmt_sysctl_update_clock_sync_period(SYSCTLFN_PROTO);
+
+static void vm_cmd(struct vm_backdoor *);
+static void vm_ins(struct vm_backdoor *);
+static void vm_outs(struct vm_backdoor *);
+
+/* Functions for communicating with the VM Host. */
+static int vm_rpc_open(struct vm_rpc *, uint32_t);
+static int vm_rpc_close(struct vm_rpc *);
+static int vm_rpc_send(const struct vm_rpc *, const uint8_t *, uint32_t);
+static int vm_rpc_send_str(const struct vm_rpc *, const uint8_t *);
+static int vm_rpc_get_length(const struct vm_rpc *, uint32_t *, uint16_t *);
+static int vm_rpc_get_data(const struct vm_rpc *, char *, uint32_t, uint16_t);
+static int vm_rpc_send_rpci_tx_buf(struct vmt_softc *, const uint8_t *, uint32_t);
+static int vm_rpc_send_rpci_tx(struct vmt_softc *, const char *, ...)
+    __printflike(2, 3);
+static int vm_rpci_response_successful(struct vmt_softc *);
+
+static void vmt_tclo_state_change_success(struct vmt_softc *, int, char);
+static void vmt_do_reboot(struct vmt_softc *);
+static void vmt_do_shutdown(struct vmt_softc *);
+
+static void vmt_update_guest_info(struct vmt_softc *);
+static void vmt_update_guest_uptime(struct vmt_softc *);
+static void vmt_sync_guest_clock(struct vmt_softc *);
+
+static void vmt_tick(void *);
+static void vmt_tclo_tick(void *);
+static void vmt_clock_sync_tick(void *);
+static bool vmt_shutdown(device_t, int);
+static void vmt_pswitch_event(void *);
+
+extern char hostname[MAXHOSTNAMELEN];
+
+static void
+vmt_probe_cmd(struct vm_backdoor *frame, uint16_t cmd)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	(frame->eax).word = VM_MAGIC;
+	(frame->ebx).word = ~VM_MAGIC;
+	(frame->ecx).part.low = cmd;
+	(frame->ecx).part.high = 0xffff;
+	(frame->edx).part.low  = VM_PORT_CMD;
+	(frame->edx).part.high = 0;
+
+	vm_cmd(frame);
+}
+
+bool
+vmt_probe(void)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+	/*
+	 * XXX: doesn't support in big-endian.
+	 * vmt has some code depends on little-endian.
+	 */
+	return false;
+#else
+	struct vm_backdoor frame;
+
+	vmt_probe_cmd(&frame, VM_CMD_GET_VERSION);
+	if (frame.eax.word == 0xffffffff ||
+	    frame.ebx.word != VM_MAGIC)
+		return false;
+
+	vmt_probe_cmd(&frame, VM_CMD_GET_SPEED);
+	if (frame.eax.word == VM_MAGIC)
+		return false;
+
+	return true;
+#endif
+}
+
+void
+vmt_common_attach(struct vmt_softc *sc)
+{
+	device_t self;
+	struct vm_backdoor frame;
+	int rv;
+
+	self = sc->sc_dev;
+	sc->sc_log = NULL;
+
+	/* check again */
+	vmt_probe_cmd(&frame, VM_CMD_GET_VERSION);
+	if (frame.eax.word == 0xffffffff ||
+	    frame.ebx.word != VM_MAGIC) {
+		aprint_error_dev(self, "failed to get VMware version\n");
+		return;
+	}
+
+	/* show uuid */
+	{
+		struct uuid uuid;
+		uint32_t u;
+
+		vmt_probe_cmd(&frame, VM_CMD_GET_BIOS_UUID);
+		uuid.time_low = htobe32(frame.eax.word);
+		u = htobe32(frame.ebx.word);
+		uuid.time_mid = u >> 16;
+		uuid.time_hi_and_version = u;
+		u = htobe32(frame.ecx.word);
+		uuid.clock_seq_hi_and_reserved = u >> 24;
+		uuid.clock_seq_low = u >> 16;
+		uuid.node[0] = u >> 8;
+		uuid.node[1] = u;
+		u = htobe32(frame.edx.word);
+		uuid.node[2] = u >> 24;
+		uuid.node[3] = u >> 16;
+		uuid.node[4] = u >> 8;
+		uuid.node[5] = u;
+
+		uuid_snprintf(sc->sc_uuid, sizeof(sc->sc_uuid), &uuid);
+		device_printf(sc->sc_dev, "UUID: %s\n", sc->sc_uuid);
+	}
+
+	callout_init(&sc->sc_tick, 0);
+	callout_init(&sc->sc_tclo_tick, 0);
+	callout_init(&sc->sc_clock_sync_tick, 0);
+
+	sc->sc_clock_sync_period_seconds = VMT_CLOCK_SYNC_PERIOD_SECONDS;
+
+	rv = vmt_sysctl_setup_root(self);
+	if (rv != 0) {
+		aprint_error_dev(self, "failed to initialize sysctl "
+		    "(err %d)\n", rv);
+		goto free;
+	}
+
+	sc->sc_rpc_buf = kmem_alloc(VMT_RPC_BUFLEN, KM_SLEEP);
+
+	if (vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO) != 0) {
+		aprint_error_dev(self, "failed to open backdoor RPC channel (TCLO protocol)\n");
+		goto free;
+	}
+	sc->sc_tclo_rpc_open = true;
+
+	/* don't know if this is important at all yet */
+	if (vm_rpc_send_rpci_tx(sc, "tools.capability.hgfs_server toolbox 1") != 0) {
+		aprint_error_dev(self, "failed to set HGFS server capability\n");
+		goto free;
+	}
+
+	pmf_device_register1(self, NULL, NULL, vmt_shutdown);
+
+	sysmon_task_queue_init();
+
+	sc->sc_ev_power.ev_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
+	sc->sc_ev_power.ev_smpsw.smpsw_name = device_xname(self);
+	sc->sc_ev_power.ev_code = PSWITCH_EVENT_PRESSED;
+	sysmon_pswitch_register(&sc->sc_ev_power.ev_smpsw);
+	sc->sc_ev_reset.ev_smpsw.smpsw_type = PSWITCH_TYPE_RESET;
+	sc->sc_ev_reset.ev_smpsw.smpsw_name = device_xname(self);
+	sc->sc_ev_reset.ev_code = PSWITCH_EVENT_PRESSED;
+	sysmon_pswitch_register(&sc->sc_ev_reset.ev_smpsw);
+	sc->sc_ev_sleep.ev_smpsw.smpsw_type = PSWITCH_TYPE_SLEEP;
+	sc->sc_ev_sleep.ev_smpsw.smpsw_name = device_xname(self);
+	sc->sc_ev_sleep.ev_code = PSWITCH_EVENT_RELEASED;
+	sysmon_pswitch_register(&sc->sc_ev_sleep.ev_smpsw);
+	sc->sc_smpsw_valid = true;
+
+	callout_setfunc(&sc->sc_tick, vmt_tick, sc);
+	callout_schedule(&sc->sc_tick, hz);
+
+	callout_setfunc(&sc->sc_tclo_tick, vmt_tclo_tick, sc);
+	callout_schedule(&sc->sc_tclo_tick, hz);
+	sc->sc_tclo_ping = 1;
+
+	callout_setfunc(&sc->sc_clock_sync_tick, vmt_clock_sync_tick, sc);
+	callout_schedule(&sc->sc_clock_sync_tick,
+	    mstohz(sc->sc_clock_sync_period_seconds * 1000));
+
+	vmt_sync_guest_clock(sc);
+
+	return;
+
+free:
+	if (sc->sc_rpc_buf)
+		kmem_free(sc->sc_rpc_buf, VMT_RPC_BUFLEN);
+	pmf_device_register(self, NULL, NULL);
+	if (sc->sc_log)
+		sysctl_teardown(&sc->sc_log);
+}
+
+int
+vmt_common_detach(struct vmt_softc *sc)
+{
+	if (sc->sc_tclo_rpc_open)
+		vm_rpc_close(&sc->sc_tclo_rpc);
+
+	if (sc->sc_smpsw_valid) {
+		sysmon_pswitch_unregister(&sc->sc_ev_sleep.ev_smpsw);
+		sysmon_pswitch_unregister(&sc->sc_ev_reset.ev_smpsw);
+		sysmon_pswitch_unregister(&sc->sc_ev_power.ev_smpsw);
+	}
+
+	callout_halt(&sc->sc_tick, NULL);
+	callout_destroy(&sc->sc_tick);
+
+	callout_halt(&sc->sc_tclo_tick, NULL);
+	callout_destroy(&sc->sc_tclo_tick);
+
+	callout_halt(&sc->sc_clock_sync_tick, NULL);
+	callout_destroy(&sc->sc_clock_sync_tick);
+
+	if (sc->sc_rpc_buf)
+		kmem_free(sc->sc_rpc_buf, VMT_RPC_BUFLEN);
+
+	if (sc->sc_log) {
+		sysctl_teardown(&sc->sc_log);
+		sc->sc_log = NULL;
+	}
+
+	return 0;
+}
+
+static int
+vmt_sysctl_setup_root(device_t self)
+{
+	const struct sysctlnode *machdep_node, *vmt_node;
+	struct vmt_softc *sc = device_private(self);
+	int rv;
+
+	rv = sysctl_createv(&sc->sc_log, 0, NULL, &machdep_node,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
+	if (rv != 0)
+		goto fail;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &machdep_node, &vmt_node,
+	    0, CTLTYPE_NODE, device_xname(self), NULL,
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (rv != 0)
+		goto fail;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &vmt_node, NULL,
+	    CTLFLAG_READONLY, CTLTYPE_STRING, "uuid",
+	    SYSCTL_DESCR("UUID of virtual machine"),
+	    NULL, 0, sc->sc_uuid, 0,
+	    CTL_CREATE, CTL_EOL);
+
+	rv = vmt_sysctl_setup_clock_sync(self, vmt_node);
+	if (rv != 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	sysctl_teardown(&sc->sc_log);
+	sc->sc_log = NULL;
+
+	return rv;
+}
+
+static int
+vmt_sysctl_setup_clock_sync(device_t self, const struct sysctlnode *root_node)
+{
+	const struct sysctlnode *node, *period_node;
+	struct vmt_softc *sc = device_private(self);
+	int rv;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &root_node, &node,
+	    0, CTLTYPE_NODE, "clock_sync", NULL,
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (rv != 0)
+		return rv;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &node, &period_node,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "period",
+	    SYSCTL_DESCR("Period, in seconds, at which to update the "
+	        "guest's clock"),
+	    vmt_sysctl_update_clock_sync_period, 0, (void *)sc, 0,
+	    CTL_CREATE, CTL_EOL);
+	return rv;
+}
+
+static int
+vmt_sysctl_update_clock_sync_period(SYSCTLFN_ARGS)
+{
+	int error, period;
+	struct sysctlnode node;
+	struct vmt_softc *sc;
+
+	node = *rnode;
+	sc = (struct vmt_softc *)node.sysctl_data;
+
+	period = sc->sc_clock_sync_period_seconds;
+	node.sysctl_data = &period;
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error || newp == NULL)
+		return error;
+
+	if (sc->sc_clock_sync_period_seconds != period) {
+		callout_halt(&sc->sc_clock_sync_tick, NULL);
+		sc->sc_clock_sync_period_seconds = period;
+		if (sc->sc_clock_sync_period_seconds > 0)
+			callout_schedule(&sc->sc_clock_sync_tick,
+			    mstohz(sc->sc_clock_sync_period_seconds * 1000));
+	}
+	return 0;
+}
+
+static void
+vmt_clock_sync_tick(void *xarg)
+{
+	struct vmt_softc *sc = xarg;
+
+	vmt_sync_guest_clock(sc);
+
+	callout_schedule(&sc->sc_clock_sync_tick,
+	    mstohz(sc->sc_clock_sync_period_seconds * 1000));
+}
+
+static void
+vmt_update_guest_uptime(struct vmt_softc *sc)
+{
+	/* host wants uptime in hundredths of a second */
+	if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %" PRId64 "00",
+	    VM_GUEST_INFO_UPTIME, time_uptime) != 0) {
+		device_printf(sc->sc_dev, "unable to set guest uptime\n");
+		sc->sc_rpc_error = 1;
+	}
+}
+
+static void
+vmt_update_guest_info(struct vmt_softc *sc)
+{
+	if (strncmp(sc->sc_hostname, hostname, sizeof(sc->sc_hostname)) != 0) {
+		strlcpy(sc->sc_hostname, hostname, sizeof(sc->sc_hostname));
+		if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s",
+		    VM_GUEST_INFO_DNS_NAME, sc->sc_hostname) != 0) {
+			device_printf(sc->sc_dev, "unable to set hostname\n");
+			sc->sc_rpc_error = 1;
+		}
+	}
+
+	/*
+	 * we're supposed to pass the full network address information back here,
+	 * but that involves xdr (sunrpc) data encoding, which seems a bit unreasonable.
+	 */
+
+	if (sc->sc_set_guest_os == 0) {
+		if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s %s %s",
+		    VM_GUEST_INFO_OS_NAME_FULL, ostype, osrelease, machine_arch) != 0) {
+			device_printf(sc->sc_dev, "unable to set full guest OS\n");
+			sc->sc_rpc_error = 1;
+		}
+
+		/*
+		 * host doesn't like it if we send an OS name it doesn't recognise,
+		 * so use "other" for i386 and "other-64" for amd64
+		 */
+		if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s",
+		    VM_GUEST_INFO_OS_NAME, VM_OS_NAME) != 0) {
+			device_printf(sc->sc_dev, "unable to set guest OS\n");
+			sc->sc_rpc_error = 1;
+		}
+
+		sc->sc_set_guest_os = 1;
+	}
+}
+
+static void
+vmt_sync_guest_clock(struct vmt_softc *sc)
+{
+	struct vm_backdoor frame;
+	struct timespec ts;
+
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word = VM_MAGIC;
+	frame.ecx.part.low = VM_CMD_GET_TIME_FULL;
+	frame.edx.part.low  = VM_PORT_CMD;
+	vm_cmd(&frame);
+
+	if (frame.eax.word != 0xffffffff) {
+		ts.tv_sec = ((uint64_t)frame.esi.word << 32) | frame.edx.word;
+		ts.tv_nsec = frame.ebx.word * 1000;
+		tc_setclock(&ts);
+	}
+}
+
+static void
+vmt_tick(void *xarg)
+{
+	struct vmt_softc *sc = xarg;
+
+	vmt_update_guest_info(sc);
+	vmt_update_guest_uptime(sc);
+
+	callout_schedule(&sc->sc_tick, hz * 15);
+}
+
+static void
+vmt_tclo_state_change_success(struct vmt_softc *sc, int success, char state)
+{
+	if (vm_rpc_send_rpci_tx(sc, "tools.os.statechange.status %d %d",
+	    success, state) != 0) {
+		device_printf(sc->sc_dev, "unable to send state change result\n");
+		sc->sc_rpc_error = 1;
+	}
+}
+
+static void
+vmt_do_shutdown(struct vmt_softc *sc)
+{
+	vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_HALT);
+	vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK);
+
+	device_printf(sc->sc_dev, "host requested shutdown\n");
+	sysmon_task_queue_sched(0, vmt_pswitch_event, &sc->sc_ev_power);
+}
+
+static void
+vmt_do_reboot(struct vmt_softc *sc)
+{
+	vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_REBOOT);
+	vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK);
+
+	device_printf(sc->sc_dev, "host requested reboot\n");
+	sysmon_task_queue_sched(0, vmt_pswitch_event, &sc->sc_ev_reset);
+}
+
+static void
+vmt_do_resume(struct vmt_softc *sc)
+{
+	device_printf(sc->sc_dev, "guest resuming from suspended state\n");
+
+	vmt_sync_guest_clock(sc);
+
+	/* force guest info update */
+	sc->sc_hostname[0] = '\0';
+	sc->sc_set_guest_os = 0;
+	vmt_update_guest_info(sc);
+
+	vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_RESUME);
+	if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
+		device_printf(sc->sc_dev, "error sending resume response\n");
+		sc->sc_rpc_error = 1;
+	}
+
+	sysmon_task_queue_sched(0, vmt_pswitch_event, &sc->sc_ev_sleep);
+}
+
+static bool
+vmt_shutdown(device_t self, int flags)
+{
+	struct vmt_softc *sc = device_private(self);
+
+	if (vm_rpc_send_rpci_tx(sc, "tools.capability.hgfs_server toolbox 0") != 0) {
+		device_printf(sc->sc_dev, "failed to disable hgfs server capability\n");
+	}
+
+	if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0) {
+		device_printf(sc->sc_dev, "failed to send shutdown ping\n");
+	}
+
+	vm_rpc_close(&sc->sc_tclo_rpc);
+
+	return true;
+}
+
+static void
+vmt_pswitch_event(void *xarg)
+{
+	struct vmt_event *ev = xarg;
+
+	sysmon_pswitch_event(&ev->ev_smpsw, ev->ev_code);
+}
+
+static void
+vmt_tclo_tick(void *xarg)
+{
+	struct vmt_softc *sc = xarg;
+	u_int32_t rlen;
+	u_int16_t ack;
+
+	/* reopen tclo channel if it's currently closed */
+	if (sc->sc_tclo_rpc.channel == 0 &&
+	    sc->sc_tclo_rpc.cookie1 == 0 &&
+	    sc->sc_tclo_rpc.cookie2 == 0) {
+		if (vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO) != 0) {
+			device_printf(sc->sc_dev, "unable to reopen TCLO channel\n");
+			callout_schedule(&sc->sc_tclo_tick, hz * 15);
+			return;
+		}
+
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_RESET_REPLY) != 0) {
+			device_printf(sc->sc_dev, "failed to send reset reply\n");
+			sc->sc_rpc_error = 1;
+			goto out;
+		} else {
+			sc->sc_rpc_error = 0;
+		}
+	}
+
+	if (sc->sc_tclo_ping) {
+		if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0) {
+			device_printf(sc->sc_dev, "failed to send TCLO outgoing ping\n");
+			sc->sc_rpc_error = 1;
+			goto out;
+		}
+	}
+
+	if (vm_rpc_get_length(&sc->sc_tclo_rpc, &rlen, &ack) != 0) {
+		device_printf(sc->sc_dev, "failed to get length of incoming TCLO data\n");
+		sc->sc_rpc_error = 1;
+		goto out;
+	}
+
+	if (rlen == 0) {
+		sc->sc_tclo_ping = 1;
+		goto out;
+	}
+
+	if (rlen >= VMT_RPC_BUFLEN) {
+		rlen = VMT_RPC_BUFLEN - 1;
+	}
+	if (vm_rpc_get_data(&sc->sc_tclo_rpc, sc->sc_rpc_buf, rlen, ack) != 0) {
+		device_printf(sc->sc_dev, "failed to get incoming TCLO data\n");
+		sc->sc_rpc_error = 1;
+		goto out;
+	}
+	sc->sc_tclo_ping = 0;
+
+#ifdef VMT_DEBUG
+	printf("vmware: received message '%s'\n", sc->sc_rpc_buf);
+#endif
+
+	if (strcmp(sc->sc_rpc_buf, "reset") == 0) {
+
+		if (sc->sc_rpc_error != 0) {
+			device_printf(sc->sc_dev, "resetting rpc\n");
+			vm_rpc_close(&sc->sc_tclo_rpc);
+			/* reopen and send the reset reply next time around */
+			goto out;
+		}
+
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_RESET_REPLY) != 0) {
+			device_printf(sc->sc_dev, "failed to send reset reply\n");
+			sc->sc_rpc_error = 1;
+		}
+
+	} else if (strcmp(sc->sc_rpc_buf, "ping") == 0) {
+
+		vmt_update_guest_info(sc);
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
+			device_printf(sc->sc_dev, "error sending ping response\n");
+			sc->sc_rpc_error = 1;
+		}
+
+	} else if (strcmp(sc->sc_rpc_buf, "OS_Halt") == 0) {
+		vmt_do_shutdown(sc);
+	} else if (strcmp(sc->sc_rpc_buf, "OS_Reboot") == 0) {
+		vmt_do_reboot(sc);
+	} else if (strcmp(sc->sc_rpc_buf, "OS_PowerOn") == 0) {
+		vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_POWERON);
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
+			device_printf(sc->sc_dev, "error sending poweron response\n");
+			sc->sc_rpc_error = 1;
+		}
+	} else if (strcmp(sc->sc_rpc_buf, "OS_Suspend") == 0) {
+		log(LOG_KERN | LOG_NOTICE, "VMware guest entering suspended state\n");
+
+		vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_SUSPEND);
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
+			device_printf(sc->sc_dev, "error sending suspend response\n");
+			sc->sc_rpc_error = 1;
+		}
+	} else if (strcmp(sc->sc_rpc_buf, "OS_Resume") == 0) {
+		vmt_do_resume(sc);
+	} else if (strcmp(sc->sc_rpc_buf, "Capabilities_Register") == 0) {
+
+		/* don't know if this is important at all */
+		if (vm_rpc_send_rpci_tx(sc, "vmx.capability.unified_loop toolbox") != 0) {
+			device_printf(sc->sc_dev, "unable to set unified loop\n");
+			sc->sc_rpc_error = 1;
+		}
+		if (vm_rpci_response_successful(sc) == 0) {
+			device_printf(sc->sc_dev, "host rejected unified loop setting\n");
+		}
+
+		/* the trailing space is apparently important here */
+		if (vm_rpc_send_rpci_tx(sc, "tools.capability.statechange ") != 0) {
+			device_printf(sc->sc_dev, "unable to send statechange capability\n");
+			sc->sc_rpc_error = 1;
+		}
+		if (vm_rpci_response_successful(sc) == 0) {
+			device_printf(sc->sc_dev, "host rejected statechange capability\n");
+		}
+
+		if (vm_rpc_send_rpci_tx(sc, "tools.set.version %u", VM_VERSION_UNMANAGED) != 0) {
+			device_printf(sc->sc_dev, "unable to set tools version\n");
+			sc->sc_rpc_error = 1;
+		}
+
+		vmt_update_guest_uptime(sc);
+
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
+			device_printf(sc->sc_dev, "error sending capabilities_register response\n");
+			sc->sc_rpc_error = 1;
+		}
+	} else if (strcmp(sc->sc_rpc_buf, "Set_Option broadcastIP 1") == 0) {
+		struct ifaddr *iface_addr = NULL;
+		struct ifnet *iface;
+		struct sockaddr_in *guest_ip;
+		int s;
+		struct psref psref;
+
+		/* find first available ipv4 address */
+		guest_ip = NULL;
+		s = pserialize_read_enter();
+		IFNET_READER_FOREACH(iface) {
+
+			/* skip loopback */
+			if (strncmp(iface->if_xname, "lo", 2) == 0 &&
+			    iface->if_xname[2] >= '0' && iface->if_xname[2] <= '9') {
+				continue;
+			}
+
+			IFADDR_READER_FOREACH(iface_addr, iface) {
+				if (iface_addr->ifa_addr->sa_family != AF_INET) {
+					continue;
+				}
+
+				guest_ip = satosin(iface_addr->ifa_addr);
+				ifa_acquire(iface_addr, &psref);
+				goto got;
+			}
+		}
+	got:
+		pserialize_read_exit(s);
+
+		if (guest_ip != NULL) {
+			if (vm_rpc_send_rpci_tx(sc, "info-set guestinfo.ip %s",
+			    inet_ntoa(guest_ip->sin_addr)) != 0) {
+				device_printf(sc->sc_dev, "unable to send guest IP address\n");
+				sc->sc_rpc_error = 1;
+			}
+			ifa_release(iface_addr, &psref);
+
+			if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
+				device_printf(sc->sc_dev, "error sending broadcastIP response\n");
+				sc->sc_rpc_error = 1;
+			}
+		} else {
+			if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_ERROR_IP_ADDR) != 0) {
+				device_printf(sc->sc_dev,
+				    "error sending broadcastIP error response\n");
+				sc->sc_rpc_error = 1;
+			}
+		}
+	} else {
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_ERROR) != 0) {
+			device_printf(sc->sc_dev, "error sending unknown command reply\n");
+			sc->sc_rpc_error = 1;
+		}
+	}
+
+out:
+	callout_schedule(&sc->sc_tclo_tick, sc->sc_tclo_ping ? hz : 1);
+}
+
+static void
+vm_cmd(struct vm_backdoor *frame)
+{
+	BACKDOOR_OP(BACKDOOR_OP_CMD, frame);
+}
+
+static void
+vm_ins(struct vm_backdoor *frame)
+{
+	BACKDOOR_OP(BACKDOOR_OP_IN, frame);
+}
+
+static void
+vm_outs(struct vm_backdoor *frame)
+{
+	BACKDOOR_OP(BACKDOOR_OP_OUT, frame);
+}
+
+static int
+vm_rpc_open(struct vm_rpc *rpc, uint32_t proto)
+{
+	struct vm_backdoor frame;
+
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word      = VM_MAGIC;
+	frame.ebx.word      = proto | VM_RPC_FLAG_COOKIE;
+	frame.ecx.part.low  = VM_CMD_RPC;
+	frame.ecx.part.high = VM_RPC_OPEN;
+	frame.edx.part.low  = VM_PORT_CMD;
+	frame.edx.part.high = 0;
+
+	vm_cmd(&frame);
+
+	if (frame.ecx.part.high != 1 || frame.edx.part.low != 0) {
+		/* open-vm-tools retries without VM_RPC_FLAG_COOKIE here.. */
+		printf("vmware: open failed, eax=%08x, ecx=%08x, edx=%08x\n",
+			frame.eax.word, frame.ecx.word, frame.edx.word);
+		return EIO;
+	}
+
+	rpc->channel = frame.edx.part.high;
+	rpc->cookie1 = frame.esi.word;
+	rpc->cookie2 = frame.edi.word;
+
+	return 0;
+}
+
+static int
+vm_rpc_close(struct vm_rpc *rpc)
+{
+	struct vm_backdoor frame;
+
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word      = VM_MAGIC;
+	frame.ebx.word      = 0;
+	frame.ecx.part.low  = VM_CMD_RPC;
+	frame.ecx.part.high = VM_RPC_CLOSE;
+	frame.edx.part.low  = VM_PORT_CMD;
+	frame.edx.part.high = rpc->channel;
+	frame.edi.word      = rpc->cookie2;
+	frame.esi.word      = rpc->cookie1;
+
+	vm_cmd(&frame);
+
+	if (frame.ecx.part.high == 0 || frame.ecx.part.low != 0) {
+		printf("vmware: close failed, eax=%08x, ecx=%08x\n",
+				frame.eax.word, frame.ecx.word);
+		return EIO;
+	}
+
+	rpc->channel = 0;
+	rpc->cookie1 = 0;
+	rpc->cookie2 = 0;
+
+	return 0;
+}
+
+static int
+vm_rpc_send(const struct vm_rpc *rpc, const uint8_t *buf, uint32_t length)
+{
+	struct vm_backdoor frame;
+
+	/* Send the length of the command. */
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word = VM_MAGIC;
+	frame.ebx.word = length;
+	frame.ecx.part.low  = VM_CMD_RPC;
+	frame.ecx.part.high = VM_RPC_SET_LENGTH;
+	frame.edx.part.low  = VM_PORT_CMD;
+	frame.edx.part.high = rpc->channel;
+	frame.esi.word = rpc->cookie1;
+	frame.edi.word = rpc->cookie2;
+
+	vm_cmd(&frame);
+
+	if ((frame.ecx.part.high & VM_RPC_REPLY_SUCCESS) == 0) {
+		printf("vmware: sending length failed, eax=%08x, ecx=%08x\n",
+				frame.eax.word, frame.ecx.word);
+		return EIO;
+	}
+
+	if (length == 0)
+		return 0; /* Only need to poke once if command is null. */
+
+	/* Send the command using enhanced RPC. */
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word = VM_MAGIC;
+	frame.ebx.word = VM_RPC_ENH_DATA;
+	frame.ecx.word = length;
+	frame.edx.part.low  = VM_PORT_RPC;
+	frame.edx.part.high = rpc->channel;
+	frame.ebp.word = rpc->cookie1;
+	frame.edi.word = rpc->cookie2;
+#if defined(__amd64__) || defined(__aarch64__)
+	frame.esi.quad = (uint64_t)buf;
+#else
+	frame.esi.word = (uint32_t)buf;
+#endif
+
+	vm_outs(&frame);
+
+	if (frame.ebx.word != VM_RPC_ENH_DATA) {
+		/* open-vm-tools retries on VM_RPC_REPLY_CHECKPOINT */
+		printf("vmware: send failed, ebx=%08x\n", frame.ebx.word);
+		return EIO;
+	}
+
+	return 0;
+}
+
+static int
+vm_rpc_send_str(const struct vm_rpc *rpc, const uint8_t *str)
+{
+	return vm_rpc_send(rpc, str, strlen(str));
+}
+
+static int
+vm_rpc_get_data(const struct vm_rpc *rpc, char *data, uint32_t length,
+    uint16_t dataid)
+{
+	struct vm_backdoor frame;
+
+	/* Get data using enhanced RPC. */
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word      = VM_MAGIC;
+	frame.ebx.word      = VM_RPC_ENH_DATA;
+	frame.ecx.word      = length;
+	frame.edx.part.low  = VM_PORT_RPC;
+	frame.edx.part.high = rpc->channel;
+	frame.esi.word      = rpc->cookie1;
+#if defined(__amd64__) || defined(__aarch64__)
+	frame.edi.quad      = (uint64_t)data;
+#else
+	frame.edi.word      = (uint32_t)data;
+#endif
+	frame.ebp.word      = rpc->cookie2;
+
+	vm_ins(&frame);
+
+	/* NUL-terminate the data */
+	data[length] = '\0';
+
+	if (frame.ebx.word != VM_RPC_ENH_DATA) {
+		printf("vmware: get data failed, ebx=%08x\n",
+				frame.ebx.word);
+		return EIO;
+	}
+
+	/* Acknowledge data received. */
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word      = VM_MAGIC;
+	frame.ebx.word      = dataid;
+	frame.ecx.part.low  = VM_CMD_RPC;
+	frame.ecx.part.high = VM_RPC_GET_END;
+	frame.edx.part.low  = VM_PORT_CMD;
+	frame.edx.part.high = rpc->channel;
+	frame.esi.word      = rpc->cookie1;
+	frame.edi.word      = rpc->cookie2;
+
+	vm_cmd(&frame);
+
+	if (frame.ecx.part.high == 0) {
+		printf("vmware: ack data failed, eax=%08x, ecx=%08x\n",
+				frame.eax.word, frame.ecx.word);
+		return EIO;
+	}
+
+	return 0;
+}
+
+static int
+vm_rpc_get_length(const struct vm_rpc *rpc, uint32_t *length, uint16_t *dataid)
+{
+	struct vm_backdoor frame;
+
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word      = VM_MAGIC;
+	frame.ebx.word      = 0;
+	frame.ecx.part.low  = VM_CMD_RPC;
+	frame.ecx.part.high = VM_RPC_GET_LENGTH;
+	frame.edx.part.low  = VM_PORT_CMD;
+	frame.edx.part.high = rpc->channel;
+	frame.esi.word      = rpc->cookie1;
+	frame.edi.word      = rpc->cookie2;
+
+	vm_cmd(&frame);
+
+	if ((frame.ecx.part.high & VM_RPC_REPLY_SUCCESS) == 0) {
+		printf("vmware: get length failed, eax=%08x, ecx=%08x\n",
+				frame.eax.word, frame.ecx.word);
+		return EIO;
+	}
+	if ((frame.ecx.part.high & VM_RPC_REPLY_DORECV) == 0) {
+		*length = 0;
+		*dataid = 0;
+	} else {
+		*length = frame.ebx.word;
+		*dataid = frame.edx.part.high;
+	}
+
+	return 0;
+}
+
+static int
+vm_rpci_response_successful(struct vmt_softc *sc)
+{
+	return (sc->sc_rpc_buf[0] == '1' && sc->sc_rpc_buf[1] == ' ');
+}
+
+static int
+vm_rpc_send_rpci_tx_buf(struct vmt_softc *sc, const uint8_t *buf, uint32_t length)
+{
+	struct vm_rpc rpci;
+	u_int32_t rlen;
+	u_int16_t ack;
+	int result = 0;
+
+	if (vm_rpc_open(&rpci, VM_RPC_OPEN_RPCI) != 0) {
+		device_printf(sc->sc_dev, "rpci channel open failed\n");
+		return EIO;
+	}
+
+	if (vm_rpc_send(&rpci, sc->sc_rpc_buf, length) != 0) {
+		device_printf(sc->sc_dev, "unable to send rpci command\n");
+		result = EIO;
+		goto out;
+	}
+
+	if (vm_rpc_get_length(&rpci, &rlen, &ack) != 0) {
+		device_printf(sc->sc_dev, "failed to get length of rpci response data\n");
+		result = EIO;
+		goto out;
+	}
+
+	if (rlen > 0) {
+		if (rlen >= VMT_RPC_BUFLEN) {
+			rlen = VMT_RPC_BUFLEN - 1;
+		}
+
+		if (vm_rpc_get_data(&rpci, sc->sc_rpc_buf, rlen, ack) != 0) {
+			device_printf(sc->sc_dev, "failed to get rpci response data\n");
+			result = EIO;
+			goto out;
+		}
+	}
+
+out:
+	if (vm_rpc_close(&rpci) != 0) {
+		device_printf(sc->sc_dev, "unable to close rpci channel\n");
+	}
+
+	return result;
+}
+
+static int
+vm_rpc_send_rpci_tx(struct vmt_softc *sc, const char *fmt, ...)
+{
+	va_list args;
+	int len;
+
+	va_start(args, fmt);
+	len = vsnprintf(sc->sc_rpc_buf, VMT_RPC_BUFLEN, fmt, args);
+	va_end(args);
+
+	if (len >= VMT_RPC_BUFLEN) {
+		device_printf(sc->sc_dev, "rpci command didn't fit in buffer\n");
+		return EIO;
+	}
+
+	return vm_rpc_send_rpci_tx_buf(sc, sc->sc_rpc_buf, len);
+}
+
+#if 0
+	struct vm_backdoor frame;
+
+	memset(&frame, 0, sizeof(frame));
+
+	frame.eax.word = VM_MAGIC;
+	frame.ecx.part.low = VM_CMD_GET_VERSION;
+	frame.edx.part.low  = VM_PORT_CMD;
+
+	printf("\n");
+	printf("eax 0x%08x\n", frame.eax.word);
+	printf("ebx 0x%08x\n", frame.ebx.word);
+	printf("ecx 0x%08x\n", frame.ecx.word);
+	printf("edx 0x%08x\n", frame.edx.word);
+	printf("ebp 0x%08x\n", frame.ebp.word);
+	printf("edi 0x%08x\n", frame.edi.word);
+	printf("esi 0x%08x\n", frame.esi.word);
+
+	vm_cmd(&frame);
+
+	printf("-\n");
+	printf("eax 0x%08x\n", frame.eax.word);
+	printf("ebx 0x%08x\n", frame.ebx.word);
+	printf("ecx 0x%08x\n", frame.ecx.word);
+	printf("edx 0x%08x\n", frame.edx.word);
+	printf("ebp 0x%08x\n", frame.ebp.word);
+	printf("edi 0x%08x\n", frame.edi.word);
+	printf("esi 0x%08x\n", frame.esi.word);
+#endif
+
+/*
+ * Notes on tracing backdoor activity in vmware-guestd:
+ *
+ * - Find the addresses of the inl / rep insb / rep outsb
+ *   instructions used to perform backdoor operations.
+ *   One way to do this is to disassemble vmware-guestd:
+ *
+ *   $ objdump -S /emul/freebsd/sbin/vmware-guestd > vmware-guestd.S
+ *
+ *   and search for '<tab>in ' in the resulting file.  The rep insb and
+ *   rep outsb code is directly below that.
+ *
+ * - Run vmware-guestd under gdb, setting up breakpoints as follows:
+ *   (the addresses shown here are the ones from VMware-server-1.0.10-203137,
+ *   the last version that actually works in FreeBSD emulation on OpenBSD)
+ *
+ * break *0x805497b   (address of 'in' instruction)
+ * commands 1
+ * silent
+ * echo INOUT\n
+ * print/x $ecx
+ * print/x $ebx
+ * print/x $edx
+ * continue
+ * end
+ * break *0x805497c   (address of instruction after 'in')
+ * commands 2
+ * silent
+ * echo ===\n
+ * print/x $ecx
+ * print/x $ebx
+ * print/x $edx
+ * echo \n
+ * continue
+ * end
+ * break *0x80549b7   (address of instruction before 'rep insb')
+ * commands 3
+ * silent
+ * set variable $inaddr = $edi
+ * set variable $incount = $ecx
+ * continue
+ * end
+ * break *0x80549ba   (address of instruction after 'rep insb')
+ * commands 4
+ * silent
+ * echo IN\n
+ * print $incount
+ * x/s $inaddr
+ * echo \n
+ * continue
+ * end
+ * break *0x80549fb    (address of instruction before 'rep outsb')
+ * commands 5
+ * silent
+ * echo OUT\n
+ * print $ecx
+ * x/s $esi
+ * echo \n
+ * continue
+ * end
+ *
+ * This will produce a log of the backdoor operations, including the
+ * data sent and received and the relevant register values.  You can then
+ * match the register values to the various constants in this file.
+ */
Index: src/sys/dev/vmt/vmtreg.h
diff -u /dev/null src/sys/dev/vmt/vmtreg.h:1.1
--- /dev/null	Tue Oct 27 08:57:12 2020
+++ src/sys/dev/vmt/vmtreg.h	Tue Oct 27 08:57:11 2020
@@ -0,0 +1,117 @@
+/* $NetBSD: vmtreg.h,v 1.1 2020/10/27 08:57:11 ryo Exp $ */
+/* NetBSD: vmt.c,v 1.15 2016/11/10 03:32:04 ozaki-r Exp */
+/* $OpenBSD: vmt.c,v 1.11 2011/01/27 21:29:25 dtucker Exp $ */
+
+/*
+ * Copyright (c) 2007 David Crawshaw <da...@zentus.com>
+ * Copyright (c) 2008 David Gwynne <d...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_VMT_VMTREG_H_
+#define _DEV_VMT_VMTREG_H_
+
+/* OS name to report to host */
+#ifdef __i386__
+#define VM_OS_NAME	"other"
+#else
+#define VM_OS_NAME	"other-64"
+#endif
+
+/* "The" magic number, always occupies the EAX register. */
+#define VM_MAGIC			0x564D5868
+
+/* Port numbers, passed on EDX.LOW . */
+#define VM_PORT_CMD			0x5658
+#define VM_PORT_RPC			0x5659
+
+/* Commands, passed on ECX.LOW. */
+#define VM_CMD_GET_SPEED		0x01
+#define VM_CMD_APM			0x02
+#define VM_CMD_GET_MOUSEPOS		0x04
+#define VM_CMD_SET_MOUSEPOS		0x05
+#define VM_CMD_GET_CLIPBOARD_LEN	0x06
+#define VM_CMD_GET_CLIPBOARD		0x07
+#define VM_CMD_SET_CLIPBOARD_LEN	0x08
+#define VM_CMD_SET_CLIPBOARD		0x09
+#define VM_CMD_GET_VERSION		0x0a
+#define  VM_VERSION_UNMANAGED			0x7fffffff
+#define VM_CMD_GET_DEVINFO		0x0b
+#define VM_CMD_DEV_ADDREMOVE		0x0c
+#define VM_CMD_GET_GUI_OPTIONS		0x0d
+#define VM_CMD_SET_GUI_OPTIONS		0x0e
+#define VM_CMD_GET_SCREEN_SIZE		0x0f
+#define VM_CMD_GET_HWVER		0x11
+#define VM_CMD_POPUP_OSNOTFOUND		0x12
+#define VM_CMD_GET_BIOS_UUID		0x13
+#define VM_CMD_GET_MEM_SIZE		0x14
+/*#define VM_CMD_GET_TIME		0x17 */	/* deprecated */
+#define VM_CMD_RPC			0x1e
+#define VM_CMD_GET_HZ			0x2d
+#define VM_CMD_GET_TIME_FULL		0x2e
+#define VM_CMD_GET_VCPU_INFO		0x44
+#define  VCPUINFO_LEGACY_X2APIC			__BIT(3)
+#define  VCPUINFO_VCPU_RESERVED			__BIT(31)
+
+/* RPC sub-commands, passed on ECX.HIGH. */
+#define VM_RPC_OPEN			0x00
+#define VM_RPC_SET_LENGTH		0x01
+#define VM_RPC_SET_DATA			0x02
+#define VM_RPC_GET_LENGTH		0x03
+#define VM_RPC_GET_DATA			0x04
+#define VM_RPC_GET_END			0x05
+#define VM_RPC_CLOSE			0x06
+
+/* RPC magic numbers, passed on EBX. */
+#define VM_RPC_OPEN_RPCI	0x49435052UL /* with VM_RPC_OPEN. */
+#define VM_RPC_OPEN_TCLO	0x4F4C4354UL /* with VP_RPC_OPEN. */
+#define VM_RPC_ENH_DATA		0x00010000UL /* with enhanced RPC data calls. */
+
+#define VM_RPC_FLAG_COOKIE	0x80000000UL
+
+/* RPC reply flags */
+#define VM_RPC_REPLY_SUCCESS	0x0001
+#define VM_RPC_REPLY_DORECV	0x0002		/* incoming message available */
+#define VM_RPC_REPLY_CLOSED	0x0004		/* RPC channel is closed */
+#define VM_RPC_REPLY_UNSENT	0x0008		/* incoming message was removed? */
+#define VM_RPC_REPLY_CHECKPOINT	0x0010		/* checkpoint occurred -> retry */
+#define VM_RPC_REPLY_POWEROFF	0x0020		/* underlying device is powering off */
+#define VM_RPC_REPLY_TIMEOUT	0x0040
+#define VM_RPC_REPLY_HB		0x0080		/* high-bandwidth tx/rx available */
+
+/* VM state change IDs */
+#define VM_STATE_CHANGE_HALT	1
+#define VM_STATE_CHANGE_REBOOT	2
+#define VM_STATE_CHANGE_POWERON 3
+#define VM_STATE_CHANGE_RESUME  4
+#define VM_STATE_CHANGE_SUSPEND 5
+
+/* VM guest info keys */
+#define VM_GUEST_INFO_DNS_NAME		1
+#define VM_GUEST_INFO_IP_ADDRESS	2
+#define VM_GUEST_INFO_DISK_FREE_SPACE	3
+#define VM_GUEST_INFO_BUILD_NUMBER	4
+#define VM_GUEST_INFO_OS_NAME_FULL	5
+#define VM_GUEST_INFO_OS_NAME		6
+#define VM_GUEST_INFO_UPTIME		7
+#define VM_GUEST_INFO_MEMORY		8
+#define VM_GUEST_INFO_IP_ADDRESS_V2	9
+
+/* RPC responses */
+#define VM_RPC_REPLY_OK			"OK "
+#define VM_RPC_RESET_REPLY		"OK ATR toolbox"
+#define VM_RPC_REPLY_ERROR		"ERROR Unknown command"
+#define VM_RPC_REPLY_ERROR_IP_ADDR	"ERROR Unable to find guest IP address"
+
+#endif /* _DEV_VMT_VMTREG_H_ */
Index: src/sys/dev/vmt/vmtvar.h
diff -u /dev/null src/sys/dev/vmt/vmtvar.h:1.1
--- /dev/null	Tue Oct 27 08:57:12 2020
+++ src/sys/dev/vmt/vmtvar.h	Tue Oct 27 08:57:11 2020
@@ -0,0 +1,221 @@
+/* $NetBSD: vmtvar.h,v 1.1 2020/10/27 08:57:11 ryo Exp $ */
+/* NetBSD: vmt.c,v 1.15 2016/11/10 03:32:04 ozaki-r Exp */
+/* $OpenBSD: vmt.c,v 1.11 2011/01/27 21:29:25 dtucker Exp $ */
+
+/*
+ * Copyright (c) 2007 David Crawshaw <da...@zentus.com>
+ * Copyright (c) 2008 David Gwynne <d...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_VMT_VMTVAR_H_
+#define _DEV_VMT_VMTVAR_H_
+
+#include <sys/uuid.h>
+#include <dev/sysmon/sysmonvar.h>
+
+/* XXX: depend on little-endian */
+/* A register. */
+union vm_reg {
+	struct {
+		uint16_t low;
+		uint16_t high;
+	} part;
+	uint32_t word;
+#if defined(__amd64__) || defined(__aarch64__)
+	struct {
+		uint32_t low;
+		uint32_t high;
+	} words;
+	uint64_t quad;
+#endif
+} __packed;
+
+/* A register frame. */
+/* XXX 'volatile' as a workaround because BACKDOOR_OP is likely broken */
+struct vm_backdoor {
+	volatile union vm_reg eax;
+	volatile union vm_reg ebx;
+	volatile union vm_reg ecx;
+	volatile union vm_reg edx;
+	volatile union vm_reg esi;
+	volatile union vm_reg edi;
+	volatile union vm_reg ebp;
+} __packed;
+
+/* RPC context. */
+struct vm_rpc {
+	uint16_t channel;
+	uint32_t cookie1;
+	uint32_t cookie2;
+};
+
+struct vmt_event {
+	struct sysmon_pswitch	ev_smpsw;
+	int			ev_code;
+};
+
+struct vmt_softc {
+	device_t		sc_dev;
+
+	struct sysctllog	*sc_log;
+	struct vm_rpc		sc_tclo_rpc;
+	bool			sc_tclo_rpc_open;
+	char			*sc_rpc_buf;
+	int			sc_rpc_error;
+	int			sc_tclo_ping;
+	int			sc_set_guest_os;
+#define VMT_RPC_BUFLEN			256
+
+	struct callout		sc_tick;
+	struct callout		sc_tclo_tick;
+
+#define VMT_CLOCK_SYNC_PERIOD_SECONDS 60
+	int			sc_clock_sync_period_seconds;
+	struct callout		sc_clock_sync_tick;
+
+	struct vmt_event	sc_ev_power;
+	struct vmt_event	sc_ev_reset;
+	struct vmt_event	sc_ev_sleep;
+	bool			sc_smpsw_valid;
+
+	char			sc_hostname[MAXHOSTNAMELEN];
+	char			sc_uuid[_UUID_STR_LEN];
+};
+
+bool vmt_probe(void);
+void vmt_common_attach(struct vmt_softc *);
+int vmt_common_detach(struct vmt_softc *);
+
+#define BACKDOOR_OP_I386(op, frame)		\
+	__asm__ __volatile__ (			\
+		"pushal;"			\
+		"pushl %%eax;"			\
+		"movl 0x18(%%eax), %%ebp;"	\
+		"movl 0x14(%%eax), %%edi;"	\
+		"movl 0x10(%%eax), %%esi;"	\
+		"movl 0x0c(%%eax), %%edx;"	\
+		"movl 0x08(%%eax), %%ecx;"	\
+		"movl 0x04(%%eax), %%ebx;"	\
+		"movl 0x00(%%eax), %%eax;"	\
+		op				\
+		"xchgl %%eax, 0x00(%%esp);"	\
+		"movl %%ebp, 0x18(%%eax);"	\
+		"movl %%edi, 0x14(%%eax);"	\
+		"movl %%esi, 0x10(%%eax);"	\
+		"movl %%edx, 0x0c(%%eax);"	\
+		"movl %%ecx, 0x08(%%eax);"	\
+		"movl %%ebx, 0x04(%%eax);"	\
+		"popl 0x00(%%eax);"		\
+		"popal;"			\
+		:				\
+		:"a"(frame)			\
+	)
+
+#define BACKDOOR_OP_AMD64(op, frame)		\
+	__asm__ __volatile__ (			\
+		"pushq %%rbp;			\n\t" \
+		"pushq %%rax;			\n\t" \
+		"movq 0x30(%%rax), %%rbp;	\n\t" \
+		"movq 0x28(%%rax), %%rdi;	\n\t" \
+		"movq 0x20(%%rax), %%rsi;	\n\t" \
+		"movq 0x18(%%rax), %%rdx;	\n\t" \
+		"movq 0x10(%%rax), %%rcx;	\n\t" \
+		"movq 0x08(%%rax), %%rbx;	\n\t" \
+		"movq 0x00(%%rax), %%rax;	\n\t" \
+		op				"\n\t" \
+		"xchgq %%rax, 0x00(%%rsp);	\n\t" \
+		"movq %%rbp, 0x30(%%rax);	\n\t" \
+		"movq %%rdi, 0x28(%%rax);	\n\t" \
+		"movq %%rsi, 0x20(%%rax);	\n\t" \
+		"movq %%rdx, 0x18(%%rax);	\n\t" \
+		"movq %%rcx, 0x10(%%rax);	\n\t" \
+		"movq %%rbx, 0x08(%%rax);	\n\t" \
+		"popq 0x00(%%rax);		\n\t" \
+		"popq %%rbp;			\n\t" \
+		: /* No outputs. */ \
+		: "a" (frame) \
+		  /* No pushal on amd64 so warn gcc about the clobbered registers. */\
+		: "rbx", "rcx", "rdx", "rdi", "rsi", "cc", "memory" \
+	)
+
+#define X86_IO_MAGIC		0x86	/* magic for upper 32bit of x7 */
+#define X86_IO_W7_SIZE_MASK	__BITS(1, 0)
+#define X86_IO_W7_SIZE(n)	__SHIFTIN((n), X86_IO_W7_SIZE_MASK)
+#define X86_IO_W7_DIR		__BIT(2)
+#define X86_IO_W7_WITH		__BIT(3)
+#define X86_IO_W7_STR		__BIT(4)
+#define X86_IO_W7_DF		__BIT(5)
+#define X86_IO_W7_IMM_MASK	__BITS(12, 5)
+#define X86_IO_W7_IMM(imm)	__SHIFTIN((imm), X86_IO_W7_IMM_MASK)
+#define BACKDOOR_OP_AARCH64(op, frame)		\
+	__asm__ __volatile__ (			\
+		"ldp x0, x1, [%0, 8 * 0];	\n\t" \
+		"ldp x2, x3, [%0, 8 * 2];	\n\t" \
+		"ldp x4, x5, [%0, 8 * 4];	\n\t" \
+		"ldr x6,     [%0, 8 * 6];	\n\t" \
+		"mov x7, %1			\n\t" \
+		"movk x7, %2, lsl #32;		\n\t" \
+		"mrs xzr, mdccsr_el0;		\n\t" \
+		"stp x0, x1, [%0, 8 * 0];	\n\t" \
+		"stp x2, x3, [%0, 8 * 2];	\n\t" \
+		"stp x4, x5, [%0, 8 * 4];	\n\t" \
+		"str x6,     [%0, 8 * 6];	\n\t" \
+		: /* No outputs. */ \
+		: "r" (frame), \
+		  "r" (op), \
+		  "i" (X86_IO_MAGIC) \
+		: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "memory" \
+	)
+
+#if defined(__i386__)
+#define BACKDOOR_OP(op, frame) BACKDOOR_OP_I386(op, frame)
+#elif defined(__amd64__)
+#define BACKDOOR_OP(op, frame) BACKDOOR_OP_AMD64(op, frame)
+#elif defined(__aarch64__)
+#define BACKDOOR_OP(op, frame) BACKDOOR_OP_AARCH64(op, frame)
+#endif
+
+#if defined(__i386__) || defined(__amd64__)
+#define BACKDOOR_OP_CMD	"inl %%dx, %%eax;"
+#define BACKDOOR_OP_IN	"cld;\n\trep insb;"
+#define BACKDOOR_OP_OUT	"cld;\n\trep outsb;"
+#elif defined(__aarch64__)
+#define BACKDOOR_OP_CMD	(X86_IO_W7_WITH | X86_IO_W7_DIR | X86_IO_W7_SIZE(2))
+#define BACKDOOR_OP_IN	(X86_IO_W7_WITH | X86_IO_W7_STR | X86_IO_W7_DIR)
+#define BACKDOOR_OP_OUT	(X86_IO_W7_WITH | X86_IO_W7_STR)
+#endif
+
+static __inline void
+vmt_hvcall(uint8_t cmd, u_int regs[6])
+{
+	struct vm_backdoor frame;
+
+	memset(&frame, 0, sizeof(frame));
+	frame.eax.word = VM_MAGIC;
+	frame.ebx.word = UINT_MAX;
+	frame.ecx.part.low = cmd;
+	frame.edx.part.low = VM_PORT_CMD;
+
+	BACKDOOR_OP(BACKDOOR_OP_CMD, &frame);
+
+	regs[0] = frame.eax.word;
+	regs[1] = frame.ebx.word;
+	regs[2] = frame.ecx.word;
+	regs[3] = frame.edx.word;
+	regs[4] = frame.esi.word;
+	regs[5] = frame.edi.word;
+}
+
+#endif /* _DEV_VMT_VMTVAR_H_ */

Reply via email to