vmware-svga/lib/util/vmbackdoor.c

751 lines
17 KiB
C
Raw Permalink Normal View History

2009-04-13 02:05:42 -05:00
/**********************************************************
* 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 "console.h"
2009-04-13 02:05:42 -05:00
#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) {
Console_Panic("VMBackDoor: Failed to open message channel 0x%08x\n",
proto);
2009-04-13 02:05:42 -05:00
}
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()
if (size == 0) {
return;
}
2009-04-13 02:05:42 -05:00
if (((ecx >> 16) & 0x0081) != 0x0081) {
Console_Panic("VMBackdoor: Only the high-bandwidth backdoor port is supported.");
2009-04-13 02:05:42 -05:00
}
ebx = 0x00010000 | BDOORHB_CMD_MESSAGE;
ecx = size;
edx = channel->id << 16;
esi = (uint32)buf;
BACKDOOR_ASM_HB_OUT()
/* Success? */
if (!(ebx & 0x00010000)) {
Console_Panic("VMBackdoor: Failed to send %d byte message:\n%s", size, buf);
2009-04-13 02:05:42 -05:00
}
}
/*
*-----------------------------------------------------------------------------
*
* VMBackdoor_MsgReceive --
*
* Receive a message waiting on a VMMessageChannel.
*
* Results:
* Returns the number of bytes received, or 0 if no
* message is available.
2009-04-13 02:05:42 -05:00
*
* Side effects:
* Console_Panic on protocol error or buffer overflow.
2009-04-13 02:05:42 -05:00
*
*-----------------------------------------------------------------------------
*/
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()
if ((edx >> 16) != 0x0001) {
Console_Panic("VMBackdoor: Error receiving message size.");
2009-04-13 02:05:42 -05:00
}
size = ebx;
if (size > bufSize) {
Console_Panic("VMBackdoor: Receive buffer overflow.");
}
if (size == 0) {
return 0;
}
if (((ecx >> 16) & 0x0083) != 0x0083) {
Console_Panic("VMBackdoor: Only the high-bandwidth backdoor port is supported.");
2009-04-13 02:05:42 -05:00
}
/* Receive payload */
ebx = BDOORHB_CMD_MESSAGE | 0x00010000;
ecx = size;
edx = channel->id << 16;
edi = (uint32)buf;
BACKDOOR_ASM_HB_IN()
/* Success? */
if (!(ebx & 0x00010000)) {
Console_Panic("VMBackdoor: Failed to receive %d byte message.", size);
2009-04-13 02:05:42 -05:00
}
/* 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_GetTCLOChannel --
*
* Return the channel to use for TCLO messages.
*
* Results:
* Always returns a VMMessageChannel.
*
* Side effects:
* Opens the channel if necessary.
*
*-----------------------------------------------------------------------------
*/
VMMessageChannel *
VMBackdoor_GetTCLOChannel(void)
{
static VMMessageChannel channel;
static Bool initialized;
if (!initialized) {
VMBackdoor_MsgOpen(&channel, 0x4f4c4354); /* 'TCLO' */
initialized = TRUE;
}
return &channel;
}
2009-04-13 02:05:42 -05:00
/*
*-----------------------------------------------------------------------------
*
* 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_CheckedRPCI --
2009-04-13 02:05:42 -05:00
*
* Synchronously deliver an RPCI message, and expect a status
* response ("1" on success).
*
* Results:
* None.
*
* Side effects:
* Opens the channel if necessary.
* Console_Panic on error.
2009-04-13 02:05:42 -05:00
*
*-----------------------------------------------------------------------------
*/
void
VMBackdoor_CheckedRPCI(const void *request, // IN
uint32 reqSize) // IN
2009-04-13 02:05:42 -05:00
{
uint8 replyBuf[16];
uint32 replyLen = VMBackdoor_RPCI(request, reqSize, replyBuf, sizeof replyBuf);
if (replyLen < 1 || replyBuf[0] != '1') {
Console_Panic("VMBackdoor: RPCI response invalid (%s)\n", replyBuf);
2009-04-13 02:05:42 -05:00
}
}
/*
*-----------------------------------------------------------------------------
*
* VMBackdoor_VGAScreenshot --
*
* Log a screenshot of the VGA framebuffer over the backdoor.
*
* Results:
* None.
*
* Side effects:
* Opens the channel if necessary.
* Console_Panic on error.
2009-04-13 02:05:42 -05:00
*
*-----------------------------------------------------------------------------
*/
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_CheckedRPCI(lineBuf, lineLen);
2009-04-13 02:05:42 -05:00
}
}
}
/*
*-----------------------------------------------------------------------------
*
* VMBackdoor_PollTCLO --
*
* Poll for incoming commands from the TCLO channel, and flush
* the last reply if we had one. We internally handle the 'ping'
* and 'reset' commands.
*
* If 'verbose' is set, we print all incoming and outgoing
* messages to the console.
*
* Results:
* Sets replyLen to zero, populates the 'command' buffer and sets
* commandLen.
*
* Returns TRUE if there was a command.
*
* Side effects:
* Opens the channel if necessary.
*
*-----------------------------------------------------------------------------
*/
Bool
VMBackdoor_PollTCLO(VMTCLOState *state, // IN/OUT
Bool verbose) // IN
{
VMMessageChannel *channel = VMBackdoor_GetTCLOChannel();
while (1) {
if (state->replyLen && verbose) {
Console_Format("[TCLO OUT] '%s'\n", state->reply);
}
VMBackdoor_MsgSend(channel, state->reply, state->replyLen);
state->replyLen = 0;
if (verbose) {
memset(state->reply, 0, sizeof state->reply);
memset(state->command, 0, sizeof state->command);
}
state->commandLen = VMBackdoor_MsgReceive(channel, state->command,
sizeof state->command);
if (state->commandLen == 0) {
return FALSE;
}
if (verbose) {
Console_Format("[TCLO IN ] '%s'\n", state->command);
}
if (VMBackdoor_CheckPrefixTCLO(state, "reset")) {
/* Send "Answer To Reply" */
VMBackdoor_ReplyTCLO(state, TCLO_SUCCESS "ATR toolbox");
} else if (VMBackdoor_CheckPrefixTCLO(state, "ping")) {
VMBackdoor_ReplyTCLO(state, TCLO_SUCCESS);
} else {
return TRUE;
}
}
}
/*
*-----------------------------------------------------------------------------
*
* VMBackdoor_CheckPrefixTCLO --
*
* Check for a particular TCLO command, by examining the command prefix.
*
* Results:
* TRUE if 'prefix' matches, FALSE if not.
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Bool
VMBackdoor_CheckPrefixTCLO(VMTCLOState *state, // IN
const char *prefix) // IN
{
char *cmd = (char*) state->command;
char *cmdEnd = (char*) state->command + state->commandLen;
while (cmd < cmdEnd) {
if (*prefix == '\0') {
/* End of prefix. Matched! */
return TRUE;
}
if (*cmd != *prefix) {
/* Not matched */
return FALSE;
}
cmd++;
prefix++;
}
/*
* Command length is <= prefix length. We only matched successfully
* if this is also the end of the prefix.
*/
return *prefix == '\0';
}
/*
*-----------------------------------------------------------------------------
*
* VMBackdoor_IntParamTCLO --
*
* Parse an integer parameter out of a TCLO command.
* "index" is the zero-based index of the command token to start
* at. Zero is the command prefix, one is the first space-separated
* token after the prefix, etc.
*
* Results:
* Returns the parsed integer.
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
int32
VMBackdoor_IntParamTCLO(VMTCLOState *state, // IN
int index) // IN
{
char *reply = (char*) state->command;
char *replyEnd = (char*) state->command + state->commandLen;
int32 result = 0;
Bool sign = FALSE;
while (reply < replyEnd && index > 0) {
if (*reply == ' ') {
index--;
}
reply++;
}
while (reply < replyEnd) {
char c = *reply;
if (c == '-') {
sign = !sign;
} else if (c >= '0' && c <= '9') {
result *= 10;
result += c - '0';
} else {
break;
}
reply++;
}
return sign ? -result : result;
}
/*
*-----------------------------------------------------------------------------
*
* VMBackdoor_ReplyTCLO --
*
* Copy a reply string to the current TCLO state.
*
* Results:
* Modifies the reply buffer.
*
* Side effects:
* Panic on buffer overflow.
*
*-----------------------------------------------------------------------------
*/
void
VMBackdoor_ReplyTCLO(VMTCLOState *state, // IN/OUT
const char *reply) // IN
{
state->replyLen = 0;
while (*reply) {
if (state->replyLen >= sizeof state->reply) {
Console_Panic("VMBackdoor: TCLO reply buffer overflow");
}
state->reply[state->replyLen] = *reply;
state->replyLen++;
reply++;
}
}