VMMDevHGCM.cpp revision 8021e7f4e7fa0ca88dcd007624b3e07abe14a033
0N/A/* $Id$ */
0N/A/** @file
0N/A * VMMDev - HGCM - Host-Guest Communication Manager Device.
0N/A */
0N/A
0N/A/*
0N/A * Copyright (C) 2006-2007 Sun Microsystems, Inc.
0N/A *
0N/A * This file is part of VirtualBox Open Source Edition (OSE), as
0N/A * available from http://www.virtualbox.org. This file is free software;
0N/A * you can redistribute it and/or modify it under the terms of the GNU
0N/A * General Public License (GPL) as published by the Free Software
0N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
0N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
0N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
0N/A *
0N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
2362N/A * Clara, CA 95054 USA or visit http://www.sun.com if you need
2362N/A * additional information or have any questions.
2362N/A */
1178N/A
1178N/A
1178N/A#define LOG_GROUP LOG_GROUP_DEV_VMM
1178N/A#include <iprt/alloc.h>
1178N/A#include <iprt/asm.h>
0N/A#include <iprt/assert.h>
1178N/A#include <iprt/param.h>
1178N/A#include <iprt/string.h>
1178N/A
1178N/A#include <VBox/err.h>
1178N/A#include <VBox/hgcmsvc.h>
0N/A
0N/A#include <VBox/log.h>
0N/A
0N/A#include "VMMDevHGCM.h"
0N/A
0N/A#ifdef VBOX_WITH_DTRACE
0N/A# include "VBoxDD-dtrace.h"
0N/A#else
0N/A# define VBOXDD_HGCMCALL_ENTER(a,b,c,d) do { } while (0)
0N/A# define VBOXDD_HGCMCALL_COMPLETED_REQ(a,b) do { } while (0)
0N/A# define VBOXDD_HGCMCALL_COMPLETED_EMT(a,b) do { } while (0)
0N/A# define VBOXDD_HGCMCALL_COMPLETED_DONE(a,b,c,d) do { } while (0)
0N/A#endif
0N/A
0N/Atypedef enum _VBOXHGCMCMDTYPE
0N/A{
0N/A VBOXHGCMCMDTYPE_LOADSTATE = 0,
0N/A VBOXHGCMCMDTYPE_CONNECT,
0N/A VBOXHGCMCMDTYPE_DISCONNECT,
0N/A VBOXHGCMCMDTYPE_CALL,
1178N/A VBOXHGCMCMDTYPE_SizeHack = 0x7fffffff
1178N/A} VBOXHGCMCMDTYPE;
1178N/A
1178N/A/* Information about a linear ptr parameter. */
1178N/Atypedef struct _VBOXHGCMLINPTR
0N/A{
0N/A /* Index of the parameter. */
0N/A uint32_t iParm;
1178N/A
1178N/A /* Offset in the first physical page of the region. */
1178N/A uint32_t offFirstPage;
1178N/A
1178N/A /* How many pages. */
1178N/A uint32_t cPages;
1178N/A
1178N/A /* Pointer to array of the GC physical addresses for these pages.
1178N/A * It is assumed that the physical address of the locked resident
1178N/A * guest page does not change.
1178N/A */
1178N/A RTGCPHYS *paPages;
1178N/A
1178N/A} VBOXHGCMLINPTR;
1178N/A
1178N/Astruct VBOXHGCMCMD
1178N/A{
1178N/A /* Active commands, list is protected by critsectHGCMCmdList. */
1178N/A struct VBOXHGCMCMD *pNext;
0N/A struct VBOXHGCMCMD *pPrev;
0N/A
1178N/A /* Size of memory buffer for this command structure, including trailing paHostParms.
1178N/A * This field simplifies loading of saved state.
1178N/A */
1178N/A uint32_t cbCmd;
1178N/A
1178N/A /* The type of the command. */
1178N/A VBOXHGCMCMDTYPE enmCmdType;
1178N/A
0N/A /* Whether the command was cancelled by the guest. */
0N/A bool fCancelled;
0N/A
0N/A /* GC physical address of the guest request. */
0N/A RTGCPHYS GCPhys;
0N/A
0N/A /* Request packet size */
0N/A uint32_t cbSize;
0N/A
0N/A /* Pointer to converted host parameters in case of a Call request.
0N/A * Parameters follow this structure in the same memory block.
0N/A */
0N/A VBOXHGCMSVCPARM *paHostParms;
0N/A
0N/A /* Linear pointer parameters information. */
0N/A int cLinPtrs;
0N/A
0N/A /* How many pages for all linptrs of this command.
0N/A * Only valid if cLinPtrs > 0. This field simplifies loading of saved state.
0N/A */
0N/A int cLinPtrPages;
0N/A
0N/A /* Pointer to descriptions of linear pointers. */
0N/A VBOXHGCMLINPTR *paLinPtrs;
0N/A};
0N/A
0N/Astatic int vmmdevHGCMCmdListLock (VMMDevState *pVMMDevState)
0N/A{
0N/A int rc = RTCritSectEnter (&pVMMDevState->critsectHGCMCmdList);
0N/A AssertRC (rc);
0N/A return rc;
0N/A}
0N/A
0N/Astatic void vmmdevHGCMCmdListUnlock (VMMDevState *pVMMDevState)
0N/A{
0N/A int rc = RTCritSectLeave (&pVMMDevState->critsectHGCMCmdList);
0N/A AssertRC (rc);
0N/A}
0N/A
0N/Astatic int vmmdevHGCMAddCommand (VMMDevState *pVMMDevState, PVBOXHGCMCMD pCmd, RTGCPHYS GCPhys, uint32_t cbSize, VBOXHGCMCMDTYPE enmCmdType)
0N/A{
0N/A /* PPDMDEVINS pDevIns = pVMMDevState->pDevIns; */
0N/A
0N/A int rc = vmmdevHGCMCmdListLock (pVMMDevState);
0N/A
0N/A if (RT_SUCCESS (rc))
0N/A {
0N/A LogFlowFunc(("%p type %d\n", pCmd, enmCmdType));
0N/A
0N/A /* Insert at the head of the list. The vmmdevHGCMLoadStateDone depends on this. */
0N/A pCmd->pNext = pVMMDevState->pHGCMCmdList;
0N/A pCmd->pPrev = NULL;
0N/A
0N/A if (pVMMDevState->pHGCMCmdList)
0N/A {
0N/A pVMMDevState->pHGCMCmdList->pPrev = pCmd;
0N/A }
0N/A
0N/A pVMMDevState->pHGCMCmdList = pCmd;
0N/A
0N/A if (enmCmdType != VBOXHGCMCMDTYPE_LOADSTATE)
0N/A {
0N/A /* Loaded commands already have the right type. */
1178N/A pCmd->enmCmdType = enmCmdType;
0N/A }
0N/A pCmd->GCPhys = GCPhys;
0N/A pCmd->cbSize = cbSize;
0N/A
0N/A /* Automatically enable HGCM events, if there are HGCM commands. */
0N/A if ( enmCmdType == VBOXHGCMCMDTYPE_CONNECT
0N/A || enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT
0N/A || enmCmdType == VBOXHGCMCMDTYPE_CALL)
0N/A {
0N/A Log(("vmmdevHGCMAddCommand: u32HGCMEnabled = %d\n", pVMMDevState->u32HGCMEnabled));
0N/A if (ASMAtomicCmpXchgU32(&pVMMDevState->u32HGCMEnabled, 1, 0))
0N/A {
0N/A VMMDevCtlSetGuestFilterMask (pVMMDevState, VMMDEV_EVENT_HGCM, 0);
0N/A }
0N/A }
1178N/A
1178N/A vmmdevHGCMCmdListUnlock (pVMMDevState);
1178N/A }
1178N/A
1178N/A return rc;
0N/A}
0N/A
0N/Astatic int vmmdevHGCMRemoveCommand (VMMDevState *pVMMDevState, PVBOXHGCMCMD pCmd)
0N/A{
0N/A /* PPDMDEVINS pDevIns = pVMMDevState->pDevIns; */
0N/A
0N/A int rc = vmmdevHGCMCmdListLock (pVMMDevState);
1178N/A
0N/A if (RT_SUCCESS (rc))
0N/A {
0N/A LogFlowFunc(("%p\n", pCmd));
0N/A
0N/A if (pCmd->pNext)
1178N/A {
0N/A pCmd->pNext->pPrev = pCmd->pPrev;
1178N/A }
0N/A else
0N/A {
0N/A /* Tail, do nothing. */
0N/A }
0N/A
0N/A if (pCmd->pPrev)
0N/A {
1178N/A pCmd->pPrev->pNext = pCmd->pNext;
0N/A }
0N/A else
0N/A {
0N/A pVMMDevState->pHGCMCmdList = pCmd->pNext;
0N/A }
1178N/A
0N/A vmmdevHGCMCmdListUnlock (pVMMDevState);
1178N/A }
0N/A
0N/A return rc;
0N/A}
0N/A
0N/A
0N/A/**
0N/A * Find a HGCM command by its physical address.
0N/A *
0N/A * The caller is responsible for taking the command list lock before calling
0N/A * this function.
0N/A *
0N/A * @returns Pointer to the command on success, NULL otherwise.
0N/A * @param pThis The VMMDev instance data.
0N/A * @param GCPhys The physical address of the command we're looking
0N/A * for.
1178N/A */
0N/ADECLINLINE(PVBOXHGCMCMD) vmmdevHGCMFindCommandLocked (VMMDevState *pThis, RTGCPHYS GCPhys)
0N/A{
0N/A for (PVBOXHGCMCMD pCmd = pThis->pHGCMCmdList;
0N/A pCmd;
0N/A pCmd = pCmd->pNext)
0N/A {
0N/A if (pCmd->GCPhys == GCPhys)
0N/A return pCmd;
0N/A }
0N/A return NULL;
0N/A}
0N/A
0N/Astatic int vmmdevHGCMSaveLinPtr (PPDMDEVINS pDevIns,
0N/A uint32_t iParm,
0N/A RTGCPTR GCPtr,
1178N/A uint32_t u32Size,
0N/A uint32_t iLinPtr,
0N/A VBOXHGCMLINPTR *paLinPtrs,
0N/A RTGCPHYS **ppPages)
0N/A{
0N/A int rc = VINF_SUCCESS;
0N/A
0N/A AssertRelease (u32Size > 0);
0N/A
0N/A VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr];
0N/A
0N/A /* Take the offset into the current page also into account! */
0N/A u32Size += GCPtr & PAGE_OFFSET_MASK;
0N/A
0N/A uint32_t cPages = (u32Size + PAGE_SIZE - 1) / PAGE_SIZE;
0N/A
1178N/A Log(("vmmdevHGCMSaveLinPtr: parm %d: %RGv %d = %d pages\n", iParm, GCPtr, u32Size, cPages));
0N/A
0N/A pLinPtr->iParm = iParm;
0N/A pLinPtr->offFirstPage = GCPtr & PAGE_OFFSET_MASK;
0N/A pLinPtr->cPages = cPages;
0N/A pLinPtr->paPages = *ppPages;
0N/A
0N/A *ppPages += cPages;
0N/A
0N/A uint32_t iPage = 0;
0N/A
0N/A GCPtr &= PAGE_BASE_GC_MASK;
0N/A
0N/A /* Gonvert the guest linear pointers of pages to HC addresses. */
0N/A while (iPage < cPages)
0N/A {
1178N/A /* convert */
0N/A RTGCPHYS GCPhys;
0N/A
0N/A rc = PDMDevHlpPhysGCPtr2GCPhys(pDevIns, GCPtr, &GCPhys);
0N/A
0N/A Log(("vmmdevHGCMSaveLinPtr: Page %d: %RGv -> %RGp. %Rrc\n", iPage, GCPtr, GCPhys, rc));
0N/A
0N/A if (RT_FAILURE (rc))
1178N/A {
0N/A break;
0N/A }
0N/A
0N/A /* store */
0N/A pLinPtr->paPages[iPage++] = GCPhys;
1178N/A
0N/A /* next */
1178N/A GCPtr += PAGE_SIZE;
0N/A }
0N/A
0N/A AssertRelease (iPage == cPages);
0N/A
0N/A return rc;
0N/A}
0N/A
1178N/Astatic int vmmdevHGCMWriteLinPtr (PPDMDEVINS pDevIns,
0N/A uint32_t iParm,
0N/A void *pvHost,
0N/A uint32_t u32Size,
0N/A uint32_t iLinPtr,
0N/A VBOXHGCMLINPTR *paLinPtrs)
1178N/A{
0N/A int rc = VINF_SUCCESS;
1178N/A
0N/A VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr];
0N/A
0N/A AssertRelease (u32Size > 0 && iParm == (uint32_t)pLinPtr->iParm);
0N/A
0N/A RTGCPHYS GCPhysDst = pLinPtr->paPages[0] + pLinPtr->offFirstPage;
0N/A uint8_t *pu8Src = (uint8_t *)pvHost;
1178N/A
1178N/A Log(("vmmdevHGCMWriteLinPtr: parm %d: size %d, cPages = %d\n", iParm, u32Size, pLinPtr->cPages));
1178N/A
1178N/A uint32_t iPage = 0;
1178N/A
1178N/A while (iPage < pLinPtr->cPages)
1178N/A {
1178N/A /* copy */
1178N/A uint32_t cbWrite = iPage == 0?
1178N/A PAGE_SIZE - pLinPtr->offFirstPage:
1178N/A PAGE_SIZE;
1178N/A
1178N/A Log(("vmmdevHGCMWriteLinPtr: page %d: dst %RGp, src %p, cbWrite %d\n", iPage, GCPhysDst, pu8Src, cbWrite));
1178N/A
1178N/A iPage++;
1178N/A
1178N/A if (cbWrite >= u32Size)
1178N/A {
1178N/A PDMDevHlpPhysWrite(pDevIns, GCPhysDst, pu8Src, u32Size);
1178N/A u32Size = 0;
1178N/A break;
1178N/A }
1178N/A
1178N/A PDMDevHlpPhysWrite(pDevIns, GCPhysDst, pu8Src, cbWrite);
1178N/A
1178N/A /* next */
1178N/A u32Size -= cbWrite;
1178N/A pu8Src += cbWrite;
1178N/A
1178N/A GCPhysDst = pLinPtr->paPages[iPage];
1178N/A }
1178N/A
0N/A AssertRelease (iPage == pLinPtr->cPages);
0N/A Assert(u32Size == 0);
0N/A
0N/A return rc;
0N/A}
0N/A
1178N/ADECLINLINE(bool) vmmdevHGCMPageListIsContiguous(const HGCMPageListInfo *pPgLst)
0N/A{
1178N/A if (pPgLst->cPages == 1)
1178N/A return true;
1178N/A RTGCPHYS64 Phys = pPgLst->aPages[0] + PAGE_SIZE;
0N/A if (Phys != pPgLst->aPages[1])
1178N/A return false;
0N/A if (pPgLst->cPages > 2)
1178N/A {
0N/A uint32_t iPage = 2;
1178N/A do
0N/A {
1178N/A Phys += PAGE_SIZE;
0N/A if (Phys != pPgLst->aPages[iPage])
1178N/A return false;
1178N/A iPage++;
1178N/A } while (iPage < pPgLst->cPages);
0N/A }
1178N/A return true;
1178N/A}
1178N/A
0N/Astatic int vmmdevHGCMPageListRead(PPDMDEVINSR3 pDevIns, void *pvDst, uint32_t cbDst, const HGCMPageListInfo *pPageListInfo)
1178N/A{
0N/A /*
1178N/A * Try detect contiguous buffers.
0N/A */
0N/A /** @todo We need a flag for indicating this. */
0N/A if (vmmdevHGCMPageListIsContiguous(pPageListInfo))
1178N/A return PDMDevHlpPhysRead(pDevIns, pPageListInfo->aPages[0] | pPageListInfo->offFirstPage, pvDst, cbDst);
0N/A
1178N/A /*
0N/A * Page by page fallback
0N/A */
0N/A int rc = VINF_SUCCESS;
0N/A
0N/A uint8_t *pu8Dst = (uint8_t *)pvDst;
0N/A uint32_t offPage = pPageListInfo->offFirstPage;
0N/A size_t cbRemaining = (size_t)cbDst;
0N/A
1178N/A uint32_t iPage;
1178N/A
0N/A for (iPage = 0; iPage < pPageListInfo->cPages; iPage++)
0N/A {
0N/A if (cbRemaining == 0)
0N/A {
0N/A break;
0N/A }
0N/A
0N/A size_t cbChunk = PAGE_SIZE - offPage;
0N/A
0N/A if (cbChunk > cbRemaining)
1178N/A {
0N/A cbChunk = cbRemaining;
0N/A }
0N/A
0N/A rc = PDMDevHlpPhysRead(pDevIns,
0N/A pPageListInfo->aPages[iPage] + offPage,
0N/A pu8Dst, cbChunk);
0N/A
0N/A AssertRCBreak(rc);
0N/A
1178N/A offPage = 0; /* A next page is read from 0 offset. */
1178N/A cbRemaining -= cbChunk;
1178N/A pu8Dst += cbChunk;
1178N/A }
1178N/A
0N/A return rc;
0N/A}
0N/A
0N/Astatic int vmmdevHGCMPageListWrite(PPDMDEVINSR3 pDevIns, const HGCMPageListInfo *pPageListInfo, const void *pvSrc, uint32_t cbSrc)
0N/A{
0N/A int rc = VINF_SUCCESS;
0N/A
0N/A uint8_t *pu8Src = (uint8_t *)pvSrc;
0N/A uint32_t offPage = pPageListInfo->offFirstPage;
0N/A size_t cbRemaining = (size_t)cbSrc;
0N/A
1178N/A uint32_t iPage;
0N/A for (iPage = 0; iPage < pPageListInfo->cPages; iPage++)
0N/A {
0N/A if (cbRemaining == 0)
0N/A {
0N/A break;
0N/A }
0N/A
0N/A size_t cbChunk = PAGE_SIZE - offPage;
0N/A
0N/A if (cbChunk > cbRemaining)
0N/A {
0N/A cbChunk = cbRemaining;
0N/A }
0N/A
0N/A rc = PDMDevHlpPhysWrite(pDevIns,
0N/A pPageListInfo->aPages[iPage] + offPage,
0N/A pu8Src, cbChunk);
0N/A
0N/A AssertRCBreak(rc);
0N/A
0N/A offPage = 0; /* A next page is read from 0 offset. */
0N/A cbRemaining -= cbChunk;
1178N/A pu8Src += cbChunk;
0N/A }
0N/A
0N/A return rc;
0N/A}
0N/A
0N/Astatic void logRelSavedCmdSizeMismatch (const char *pszFunction, uint32_t cbExpected, uint32_t cbCmdSize)
0N/A{
0N/A LogRel(("Warning: VMMDev %s command length %d (expected %d)\n",
0N/A pszFunction, cbCmdSize, cbExpected));
0N/A}
0N/A
0N/Aint vmmdevHGCMConnect (VMMDevState *pVMMDevState, VMMDevHGCMConnect *pHGCMConnect, RTGCPHYS GCPhys)
0N/A{
0N/A int rc = VINF_SUCCESS;
0N/A
1178N/A uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size;
0N/A
0N/A PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
0N/A
0N/A if (pCmd)
0N/A {
0N/A VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1);
0N/A
0N/A vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMConnect->header.header.size, VBOXHGCMCMDTYPE_CONNECT);
0N/A
0N/A memcpy(pHGCMConnectCopy, pHGCMConnect, pHGCMConnect->header.header.size);
0N/A
0N/A pCmd->cbCmd = cbCmdSize;
0N/A pCmd->paHostParms = NULL;
0N/A pCmd->cLinPtrs = 0;
0N/A pCmd->paLinPtrs = NULL;
1178N/A
0N/A /* Only allow the guest to use existing services! */
0N/A Assert(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing);
0N/A pHGCMConnect->loc.type = VMMDevHGCMLoc_LocalHost_Existing;
0N/A
0N/A rc = pVMMDevState->pHGCMDrv->pfnConnect (pVMMDevState->pHGCMDrv, pCmd, &pHGCMConnectCopy->loc, &pHGCMConnectCopy->u32ClientID);
0N/A }
0N/A else
0N/A {
0N/A rc = VERR_NO_MEMORY;
0N/A }
0N/A
0N/A return rc;
0N/A}
0N/A
0N/Astatic int vmmdevHGCMConnectSaved (VMMDevState *pVMMDevState, VMMDevHGCMConnect *pHGCMConnect, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd)
1178N/A{
0N/A int rc = VINF_SUCCESS;
0N/A
0N/A uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size;
0N/A
0N/A if (pSavedCmd->cbCmd < cbCmdSize)
0N/A {
0N/A logRelSavedCmdSizeMismatch ("HGCMConnect", pSavedCmd->cbCmd, cbCmdSize);
0N/A return VERR_INVALID_PARAMETER;
0N/A }
0N/A
1178N/A VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pSavedCmd+1);
1178N/A
0N/A memcpy(pHGCMConnectCopy, pHGCMConnect, pHGCMConnect->header.header.size);
0N/A
0N/A /* Only allow the guest to use existing services! */
1178N/A Assert(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing);
1178N/A pHGCMConnect->loc.type = VMMDevHGCMLoc_LocalHost_Existing;
1178N/A
1178N/A rc = pVMMDevState->pHGCMDrv->pfnConnect (pVMMDevState->pHGCMDrv, pSavedCmd, &pHGCMConnectCopy->loc, &pHGCMConnectCopy->u32ClientID);
1178N/A if (RT_SUCCESS (rc))
1178N/A {
1178N/A *pfHGCMCalled = true;
1178N/A }
1178N/A
1178N/A return rc;
1178N/A}
1178N/A
1178N/Aint vmmdevHGCMDisconnect (VMMDevState *pVMMDevState, VMMDevHGCMDisconnect *pHGCMDisconnect, RTGCPHYS GCPhys)
1178N/A{
1178N/A int rc = VINF_SUCCESS;
0N/A
1178N/A uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD);
0N/A
1178N/A PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
0N/A
0N/A if (pCmd)
0N/A {
0N/A vmmdevHGCMAddCommand (pVMMDevState, pCmd, GCPhys, pHGCMDisconnect->header.header.size, VBOXHGCMCMDTYPE_DISCONNECT);
0N/A
0N/A pCmd->cbCmd = cbCmdSize;
0N/A pCmd->paHostParms = NULL;
0N/A pCmd->cLinPtrs = 0;
0N/A pCmd->paLinPtrs = NULL;
0N/A
0N/A rc = pVMMDevState->pHGCMDrv->pfnDisconnect (pVMMDevState->pHGCMDrv, pCmd, pHGCMDisconnect->u32ClientID);
0N/A }
0N/A else
0N/A {
0N/A rc = VERR_NO_MEMORY;
0N/A }
0N/A
0N/A return rc;
0N/A}
0N/A
0N/Astatic int vmmdevHGCMDisconnectSaved (VMMDevState *pVMMDevState, VMMDevHGCMDisconnect *pHGCMDisconnect, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd)
1178N/A{
1178N/A int rc = VINF_SUCCESS;
1178N/A
1178N/A uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD);
0N/A
0N/A if (pSavedCmd->cbCmd < cbCmdSize)
0N/A {
0N/A logRelSavedCmdSizeMismatch ("HGCMConnect", pSavedCmd->cbCmd, cbCmdSize);
0N/A return VERR_INVALID_PARAMETER;
1178N/A }
0N/A
0N/A rc = pVMMDevState->pHGCMDrv->pfnDisconnect (pVMMDevState->pHGCMDrv, pSavedCmd, pHGCMDisconnect->u32ClientID);
0N/A if (RT_SUCCESS (rc))
0N/A {
0N/A *pfHGCMCalled = true;
0N/A }
0N/A
0N/A return rc;
0N/A}
0N/A
0N/Aint vmmdevHGCMCall (VMMDevState *pVMMDevState, VMMDevHGCMCall *pHGCMCall, uint32_t cbHGCMCall, RTGCPHYS GCPhys, bool f64Bits)
0N/A{
0N/A int rc = VINF_SUCCESS;
0N/A
0N/A Log(("vmmdevHGCMCall: client id = %d, function = %d, %s bit\n", pHGCMCall->u32ClientID, pHGCMCall->u32Function, f64Bits? "64": "32"));
1178N/A
0N/A /* Compute size and allocate memory block to hold:
0N/A * struct VBOXHGCMCMD
0N/A * VBOXHGCMSVCPARM[cParms]
0N/A * memory buffers for pointer parameters.
0N/A */
0N/A
0N/A uint32_t cParms = pHGCMCall->cParms;
0N/A
0N/A Log(("vmmdevHGCMCall: cParms = %d\n", cParms));
0N/A
0N/A /*
0N/A * Compute size of required memory buffer.
0N/A */
0N/A
0N/A uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + cParms * sizeof (VBOXHGCMSVCPARM);
1178N/A
0N/A uint32_t i;
0N/A
0N/A uint32_t cLinPtrs = 0;
0N/A uint32_t cLinPtrPages = 0;
0N/A
0N/A if (f64Bits)
0N/A {
0N/A#ifdef VBOX_WITH_64_BITS_GUESTS
0N/A HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
0N/A#else
0N/A HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1178N/A AssertFailed (); /* This code should not be called in this case */
1178N/A#endif /* VBOX_WITH_64_BITS_GUESTS */
1178N/A
1178N/A /* Look for pointer parameters, which require a host buffer. */
1178N/A for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
1178N/A {
1178N/A switch (pGuestParm->type)
1178N/A {
1178N/A case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1178N/A case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1178N/A case VMMDevHGCMParmType_LinAddr: /* In & Out */
1178N/A {
1178N/A if (pGuestParm->u.Pointer.size > 0)
1178N/A {
1178N/A /* Only pointers with some actual data are counted. */
0N/A cbCmdSize += pGuestParm->u.Pointer.size;
1178N/A
0N/A cLinPtrs++;
1178N/A /* Take the offset into the current page also into account! */
0N/A cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
0N/A + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
0N/A }
0N/A
0N/A Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
0N/A } break;
0N/A
0N/A case VMMDevHGCMParmType_PageList:
0N/A {
1178N/A cbCmdSize += pGuestParm->u.PageList.size;
0N/A Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
0N/A } break;
0N/A
0N/A case VMMDevHGCMParmType_32bit:
0N/A case VMMDevHGCMParmType_64bit:
0N/A {
1178N/A } break;
0N/A
0N/A default:
0N/A case VMMDevHGCMParmType_PhysAddr:
0N/A {
1178N/A AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
1178N/A rc = VERR_INVALID_PARAMETER;
1178N/A break;
0N/A }
0N/A }
1178N/A }
0N/A }
0N/A else
0N/A {
0N/A#ifdef VBOX_WITH_64_BITS_GUESTS
0N/A HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
0N/A#else
0N/A HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
0N/A#endif /* VBOX_WITH_64_BITS_GUESTS */
0N/A
0N/A /* Look for pointer parameters, which require a host buffer. */
1178N/A for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
1178N/A {
1178N/A switch (pGuestParm->type)
1178N/A {
0N/A case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
0N/A case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
0N/A case VMMDevHGCMParmType_LinAddr: /* In & Out */
0N/A {
0N/A if (pGuestParm->u.Pointer.size > 0)
0N/A {
0N/A /* Only pointers with some actual data are counted. */
0N/A cbCmdSize += pGuestParm->u.Pointer.size;
0N/A
0N/A cLinPtrs++;
0N/A /* Take the offset into the current page also into account! */
0N/A cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
0N/A + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
0N/A }
0N/A
0N/A Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
0N/A } break;
0N/A
0N/A case VMMDevHGCMParmType_PageList:
1178N/A {
0N/A cbCmdSize += pGuestParm->u.PageList.size;
1178N/A Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
1178N/A } break;
1178N/A
0N/A case VMMDevHGCMParmType_32bit:
1178N/A case VMMDevHGCMParmType_64bit:
0N/A {
1178N/A } break;
0N/A
0N/A default:
0N/A {
1178N/A AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
0N/A rc = VERR_INVALID_PARAMETER;
0N/A break;
0N/A }
1178N/A }
0N/A }
0N/A }
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;
}
VBOXDD_HGCMCALL_ENTER(pCmd, pHGCMCall->u32Function, pHGCMCall->u32ClientID, cbCmdSize);
/* 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_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;
}
case VMMDevHGCMParmType_PageList:
{
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
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
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
{
/* Copy pages to the pcBuf[size]. */
rc = vmmdevHGCMPageListRead(pVMMDevState->pDevIns, pcBuf, size, pPageListInfo);
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
pHostParm->u.pointer.addr = pcBuf;
pcBuf += size;
}
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
break;
}
/* just to shut up gcc */
default:
AssertFailed();
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_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;
}
case VMMDevHGCMParmType_PageList:
{
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
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
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
{
/* Copy pages to the pcBuf[size]. */
rc = vmmdevHGCMPageListRead(pVMMDevState->pDevIns, pcBuf, size, pPageListInfo);
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
pHostParm->u.pointer.addr = pcBuf;
pcBuf += size;
}
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
break;
}
/* just to shut up gcc */
default:
AssertFailed();
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, uint32_t cbHGCMCall, 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_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;
}
case VMMDevHGCMParmType_PageList:
{
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
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
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
{
/* Copy pages to the pcBuf[size]. */
rc = vmmdevHGCMPageListRead(pVMMDevState->pDevIns, pu8Buf, size, pPageListInfo);
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
pHostParm->u.pointer.addr = pu8Buf;
pu8Buf += size;
}
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
break;
}
/* just to shut up gcc */
default:
AssertFailed();
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_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;
}
case VMMDevHGCMParmType_PageList:
{
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
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
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
{
/* Copy pages to the pcBuf[size]. */
rc = vmmdevHGCMPageListRead(pVMMDevState->pDevIns, pu8Buf, size, pPageListInfo);
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
pHostParm->u.pointer.addr = pu8Buf;
pu8Buf += size;
}
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
break;
}
/* just to shut up gcc */
default:
AssertFailed();
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;
}
/**
* VMMDevReq_HGCMCancel worker.
*
* @thread EMT
*/
int vmmdevHGCMCancel (VMMDevState *pVMMDevState, VMMDevHGCMCancel *pHGCMCancel, RTGCPHYS GCPhys)
{
NOREF(pHGCMCancel);
int rc = vmmdevHGCMCancel2(pVMMDevState, GCPhys);
return rc == VERR_NOT_FOUND ? VERR_INVALID_PARAMETER : rc;
}
/**
* VMMDevReq_HGCMCancel2 worker.
*
* @retval VINF_SUCCESS on success.
* @retval VERR_NOT_FOUND if the request was not found.
* @retval VERR_INVALID_PARAMETER if the request address is invalid.
*
* @param pThis The VMMDev instance data.
* @param GCPhys The address of the request that should be cancelled.
*
* @thread EMT
*/
int vmmdevHGCMCancel2 (VMMDevState *pThis, RTGCPHYS GCPhys)
{
if ( GCPhys == 0
|| GCPhys == NIL_RTGCPHYS
|| GCPhys == NIL_RTGCPHYS32)
{
Log(("vmmdevHGCMCancel2: GCPhys=%#x\n", GCPhys));
return VERR_INVALID_PARAMETER;
}
/*
* Locate the command and cancel it while under the protection of
* the lock. hgcmCompletedWorker makes assumptions about this.
*/
int rc = vmmdevHGCMCmdListLock (pThis);
AssertRCReturn(rc, rc);
PVBOXHGCMCMD pCmd = vmmdevHGCMFindCommandLocked (pThis, GCPhys);
if (pCmd)
{
pCmd->fCancelled = true;
Log(("vmmdevHGCMCancel2: Cancelled pCmd=%p / GCPhys=%#x\n", pCmd, GCPhys));
}
else
rc = VERR_NOT_FOUND;
vmmdevHGCMCmdListUnlock (pThis);
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);
#ifdef VBOX_WITH_DTRACE
uint32_t idFunction = 0;
uint32_t idClient = 0;
#endif
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;
}
/*
* The cancellation protocol requires us to remove the command here
* and then check the flag. Cancelled commands must not be written
* back to guest memory.
*/
VBOXDD_HGCMCALL_COMPLETED_EMT(pCmd, result);
vmmdevHGCMRemoveCommand (pVMMDevState, pCmd);
if (pCmd->fCancelled)
{
LogFlowFunc(("A cancelled command %p\n", 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);
if (pHeader == NULL)
{
LogRel(("VMMDev: Failed to allocate %u bytes for HGCM request completion!!!\n", pCmd->cbSize));
/* Free it. The command have to be excluded from list of active commands anyway. */
RTMemFree (pCmd);
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. */
PDMCritSectEnter(&pVMMDevState->CritSect, VERR_SEM_BUSY);
PDMCritSectLeave(&pVMMDevState->CritSect);
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_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;
case VMMDevHGCMParmType_PageList:
{
uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
if (size > 0)
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
{
/* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
rc = vmmdevHGCMPageListWrite(pVMMDevState->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
}
else
rc = VINF_SUCCESS;
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
} break;
default:
{
/* This indicates that the guest request memory was corrupted. */
AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
}
}
}
# ifdef VBOX_WITH_DTRACE
idFunction = pHGCMCall->u32Function;
idClient = pHGCMCall->u32ClientID;
# endif
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_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;
case VMMDevHGCMParmType_PageList:
{
uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
if (size > 0)
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
{
/* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
rc = vmmdevHGCMPageListWrite(pVMMDevState->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
}
else
rc = VINF_SUCCESS;
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
} break;
default:
{
/* This indicates that the guest request memory was corrupted. */
AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
}
}
}
# ifdef VBOX_WITH_DTRACE
idFunction = pHGCMCall->u32Function;
idClient = pHGCMCall->u32ClientID;
# endif
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_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;
case VMMDevHGCMParmType_PageList:
{
uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
uint32_t size = pGuestParm->u.PageList.size;
/* Check that the page list info is within the request. */
if ( cbHGCMCall < sizeof (HGCMPageListInfo)
|| pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
{
rc = VERR_INVALID_PARAMETER;
break;
}
/* At least the structure is within. */
HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
if ( pPageListInfo->cPages == 0
|| cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
{
rc = VERR_INVALID_PARAMETER;
break;
}
if (size > 0)
{
if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
{
/* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
rc = vmmdevHGCMPageListWrite(pVMMDevState->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
}
else
rc = VINF_SUCCESS;
}
Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
} break;
default:
{
/* This indicates that the guest request memory was corrupted. */
AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
}
}
}
# ifdef VBOX_WITH_DTRACE
idFunction = pHGCMCall->u32Function;
idClient = pHGCMCall->u32ClientID;
# endif
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);
/* 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);
VBOXDD_HGCMCALL_COMPLETED_DONE(pCmd, idFunction, idClient, result);
return;
}
DECLCALLBACK(void) hgcmCompleted (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
{
VMMDevState *pVMMDevState = PDMIHGCMPORT_2_VMMDEVSTATE(pInterface);
VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
/** @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 /*cMillies*/,
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, requestHeader->header.size, 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, requestHeader->header.size, 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;
}