VMMDevHGCM.cpp revision e1ffb76ef68821a67f50809537684bc2c9e9f758
/* $Id$ */
/** @file
* VMMDev - HGCM - Host-Guest Communication Manager Device.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#define LOG_GROUP LOG_GROUP_DEV_VMM
#include "VMMDevHGCM.h"
typedef enum _VBOXHGCMCMDTYPE
{
VBOXHGCMCMDTYPE_SizeHack = 0x7fffffff
/* Information about a linear ptr parameter. */
typedef struct _VBOXHGCMLINPTR
{
/* Index of the parameter. */
int iParm;
/* Offset in the first physical page of the region. */
/* How many pages. */
/* Pointer to array of the GC physical addresses for these pages.
* It is assumed that the physical address of the locked resident
* guest page does not change.
*/
struct VBOXHGCMCMD
{
/* Active commands, list is protected by critsectHGCMCmdList. */
struct VBOXHGCMCMD *pNext;
struct VBOXHGCMCMD *pPrev;
/* The type of the command. */
/* Whether the command was cancelled by the guest. */
bool fCancelled;
/* GC physical address of the guest request. */
/* Request packet size */
/* Pointer to converted host parameters in case of a Call request. */
/* Linear pointer parameters information. */
int cLinPtrs;
/* Pointer to descriptions of linear pointers. */
};
{
return rc;
}
{
}
static int vmmdevHGCMAddCommand (VMMDevState *pVMMDevState, PVBOXHGCMCMD pCmd, RTGCPHYS GCPhys, uint32_t cbSize, VBOXHGCMCMDTYPE enmCmdType)
{
/* PPDMDEVINS pDevIns = pVMMDevState->pDevIns; */
if (RT_SUCCESS (rc))
{
/* Insert at the head of the list. The vmmdevHGCMLoadStateDone depends on this. */
if (pVMMDevState->pHGCMCmdList)
{
}
/* Automatically enable HGCM events, if there are HGCM commands. */
if ( enmCmdType == VBOXHGCMCMDTYPE_CONNECT
|| enmCmdType == VBOXHGCMCMDTYPE_CALL)
{
{
}
}
}
return rc;
}
{
/* PPDMDEVINS pDevIns = pVMMDevState->pDevIns; */
if (RT_SUCCESS (rc))
{
{
}
else
{
/* Tail, do nothing. */
}
{
}
else
{
}
}
return rc;
}
{
if (RT_SUCCESS (rc))
{
while (pCmd)
{
{
break;
}
}
}
return pCmd;
}
{
int rc = VINF_SUCCESS;
AssertRelease (u32Size > 0);
/* Take the offset into the current page also into account! */
/* Gonvert the guest linear pointers of pages to HC addresses. */
{
/* convert */
if (RT_FAILURE (rc))
{
break;
}
/* store */
/* next */
}
return rc;
}
void *pvHost,
{
int rc = VINF_SUCCESS;
{
/* copy */
Log(("vmmdevHGCMWriteLinPtr: page %d: dst %VGp, src %p, cbWrite %d\n", iPage, GCPhysDst, pu8Src, cbWrite));
iPage++;
{
u32Size = 0;
break;
}
/* next */
}
return rc;
}
{
int rc = VINF_SUCCESS;
PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size);
if (pCmd)
{
vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMConnect->header.header.size, VBOXHGCMCMDTYPE_CONNECT);
/* Only allow the guest to use existing services! */
rc = pVMMDevState->pHGCMDrv->pfnConnect (pVMMDevState->pHGCMDrv, pCmd, &pHGCMConnectCopy->loc, &pHGCMConnectCopy->u32ClientID);
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
int vmmdevHGCMDisconnect (VMMDevState *pVMMDevState, VMMDevHGCMDisconnect *pHGCMDisconnect, RTGCPHYS GCPhys)
{
int rc = VINF_SUCCESS;
if (pCmd)
{
vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMDisconnect->header.header.size, VBOXHGCMCMDTYPE_DISCONNECT);
rc = pVMMDevState->pHGCMDrv->pfnDisconnect (pVMMDevState->pHGCMDrv, pCmd, pHGCMDisconnect->u32ClientID);
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
int vmmdevHGCMCall (VMMDevState *pVMMDevState, VMMDevHGCMCall *pHGCMCall, RTGCPHYS GCPhys, bool f64Bits)
{
int rc = VINF_SUCCESS;
Log(("vmmdevHGCMCall: client id = %d, function = %d, %s bit\n", pHGCMCall->u32ClientID, pHGCMCall->u32Function, f64Bits? "64": "32"));
/* Compute size and allocate memory block to hold:
* struct VBOXHGCMCMD
* VBOXHGCMSVCPARM[cParms]
* memory buffers for pointer parameters.
*/
/*
* Compute size of required memory buffer.
*/
uint32_t i;
uint32_t cLinPtrPages = 0;
if (f64Bits)
{
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
AssertFailed (); /* This code should not be called in this case */
#endif /* VBOX_WITH_64_BITS_GUESTS */
/* Look for pointer parameters, which require a host buffer. */
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
{
cLinPtrs++;
/* Take the offset into the current page also into account! */
}
} break;
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
{
} break;
default:
{
break;
}
}
}
}
else
{
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
#endif /* VBOX_WITH_64_BITS_GUESTS */
/* Look for pointer parameters, which require a host buffer. */
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
{
cLinPtrs++;
/* Take the offset into the current page also into account! */
}
} break;
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
{
} break;
default:
{
break;
}
}
}
}
if (RT_FAILURE (rc))
{
return rc;
}
{
return VERR_NO_MEMORY;
}
if (cLinPtrs > 0)
{
+ sizeof (RTGCPHYS) * cLinPtrPages);
{
return VERR_NO_MEMORY;
}
}
else
{
}
/* Process parameters, changing them to host context pointers for easy
* processing by connector. Guest must insure that the pointed data is actually
* in the guest RAM and remains locked there for entire request processing.
*/
if (cParms != 0)
{
/* Compute addresses of host parms array and first memory buffer. */
if (f64Bits)
{
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
AssertFailed (); /* This code should not be called in this case */
#endif /* VBOX_WITH_64_BITS_GUESTS */
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_32bit:
{
break;
}
case VMMDevHGCMParmType_64bit:
{
break;
}
{
#ifdef LOG_ENABLED
#endif
AssertFailed();
/* rc = PDMDevHlpPhys2HCVirt (pVMMDevState->pDevIns, physAddr, size, &pHostParm->u.pointer.addr); */
break;
}
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
/* Copy guest data to an allocated buffer, so
* services can use the data.
*/
if (size == 0)
{
}
else
{
/* Don't overdo it */
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
{
/* Remember the guest physical pages that belong to the virtual address
* region.
*/
rc = vmmdevHGCMSaveLinPtr (pVMMDevState->pDevIns, i, linearAddr, size, iLinPtr++, pCmd->paLinPtrs, &pPages);
}
}
}
break;
}
/* just to shut up gcc */
default:
break;
}
}
}
else
{
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
#endif /* VBOX_WITH_64_BITS_GUESTS */
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_32bit:
{
break;
}
case VMMDevHGCMParmType_64bit:
{
break;
}
{
#ifdef LOG_ENABLED
#endif
AssertFailed();
/* rc = PDMDevHlpPhys2HCVirt (pVMMDevState->pDevIns, physAddr, size, &pHostParm->u.pointer.addr); */
break;
}
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
/* Copy guest data to an allocated buffer, so
* services can use the data.
*/
if (size == 0)
{
}
else
{
/* Don't overdo it */
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
{
/* Remember the guest physical pages that belong to the virtual address
* region.
*/
rc = vmmdevHGCMSaveLinPtr (pVMMDevState->pDevIns, i, linearAddr, size, iLinPtr++, pCmd->paLinPtrs, &pPages);
}
}
}
break;
}
/* just to shut up gcc */
default:
break;
}
}
}
}
if (RT_SUCCESS (rc))
{
vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMCall->header.header.size, VBOXHGCMCMDTYPE_CALL);
/* Pass the function call to HGCM connector for actual processing */
rc = pVMMDevState->pHGCMDrv->pfnCall (pVMMDevState->pHGCMDrv, pCmd, pHGCMCall->u32ClientID, pHGCMCall->u32Function, cParms, pCmd->paHostParms);
}
else
{
{
}
}
return rc;
}
/* @thread EMT */
{
int rc = VINF_SUCCESS;
Log(("vmmdevHGCMCancel\n"));
/* Find the command in the list. */
if (pCmd)
{
pCmd->fCancelled = true;
}
else
{
}
return rc;
}
{
switch (pCmd->enmCmdType)
{
case VBOXHGCMCMDTYPE_CONNECT:
break;
break;
case VBOXHGCMCMDTYPE_CALL:
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
#endif /* VBOX_WITH_64_BITS_GUESTS */
break;
default:
AssertFailed ();
}
LogRel(("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
return VERR_INVALID_PARAMETER;
}
#define PDMIHGCMPORT_2_VMMDEVSTATE(pInterface) ( (VMMDevState *) ((uintptr_t)pInterface - RT_OFFSETOF(VMMDevState, HGCMPort)) )
DECLCALLBACK(void) hgcmCompletedWorker (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
{
int rc = VINF_SUCCESS;
if (result == VINF_HGCM_SAVE_STATE)
{
/* If the completion routine was called because HGCM saves its state,
* then currently nothing to be done here. The pCmd stays in the list
* and will be saved later when the VMMDev state will be saved.
*
* It it assumed that VMMDev saves state after the HGCM services,
* and, therefore, VBOXHGCMCMD structures are not removed by
* vmmdevHGCMSaveState from the list, while HGCM uses them.
*/
return;
}
/* Check whether the command has been already cancelled by the guest.
* If it was cancelled, then the data must not be written back to the
* guest RAM.
*/
if (pCmd->fCancelled)
{
/* Just remove the command from the internal list, so the memory can be freed. */
}
else
{
/* Preallocated block for requests which have up to 8 parameters (most of requests). */
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
#endif /* VBOX_WITH_64_BITS_GUESTS */
{
}
else
{
{
/* Do some cleanup. The command have to be excluded from list of active commands anyway. */
return;
}
}
/* Setup return codes. */
/* Verify the request type. */
if (RT_SUCCESS (rc))
{
/* Update parameters and data buffers. */
{
#ifdef VBOX_WITH_64_BITS_GUESTS
case VMMDevReq_HGCMCall64:
{
uint32_t i;
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_32bit:
{
} break;
case VMMDevHGCMParmType_64bit:
{
} break;
{
/* do nothing */
} break;
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
/* Copy buffer back to guest memory. */
{
/* Use the saved page list. */
rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr++, pCmd->paLinPtrs);
}
} break;
default:
{
/* This indicates that the guest request memory was corrupted. */
}
}
}
break;
}
case VMMDevReq_HGCMCall32:
{
uint32_t i;
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_32bit:
{
} break;
case VMMDevHGCMParmType_64bit:
{
} break;
{
/* do nothing */
} break;
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
/* Copy buffer back to guest memory. */
{
/* Use the saved page list. */
rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr++, pCmd->paLinPtrs);
}
} break;
default:
{
/* This indicates that the guest request memory was corrupted. */
}
}
}
break;
}
#else
case VMMDevReq_HGCMCall:
{
uint32_t i;
{
switch (pGuestParm->type)
{
case VMMDevHGCMParmType_32bit:
{
} break;
case VMMDevHGCMParmType_64bit:
{
} break;
{
/* do nothing */
} break;
case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
case VMMDevHGCMParmType_LinAddr: /* In & Out */
{
/* Copy buffer back to guest memory. */
{
/* Use the saved page list. */
rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr++, pCmd->paLinPtrs);
}
} break;
default:
{
/* This indicates that the guest request memory was corrupted. */
}
}
}
break;
}
#endif /* VBOX_WITH_64_BITS_GUESTS */
case VMMDevReq_HGCMConnect:
{
/* save the client id in the guest request packet */
break;
}
default:
/* make gcc happy */
break;
}
}
else
{
/* Command type is wrong. Return error to the guest. */
}
/* Mark request as processed. */
/* Write back the request */
/* The command has been completely processed and can be removed from the list. */
/* Now, when the command was removed from the internal list, notify the guest. */
{
/* Only if it was allocated from heap. */
}
}
/* Deallocate the command memory. */
{
}
return;
}
{
/* Not safe to execute asynchroneously; forward to EMT */
int rc = VMR3ReqCallEx(PDMDevHlpGetVM(pVMMDevState->pDevIns), NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID,
}
/* @thread EMT */
{
/* Save information about pending requests.
* Only GCPtrs are of interest.
*/
int rc = VINF_SUCCESS;
LogFlowFunc(("\n"));
/* Compute how many commands are pending. */
while (pIter)
{
cCmds++;
}
/* Save number of commands. */
if (cCmds > 0)
{
while (pIter)
{
}
}
return rc;
}
/* @thread EMT */
{
int rc = VINF_SUCCESS;
LogFlowFunc(("\n"));
/* Read how many commands were pending. */
while (cCmds--)
{
}
return rc;
}
/* @thread EMT */
{
LogFlowFunc(("\n"));
/* Reissue pending requests. */
if (RT_SUCCESS (rc))
{
while (pIter)
{
if (requestHeader == NULL)
return VERR_NO_MEMORY;
/* the structure size must be greater or equal to the header size */
{
}
else
{
/* check the version of the header structure */
{
Log(("VMMDev: guest header version (0x%08X) differs from ours (0x%08X)\n", requestHeader->version, VMMDEV_REQUEST_HEADER_VERSION));
}
else
{
switch (requestHeader->requestType)
{
case VMMDevReq_HGCMConnect:
{
{
AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n"));
}
else if (!pVMMDevState->pHGCMDrv)
{
Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n"));
}
else
{
Log(("VMMDevReq_HGCMConnect\n"));
}
break;
}
case VMMDevReq_HGCMDisconnect:
{
{
AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n"));
}
else if (!pVMMDevState->pHGCMDrv)
{
Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n"));
}
else
{
Log(("VMMDevReq_VMMDevHGCMDisconnect\n"));
}
break;
}
#ifdef VBOX_WITH_64_BITS_GUESTS
case VMMDevReq_HGCMCall64:
case VMMDevReq_HGCMCall32:
#else
case VMMDevReq_HGCMCall:
#endif /* VBOX_WITH_64_BITS_GUESTS */
{
{
AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n"));
}
else if (!pVMMDevState->pHGCMDrv)
{
Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n"));
}
else
{
#ifdef VBOX_WITH_64_BITS_GUESTS
#else
bool f64Bits = false;
#endif /* VBOX_WITH_64_BITS_GUESTS */
}
break;
}
default:
LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->requestType));
}
}
}
/* Write back the request */
}
}
return rc;
}