/* $Id: VMMDevHGCM.cpp $ */
/** @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 <iprt/alloc.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
#include <iprt/param.h>
#include <iprt/string.h>

#include <VBox/err.h>
#include <VBox/hgcmsvc.h>

#include <VBox/log.h>

#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;
            }
        }

        /*
         * 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_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);

/** @todo no longer necessary to forward to EMT, but it might be more
 *        efficient...? */
    /* Not safe to execute asynchroneously; forward to EMT */
    int rc = VMR3ReqCallEx(PDMDevHlpGetVM(pVMMDevState->pDevIns), VMCPUID_ANY, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID,
                           (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;
}
