/* $Id: VMMDevHGCM.cpp 20731 2009-06-19 17:24:41Z vboxsync $ */ /** @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; * you can redistribute it and/or modify it under the terms of the GNU * 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 #include #include #include #include #include #include #include #include "VMMDevHGCM.h" typedef enum _VBOXHGCMCMDTYPE { VBOXHGCMCMDTYPE_LOADSTATE = 0, VBOXHGCMCMDTYPE_CONNECT, VBOXHGCMCMDTYPE_DISCONNECT, VBOXHGCMCMDTYPE_CALL, VBOXHGCMCMDTYPE_SizeHack = 0x7fffffff } VBOXHGCMCMDTYPE; /* Information about a linear ptr parameter. */ typedef struct _VBOXHGCMLINPTR { /* Index of the parameter. */ uint32_t iParm; /* Offset in the first physical page of the region. */ uint32_t offFirstPage; /* How many pages. */ uint32_t cPages; /* 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. */ RTGCPHYS *paPages; } VBOXHGCMLINPTR; 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. */ uint32_t cbCmd; /* The type of the command. */ VBOXHGCMCMDTYPE enmCmdType; /* Whether the command was cancelled by the guest. */ bool fCancelled; /* GC physical address of the guest request. */ RTGCPHYS GCPhys; /* Request packet size */ uint32_t cbSize; /* Pointer to converted host parameters in case of a Call request. * Parameters follow this structure in the same memory block. */ VBOXHGCMSVCPARM *paHostParms; /* 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. */ VBOXHGCMLINPTR *paLinPtrs; }; static int vmmdevHGCMCmdListLock (VMMDevState *pVMMDevState) { int rc = RTCritSectEnter (&pVMMDevState->critsectHGCMCmdList); AssertRC (rc); return rc; } static void vmmdevHGCMCmdListUnlock (VMMDevState *pVMMDevState) { int rc = RTCritSectLeave (&pVMMDevState->critsectHGCMCmdList); AssertRC (rc); } static int vmmdevHGCMAddCommand (VMMDevState *pVMMDevState, PVBOXHGCMCMD pCmd, RTGCPHYS GCPhys, uint32_t cbSize, VBOXHGCMCMDTYPE enmCmdType) { /* PPDMDEVINS pDevIns = pVMMDevState->pDevIns; */ int rc = vmmdevHGCMCmdListLock (pVMMDevState); if (RT_SUCCESS (rc)) { LogFlowFunc(("%p type %d\n", pCmd, enmCmdType)); /* Insert at the head of the list. The vmmdevHGCMLoadStateDone depends on this. */ pCmd->pNext = pVMMDevState->pHGCMCmdList; pCmd->pPrev = NULL; if (pVMMDevState->pHGCMCmdList) { pVMMDevState->pHGCMCmdList->pPrev = pCmd; } pVMMDevState->pHGCMCmdList = pCmd; if (enmCmdType != VBOXHGCMCMDTYPE_LOADSTATE) { /* Loaded commands already have the right type. */ pCmd->enmCmdType = enmCmdType; } pCmd->GCPhys = GCPhys; pCmd->cbSize = cbSize; /* Automatically enable HGCM events, if there are HGCM commands. */ if ( enmCmdType == VBOXHGCMCMDTYPE_CONNECT || enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT || enmCmdType == VBOXHGCMCMDTYPE_CALL) { Log(("vmmdevHGCMAddCommand: u32HGCMEnabled = %d\n", pVMMDevState->u32HGCMEnabled)); if (ASMAtomicCmpXchgU32(&pVMMDevState->u32HGCMEnabled, 1, 0)) { VMMDevCtlSetGuestFilterMask (pVMMDevState, VMMDEV_EVENT_HGCM, 0); } } vmmdevHGCMCmdListUnlock (pVMMDevState); } return rc; } static int vmmdevHGCMRemoveCommand (VMMDevState *pVMMDevState, PVBOXHGCMCMD pCmd) { /* PPDMDEVINS pDevIns = pVMMDevState->pDevIns; */ int rc = vmmdevHGCMCmdListLock (pVMMDevState); if (RT_SUCCESS (rc)) { LogFlowFunc(("%p\n", pCmd)); if (pCmd->pNext) { pCmd->pNext->pPrev = pCmd->pPrev; } else { /* Tail, do nothing. */ } if (pCmd->pPrev) { pCmd->pPrev->pNext = pCmd->pNext; } else { pVMMDevState->pHGCMCmdList = pCmd->pNext; } vmmdevHGCMCmdListUnlock (pVMMDevState); } return rc; } static PVBOXHGCMCMD vmmdevHGCMFindCommand (VMMDevState *pVMMDevState, RTGCPHYS GCPhys) { PVBOXHGCMCMD pCmd = NULL; int rc = vmmdevHGCMCmdListLock (pVMMDevState); if (RT_SUCCESS (rc)) { pCmd = pVMMDevState->pHGCMCmdList; while (pCmd) { if (pCmd->GCPhys == GCPhys) { break; } pCmd = pCmd->pNext; } vmmdevHGCMCmdListUnlock (pVMMDevState); } LogFlowFunc(("%p\n", pCmd)); return pCmd; } static int vmmdevHGCMSaveLinPtr (PPDMDEVINS pDevIns, uint32_t iParm, RTGCPTR GCPtr, uint32_t u32Size, uint32_t iLinPtr, VBOXHGCMLINPTR *paLinPtrs, RTGCPHYS **ppPages) { int rc = VINF_SUCCESS; AssertRelease (u32Size > 0); VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr]; /* Take the offset into the current page also into account! */ u32Size += GCPtr & PAGE_OFFSET_MASK; uint32_t cPages = (u32Size + PAGE_SIZE - 1) / PAGE_SIZE; Log(("vmmdevHGCMSaveLinPtr: parm %d: %RGv %d = %d pages\n", iParm, GCPtr, u32Size, cPages)); pLinPtr->iParm = iParm; pLinPtr->offFirstPage = GCPtr & PAGE_OFFSET_MASK; pLinPtr->cPages = cPages; pLinPtr->paPages = *ppPages; *ppPages += cPages; uint32_t iPage = 0; GCPtr &= PAGE_BASE_GC_MASK; /* Gonvert the guest linear pointers of pages to HC addresses. */ while (iPage < cPages) { /* convert */ RTGCPHYS GCPhys; rc = PDMDevHlpPhysGCPtr2GCPhys(pDevIns, GCPtr, &GCPhys); Log(("vmmdevHGCMSaveLinPtr: Page %d: %RGv -> %RGp. %Rrc\n", iPage, GCPtr, GCPhys, rc)); if (RT_FAILURE (rc)) { break; } /* store */ pLinPtr->paPages[iPage++] = GCPhys; /* next */ GCPtr += PAGE_SIZE; } AssertRelease (iPage == cPages); return rc; } static int vmmdevHGCMWriteLinPtr (PPDMDEVINS pDevIns, uint32_t iParm, void *pvHost, uint32_t u32Size, uint32_t iLinPtr, VBOXHGCMLINPTR *paLinPtrs) { int rc = VINF_SUCCESS; VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr]; AssertRelease (u32Size > 0 && iParm == (uint32_t)pLinPtr->iParm); RTGCPHYS GCPhysDst = pLinPtr->paPages[0] + pLinPtr->offFirstPage; uint8_t *pu8Src = (uint8_t *)pvHost; Log(("vmmdevHGCMWriteLinPtr: parm %d: size %d, cPages = %d\n", iParm, u32Size, pLinPtr->cPages)); uint32_t iPage = 0; while (iPage < pLinPtr->cPages) { /* copy */ uint32_t cbWrite = iPage == 0? PAGE_SIZE - pLinPtr->offFirstPage: PAGE_SIZE; Log(("vmmdevHGCMWriteLinPtr: page %d: dst %RGp, src %p, cbWrite %d\n", iPage, GCPhysDst, pu8Src, cbWrite)); iPage++; if (cbWrite >= u32Size) { PDMDevHlpPhysWrite(pDevIns, GCPhysDst, pu8Src, u32Size); u32Size = 0; break; } PDMDevHlpPhysWrite(pDevIns, GCPhysDst, pu8Src, cbWrite); /* next */ u32Size -= cbWrite; pu8Src += cbWrite; GCPhysDst = pLinPtr->paPages[iPage]; } AssertRelease (iPage == pLinPtr->cPages); Assert(u32Size == 0); return rc; } static void logRelSavedCmdSizeMismatch (const char *pszFunction, uint32_t cbExpected, uint32_t cbCmdSize) { LogRel(("Warning: VMMDev %s command length %d (expected %d)\n", pszFunction, cbCmdSize, cbExpected)); } int vmmdevHGCMConnect (VMMDevState *pVMMDevState, VMMDevHGCMConnect *pHGCMConnect, RTGCPHYS GCPhys) { int rc = VINF_SUCCESS; uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size; PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize); if (pCmd) { VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1); vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMConnect->header.header.size, VBOXHGCMCMDTYPE_CONNECT); memcpy(pHGCMConnectCopy, pHGCMConnect, pHGCMConnect->header.header.size); pCmd->cbCmd = cbCmdSize; pCmd->paHostParms = NULL; pCmd->cLinPtrs = 0; pCmd->paLinPtrs = NULL; /* Only allow the guest to use existing services! */ Assert(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing); pHGCMConnect->loc.type = VMMDevHGCMLoc_LocalHost_Existing; 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; uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size; if (pSavedCmd->cbCmd < cbCmdSize) { logRelSavedCmdSizeMismatch ("HGCMConnect", pSavedCmd->cbCmd, cbCmdSize); return VERR_INVALID_PARAMETER; } VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pSavedCmd+1); memcpy(pHGCMConnectCopy, pHGCMConnect, pHGCMConnect->header.header.size); /* Only allow the guest to use existing services! */ Assert(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing); pHGCMConnect->loc.type = VMMDevHGCMLoc_LocalHost_Existing; 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; uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD); PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize); if (pCmd) { vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMDisconnect->header.header.size, VBOXHGCMCMDTYPE_DISCONNECT); pCmd->cbCmd = cbCmdSize; pCmd->paHostParms = NULL; pCmd->cLinPtrs = 0; pCmd->paLinPtrs = NULL; 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; uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD); if (pSavedCmd->cbCmd < cbCmdSize) { logRelSavedCmdSizeMismatch ("HGCMConnect", pSavedCmd->cbCmd, cbCmdSize); 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. */ uint32_t cParms = pHGCMCall->cParms; Log(("vmmdevHGCMCall: cParms = %d\n", cParms)); /* * Compute size of required memory buffer. */ uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + cParms * sizeof (VBOXHGCMSVCPARM); uint32_t i; uint32_t cLinPtrs = 0; uint32_t cLinPtrPages = 0; if (f64Bits) { #ifdef VBOX_WITH_64_BITS_GUESTS HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall); #else HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); 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. */ for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_LinAddr_In: /* In (read) */ case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */ case VMMDevHGCMParmType_LinAddr: /* In & Out */ { if (pGuestParm->u.Pointer.size > 0) { /* Only pointers with some actual data are counted. */ cbCmdSize += pGuestParm->u.Pointer.size; cLinPtrs++; /* Take the offset into the current page also into account! */ cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK) + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE; } Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size)); } break; case VMMDevHGCMParmType_32bit: case VMMDevHGCMParmType_64bit: case VMMDevHGCMParmType_PhysAddr: { } break; default: { AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type)); rc = VERR_INVALID_PARAMETER; break; } } } } else { #ifdef VBOX_WITH_64_BITS_GUESTS HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall); #else HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); #endif /* VBOX_WITH_64_BITS_GUESTS */ /* Look for pointer parameters, which require a host buffer. */ for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_LinAddr_In: /* In (read) */ case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */ case VMMDevHGCMParmType_LinAddr: /* In & Out */ { if (pGuestParm->u.Pointer.size > 0) { /* Only pointers with some actual data are counted. */ cbCmdSize += pGuestParm->u.Pointer.size; cLinPtrs++; /* Take the offset into the current page also into account! */ cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK) + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE; } Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size)); } break; case VMMDevHGCMParmType_32bit: case VMMDevHGCMParmType_64bit: case VMMDevHGCMParmType_PhysAddr: { } break; default: { AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type)); rc = VERR_INVALID_PARAMETER; break; } } } } if (RT_FAILURE (rc)) { return rc; } PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAlloc (cbCmdSize); if (pCmd == NULL) { return VERR_NO_MEMORY; } memset (pCmd, 0, sizeof (*pCmd)); pCmd->cbCmd = cbCmdSize; pCmd->paHostParms = NULL; pCmd->cLinPtrs = cLinPtrs; pCmd->cLinPtrPages = cLinPtrPages; if (cLinPtrs > 0) { pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAlloc ( sizeof (VBOXHGCMLINPTR) * cLinPtrs + sizeof (RTGCPHYS) * cLinPtrPages); if (pCmd->paLinPtrs == NULL) { RTMemFree (pCmd); return VERR_NO_MEMORY; } } else { pCmd->paLinPtrs = NULL; } /* 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 *)((char *)pCmd + sizeof (struct VBOXHGCMCMD)); uint8_t *pcBuf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM); pCmd->paHostParms = pHostParm; uint32_t iLinPtr = 0; RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) *cLinPtrs); if (f64Bits) { #ifdef VBOX_WITH_64_BITS_GUESTS HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall); #else HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); AssertFailed (); /* This code should not be called in this case */ #endif /* VBOX_WITH_64_BITS_GUESTS */ for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { uint32_t u32 = pGuestParm->u.value32; pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT; pHostParm->u.uint32 = u32; Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32)); break; } case VMMDevHGCMParmType_64bit: { uint64_t u64 = pGuestParm->u.value64; pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT; pHostParm->u.uint64 = u64; Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64)); break; } case VMMDevHGCMParmType_PhysAddr: { uint32_t size = pGuestParm->u.Pointer.size; #ifdef LOG_ENABLED RTGCPHYS physAddr = pGuestParm->u.Pointer.u.physAddr; #endif pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; AssertFailed(); /* rc = PDMDevHlpPhys2HCVirt (pVMMDevState->pDevIns, physAddr, size, &pHostParm->u.pointer.addr); */ Log(("vmmdevHGCMCall: PhysAddr guest parameter %RGp\n", physAddr)); break; } case VMMDevHGCMParmType_LinAddr_In: /* In (read) */ case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */ case VMMDevHGCMParmType_LinAddr: /* In & Out */ { uint32_t size = pGuestParm->u.Pointer.size; RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr; pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; /* Copy guest data to an allocated buffer, so * services can use the data. */ if (size == 0) { pHostParm->u.pointer.addr = NULL; } else { /* Don't overdo it */ if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out) rc = PDMDevHlpPhysReadGCVirt(pVMMDevState->pDevIns, pcBuf, linearAddr, size); else rc = VINF_SUCCESS; if (RT_SUCCESS(rc)) { pHostParm->u.pointer.addr = pcBuf; pcBuf += size; /* 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++; } } Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", linearAddr, rc)); break; } /* just to shut up gcc */ default: break; } } } else { #ifdef VBOX_WITH_64_BITS_GUESTS HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall); #else HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); #endif /* VBOX_WITH_64_BITS_GUESTS */ for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { uint32_t u32 = pGuestParm->u.value32; pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT; pHostParm->u.uint32 = u32; Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32)); break; } case VMMDevHGCMParmType_64bit: { uint64_t u64 = pGuestParm->u.value64; pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT; pHostParm->u.uint64 = u64; Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64)); break; } case VMMDevHGCMParmType_PhysAddr: { uint32_t size = pGuestParm->u.Pointer.size; #ifdef LOG_ENABLED RTGCPHYS physAddr = pGuestParm->u.Pointer.u.physAddr; #endif pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; AssertFailed(); /* rc = PDMDevHlpPhys2HCVirt (pVMMDevState->pDevIns, physAddr, size, &pHostParm->u.pointer.addr); */ Log(("vmmdevHGCMCall: PhysAddr guest parameter %RGp\n", physAddr)); break; } case VMMDevHGCMParmType_LinAddr_In: /* In (read) */ case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */ case VMMDevHGCMParmType_LinAddr: /* In & Out */ { uint32_t size = pGuestParm->u.Pointer.size; RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr; pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; /* Copy guest data to an allocated buffer, so * services can use the data. */ if (size == 0) { pHostParm->u.pointer.addr = NULL; } else { /* Don't overdo it */ if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out) rc = PDMDevHlpPhysReadGCVirt(pVMMDevState->pDevIns, pcBuf, linearAddr, size); else rc = VINF_SUCCESS; if (RT_SUCCESS(rc)) { pHostParm->u.pointer.addr = pcBuf; pcBuf += size; /* 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++; } } Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", linearAddr, rc)); 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 { if (pCmd->paLinPtrs) { RTMemFree (pCmd->paLinPtrs); } RTMemFree (pCmd); } 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", (int)iParm, (int)iSavedParm, iLinPtr, cLinPtrs)); } static void logRelLoadStateBufferSizeMismatch (uint32_t size, uint32_t iPage, uint32_t cPages) { LogRel(("Warning: VMMDev load state: buffer size mismatch: size %d, page %d/%d\n", (int)size, (int)iPage, (int)cPages)); } 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. */ uint32_t cParms = pHGCMCall->cParms; Log(("vmmdevHGCMCall: cParms = %d\n", cParms)); /* * Compute size of required memory buffer. */ pSavedCmd->paHostParms = NULL; /* 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)); uint8_t *pu8Buf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM); pSavedCmd->paHostParms = pHostParm; uint32_t iParm; int iLinPtr = 0; if (f64Bits) { #ifdef VBOX_WITH_64_BITS_GUESTS HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall); #else HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); AssertFailed (); /* This code should not be called in this case */ #endif /* VBOX_WITH_64_BITS_GUESTS */ for (iParm = 0; iParm < cParms && RT_SUCCESS(rc); iParm++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { uint32_t u32 = pGuestParm->u.value32; pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT; pHostParm->u.uint32 = u32; Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32)); break; } case VMMDevHGCMParmType_64bit: { uint64_t u64 = pGuestParm->u.value64; pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT; pHostParm->u.uint64 = u64; Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64)); break; } case VMMDevHGCMParmType_PhysAddr: { uint32_t size = pGuestParm->u.Pointer.size; #ifdef LOG_ENABLED RTGCPHYS physAddr = pGuestParm->u.Pointer.u.physAddr; #endif pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; AssertFailed(); /* rc = PDMDevHlpPhys2HCVirt (pVMMDevState->pDevIns, physAddr, size, &pHostParm->u.pointer.addr); */ Log(("vmmdevHGCMCall: PhysAddr guest parameter %RGp\n", physAddr)); break; } case VMMDevHGCMParmType_LinAddr_In: /* In (read) */ case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */ case VMMDevHGCMParmType_LinAddr: /* In & Out */ { uint32_t size = pGuestParm->u.Pointer.size; pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; /* Copy guest data to an allocated buffer, so * services can use the data. */ if (size == 0) { pHostParm->u.pointer.addr = NULL; } else { /* The saved command already have the page list in pCmd->paLinPtrs. * Read data from guest pages. */ /* Don't overdo it */ if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out) { if ( iLinPtr >= pSavedCmd->cLinPtrs || pSavedCmd->paLinPtrs[iLinPtr].iParm != iParm) { logRelLoadStatePointerIndexMismatch (iParm, pSavedCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pSavedCmd->cLinPtrs); rc = VERR_INVALID_PARAMETER; } else { VBOXHGCMLINPTR *pLinPtr = &pSavedCmd->paLinPtrs[iLinPtr]; uint32_t iPage; uint32_t offPage = pLinPtr->offFirstPage; size_t cbRemaining = size; uint8_t *pu8Dst = pu8Buf; for (iPage = 0; iPage < pLinPtr->cPages; iPage++) { if (cbRemaining == 0) { logRelLoadStateBufferSizeMismatch (size, iPage, pLinPtr->cPages); break; } size_t cbChunk = PAGE_SIZE - offPage; if (cbChunk > cbRemaining) { cbChunk = cbRemaining; } rc = PDMDevHlpPhysRead(pVMMDevState->pDevIns, pLinPtr->paPages[iPage] + offPage, pu8Dst, cbChunk); AssertRCBreak(rc); offPage = 0; /* A next page is read from 0 offset. */ cbRemaining -= cbChunk; pu8Dst += cbChunk; } } } else rc = VINF_SUCCESS; if (RT_SUCCESS(rc)) { pHostParm->u.pointer.addr = pu8Buf; pu8Buf += size; iLinPtr++; } } Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", pGuestParm->u.Pointer.u.linearAddr, rc)); break; } /* just to shut up gcc */ default: break; } } } else { #ifdef VBOX_WITH_64_BITS_GUESTS HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall); #else HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); #endif /* VBOX_WITH_64_BITS_GUESTS */ for (iParm = 0; iParm < cParms && RT_SUCCESS(rc); iParm++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { uint32_t u32 = pGuestParm->u.value32; pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT; pHostParm->u.uint32 = u32; Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32)); break; } case VMMDevHGCMParmType_64bit: { uint64_t u64 = pGuestParm->u.value64; pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT; pHostParm->u.uint64 = u64; Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64)); break; } case VMMDevHGCMParmType_PhysAddr: { uint32_t size = pGuestParm->u.Pointer.size; #ifdef LOG_ENABLED RTGCPHYS physAddr = pGuestParm->u.Pointer.u.physAddr; #endif pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; AssertFailed(); /* rc = PDMDevHlpPhys2HCVirt (pVMMDevState->pDevIns, physAddr, size, &pHostParm->u.pointer.addr); */ Log(("vmmdevHGCMCall: PhysAddr guest parameter %RGp\n", physAddr)); break; } case VMMDevHGCMParmType_LinAddr_In: /* In (read) */ case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */ case VMMDevHGCMParmType_LinAddr: /* In & Out */ { uint32_t size = pGuestParm->u.Pointer.size; pHostParm->type = VBOX_HGCM_SVC_PARM_PTR; pHostParm->u.pointer.size = size; /* Copy guest data to an allocated buffer, so * services can use the data. */ if (size == 0) { pHostParm->u.pointer.addr = NULL; } else { /* The saved command already have the page list in pCmd->paLinPtrs. * Read data from guest pages. */ /* Don't overdo it */ if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out) { if ( iLinPtr >= pSavedCmd->cLinPtrs || pSavedCmd->paLinPtrs[iLinPtr].iParm != iParm) { logRelLoadStatePointerIndexMismatch (iParm, pSavedCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pSavedCmd->cLinPtrs); rc = VERR_INVALID_PARAMETER; } else { VBOXHGCMLINPTR *pLinPtr = &pSavedCmd->paLinPtrs[iLinPtr]; uint32_t iPage; uint32_t offPage = pLinPtr->offFirstPage; size_t cbRemaining = size; uint8_t *pu8Dst = pu8Buf; for (iPage = 0; iPage < pLinPtr->cPages; iPage++) { if (cbRemaining == 0) { logRelLoadStateBufferSizeMismatch (size, iPage, pLinPtr->cPages); break; } size_t cbChunk = PAGE_SIZE - offPage; if (cbChunk > cbRemaining) { cbChunk = cbRemaining; } rc = PDMDevHlpPhysRead(pVMMDevState->pDevIns, pLinPtr->paPages[iPage] + offPage, pu8Dst, cbChunk); AssertRCBreak(rc); offPage = 0; /* A next page is read from 0 offset. */ cbRemaining -= cbChunk; pu8Dst += cbChunk; } } } else rc = VINF_SUCCESS; if (RT_SUCCESS(rc)) { pHostParm->u.pointer.addr = pu8Buf; pu8Buf += size; iLinPtr++; } } Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", pGuestParm->u.Pointer.u.linearAddr, rc)); 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 vmmdevHGCMCancel (VMMDevState *pVMMDevState, VMMDevHGCMCancel *pHGCMCancel, RTGCPHYS GCPhys) { int rc = VINF_SUCCESS; NOREF(pHGCMCancel); Log(("vmmdevHGCMCancel\n")); /* Find the command in the list. */ PVBOXHGCMCMD pCmd = vmmdevHGCMFindCommand (pVMMDevState, GCPhys); if (pCmd) { pCmd->fCancelled = true; } else { rc = VERR_INVALID_PARAMETER; } return rc; } static int vmmdevHGCMCmdVerify (PVBOXHGCMCMD pCmd, VMMDevHGCMRequestHeader *pHeader) { switch (pCmd->enmCmdType) { case VBOXHGCMCMDTYPE_CONNECT: if ( pHeader->header.requestType == VMMDevReq_HGCMConnect || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS; break; case VBOXHGCMCMDTYPE_DISCONNECT: if ( pHeader->header.requestType == VMMDevReq_HGCMDisconnect || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS; break; case VBOXHGCMCMDTYPE_CALL: #ifdef VBOX_WITH_64_BITS_GUESTS if ( pHeader->header.requestType == VMMDevReq_HGCMCall32 || pHeader->header.requestType == VMMDevReq_HGCMCall64 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS; #else if ( pHeader->header.requestType == VMMDevReq_HGCMCall || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS; #endif /* VBOX_WITH_64_BITS_GUESTS */ break; default: AssertFailed (); } LogRel(("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n", pCmd->enmCmdType, pHeader->header.requestType)); 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) { VMMDevState *pVMMDevState = PDMIHGCMPORT_2_VMMDEVSTATE(pInterface); 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. */ LogFlowFunc(("VINF_HGCM_SAVE_STATE for command %p\n", pCmd)); 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. */ LogFlowFunc(("A cancelled command %p\n", pCmd)); vmmdevHGCMRemoveCommand (pVMMDevState, pCmd); } else { /* Preallocated block for requests which have up to 8 parameters (most of requests). */ #ifdef VBOX_WITH_64_BITS_GUESTS uint8_t au8Prealloc[sizeof (VMMDevHGCMCall) + 8 * sizeof (HGCMFunctionParameter64)]; #else uint8_t au8Prealloc[sizeof (VMMDevHGCMCall) + 8 * sizeof (HGCMFunctionParameter)]; #endif /* VBOX_WITH_64_BITS_GUESTS */ VMMDevHGCMRequestHeader *pHeader; if (pCmd->cbSize <= sizeof (au8Prealloc)) { pHeader = (VMMDevHGCMRequestHeader *)&au8Prealloc[0]; } else { pHeader = (VMMDevHGCMRequestHeader *)RTMemAlloc (pCmd->cbSize); Assert(pHeader); if (pHeader == NULL) { LogRel(("VMMDev: Failed to allocate %d bytes for HGCM request completion!!!\n", pCmd->cbSize)); /* Do some cleanup. The command have to be excluded from list of active commands anyway. */ vmmdevHGCMRemoveCommand (pVMMDevState, pCmd); return; } } PDMDevHlpPhysRead(pVMMDevState->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbSize); /* Setup return codes. */ pHeader->result = result; /* Verify the request type. */ rc = vmmdevHGCMCmdVerify (pCmd, pHeader); if (RT_SUCCESS (rc)) { /* Update parameters and data buffers. */ switch (pHeader->header.requestType) { #ifdef VBOX_WITH_64_BITS_GUESTS case VMMDevReq_HGCMCall64: { VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader; uint32_t cParms = pHGCMCall->cParms; VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms; uint32_t i; uint32_t iLinPtr = 0; HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall); for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { pGuestParm->u.value32 = pHostParm->u.uint32; } break; case VMMDevHGCMParmType_64bit: { pGuestParm->u.value64 = pHostParm->u.uint64; } break; case VMMDevHGCMParmType_PhysAddr: { /* 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. */ uint32_t size = pGuestParm->u.Pointer.size; if (size > 0) { if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In) { /* 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); AssertReleaseRC(rc); } /* 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. */ AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type)); } } } break; } case VMMDevReq_HGCMCall32: { VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader; uint32_t cParms = pHGCMCall->cParms; VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms; uint32_t i; uint32_t iLinPtr = 0; HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall); for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { pGuestParm->u.value32 = pHostParm->u.uint32; } break; case VMMDevHGCMParmType_64bit: { pGuestParm->u.value64 = pHostParm->u.uint64; } break; case VMMDevHGCMParmType_PhysAddr: { /* 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. */ uint32_t size = pGuestParm->u.Pointer.size; if (size > 0) { if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In) { /* 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); AssertReleaseRC(rc); } /* 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. */ AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type)); } } } break; } #else case VMMDevReq_HGCMCall: { VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader; uint32_t cParms = pHGCMCall->cParms; VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms; uint32_t i; uint32_t iLinPtr = 0; HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++) { switch (pGuestParm->type) { case VMMDevHGCMParmType_32bit: { pGuestParm->u.value32 = pHostParm->u.uint32; } break; case VMMDevHGCMParmType_64bit: { pGuestParm->u.value64 = pHostParm->u.uint64; } break; case VMMDevHGCMParmType_PhysAddr: { /* 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. */ uint32_t size = pGuestParm->u.Pointer.size; if (size > 0) { if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In) { /* 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); AssertReleaseRC(rc); } /* 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. */ AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type)); } } } break; } #endif /* VBOX_WITH_64_BITS_GUESTS */ case VMMDevReq_HGCMConnect: { VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1); /* save the client id in the guest request packet */ VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader; pHGCMConnect->u32ClientID = pHGCMConnectCopy->u32ClientID; break; } default: /* make gcc happy */ break; } } else { /* Command type is wrong. Return error to the guest. */ pHeader->header.rc = rc; } /* Mark request as processed. */ pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE; /* Write back the request */ PDMDevHlpPhysWrite(pVMMDevState->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbSize); /* The command has been completely processed and can be removed from the list. */ vmmdevHGCMRemoveCommand (pVMMDevState, pCmd); /* Now, when the command was removed from the internal list, notify the guest. */ VMMDevNotifyGuest (pVMMDevState, VMMDEV_EVENT_HGCM); if ((uint8_t *)pHeader != &au8Prealloc[0]) { /* Only if it was allocated from heap. */ RTMemFree (pHeader); } } /* Deallocate the command memory. */ if (pCmd->paLinPtrs) { RTMemFree (pCmd->paLinPtrs); } RTMemFree (pCmd); return; } DECLCALLBACK(void) hgcmCompleted (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd) { VMMDevState *pVMMDevState = PDMIHGCMPORT_2_VMMDEVSTATE(pInterface); /* Not safe to execute asynchroneously; forward to EMT */ int rc = VMR3ReqCallEx(PDMDevHlpGetVM(pVMMDevState->pDevIns), VMCPUID_ANY, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID, (PFNRT)hgcmCompletedWorker, 3, pInterface, result, pCmd); AssertRC(rc); } /* @thread EMT */ int vmmdevHGCMSaveState(VMMDevState *pVMMDevState, PSSMHANDLE pSSM) { /* Save information about pending requests. * Only GCPtrs are of interest. */ int rc = VINF_SUCCESS; LogFlowFunc(("\n")); /* Compute how many commands are pending. */ uint32_t cCmds = 0; PVBOXHGCMCMD pIter = pVMMDevState->pHGCMCmdList; while (pIter) { LogFlowFunc (("pIter %p\n", pIter)); cCmds++; pIter = pIter->pNext; } LogFlowFunc(("cCmds = %d\n", cCmds)); /* Save number of commands. */ rc = SSMR3PutU32(pSSM, cCmds); AssertRCReturn(rc, rc); if (cCmds > 0) { pIter = pVMMDevState->pHGCMCmdList; while (pIter) { PVBOXHGCMCMD pNext = pIter->pNext; LogFlowFunc (("Saving %RGp, size %d\n", pIter->GCPhys, pIter->cbSize)); /* GC physical address of the guest request. */ rc = SSMR3PutGCPhys(pSSM, pIter->GCPhys); AssertRCReturn(rc, rc); /* Request packet size */ rc = SSMR3PutU32(pSSM, pIter->cbSize); AssertRCReturn(rc, rc); /* * Version 9+: save complete information about commands. */ /* Size of entire command. */ rc = SSMR3PutU32(pSSM, pIter->cbCmd); AssertRCReturn(rc, rc); /* The type of the command. */ rc = SSMR3PutU32(pSSM, (uint32_t)pIter->enmCmdType); AssertRCReturn(rc, rc); /* Whether the command was cancelled by the guest. */ rc = SSMR3PutBool(pSSM, pIter->fCancelled); AssertRCReturn(rc, rc); /* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */ rc = SSMR3PutU32(pSSM, (uint32_t)pIter->cLinPtrs); AssertRCReturn(rc, rc); if (pIter->cLinPtrs > 0) { /* How many pages for all linptrs in this command. */ rc = SSMR3PutU32(pSSM, (uint32_t)pIter->cLinPtrPages); AssertRCReturn(rc, rc); } int i; for (i = 0; i < pIter->cLinPtrs; i++) { /* Pointer to descriptions of linear pointers. */ VBOXHGCMLINPTR *pLinPtr = &pIter->paLinPtrs[i]; /* Index of the parameter. */ rc = SSMR3PutU32(pSSM, (uint32_t)pLinPtr->iParm); AssertRCReturn(rc, rc); /* Offset in the first physical page of the region. */ rc = SSMR3PutU32(pSSM, pLinPtr->offFirstPage); AssertRCReturn(rc, rc); /* How many pages. */ rc = SSMR3PutU32(pSSM, pLinPtr->cPages); AssertRCReturn(rc, rc); uint32_t iPage; for (iPage = 0; iPage < pLinPtr->cPages; iPage++) { /* 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. */ rc = SSMR3PutGCPhys(pSSM, pLinPtr->paPages[iPage]); AssertRCReturn(rc, rc); } } /* A reserved field, will allow to extend saved data for a command. */ rc = SSMR3PutU32(pSSM, 0); AssertRCReturn(rc, rc); vmmdevHGCMRemoveCommand (pVMMDevState, pIter); pIter = pNext; } } /* A reserved field, will allow to extend saved data for VMMDevHGCM. */ rc = SSMR3PutU32(pSSM, 0); AssertRCReturn(rc, rc); return rc; } /* @thread EMT */ int vmmdevHGCMLoadState(VMMDevState *pVMMDevState, PSSMHANDLE pSSM, uint32_t u32Version) { int rc = VINF_SUCCESS; LogFlowFunc(("\n")); /* Read how many commands were pending. */ uint32_t cCmds = 0; rc = SSMR3GetU32(pSSM, &cCmds); AssertRCReturn(rc, rc); LogFlowFunc(("cCmds = %d\n", cCmds)); if ( SSM_VERSION_MAJOR(u32Version) == 0 && SSM_VERSION_MINOR(u32Version) < 9) { /* Only the guest physical address is saved. */ while (cCmds--) { RTGCPHYS GCPhys; uint32_t cbSize; rc = SSMR3GetGCPhys(pSSM, &GCPhys); AssertRCReturn(rc, rc); rc = SSMR3GetU32(pSSM, &cbSize); AssertRCReturn(rc, rc); LogFlowFunc (("Restoring %RGp size %x bytes\n", GCPhys, cbSize)); PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (sizeof (struct VBOXHGCMCMD)); AssertReturn(pCmd, VERR_NO_MEMORY); vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, cbSize, VBOXHGCMCMDTYPE_LOADSTATE); } } else { /* * Version 9+: Load complete information about commands. */ uint32_t u32; bool f; while (cCmds--) { RTGCPHYS GCPhys; uint32_t cbSize; /* GC physical address of the guest request. */ rc = SSMR3GetGCPhys(pSSM, &GCPhys); AssertRCReturn(rc, rc); /* The request packet size */ rc = SSMR3GetU32(pSSM, &cbSize); AssertRCReturn(rc, rc); LogFlowFunc (("Restoring %RGp size %x bytes\n", GCPhys, cbSize)); /* Size of entire command. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (u32); AssertReturn(pCmd, VERR_NO_MEMORY); pCmd->cbCmd = u32; /* The type of the command. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); pCmd->enmCmdType = (VBOXHGCMCMDTYPE)u32; /* Whether the command was cancelled by the guest. */ rc = SSMR3GetBool(pSSM, &f); AssertRCReturn(rc, rc); pCmd->fCancelled = f; /* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); pCmd->cLinPtrs = u32; if (pCmd->cLinPtrs > 0) { /* How many pages for all linptrs in this command. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); pCmd->cLinPtrPages = u32; pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAllocZ ( sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs + sizeof (RTGCPHYS) * pCmd->cLinPtrPages); AssertReturn(pCmd->paLinPtrs, VERR_NO_MEMORY); RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs); int cPages = 0; int i; for (i = 0; i < pCmd->cLinPtrs; i++) { /* Pointer to descriptions of linear pointers. */ VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[i]; pLinPtr->paPages = pPages; /* Index of the parameter. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); pLinPtr->iParm = u32; /* Offset in the first physical page of the region. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); pLinPtr->offFirstPage = u32; /* How many pages. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); pLinPtr->cPages = u32; uint32_t iPage; for (iPage = 0; iPage < pLinPtr->cPages; iPage++) { /* 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. */ RTGCPHYS GCPhysPage; rc = SSMR3GetGCPhys(pSSM, &GCPhysPage); AssertRCReturn(rc, rc); /* Verify that the number of loaded pages is valid. */ cPages++; if (cPages > pCmd->cLinPtrPages) { LogRel(("VMMDevHGCM load state failure: cPages %d, expected %d, ptr %d/%d\n", cPages, pCmd->cLinPtrPages, i, pCmd->cLinPtrs)); return VERR_SSM_UNEXPECTED_DATA; } *pPages++ = GCPhysPage; } } } /* A reserved field, will allow to extend saved data for a command. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, cbSize, VBOXHGCMCMDTYPE_LOADSTATE); } /* A reserved field, will allow to extend saved data for VMMDevHGCM. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); } return rc; } /* @thread EMT */ int vmmdevHGCMLoadStateDone(VMMDevState *pVMMDevState, PSSMHANDLE pSSM) { LogFlowFunc(("\n")); /* Reissue pending requests. */ PPDMDEVINS pDevIns = pVMMDevState->pDevIns; int rc = vmmdevHGCMCmdListLock (pVMMDevState); 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. */ PVBOXHGCMCMD pIter = pVMMDevState->pHGCMCmdList; while (pIter) { /* This will remove the command from the list if resubmitting fails. */ bool fHGCMCalled = false; LogFlowFunc (("pIter %p\n", pIter)); PVBOXHGCMCMD pNext = pIter->pNext; VMMDevHGCMRequestHeader *requestHeader = (VMMDevHGCMRequestHeader *)RTMemAllocZ (pIter->cbSize); Assert(requestHeader); if (requestHeader == NULL) return VERR_NO_MEMORY; PDMDevHlpPhysRead(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize); /* the structure size must be greater or equal to the header size */ if (requestHeader->header.size < sizeof(VMMDevHGCMRequestHeader)) { Log(("VMMDev request header size too small! size = %d\n", requestHeader->header.size)); } else { /* check the version of the header structure */ if (requestHeader->header.version != VMMDEV_REQUEST_HEADER_VERSION) { 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: { if (requestHeader->header.size < sizeof(VMMDevHGCMConnect)) { AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n")); requestHeader->header.rc = VERR_INVALID_PARAMETER; } else if (!pVMMDevState->pHGCMDrv) { Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n")); requestHeader->header.rc = VERR_NOT_SUPPORTED; } else { VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)requestHeader; Log(("VMMDevReq_HGCMConnect\n")); requestHeader->header.rc = vmmdevHGCMConnectSaved (pVMMDevState, pHGCMConnect, &fHGCMCalled, pIter); } break; } case VBOXHGCMCMDTYPE_DISCONNECT: { if (requestHeader->header.size < sizeof(VMMDevHGCMDisconnect)) { AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n")); requestHeader->header.rc = VERR_INVALID_PARAMETER; } else if (!pVMMDevState->pHGCMDrv) { Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n")); requestHeader->header.rc = VERR_NOT_SUPPORTED; } else { VMMDevHGCMDisconnect *pHGCMDisconnect = (VMMDevHGCMDisconnect *)requestHeader; Log(("VMMDevReq_VMMDevHGCMDisconnect\n")); requestHeader->header.rc = vmmdevHGCMDisconnectSaved (pVMMDevState, pHGCMDisconnect, &fHGCMCalled, pIter); } break; } case VBOXHGCMCMDTYPE_CALL: { if (requestHeader->header.size < sizeof(VMMDevHGCMCall)) { AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n")); requestHeader->header.rc = VERR_INVALID_PARAMETER; } else if (!pVMMDevState->pHGCMDrv) { Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n")); requestHeader->header.rc = VERR_NOT_SUPPORTED; } else { VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)requestHeader; Log(("VMMDevReq_HGCMCall: sizeof (VMMDevHGCMRequest) = %04X\n", sizeof (VMMDevHGCMCall))); Log(("%.*Rhxd\n", requestHeader->header.size, requestHeader)); #ifdef VBOX_WITH_64_BITS_GUESTS bool f64Bits = (requestHeader->header.requestType == VMMDevReq_HGCMCall64); #else bool f64Bits = false; #endif /* VBOX_WITH_64_BITS_GUESTS */ requestHeader->header.rc = vmmdevHGCMCallSaved (pVMMDevState, pHGCMCall, f64Bits, &fHGCMCalled, pIter); } break; } case VBOXHGCMCMDTYPE_LOADSTATE: { /* Old saved state. */ switch (requestHeader->header.requestType) { case VMMDevReq_HGCMConnect: { if (requestHeader->header.size < sizeof(VMMDevHGCMConnect)) { AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n")); requestHeader->header.rc = VERR_INVALID_PARAMETER; } else if (!pVMMDevState->pHGCMDrv) { Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n")); requestHeader->header.rc = VERR_NOT_SUPPORTED; } else { VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)requestHeader; Log(("VMMDevReq_HGCMConnect\n")); requestHeader->header.rc = vmmdevHGCMConnect (pVMMDevState, pHGCMConnect, pIter->GCPhys); } break; } case VMMDevReq_HGCMDisconnect: { if (requestHeader->header.size < sizeof(VMMDevHGCMDisconnect)) { AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n")); requestHeader->header.rc = VERR_INVALID_PARAMETER; } else if (!pVMMDevState->pHGCMDrv) { Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n")); requestHeader->header.rc = VERR_NOT_SUPPORTED; } else { VMMDevHGCMDisconnect *pHGCMDisconnect = (VMMDevHGCMDisconnect *)requestHeader; Log(("VMMDevReq_VMMDevHGCMDisconnect\n")); requestHeader->header.rc = vmmdevHGCMDisconnect (pVMMDevState, pHGCMDisconnect, pIter->GCPhys); } break; } #ifdef VBOX_WITH_64_BITS_GUESTS case VMMDevReq_HGCMCall64: case VMMDevReq_HGCMCall32: #else case VMMDevReq_HGCMCall: #endif /* VBOX_WITH_64_BITS_GUESTS */ { if (requestHeader->header.size < sizeof(VMMDevHGCMCall)) { AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n")); requestHeader->header.rc = VERR_INVALID_PARAMETER; } else if (!pVMMDevState->pHGCMDrv) { Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n")); requestHeader->header.rc = VERR_NOT_SUPPORTED; } else { VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)requestHeader; Log(("VMMDevReq_HGCMCall: sizeof (VMMDevHGCMRequest) = %04X\n", sizeof (VMMDevHGCMCall))); Log(("%.*Rhxd\n", requestHeader->header.size, requestHeader)); #ifdef VBOX_WITH_64_BITS_GUESTS bool f64Bits = (requestHeader->header.requestType == VMMDevReq_HGCMCall64); #else bool f64Bits = false; #endif /* VBOX_WITH_64_BITS_GUESTS */ requestHeader->header.rc = vmmdevHGCMCall (pVMMDevState, pHGCMCall, pIter->GCPhys, f64Bits); } break; } default: AssertMsgFailed(("Unknown request type %x during LoadState\n", requestHeader->header.requestType)); LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType)); } } break; default: AssertMsgFailed(("Unknown request type %x during LoadState\n", requestHeader->header.requestType)); LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType)); } } } if (pIter->enmCmdType == VBOXHGCMCMDTYPE_LOADSTATE) { /* Old saved state. Remove the LOADSTATE command. */ /* Write back the request */ PDMDevHlpPhysWrite(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize); RTMemFree(requestHeader); requestHeader = NULL; vmmdevHGCMRemoveCommand (pVMMDevState, pIter); if (pIter->paLinPtrs != NULL) { RTMemFree(pIter->paLinPtrs); } RTMemFree(pIter); } else { if (!fHGCMCalled) { /* HGCM was not called. Return the error to the guest. Guest may try to repeat the call. */ requestHeader->header.rc = VERR_TRY_AGAIN; requestHeader->fu32Flags |= VBOX_HGCM_REQ_DONE; } /* Write back the request */ PDMDevHlpPhysWrite(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize); RTMemFree(requestHeader); requestHeader = NULL; if (!fHGCMCalled) { /* HGCM was not called. Deallocate the current command and then notify guest. */ vmmdevHGCMRemoveCommand (pVMMDevState, pIter); if (pIter->paLinPtrs != NULL) { RTMemFree(pIter->paLinPtrs); } RTMemFree(pIter); VMMDevNotifyGuest (pVMMDevState, VMMDEV_EVENT_HGCM); } } pIter = pNext; } vmmdevHGCMCmdListUnlock (pVMMDevState); } return rc; }