512 lines
12 KiB
C
512 lines
12 KiB
C
|
/**********************************************************
|
||
|
* Copyright 2008-2009 VMware, Inc. All rights reserved.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person
|
||
|
* obtaining a copy of this software and associated documentation
|
||
|
* files (the "Software"), to deal in the Software without
|
||
|
* restriction, including without limitation the rights to use, copy,
|
||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||
|
* of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be
|
||
|
* included in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*
|
||
|
**********************************************************/
|
||
|
|
||
|
/*
|
||
|
* vmbackdoor.c --
|
||
|
*
|
||
|
* This is a tiny and self-contained client implementation of
|
||
|
* VMware's backdoor protocols for VMMouse, logging, and time.
|
||
|
*/
|
||
|
|
||
|
#include "vmbackdoor.h"
|
||
|
#include "backdoor_def.h"
|
||
|
#include "vmmouse_defs.h"
|
||
|
#include "intr.h"
|
||
|
|
||
|
#define BACKDOOR_VARS() \
|
||
|
uint32 eax = 0, ebx = 0, ecx = 0, edx = 0, esi = 0, edi = 0; \
|
||
|
|
||
|
#define BACKDOOR_ASM(op, port) \
|
||
|
{ \
|
||
|
eax = BDOOR_MAGIC; \
|
||
|
edx = (edx & 0xFFFF0000) | port; \
|
||
|
asm volatile (op : "+a" (eax), "+b" (ebx), \
|
||
|
"+c" (ecx), "+d" (edx), "+S" (esi), "+D" (edi)); \
|
||
|
}
|
||
|
|
||
|
#define BACKDOOR_ASM_IN() BACKDOOR_ASM("in %%dx, %0", BDOOR_PORT)
|
||
|
#define BACKDOOR_ASM_HB_OUT() BACKDOOR_ASM("cld; rep; outsb", BDOORHB_PORT)
|
||
|
#define BACKDOOR_ASM_HB_IN() BACKDOOR_ASM("cld; rep; insb", BDOORHB_PORT)
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_MouseInit --
|
||
|
*
|
||
|
* Initialize the backdoor VMMouse device. This is the virtualized
|
||
|
* mouse device that all modern versions of VMware Tools use.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Puts the mouse in absolute or relative mode.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_MouseInit(Bool absolute) // IN
|
||
|
{
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ebx = VMMOUSE_CMD_READ_ID;
|
||
|
ecx = BDOOR_CMD_ABSPOINTER_COMMAND;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
ebx = 0;
|
||
|
ecx = BDOOR_CMD_ABSPOINTER_STATUS;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
ebx = 1;
|
||
|
ecx = BDOOR_CMD_ABSPOINTER_DATA;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
ebx = absolute ? VMMOUSE_CMD_REQUEST_ABSOLUTE : VMMOUSE_CMD_REQUEST_RELATIVE;
|
||
|
ecx = BDOOR_CMD_ABSPOINTER_COMMAND;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_MouseGetPacket --
|
||
|
*
|
||
|
* Poll for VMMouse packets.
|
||
|
*
|
||
|
* Results:
|
||
|
* If a packet is available, returns TRUE and copies it to 'packet'.
|
||
|
* Returns FALSE if no packet is available.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* None.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
Bool
|
||
|
VMBackdoor_MouseGetPacket(VMMousePacket *packet) // OUT
|
||
|
{
|
||
|
const uint32 wordsToRead = 4;
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ebx = 0;
|
||
|
ecx = BDOOR_CMD_ABSPOINTER_STATUS;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
/* Low word of 'status' is the number of DWORDs in the device's FIFO */
|
||
|
if ((eax & 0x0000ffff) < wordsToRead) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ebx = wordsToRead;
|
||
|
ecx = BDOOR_CMD_ABSPOINTER_DATA;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
packet->x = (int32)ebx;
|
||
|
packet->y = (int32)ecx;
|
||
|
packet->z = (int32)edx;
|
||
|
packet->flags = eax >> 16;
|
||
|
packet->buttons = eax & 0xFFFF;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_GetTime --
|
||
|
*
|
||
|
* Read the host's real-time clock, with microsecond precision.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* None.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_GetTime(VMTime *time) // OUT
|
||
|
{
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ebx = 0;
|
||
|
ecx = BDOOR_CMD_GETTIMEFULL;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
time->usecs = ebx;
|
||
|
time->maxTimeLag = ecx;
|
||
|
time->secsLow = edx;
|
||
|
time->secsHigh = esi;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_TimeDiffUS --
|
||
|
*
|
||
|
* Compute the differene, in microseconds, of two time values in VMTime format.
|
||
|
*
|
||
|
* Results:
|
||
|
* Number of microseconds.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* None.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
int32
|
||
|
VMBackdoor_TimeDiffUS(VMTime *first, // IN
|
||
|
VMTime *second) // IN
|
||
|
{
|
||
|
int32 secs = second->secsLow - first->secsLow;
|
||
|
int32 usec = second->usecs - first->usecs;
|
||
|
|
||
|
return (secs * 1000000) + usec;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_MsgOpen --
|
||
|
*
|
||
|
* Open a backdoor message channel.
|
||
|
*
|
||
|
* Results:
|
||
|
* Initializes 'channel'.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* None.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_MsgOpen(VMMessageChannel *channel, // OUT
|
||
|
uint32 proto) // IN
|
||
|
{
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ecx = BDOOR_CMD_MESSAGE | 0x00000000; /* Open */
|
||
|
ebx = proto;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
if ((ecx & 0x00010000) == 0) {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
|
||
|
channel->proto = proto;
|
||
|
channel->id = edx >> 16;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_MsgClose --
|
||
|
*
|
||
|
* Close a backdoor message channel.
|
||
|
*
|
||
|
* Results:
|
||
|
* void.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* None.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_MsgClose(VMMessageChannel *channel) // OUT
|
||
|
{
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ecx = BDOOR_CMD_MESSAGE | 0x00060000; /* Close */
|
||
|
ebx = 0;
|
||
|
edx = channel->id << 16;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_MsgSend --
|
||
|
*
|
||
|
* Send a message over a VMMessageChannel.
|
||
|
*
|
||
|
* Results:
|
||
|
* void.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* None.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_MsgSend(VMMessageChannel *channel, // IN
|
||
|
const void *buf, // IN
|
||
|
uint32 size) // IN
|
||
|
{
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ecx = BDOOR_CMD_MESSAGE | 0x00010000; /* Send size */
|
||
|
ebx = size;
|
||
|
edx = channel->id << 16;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
/* We only support the high-bandwidth backdoor port. */
|
||
|
if (((ecx >> 16) & 0x0081) != 0x0081) {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
|
||
|
ebx = 0x00010000 | BDOORHB_CMD_MESSAGE;
|
||
|
ecx = size;
|
||
|
edx = channel->id << 16;
|
||
|
esi = (uint32)buf;
|
||
|
BACKDOOR_ASM_HB_OUT()
|
||
|
|
||
|
/* Success? */
|
||
|
if (!(ebx & 0x00010000)) {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_MsgReceive --
|
||
|
*
|
||
|
* Receive a message waiting on a VMMessageChannel.
|
||
|
*
|
||
|
* Results:
|
||
|
* Returns the number of bytes received.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Intr_Break on protocol error or buffer overflow.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
uint32
|
||
|
VMBackdoor_MsgReceive(VMMessageChannel *channel, // IN
|
||
|
void *buf, // IN
|
||
|
uint32 bufSize) // IN
|
||
|
{
|
||
|
uint32 size;
|
||
|
BACKDOOR_VARS()
|
||
|
|
||
|
ecx = BDOOR_CMD_MESSAGE | 0x00030000; /* Receive size */
|
||
|
edx = channel->id << 16;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
/*
|
||
|
* Check for success, and make sure a message is waiting.
|
||
|
* The host must have just sent us a SENDSIZE request.
|
||
|
* Also make sure the host supports high-bandwidth transfers.
|
||
|
*/
|
||
|
if (((ecx >> 16) & 0x0083) != 0x0083 ||
|
||
|
(edx >> 16) != 0x0001) {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
|
||
|
size = ebx;
|
||
|
if (size > bufSize) {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
|
||
|
/* Receive payload */
|
||
|
ebx = BDOORHB_CMD_MESSAGE | 0x00010000;
|
||
|
ecx = size;
|
||
|
edx = channel->id << 16;
|
||
|
edi = (uint32)buf;
|
||
|
BACKDOOR_ASM_HB_IN()
|
||
|
|
||
|
/* Success? */
|
||
|
if (!(ebx & 0x00010000)) {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
|
||
|
/* Acknowledge status */
|
||
|
ecx = BDOOR_CMD_MESSAGE | 0x00050000;
|
||
|
ebx = 0x0001;
|
||
|
edx = channel->id << 16;
|
||
|
BACKDOOR_ASM_IN()
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_GetRPCIChannel --
|
||
|
*
|
||
|
* Return the channel to use for RPCI messages.
|
||
|
*
|
||
|
* Results:
|
||
|
* Always returns a VMMessageChannel.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Opens the channel if necessary.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
VMMessageChannel *
|
||
|
VMBackdoor_GetRPCIChannel(void)
|
||
|
{
|
||
|
static VMMessageChannel channel;
|
||
|
static Bool initialized;
|
||
|
|
||
|
if (!initialized) {
|
||
|
VMBackdoor_MsgOpen(&channel, 0x49435052); /* 'RPCI' */
|
||
|
initialized = TRUE;
|
||
|
}
|
||
|
|
||
|
return &channel;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_RPCI --
|
||
|
*
|
||
|
* Synchronously deliver an RPCI message and collect its response.
|
||
|
*
|
||
|
* Results:
|
||
|
* Returns the number of response bytes.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Opens the channel if necessary.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
uint32
|
||
|
VMBackdoor_RPCI(const void *request, // IN
|
||
|
uint32 reqSize, // IN
|
||
|
void *replyBuffer, // OUT
|
||
|
uint32 replyBufferLen) // IN
|
||
|
{
|
||
|
VMMessageChannel *channel = VMBackdoor_GetRPCIChannel();
|
||
|
VMBackdoor_MsgSend(channel, request, reqSize);
|
||
|
return VMBackdoor_MsgReceive(channel, replyBuffer, replyBufferLen);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_RPCIOut --
|
||
|
*
|
||
|
* Synchronously deliver an RPCI message, and expect a status
|
||
|
* response ("1" on success).
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Opens the channel if necessary.
|
||
|
* Intr_Break on error.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_RPCIOut(const void *request, // IN
|
||
|
uint32 reqSize) // IN
|
||
|
{
|
||
|
uint8 replyBuf[16];
|
||
|
uint32 replyLen = VMBackdoor_RPCI(request, reqSize, replyBuf, sizeof replyBuf);
|
||
|
if (replyLen < 1 || replyBuf[0] != '1') {
|
||
|
Intr_Break();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
* VMBackdoor_VGAScreenshot --
|
||
|
*
|
||
|
* Log a screenshot of the VGA framebuffer over the backdoor.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Opens the channel if necessary.
|
||
|
* Intr_Break on error.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
VMBackdoor_VGAScreenshot(void)
|
||
|
{
|
||
|
int x, y;
|
||
|
const uint8 *fb = (void*)0xB8000;
|
||
|
static const char prefix[] = "log VGA: [00] ";
|
||
|
char lineBuf[81 + sizeof prefix];
|
||
|
char *linePtr;
|
||
|
uint32 lineLen;
|
||
|
|
||
|
memcpy(lineBuf, prefix, sizeof prefix);
|
||
|
|
||
|
for (y = 0; y < 25; y++) {
|
||
|
linePtr = lineBuf + sizeof prefix - 1;
|
||
|
lineLen = 0;
|
||
|
|
||
|
lineBuf[10] = '0' + y / 10;
|
||
|
lineBuf[11] = '0' + y % 10;
|
||
|
|
||
|
for (x = 0; x < 80; x++) {
|
||
|
*linePtr = *fb;
|
||
|
linePtr++;
|
||
|
if (*fb != ' ') {
|
||
|
lineLen = linePtr - lineBuf;
|
||
|
}
|
||
|
fb += 2;
|
||
|
}
|
||
|
|
||
|
if (lineLen > 0) {
|
||
|
VMBackdoor_RPCIOut(lineBuf, lineLen);
|
||
|
}
|
||
|
}
|
||
|
}
|