VMMDevHGCM.cpp revision eb8c3a611107e54d7a4ae2fdae311121786c2988
/* $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. */
/* 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;
/* Size of memory buffer for this command structure, including trailing paHostParms.
* This field simplifies loading of saved state.
*/
/* 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.
* Parameters follow this structure in the same memory block.
*/
/* Linear pointer parameters information. */
int cLinPtrs;
/* How many pages for all linptrs of this command.
* Only valid if cLinPtrs > 0. This field simplifies loading of saved state.
*/
int cLinPtrPages;
/* 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)
{
}
if (enmCmdType != VBOXHGCMCMDTYPE_LOADSTATE)
{
/* Loaded commands already have the right type. */
}
/* 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 %RGp, src %p, cbWrite %d\n", iPage, GCPhysDst, pu8Src, cbWrite));
iPage++;
{
u32Size = 0;
break;
}
/* next */
}
return rc;
}
static void logRelSavedCmdSizeMismatch (const char *pszFunction, uint32_t cbExpected, uint32_t cbCmdSize)
{
LogRel(("Warning: VMMDev %s command length %d (expected %d)\n",
}
{
int rc = VINF_SUCCESS;
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;
}
static int vmmdevHGCMConnectSaved (VMMDevState *pVMMDevState, VMMDevHGCMConnect *pHGCMConnect, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd)
{
int rc = VINF_SUCCESS;
{
return VERR_INVALID_PARAMETER;
}
/* Only allow the guest to use existing services! */
rc = pVMMDevState->pHGCMDrv->pfnConnect (pVMMDevState->pHGCMDrv, pSavedCmd, &pHGCMConnectCopy->loc, &pHGCMConnectCopy->u32ClientID);
if (RT_SUCCESS (rc))
{
*pfHGCMCalled = true;
}
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;
}
static int vmmdevHGCMDisconnectSaved (VMMDevState *pVMMDevState, VMMDevHGCMDisconnect *pHGCMDisconnect, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd)
{
int rc = VINF_SUCCESS;
{
return VERR_INVALID_PARAMETER;
}
rc = pVMMDevState->pHGCMDrv->pfnDisconnect (pVMMDevState->pHGCMDrv, pSavedCmd, pHGCMDisconnect->u32ClientID);
if (RT_SUCCESS (rc))
{
*pfHGCMCalled = true;
}
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 */
{
{
/* Only pointers with some actual data are counted. */
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 */
{
{
/* Only pointers with some actual data are counted. */
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.
* Do it for all linear pointers because load state will require In pointer info too.
*/
rc = vmmdevHGCMSaveLinPtr (pVMMDevState->pDevIns, i, linearAddr, size, iLinPtr, pCmd->paLinPtrs, &pPages);
iLinPtr++;
}
}
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.
* Do it for all linear pointers because load state will require In pointer info too.
*/
rc = vmmdevHGCMSaveLinPtr (pVMMDevState->pDevIns, i, linearAddr, size, iLinPtr, pCmd->paLinPtrs, &pPages);
iLinPtr++;
}
}
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;
}
static void logRelLoadStatePointerIndexMismatch (uint32_t iParm, uint32_t iSavedParm, int iLinPtr, int cLinPtrs)
{
LogRel(("Warning: VMMDev load state: a pointer parameter index mismatch %d (expected %d) (%d/%d)\n",
}
{
LogRel(("Warning: VMMDev load state: buffer size mismatch: size %d, page %d/%d\n",
}
static int vmmdevHGCMCallSaved (VMMDevState *pVMMDevState, VMMDevHGCMCall *pHGCMCall, bool f64Bits, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd)
{
int rc = VINF_SUCCESS;
Log(("vmmdevHGCMCallSaved: 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.
*/
/* 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. */
VBOXHGCMSVCPARM *pHostParm = (VBOXHGCMSVCPARM *)((uint8_t *)pSavedCmd + sizeof (struct VBOXHGCMCMD));
int iLinPtr = 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 */
{
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
{
/* The saved command already have the page list in pCmd->paLinPtrs.
* Read data from guest pages.
*/
/* Don't overdo it */
{
{
logRelLoadStatePointerIndexMismatch (iParm, pSavedCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pSavedCmd->cLinPtrs);
}
else
{
{
if (cbRemaining == 0)
{
break;
}
if (cbChunk > cbRemaining)
{
}
offPage = 0; /* A next page is read from 0 offset. */
cbRemaining -= cbChunk;
}
}
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
iLinPtr++;
}
}
Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n",
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
{
/* The saved command already have the page list in pCmd->paLinPtrs.
* Read data from guest pages.
*/
/* Don't overdo it */
{
{
logRelLoadStatePointerIndexMismatch (iParm, pSavedCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pSavedCmd->cLinPtrs);
}
else
{
{
if (cbRemaining == 0)
{
break;
}
if (cbChunk > cbRemaining)
{
}
offPage = 0; /* A next page is read from 0 offset. */
cbRemaining -= cbChunk;
}
}
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
iLinPtr++;
}
}
Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n",
break;
}
/* just to shut up gcc */
default:
break;
}
}
}
}
if (RT_SUCCESS (rc))
{
/* Pass the function call to HGCM connector for actual processing */
rc = pVMMDevState->pHGCMDrv->pfnCall (pVMMDevState->pHGCMDrv, pSavedCmd, pHGCMCall->u32ClientID, pHGCMCall->u32Function, cParms, pSavedCmd->paHostParms);
if (RT_SUCCESS (rc))
{
*pfHGCMCalled = true;
}
}
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;
}
}
/*
* Enter and leave the critical section here so we make sure
* vmmdevRequestHandler has completed before we read & write
* the request. (This isn't 100% optimal, but it solves the
* 3.0 blocker.)
*/
/** @todo s/pVMMDevState/pThis/g */
/** @todo It would be faster if this interface would use MMIO2 memory and we
* didn't have to mess around with PDMDevHlpPhysRead/Write. We're
* reading the header 3 times now and writing the request back twice. */
/* 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. */
if (size > 0)
{
{
/* Use the saved page list to write data back to the guest RAM. */
rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
}
/* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
iLinPtr++;
}
} 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. */
if (size > 0)
{
{
/* Use the saved page list to write data back to the guest RAM. */
rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
}
/* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
iLinPtr++;
}
} 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. */
if (size > 0)
{
{
/* Use the saved page list to write data back to the guest RAM. */
rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
}
/* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
iLinPtr++;
}
} 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;
}
{
/** @todo no longer necessary to forward to EMT, but it might be more
* efficient...? */
/* Not safe to execute asynchroneously; forward to EMT */
int rc = VMR3ReqCallEx(PDMDevHlpGetVM(pVMMDevState->pDevIns), VMCPUID_ANY, 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)
{
/* GC physical address of the guest request. */
/* Request packet size */
/*
* Version 9+: save complete information about commands.
*/
/* Size of entire command. */
/* The type of the command. */
/* Whether the command was cancelled by the guest. */
/* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */
{
/* How many pages for all linptrs in this command. */
}
int i;
{
/* Pointer to descriptions of linear pointers. */
/* Index of the parameter. */
/* Offset in the first physical page of the region. */
/* How many pages. */
{
/* 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.
*/
}
}
/* A reserved field, will allow to extend saved data for a command. */
}
}
/* A reserved field, will allow to extend saved data for VMMDevHGCM. */
return rc;
}
/* @thread EMT */
{
int rc = VINF_SUCCESS;
LogFlowFunc(("\n"));
/* Read how many commands were pending. */
if ( SSM_VERSION_MAJOR(u32Version) == 0
{
/* Only the guest physical address is saved. */
while (cCmds--)
{
}
}
else
{
/*
* Version 9+: Load complete information about commands.
*/
bool f;
while (cCmds--)
{
/* GC physical address of the guest request. */
/* The request packet size */
/* Size of entire command. */
/* The type of the command. */
/* Whether the command was cancelled by the guest. */
pCmd->fCancelled = f;
/* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */
{
/* How many pages for all linptrs in this command. */
RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs);
int cPages = 0;
int i;
{
/* Pointer to descriptions of linear pointers. */
/* Index of the parameter. */
/* Offset in the first physical page of the region. */
/* How many pages. */
{
/* 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.
*/
/* Verify that the number of loaded pages is valid. */
cPages++;
{
LogRel(("VMMDevHGCM load state failure: cPages %d, expected %d, ptr %d/%d\n",
return VERR_SSM_UNEXPECTED_DATA;
}
*pPages++ = GCPhysPage;
}
}
}
/* A reserved field, will allow to extend saved data for a command. */
}
/* A reserved field, will allow to extend saved data for VMMDevHGCM. */
}
return rc;
}
/* @thread EMT */
{
LogFlowFunc(("\n"));
/* Reissue pending requests. */
if (RT_SUCCESS (rc))
{
/* Start from the current list head and commands loaded from saved state.
* New commands will be inserted at the list head, so they will not be seen by
* this loop.
*/
while (pIter)
{
/* This will remove the command from the list if resubmitting fails. */
bool fHGCMCalled = false;
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->header.version, VMMDEV_REQUEST_HEADER_VERSION));
}
else
{
Log(("VMMDev request issued: %d, command type %d\n", requestHeader->header.requestType, pIter->enmCmdType));
/* Use the saved command type. Even if the guest has changed the memory already,
* HGCM should see the same command as it was before saving state.
*/
switch (pIter->enmCmdType)
{
case VBOXHGCMCMDTYPE_CONNECT:
{
{
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"));
requestHeader->header.rc = vmmdevHGCMConnectSaved (pVMMDevState, pHGCMConnect, &fHGCMCalled, pIter);
}
break;
}
{
{
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"));
requestHeader->header.rc = vmmdevHGCMDisconnectSaved (pVMMDevState, pHGCMDisconnect, &fHGCMCalled, pIter);
}
break;
}
case VBOXHGCMCMDTYPE_CALL:
{
{
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 */
requestHeader->header.rc = vmmdevHGCMCallSaved (pVMMDevState, pHGCMCall, f64Bits, &fHGCMCalled, pIter);
}
break;
}
{
/* Old saved state. */
{
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->header.requestType));
}
} break;
default:
LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType));
}
}
}
{
/* Old saved state. Remove the LOADSTATE command. */
/* Write back the request */
{
}
}
else
{
if (!fHGCMCalled)
{
/* HGCM was not called. Return the error to the guest. Guest may try to repeat the call. */
}
/* Write back the request */
if (!fHGCMCalled)
{
/* HGCM was not called. Deallocate the current command and then notify guest. */
{
}
}
}
}
}
return rc;
}