vmware-svga/lib/util/vmbackdoor.c
2009-04-13 07:05:42 +00:00

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);
}
}
}