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 = . - 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 = . + 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_ */