Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 4 Aug 2013 22:14:02 +0000 (UTC)
From:      Bryan Venteicher <bryanv@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r253945 - in projects/vmxnet/sys: dev/vmware/vmt modules/vmware/vmt
Message-ID:  <201308042214.r74ME2Bq017053@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: bryanv
Date: Sun Aug  4 22:14:02 2013
New Revision: 253945
URL: http://svnweb.freebsd.org/changeset/base/253945

Log:
  Add compile only tested port of OpenBSD VMware Tools driver (vmt)
  
  I doubt I'll have any more time to work on this any time soon,
  but commit it now in case somebody is interested in hacking on it.

Added:
  projects/vmxnet/sys/dev/vmware/vmt/
  projects/vmxnet/sys/dev/vmware/vmt/vmt.c   (contents, props changed)
  projects/vmxnet/sys/dev/vmware/vmt/vmtreg.h   (contents, props changed)
  projects/vmxnet/sys/modules/vmware/vmt/
  projects/vmxnet/sys/modules/vmware/vmt/Makefile   (contents, props changed)

Added: projects/vmxnet/sys/dev/vmware/vmt/vmt.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/vmxnet/sys/dev/vmware/vmt/vmt.c	Sun Aug  4 22:14:02 2013	(r253945)
@@ -0,0 +1,1069 @@
+/*-
+ * Copyright (c) 2007 David Crawshaw <david@zentus.com>
+ * Copyright (c) 2008 David Gwynne <dlg@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.
+ *
+ * $OpenBSD: src/sys/dev/vmt.c,v 1.13 2013/07/03 15:26:02 sf Exp $
+ */
+
+/*
+ * Protocol reverse engineered by Ken Kato:
+ * http://chitchat.at.infoseek.co.jp/vmware/backdoor.html
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/reboot.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <machine/stdarg.h>
+
+#include "vmtreg.h"
+
+#define VMT_RPC_BUFLEN			256
+
+struct vmt_softc {
+	struct mtx		 sc_mtx;
+	device_t		 sc_dev;
+	struct vm_rpc		 sc_tclo_rpc;
+	char			*sc_rpc_buf;
+	int			 sc_rpc_error;
+	int			 sc_tclo_ping;
+	int			 sc_set_guest_os;
+	int			 sc_removing;
+	struct callout		 sc_tick;
+	struct callout		 sc_tclo_tick;
+	char			 sc_hostname[MAXHOSTNAMELEN];
+};
+
+#define VMT_LOCK(_sc)	mtx_lock(&(_sc)->sc_mtx)
+#define VMT_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
+
+static void	vmt_identify(driver_t *, device_t);
+static int	vmt_probe(device_t);
+static int	vmt_attach(device_t);
+static int	vmt_detach(device_t);
+static int	vmt_shutdown(device_t);
+
+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_probe_cmd(struct vm_backdoor *, uint16_t);
+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_disconnect(struct vmt_softc *);
+
+static void	vmt_update_guest_info(struct vmt_softc *);
+static void	vmt_update_guest_uptime(struct vmt_softc *);
+
+static void	vmt_tick(void *);
+static void	vmt_tclo_tick(void *);
+
+extern char hostname[MAXHOSTNAMELEN];	/* prison0.pr_hostname */
+
+static device_method_t vmt_methods[] = {
+	DEVMETHOD(device_identify,	vmt_identify),
+	DEVMETHOD(device_probe,		vmt_probe),
+	DEVMETHOD(device_attach,	vmt_attach),
+	DEVMETHOD(device_detach,	vmt_detach),
+	DEVMETHOD(device_shutdown,	vmt_shutdown),
+
+	DEVMETHOD_END
+};
+
+static driver_t vmt_driver = {
+	"vmt", vmt_methods, sizeof(struct vmt_softc)
+};
+
+static devclass_t vmt_devclass;
+DRIVER_MODULE(vmt, nexus, vmt_driver, vmt_devclass, 0, 0);
+
+static void
+vmt_probe_cmd(struct vm_backdoor *frame, uint16_t cmd)
+{
+
+	bzero(frame, 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 void
+vmt_identify(driver_t *driver, device_t parent)
+{
+	struct vm_backdoor frame;
+
+	if (vm_guest != VM_GUEST_VM)
+		return;
+
+	if (device_find_child(parent, driver->name, -1) != NULL)
+		return;
+
+	vmt_probe_cmd(&frame, VM_CMD_GET_VERSION);
+	if (frame.eax.word == 0XFFFFFFFF || frame.ebx.word != VM_MAGIC)
+		return;
+
+	vmt_probe_cmd(&frame, VM_CMD_GET_SPEED);
+	if (frame.eax.word == VM_MAGIC)
+		return;
+
+	if (BUS_ADD_CHILD(parent, 0, driver->name, 0) == NULL)
+		device_printf(parent, "add vmt child failed\n");
+}
+
+static int
+vmt_probe(device_t dev)
+{
+
+	device_set_desc(dev, "VMware Tools Device");
+	return (0);
+}
+
+static int
+vmt_attach(device_t dev)
+{
+	struct vmt_softc *sc;
+	int error;
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+
+	mtx_init(&sc->sc_mtx, "vmt", NULL, MTX_DEF);
+
+	sc->sc_rpc_buf = malloc(VMT_RPC_BUFLEN, M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (sc->sc_rpc_buf == NULL) {
+		error = ENOMEM;
+		device_printf(dev, "unable to allocate buffer for RPC\n");
+		goto fail;
+	}
+
+	error = vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO);
+	if (error) {
+		device_printf(dev,
+		    "failed to open backdoor RPC channel (TCLO protocol)\n");
+		goto fail;
+	}
+
+	/* Don't know if this is important at all yet. */
+	error = vm_rpc_send_rpci_tx(sc,
+	    "tools.capability.hgfs_server toolbox 1");
+	if (error) {
+		device_printf(dev, "failed to set HGFS server capability\n");
+		goto fail;
+	}
+
+	callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0);
+	callout_reset(&sc->sc_tick, hz, vmt_tick, sc);
+
+	sc->sc_tclo_ping = 1;
+	callout_init_mtx(&sc->sc_tclo_tick, &sc->sc_mtx, 0);
+	callout_reset(&sc->sc_tclo_tick, hz, vmt_tclo_tick, sc);
+
+fail:
+	if (error)
+		vmt_detach(dev);
+
+	return (error);
+}
+
+static int
+vmt_detach(device_t dev)
+{
+	struct vmt_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (device_is_attached(dev)) {
+		VMT_LOCK(sc);
+		sc->sc_removing = 1;
+		vmt_disconnect(sc);
+		VMT_UNLOCK(sc);
+
+		callout_drain(&sc->sc_tick);
+		callout_drain(&sc->sc_tclo_tick);
+	}
+
+	if (sc->sc_rpc_buf != NULL) {
+		free(sc->sc_rpc_buf, M_DEVBUF);
+		sc->sc_rpc_buf = NULL;
+	}
+
+	mtx_destroy(&sc->sc_mtx);
+
+	return (0);
+}
+
+static int
+vmt_shutdown(device_t dev)
+{
+
+	return (0);
+}
+
+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 %lld00",
+	    VM_GUEST_INFO_UPTIME, (long long)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)
+{
+	device_t dev;
+	int error;
+
+	dev = sc->sc_dev;
+
+	if (strncmp(sc->sc_hostname, hostname, sizeof(sc->sc_hostname)) != 0) {
+		strlcpy(sc->sc_hostname, hostname, sizeof(sc->sc_hostname));
+
+		error = vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s",
+		    VM_GUEST_INFO_DNS_NAME, sc->sc_hostname);
+		if (error) {
+			device_printf(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) {
+		/* See linux_misc.c for this ... */
+		error = vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s %s %s",
+		    VM_GUEST_INFO_OS_NAME_FULL, ostype, osrelease, version);
+		if (error) {
+			device_printf(dev, "unable to set full guest OS\n");
+			sc->sc_rpc_error = 1;
+		}
+
+		error = vm_rpc_send_rpci_tx(sc, "SetGuestInfo  %d %s",
+		    VM_GUEST_INFO_OS_NAME, "FreeBSD");
+		if (error) {
+			device_printf(dev, "unable to set guest OS\n");
+			sc->sc_rpc_error = 1;
+		}
+
+		sc->sc_set_guest_os = 1;
+	}
+}
+
+static void
+vmt_tick(void *xsc)
+{
+	struct vmt_softc *sc;
+	struct vm_backdoor frame;
+	struct timeval guest;
+	struct timeval host, diff __unused;
+
+	sc = xsc;
+
+	if (sc->sc_removing != 0)
+		return;
+
+	microtime(&guest);
+
+	bzero(&frame, 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) {
+		host.tv_sec = ((uint64_t)frame.esi.word << 32) | frame.edx.word;
+		host.tv_usec = frame.ebx.word;
+
+#if 0
+		timersub(&guest, &host, &diff);
+		sc->sc_sensor.value = (u_int64_t)diff.tv_sec * 1000000000LL +
+		    (u_int64_t)diff.tv_usec * 1000LL;
+		sc->sc_sensor.status = SENSOR_S_OK;
+#endif
+	}
+#if 0
+	else
+		sc->sc_sensor.status = SENSOR_S_UNKNOWN;
+#endif
+
+	vmt_update_guest_info(sc);
+	vmt_update_guest_uptime(sc);
+
+	callout_schedule(&sc->sc_tick, 15 * hz);
+}
+
+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_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);
+
+	log(LOG_KERN | LOG_NOTICE,
+	    "Rebooting in response to request from VMware host\n");
+	shutdown_nice(0);
+}
+
+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);
+
+	log(LOG_KERN | LOG_NOTICE,
+	    "Shutting down in response to request from VMware host\n");
+	shutdown_nice(RB_POWEROFF | RB_HALT);
+}
+
+static void
+vmt_disconnect(struct vmt_softc *sc)
+{
+	device_t dev;
+	int error;
+
+	dev = sc->sc_dev;
+
+	error = vm_rpc_send_rpci_tx(sc,
+	    "tools.capability.hgfs_server toolbox 0");
+	if (error)
+		device_printf(dev, "failed to disable hgfs server capability\n");
+
+	if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0)
+		device_printf(dev, "failed to send shutdown ping\n");
+
+	vm_rpc_close(&sc->sc_tclo_rpc);
+}
+
+static void
+vmt_tclo_tick(void *xsc)
+{
+	struct vmt_softc *sc;
+	device_t dev;
+	uint32_t rlen;
+	uint16_t ack;
+	int error;
+
+	sc = xsc;
+	dev = sc->sc_dev;
+
+	if (sc->sc_removing != 0)
+		return;
+
+	/* 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(dev, "unable to reopen TCLO channel\n");
+			callout_schedule(&sc->sc_tclo_tick, 15 * hz);
+			return;
+		}
+
+		if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_RESET_REPLY) != 0) {
+			device_printf(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(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(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;
+	} else 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(dev, "failed to get incoming TCLO data\n");
+		sc->sc_rpc_error = 1;
+		goto out;
+	}
+
+	sc->sc_tclo_ping = 0;
+
+	if (strcmp(sc->sc_rpc_buf, "reset") == 0) {
+		if (sc->sc_rpc_error != 0) {
+			device_printf(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(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(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(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(dev, "error sending suspend response\n");
+			sc->sc_rpc_error = 1;
+		}
+	} else if (strcmp(sc->sc_rpc_buf, "OS_Resume") == 0) {
+		log(LOG_KERN | LOG_NOTICE,
+		    "VMware guest resuming from suspended state\n");
+
+		/* 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(dev, "error sending resume response\n");
+			sc->sc_rpc_error = 1;
+		}
+	} 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(dev, "unable to set unified loop\n");
+			sc->sc_rpc_error = 1;
+		} else if (vm_rpci_response_successful(sc) == 0)
+			device_printf(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(dev,
+			    "unable to send statechange capability\n");
+			sc->sc_rpc_error = 1;
+		} else if (vm_rpci_response_successful(sc) == 0)
+			device_printf(dev, "host rejected statechange capability\n");
+
+		if (vm_rpc_send_rpci_tx(sc, "tools.set.version %u",
+		    VM_VERSION_UNMANAGED) != 0) {
+			device_printf(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(dev,
+			    "error sending capabilities_register response\n");
+			sc->sc_rpc_error = 1;
+		}
+	} else if (strcmp(sc->sc_rpc_buf, "Set_Option broadcastIP 1") == 0) {
+#if 0
+		struct ifnet *iface;
+		struct sockaddr_in *guest_ip;
+
+		/* Find first available ipv4 address. */
+		guest_ip = NULL;
+		TAILQ_FOREACH(iface, &ifnet, if_list) {
+			struct ifaddr *iface_addr;
+
+			/* skip loopback */
+			if (strncmp(iface->if_xname, "lo", 2) == 0 &&
+			    iface->if_xname[2] >= '0' && iface->if_xname[2] <= '9') {
+				continue;
+			}
+
+			TAILQ_FOREACH(iface_addr, &iface->if_addrlist, ifa_list) {
+				if (iface_addr->ifa_addr->sa_family != AF_INET) {
+					continue;
+				}
+
+				guest_ip = satosin(iface_addr->ifa_addr);
+				break;
+			}
+		}
+
+		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(dev,
+				    "unable to send guest IP address\n");
+				sc->sc_rpc_error = 1;
+			}
+
+			if (vm_rpc_send_str(&sc->sc_tclo_rpc,
+			    VM_RPC_REPLY_OK) != 0) {
+				device_printf(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(dev,
+				    "error sending broadcastIP error response\n");
+				sc->sc_rpc_error = 1;
+			}
+		}
+#endif
+	} else {
+		error = vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_ERROR);
+		if (error) {
+			device_printf(dev,
+			    "error sending unknown command reply\n");
+			sc->sc_rpc_error = 1;
+		}
+	}
+
+out:
+	callout_schedule(&sc->sc_tclo_tick, hz);
+}
+
+#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" \
+	)
+
+#ifdef __i386__
+#define BACKDOOR_OP(op, frame) BACKDOOR_OP_I386(op, frame)
+#else
+#define BACKDOOR_OP(op, frame) BACKDOOR_OP_AMD64(op, frame)
+#endif
+
+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;
+
+	bzero(&frame, 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("vmt: 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;
+
+	bzero(&frame, 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("vmt: 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. */
+	bzero(&frame, 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("vmt: sending length failed, eax=%08x, ecx=%08x\n",
+		    frame.eax.word, frame.ecx.word);
+		return (EIO);
+	}
+
+	/* Only need to poke once if command is null. */
+	if (length == 0)
+		return (0);
+
+	/* Send the command using enhanced RPC. */
+	bzero(&frame, 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("vmt: 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. */
+	bzero(&frame, 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("vmt: get data failed, ebx=%08x\n", frame.ebx.word);
+		return (EIO);
+	}
+
+	/* Acknowledge data received. */
+	bzero(&frame, 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("vmt: 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;
+
+	bzero(&frame, 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("vmt: 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)
+{
+	device_t dev;
+	struct vm_rpc rpci;
+	uint32_t rlen;
+	uint16_t ack;
+	int error;
+
+	dev = sc->sc_dev;
+	error = 0;
+
+	if (vm_rpc_open(&rpci, VM_RPC_OPEN_RPCI) != 0) {
+		device_printf(dev, "rpci channel open failed\n");
+		return (EIO);
+	}
+
+	if (vm_rpc_send(&rpci, sc->sc_rpc_buf, length) != 0) {
+		device_printf(dev, "unable to send rpci command\n");
+		error = EIO;
+		goto out;
+	}
+
+	if (vm_rpc_get_length(&rpci, &rlen, &ack) != 0) {
+		device_printf(dev,
+		    "failed to get length of rpci response data\n");
+		error = 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(dev,
+			    "failed to get rpci response data\n");
+			error = EIO;
+			goto out;
+		}
+	}
+
+out:
+	if (vm_rpc_close(&rpci) != 0)
+		device_printf(dev, "unable to close rpci channel\n");
+
+	return (error);
+}
+
+static int
+vm_rpc_send_rpci_tx(struct vmt_softc *sc, const char *fmt, ...)
+{
+	va_list args;
+	int len, error;
+
+	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");
+		error = EIO;
+	} else
+		error = vm_rpc_send_rpci_tx_buf(sc, sc->sc_rpc_buf, len);
+
+	return (error);
+}
+
+#if 0
+	struct vm_backdoor frame;
+
+	bzero(&frame, 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);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201308042214.r74ME2Bq017053>