typedef struct VBGLREQHDR { /** IN: The request input size, and output size if cbOut is zero. * @sa VMMDevRequestHeader::size */ uint32_t cbIn; /** IN: Structure version (VBGLREQHDR_VERSION) * @sa VMMDevRequestHeader::version */ uint32_t uVersion; /** IN: The VMMDev request type, set to VBGLREQHDR_TYPE_DEFAULT unless this is a * kind of VMMDev request. * @sa VMMDevRequestType, VMMDevRequestHeader::requestType */ uint32_t uType; /** OUT: The VBox status code of the operation, out direction only. */ int32_t rc; /** IN: The output size. This is optional - set to zero to use cbIn as the * output size. */ uint32_t cbOut; /** Reserved / filled in by kernel, MBZ. * @sa VMMDevRequestHeader::fRequestor */ uint32_t uReserved; } VBGLREQHDR;
static int vgdrvIoCtl_HGCMConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCONNECT pInfo) { int rc; HGCMCLIENTID idClient = 0;
/* * The VbglHGCMConnect call will invoke the callback if the HGCM * call is performed in an ASYNC fashion. The function is not able * to deal with cancelled requests. */ Log(("VBOXGUEST_IOCTL_HGCM_CONNECT: %.128s\n", pInfo->u.In.Loc.type == VMMDevHGCMLoc_LocalHost || pInfo->u.In.Loc.type == VMMDevHGCMLoc_LocalHost_Existing ? pInfo->u.In.Loc.u.host.achName : "<not local host>"));
rc = VbglR0HGCMInternalConnect(&pInfo->u.In.Loc, pSession->fRequestor, &idClient, vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT); Log(("VBOXGUEST_IOCTL_HGCM_CONNECT: idClient=%RX32 (rc=%Rrc)\n", idClient, rc)); if (RT_SUCCESS(rc)) { /* * Append the client id to the client id table. * If the table has somehow become filled up, we'll disconnect the session. */ unsigned i; RTSpinlockAcquire(pDevExt->SessionSpinlock); for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++) if (!pSession->aHGCMClientIds[i]) { pSession->aHGCMClientIds[i] = idClient; break; } RTSpinlockRelease(pDevExt->SessionSpinlock); if (i >= RT_ELEMENTS(pSession->aHGCMClientIds)) { LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CONNECT: too many HGCMConnect calls for one session!\n")); VbglR0HGCMInternalDisconnect(idClient, pSession->fRequestor, vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
/* * The caller has passed the guest context physical address of the request * structure. We'll copy all of it into a heap buffer eventually, but we * will have to start off with the header. */ VMMDevRequestHeader requestHeader; RT_ZERO(requestHeader); PDMDevHlpPhysRead(pDevIns, (RTGCPHYS)u32, &requestHeader, sizeof(requestHeader));
......................................................... if (pRequestHeader) { memcpy(pRequestHeader, &requestHeader, sizeof(VMMDevRequestHeader));
/* Try lock the request if it's a HGCM call and not crossing a page boundrary. Saves on PGM interaction. */ VMMDEVREQLOCK Lock = { NULL, { 0, NULL } }; PVMMDEVREQLOCK pLock = NULL; size_t cbLeft = requestHeader.size - sizeof(VMMDevRequestHeader); if (cbLeft) { ............................... }
/** * Dispatch the request to the appropriate handler function. * * @returns Port I/O handler exit code. * @param pThis The VMM device instance data. * @param pReqHdr The request header (cached in host memory). * @param GCPhysReqHdr The guest physical address of the request (for * HGCM). * @param tsArrival The STAM_GET_TS() value when the request arrived. * @param pfPostOptimize HGCM optimizations, VMMDEVREQDISP_POST_F_XXX. * @param ppLock Pointer to the lock info pointer (latter can be * NULL). Set to NULL if HGCM takes lock ownership. */ static int vmmdevReqDispatcher(PVMMDEV pThis, VMMDevRequestHeader *pReqHdr, RTGCPHYS GCPhysReqHdr, uint64_t tsArrival, uint32_t *pfPostOptimize, PVMMDEVREQLOCK *ppLock) { int rcRet = VINF_SUCCESS; Assert(*pfPostOptimize == 0);
/* Only allow the guest to use existing services! */ ASSERT_GUEST(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing); pCmd->u.connect.pLoc->type = VMMDevHGCMLoc_LocalHost_Existing;
/* Check if service name is a string terminated by zero*/ size_t cchInfo = 0; if (RTStrNLenEx(pServiceLocation->u.host.achName, sizeof(pServiceLocation->u.host.achName), &cchInfo) != VINF_SUCCESS) { return VERR_INVALID_PARAMETER; }
while (!fQuit) { HGCMMsgCore *pMsgCore; int rc = hgcmMsgGet(pThread, &pMsgCore);
if (RT_FAILURE(rc)) { /* The error means some serious unrecoverable problem in the hgcmMsg/hgcmThread layer. */ AssertMsgFailed(("%Rrc\n", rc)); break; }
/* Resolve the service name to the pointer to service instance. */ HGCMService *pService; rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName);
if (RT_SUCCESS(rc)) { /* Call the service instance method. */ rc = pService->CreateAndConnectClient(pMsg->pu32ClientId, 0, pMsg->pHGCMPort->pfnGetRequestor(pMsg->pHGCMPort, pMsg->pCmd), pMsg->pHGCMPort->pfnIsCmdRestored(pMsg->pHGCMPort, pMsg->pCmd));
/* Release the service after resolve. */ pService->ReleaseService(); } } break;
/** The method obtains a referenced pointer to the service with * specified name. The caller must call ReleaseService when * the pointer is no longer needed. * * @param ppSvc Where to store the pointer to the service. * @param pszServiceName The name of the service. * @return VBox rc. * @thread main HGCM */ /* static */ int HGCMService::ResolveService(HGCMService **ppSvc, const char *pszServiceName) { LogFlowFunc(("ppSvc = %p name = %s\n", ppSvc, pszServiceName));
if (!ppSvc || !pszServiceName) { return VERR_INVALID_PARAMETER; }
HGCMService *pSvc = sm_pSvcListHead;
while (pSvc) { if (strcmp(pSvc->m_pszSvcName, pszServiceName) == 0) { break; }
pSvc = pSvc->m_pSvcNext; }
LogFlowFunc(("lookup in the list is %p\n", pSvc));
/* Create a new client instance and connect it to the service. * * @param pu32ClientIdOut If not NULL, then the method must generate a new handle for the client. * If NULL, use the given 'u32ClientIdIn' handle. * @param u32ClientIdIn The handle for the client, when 'pu32ClientIdOut' is NULL. * @param fRequestor The requestor flags, VMMDEV_REQUESTOR_LEGACY if not available. * @param fRestoring Set if we're restoring a saved state. * @return VBox status code. */ int HGCMService::CreateAndConnectClient(uint32_t *pu32ClientIdOut, uint32_t u32ClientIdIn, uint32_t fRequestor, bool fRestoring) { LogFlowFunc(("pu32ClientIdOut = %p, u32ClientIdIn = %d, fRequestor = %#x, fRestoring = %d\n", pu32ClientIdOut, u32ClientIdIn, fRequestor, fRestoring));
/* Allocate a client information structure. */ HGCMClient *pClient = new (std::nothrow) HGCMClient(fRequestor);
if (!pClient) { Log1WarningFunc(("Could not allocate HGCMClient!!!\n")); return VERR_NO_MEMORY; }
if (RT_SUCCESS(rc)) { /* Add the client Id to the array. */ if (m_cClients == m_cClientsAllocated) { const uint32_t cDelta = 64;
/* Guards against integer overflow on 32bit arch and also limits size of m_paClientIds array to 4GB*/ if (m_cClientsAllocated < UINT32_MAX / sizeof(m_paClientIds[0]) - cDelta) { uint32_t *paClientIdsNew;
/** * For VBGL_IOCTL_HGCM_CALL and VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA. * * @note This is used by alot of HGCM call structures. */ typedef struct VBGLIOCHGCMCALL { /** Common header. */ VBGLREQHDR Hdr; /** Input: The id of the caller. */ uint32_t u32ClientID; /** Input: Function number. */ uint32_t u32Function; /** Input: How long to wait (milliseconds) for completion before cancelling the * call. This is ignored if not a VBGL_IOCTL_HGCM_CALL_TIMED or * VBGL_IOCTL_HGCM_CALL_TIMED_32 request. */ uint32_t cMsTimeout; /** Input: Whether a timed call is interruptible (ring-0 only). This is ignored * if not a VBGL_IOCTL_HGCM_CALL_TIMED or VBGL_IOCTL_HGCM_CALL_TIMED_32 * request, or if made from user land. */ bool fInterruptible; /** Explicit padding, MBZ. */ uint8_t bReserved; /** Input: How many parameters following this structure. * * The parameters are either HGCMFunctionParameter64 or HGCMFunctionParameter32, * depending on whether we're receiving a 64-bit or 32-bit request. * * The current maximum is 61 parameters (given a 1KB max request size, * and a 64-bit parameter size of 16 bytes). * * @note This information is duplicated by Hdr.cbIn, but it's currently too much * work to eliminate this. */ uint16_t cParms; /* Parameters follow in form HGCMFunctionParameter aParms[cParms] */ } VBGLIOCHGCMCALL, RT_FAR *PVBGLIOCHGCMCALL;
/* * Some more validations. */ if (RT_LIKELY(pInfo->cParms <= VMMDEV_MAX_HGCM_PARMS)) /* (Just make sure it doesn't overflow the next check.) */ { /* likely */} else { LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cParm=%RX32 is not sane\n", pInfo->cParms)); return VERR_INVALID_PARAMETER; }
/** * Handles VMMDevHGCMCall request. * * @returns VBox status code that the guest should see. * @param pThis The VMMDev instance data. * @param pHGCMCall The request to handle (cached in host memory). * @param cbHGCMCall Size of the entire request (including HGCM parameters). * @param GCPhys The guest physical address of the request. * @param enmRequestType The request type. Distinguishes 64 and 32 bit calls. * @param tsArrival The STAM_GET_TS() value when the request arrived. * @param ppLock Pointer to the lock info pointer (latter can be * NULL). Set to NULL if HGCM takes lock ownership. */ int vmmdevHGCMCall(PVMMDEV pThis, const VMMDevHGCMCall *pHGCMCall, uint32_t cbHGCMCall, RTGCPHYS GCPhys, VMMDevRequestType enmRequestType, uint64_t tsArrival, PVMMDEVREQLOCK *ppLock) { ............................................................. rc = vmmdevHGCMCallFetchGuestParms(pThis, pCmd, pHGCMCall, cbHGCMCall, enmRequestType, cbHGCMParmStruct); if (RT_SUCCESS(rc)) { /* Copy guest data to host parameters, so HGCM services can use the data. */ rc = vmmdevHGCMInitHostParameters(pThis, pCmd, (uint8_t const *)pHGCMCall); if (RT_SUCCESS(rc)) { /* * Pass the function call to HGCM connector for actual processing */ vmmdevHGCMAddCommand(pThis, pCmd);
#if 0 /* DONT ENABLE - for performance hacking. */ if ( pCmd->u.call.u32Function == 9 && pCmd->u.call.cParms == 5) { vmmdevHGCMRemoveCommand(pThis, pCmd);
/* * The service thread. Loads the service library and calls the service entry points. */ DECLCALLBACK(void) hgcmServiceThread(HGCMThread *pThread, void *pvUser) { HGCMService *pSvc = (HGCMService *)pvUser; AssertRelease(pSvc != NULL); /* Cache required information to avoid unnecessary pMsgCore access. */ uint32_t u32MsgId = pMsgCore->MsgId();
/** Guest Physical Memory Address; limited to 64 bits.*/ typedef uint64_t RTGCPHYS64; /** Unsigned integer which can contain a 64 bits GC pointer. */ typedef uint64_t RTGCUINTPTR64; /** Guest context pointer, 64 bits. */ typedef RTGCUINTPTR64 RTGCPTR64;
typedef uint8_t bool;
typedef struct VBGLREQHDR { /** IN: The request input size, and output size if cbOut is zero. * @sa VMMDevRequestHeader::size */ uint32_t cbIn; /** IN: Structure version (VBGLREQHDR_VERSION) * @sa VMMDevRequestHeader::version */ uint32_t uVersion; /** IN: The VMMDev request type, set to VBGLREQHDR_TYPE_DEFAULT unless this is a * kind of VMMDev request. * @sa VMMDevRequestType, VMMDevRequestHeader::requestType */ uint32_t uType; /** OUT: The VBox status code of the operation, out direction only. */ int32_t rc; /** IN: The output size. This is optional - set to zero to use cbIn as the * output size. */ uint32_t cbOut; /** Reserved / filled in by kernel, MBZ. * @sa VMMDevRequestHeader::fRequestor */ uint32_t uReserved; } VBGLREQHDR;
/** * HGCM host service location. * @ingroup grp_vmmdev_req */ typedef struct { char achName[128]; /**< This is really szName. */ } HGCMServiceLocationHost;
/** * For VBGL_IOCTL_HGCM_CALL and VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA. * * @note This is used by alot of HGCM call structures. */ typedef struct VBGLIOCHGCMCALL { /** Common header. */ VBGLREQHDR Hdr; /** Input: The id of the caller. */ uint32_t u32ClientID; /** Input: Function number. */ uint32_t u32Function; /** Input: How long to wait (milliseconds) for completion before cancelling the * call. This is ignored if not a VBGL_IOCTL_HGCM_CALL_TIMED or * VBGL_IOCTL_HGCM_CALL_TIMED_32 request. */ uint32_t cMsTimeout; /** Input: Whether a timed call is interruptible (ring-0 only). This is ignored * if not a VBGL_IOCTL_HGCM_CALL_TIMED or VBGL_IOCTL_HGCM_CALL_TIMED_32 * request, or if made from user land. */ bool fInterruptible; /** Explicit padding, MBZ. */ uint8_t bReserved; /** Input: How many parameters following this structure. * * The parameters are either HGCMFunctionParameter64 or HGCMFunctionParameter32, * depending on whether we're receiving a 64-bit or 32-bit request. * * The current maximum is 61 parameters (given a 1KB max request size, * and a 64-bit parameter size of 16 bytes). * * @note This information is duplicated by Hdr.cbIn, but it's currently too much * work to eliminate this. */ uint16_t cParms; /* Parameters follow in form HGCMFunctionParameter aParms[cParms] */ } VBGLIOCHGCMCALL;
/** * HGCM parameter type. */ typedef enum { VMMDevHGCMParmType_Invalid = 0, VMMDevHGCMParmType_32bit = 1, VMMDevHGCMParmType_64bit = 2, VMMDevHGCMParmType_PhysAddr = 3, /**< @deprecated Doesn't work, use PageList. */ VMMDevHGCMParmType_LinAddr = 4, /**< In and Out */ VMMDevHGCMParmType_LinAddr_In = 5, /**< In (read; host<-guest) */ VMMDevHGCMParmType_LinAddr_Out = 6, /**< Out (write; host->guest) */ VMMDevHGCMParmType_LinAddr_Locked = 7, /**< Locked In and Out */ VMMDevHGCMParmType_LinAddr_Locked_In = 8, /**< Locked In (read; host<-guest) */ VMMDevHGCMParmType_LinAddr_Locked_Out = 9, /**< Locked Out (write; host->guest) */ VMMDevHGCMParmType_PageList = 10, /**< Physical addresses of locked pages for a buffer. */ VMMDevHGCMParmType_Embedded = 11, /**< Small buffer embedded in request. */ VMMDevHGCMParmType_ContiguousPageList = 12, /**< Like PageList but with physically contiguous memory, so only one page entry. */ VMMDevHGCMParmType_SizeHack = 0x7fffffff } HGCMFunctionParameterType;