Index: /trunk/src/VBox/Devices/Graphics/DevVGA_VBVA.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA_VBVA.cpp	(revision 22622)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA_VBVA.cpp	(revision 22622)
@@ -0,0 +1,1413 @@
+/*
+ * VirtualBox Video Acceleration (VBVA).
+ */
+
+/*
+ * Copyright (C) 2006-2009 Sun Microsystems, Inc.
+ *
+ * Sun Microsystems, Inc. confidential
+ * All rights reserved
+ */
+
+#define LOG_GROUP LOG_GROUP_DEV_VGA
+#include <VBox/pdmifs.h>
+#include <VBox/pdmdev.h>
+#include <VBox/pgm.h>
+#include <VBox/ssm.h>
+#include <VBox/VMMDev.h>
+#include <VBox/VBoxVideo.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#ifdef VBOX_WITH_VIDEOHWACCEL
+#include <iprt/semaphore.h>
+#endif
+
+/** The default amount of VRAM. */
+#define VGA_VRAM_DEFAULT    (_4M)
+/** The maximum amount of VRAM. */
+#define VGA_VRAM_MAX        (128 * _1M)
+/** The minimum amount of VRAM. */
+#define VGA_VRAM_MIN        (_1M)
+
+#include "DevVGA.h"
+
+/* A very detailed logging. */
+#if 0 // def DEBUG_sunlover
+#define LOGVBVABUFFER(a) LogFlow(a)
+#else
+#define LOGVBVABUFFER(a) do {} while(0)
+#endif
+
+typedef struct _VBVAPARTIALRECORD
+{
+    uint8_t *pu8;
+    uint32_t cb;
+} VBVAPARTIALRECORD;
+
+typedef struct _VBVAVIEW
+{
+    VBVAINFOVIEW    view;
+    VBVAINFOSCREEN  screen;
+    VBVABUFFER     *pVBVA;
+    uint32_t        u32VBVAOffset;
+    VBVAPARTIALRECORD partialRecord;
+} VBVAVIEW;
+
+/* @todo saved state: save and restore VBVACONTEXT */
+typedef struct _VBVACONTEXT
+{
+    uint32_t cViews;
+    VBVAVIEW aViews[64 /* @todo SchemaDefs::MaxGuestMonitors*/];
+} VBVACONTEXT;
+
+/* Copies 'cb' bytes from the VBVA ring buffer to the 'pu8Dst'.
+ * Used for partial records or for records which cross the ring boundary.
+ */
+static void vbvaFetchBytes (VBVABUFFER *pVBVA, uint8_t *pu8Dst, uint32_t cb)
+{
+    /* @todo replace the 'if' with an assert. The caller must ensure this condition. */
+    if (cb >= pVBVA->cbData)
+    {
+        AssertMsgFailed (("cb = 0x%08X, ring buffer size 0x%08X", cb, pVBVA->cbData));
+        return;
+    }
+
+    const uint32_t u32BytesTillBoundary = pVBVA->cbData - pVBVA->off32Data;
+    const uint8_t  *src                 = &pVBVA->au8Data[pVBVA->off32Data];
+    const int32_t i32Diff               = cb - u32BytesTillBoundary;
+
+    if (i32Diff <= 0)
+    {
+        /* Chunk will not cross buffer boundary. */
+        memcpy (pu8Dst, src, cb);
+    }
+    else
+    {
+        /* Chunk crosses buffer boundary. */
+        memcpy (pu8Dst, src, u32BytesTillBoundary);
+        memcpy (pu8Dst + u32BytesTillBoundary, &pVBVA->au8Data[0], i32Diff);
+    }
+
+    /* Advance data offset. */
+    pVBVA->off32Data = (pVBVA->off32Data + cb) % pVBVA->cbData;
+
+    return;
+}
+
+
+static bool vbvaPartialRead (VBVAPARTIALRECORD *pPartialRecord, uint32_t cbRecord, VBVABUFFER *pVBVA)
+{
+    uint8_t *pu8New;
+
+    LOGVBVABUFFER(("vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
+                   pPartialRecord->pu8, pPartialRecord->cb, cbRecord));
+
+    if (pPartialRecord->pu8)
+    {
+        Assert (pPartialRecord->cb);
+        pu8New = (uint8_t *)RTMemRealloc (pPartialRecord->pu8, cbRecord);
+    }
+    else
+    {
+        Assert (!pPartialRecord->cb);
+        pu8New = (uint8_t *)RTMemAlloc (cbRecord);
+    }
+
+    if (!pu8New)
+    {
+        /* Memory allocation failed, fail the function. */
+        Log(("vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
+             cbRecord));
+
+        if (pPartialRecord->pu8)
+        {
+            RTMemFree (pPartialRecord->pu8);
+        }
+
+        pPartialRecord->pu8 = NULL;
+        pPartialRecord->cb = 0;
+
+        return false;
+    }
+
+    /* Fetch data from the ring buffer. */
+    vbvaFetchBytes (pVBVA, pu8New + pPartialRecord->cb, cbRecord - pPartialRecord->cb);
+
+    pPartialRecord->pu8 = pu8New;
+    pPartialRecord->cb = cbRecord;
+
+    return true;
+}
+
+/* For contiguous chunks just return the address in the buffer.
+ * For crossing boundary - allocate a buffer from heap.
+ */
+static bool vbvaFetchCmd (VBVAPARTIALRECORD *pPartialRecord, VBVABUFFER *pVBVA, VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
+{
+    uint32_t indexRecordFirst = pVBVA->indexRecordFirst;
+    uint32_t indexRecordFree = pVBVA->indexRecordFree;
+
+    LOGVBVABUFFER(("first = %d, free = %d\n",
+                   indexRecordFirst, indexRecordFree));
+
+    if (indexRecordFirst == indexRecordFree)
+    {
+        /* No records to process. Return without assigning output variables. */
+        return true;
+    }
+
+    VBVARECORD *pRecord = &pVBVA->aRecords[indexRecordFirst];
+
+    LOGVBVABUFFER(("cbRecord = 0x%08X\n", pRecord->cbRecord));
+
+    uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
+
+    if (pPartialRecord->cb)
+    {
+        /* There is a partial read in process. Continue with it. */
+        Assert (pPartialRecord->pu8);
+
+        LOGVBVABUFFER(("continue partial record cb = %d cbRecord 0x%08X, first = %d, free = %d\n",
+                      pPartialRecord->cb, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
+
+        if (cbRecord > pPartialRecord->cb)
+        {
+            /* New data has been added to the record. */
+            if (!vbvaPartialRead (pPartialRecord, cbRecord, pVBVA))
+            {
+                return false;
+            }
+        }
+
+        if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
+        {
+            /* The record is completed by guest. Return it to the caller. */
+            *ppHdr = (VBVACMDHDR *)pPartialRecord->pu8;
+            *pcbCmd = pPartialRecord->cb;
+
+            pPartialRecord->pu8 = NULL;
+            pPartialRecord->cb = 0;
+
+            /* Advance the record index. */
+            pVBVA->indexRecordFirst = (indexRecordFirst + 1) % RT_ELEMENTS(pVBVA->aRecords);
+
+            LOGVBVABUFFER(("partial done ok, data = %d, free = %d\n",
+                          pVBVA->off32Data, pVBVA->off32Free));
+        }
+
+        return true;
+    }
+
+    /* A new record need to be processed. */
+    if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
+    {
+        /* Current record is being written by guest. '=' is important here,
+         * because the guest will do a FLUSH at this condition.
+         * This partual record is too large for the ring buffer and must
+         * be accumulated in an allocated buffer.
+         */
+        if (cbRecord >= pVBVA->cbData - pVBVA->cbPartialWriteThreshold)
+        {
+            /* Partial read must be started. */
+            if (!vbvaPartialRead (pPartialRecord, cbRecord, pVBVA))
+            {
+                return false;
+            }
+
+            LOGVBVABUFFER(("started partial record cb = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
+                          pPartialRecord->cb, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
+        }
+
+        return true;
+    }
+
+    /* Current record is complete. If it is not empty, process it. */
+    if (cbRecord)
+    {
+        /* The size of largest contiguos chunk in the ring biffer. */
+        uint32_t u32BytesTillBoundary = pVBVA->cbData - pVBVA->off32Data;
+
+        /* The pointer to data in the ring buffer. */
+        uint8_t *src = &pVBVA->au8Data[pVBVA->off32Data];
+
+        /* Fetch or point the data. */
+        if (u32BytesTillBoundary >= cbRecord)
+        {
+            /* The command does not cross buffer boundary. Return address in the buffer. */
+            *ppHdr = (VBVACMDHDR *)src;
+
+            /* Advance data offset. */
+            pVBVA->off32Data = (pVBVA->off32Data + cbRecord) % pVBVA->cbData;
+        }
+        else
+        {
+            /* The command crosses buffer boundary. Rare case, so not optimized. */
+            uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
+
+            if (!dst)
+            {
+                LogFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
+                pVBVA->off32Data = (pVBVA->off32Data + cbRecord) % pVBVA->cbData;
+                return false;
+            }
+
+            vbvaFetchBytes (pVBVA, dst, cbRecord);
+
+            *ppHdr = (VBVACMDHDR *)dst;
+
+            LOGVBVABUFFER(("Allocated from heap %p\n", dst));
+        }
+    }
+
+    *pcbCmd = cbRecord;
+
+    /* Advance the record index. */
+    pVBVA->indexRecordFirst = (indexRecordFirst + 1) % RT_ELEMENTS(pVBVA->aRecords);
+
+    LOGVBVABUFFER(("done ok, data = %d, free = %d\n",
+                  pVBVA->off32Data, pVBVA->off32Free));
+
+    return true;
+}
+
+static void vbvaReleaseCmd (VBVAPARTIALRECORD *pPartialRecord, VBVABUFFER *pVBVA, VBVACMDHDR *pHdr, uint32_t cbCmd)
+{
+    uint8_t *au8RingBuffer = &pVBVA->au8Data[0];
+
+    if (   (uint8_t *)pHdr >= au8RingBuffer
+        && (uint8_t *)pHdr < &au8RingBuffer[pVBVA->cbData])
+    {
+        /* The pointer is inside ring buffer. Must be continuous chunk. */
+        Assert (pVBVA->cbData - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
+
+        /* Do nothing. */
+
+        Assert (!pPartialRecord->pu8 && pPartialRecord->cb == 0);
+    }
+    else
+    {
+        /* The pointer is outside. It is then an allocated copy. */
+        LOGVBVABUFFER(("Free heap %p\n", pHdr));
+
+        if ((uint8_t *)pHdr == pPartialRecord->pu8)
+        {
+            pPartialRecord->pu8 = NULL;
+            pPartialRecord->cb = 0;
+        }
+        else
+        {
+            Assert (!pPartialRecord->pu8 && pPartialRecord->cb == 0);
+        }
+
+        RTMemFree (pHdr);
+    }
+
+    return;
+}
+
+static int vbvaFlushProcess (unsigned uScreenId, PVGASTATE pVGAState, VBVAPARTIALRECORD *pPartialRecord, VBVABUFFER *pVBVA)
+{
+    LOGVBVABUFFER(("uScreenId %d, indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
+                  uScreenId, pVBVA->indexRecordFirst, pVBVA->indexRecordFree, pVBVA->off32Data, pVBVA->off32Free));
+    struct {
+        /* The rectangle that includes all dirty rectangles. */
+        int32_t xLeft;
+        int32_t xRight;
+        int32_t yTop;
+        int32_t yBottom;
+    } dirtyRect;
+    memset(&dirtyRect, 0, sizeof(dirtyRect));
+    
+    bool fUpdate = false; /* Whether there were any updates. */
+
+    for (;;)
+    {
+        VBVACMDHDR *phdr = NULL;
+        uint32_t cbCmd = ~0;
+
+        /* Fetch the command data. */
+        if (!vbvaFetchCmd (pPartialRecord, pVBVA, &phdr, &cbCmd))
+        {
+            LogFunc(("unable to fetch command. off32Data = %d, off32Free = %d!!!\n",
+                  pVBVA->off32Data, pVBVA->off32Free));
+
+            /* @todo old code disabled VBVA processing here. */
+            return VERR_NOT_SUPPORTED;
+        }
+
+        if (cbCmd == uint32_t(~0))
+        {
+            /* No more commands yet in the queue. */
+            break;
+        }
+
+        if (cbCmd != 0)
+        {
+            if (!fUpdate)
+            {
+                pVGAState->pDrv->pfnVBVAUpdateBegin (pVGAState->pDrv, uScreenId);
+                fUpdate = true;
+            }
+
+            /* Updates the rectangle and sends the command to the VRDP server. */
+            pVGAState->pDrv->pfnVBVAUpdateProcess (pVGAState->pDrv, uScreenId, phdr, cbCmd);
+
+            int32_t xRight  = phdr->x + phdr->w;
+            int32_t yBottom = phdr->y + phdr->h;
+
+            /* These are global coords, relative to the primary screen. */
+
+            LOGVBVABUFFER(("cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
+                           cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
+
+            /* Collect all rects into one. */
+            if (dirtyRect.xRight == 0)
+            {
+                /* This is the first rectangle to be added. */
+                dirtyRect.xLeft   = phdr->x;
+                dirtyRect.yTop    = phdr->y;
+                dirtyRect.xRight  = xRight;
+                dirtyRect.yBottom = yBottom;
+            }
+            else
+            {
+                /* Adjust region coordinates. */
+                if (dirtyRect.xLeft > phdr->x)
+                {
+                    dirtyRect.xLeft = phdr->x;
+                }
+
+                if (dirtyRect.yTop > phdr->y)
+                {
+                    dirtyRect.yTop = phdr->y;
+                }
+
+                if (dirtyRect.xRight < xRight)
+                {
+                    dirtyRect.xRight = xRight;
+                }
+
+                if (dirtyRect.yBottom < yBottom)
+                {
+                    dirtyRect.yBottom = yBottom;
+                }
+            }
+        }
+
+        vbvaReleaseCmd (pPartialRecord, pVBVA, phdr, cbCmd);
+    }
+
+    if (fUpdate)
+    {
+        if(dirtyRect.xRight)
+        {
+            pVGAState->pDrv->pfnVBVAUpdateEnd (pVGAState->pDrv, uScreenId, dirtyRect.xLeft, dirtyRect.yTop,
+                                               dirtyRect.xRight - dirtyRect.xLeft, dirtyRect.yBottom - dirtyRect.yTop);
+        }
+        else
+        {
+            pVGAState->pDrv->pfnVBVAUpdateEnd (pVGAState->pDrv, uScreenId, 0, 0, 0, 0);
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int vbvaFlush (PVGASTATE pVGAState, VBVACONTEXT *pCtx)
+{
+    unsigned uScreenId;
+
+    for (uScreenId = 0; uScreenId < pCtx->cViews; uScreenId++)
+    {
+        VBVAPARTIALRECORD *pPartialRecord = &pCtx->aViews[uScreenId].partialRecord;
+        VBVABUFFER *pVBVA = pCtx->aViews[uScreenId].pVBVA;
+
+        if (pVBVA)
+        {
+            vbvaFlushProcess (uScreenId, pVGAState, pPartialRecord, pVBVA);
+        }
+    }
+
+    /* @todo rc */
+    return VINF_SUCCESS;
+}
+
+static int vbvaResize (PVGASTATE pVGAState, VBVAVIEW *pView, const VBVAINFOSCREEN *pNewScreen)
+{
+    /* Verify pNewScreen. */
+    /* @todo */
+
+    /* Apply these changes. */
+    pView->screen = *pNewScreen;
+
+    uint8_t *pu8VRAM = pVGAState->vram_ptrR3 + pView->view.u32ViewOffset;
+
+    int rc = pVGAState->pDrv->pfnVBVAResize (pVGAState->pDrv, &pView->view, &pView->screen, pu8VRAM);
+    
+    /* @todo process VINF_VGA_RESIZE_IN_PROGRESS? */
+    
+    return rc;
+}
+
+static int vbvaEnable (unsigned uScreenId, PVGASTATE pVGAState, VBVACONTEXT *pCtx, VBVABUFFER *pVBVA, uint32_t u32Offset)
+{
+    /* Process any pending orders and empty the VBVA ring buffer. */
+    vbvaFlush (pVGAState, pCtx);
+
+    /* @todo old code did a UpdateDisplayAll at this place. */
+
+    int rc;
+
+    if (pVGAState->pDrv->pfnVBVAEnable)
+    {
+        rc = pVGAState->pDrv->pfnVBVAEnable (pVGAState->pDrv, uScreenId);
+    }
+    else
+    {
+        rc = VERR_NOT_SUPPORTED;
+    }
+
+    if (RT_SUCCESS (rc))
+    {
+        /* Setup flags. */
+        pVBVA->u32HostEvents = VBVA_F_MODE_ENABLED |
+                               VBVA_F_MODE_VRDP |
+                               VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
+
+        pVBVA->u32SupportedOrders = ~0;
+
+        pCtx->aViews[uScreenId].partialRecord.pu8 = NULL;
+        pCtx->aViews[uScreenId].partialRecord.cb = 0;
+
+        pCtx->aViews[uScreenId].pVBVA = pVBVA;
+        pCtx->aViews[uScreenId].u32VBVAOffset = u32Offset;
+    }
+
+    return rc;
+}
+
+static int vbvaDisable (unsigned uScreenId, PVGASTATE pVGAState, VBVACONTEXT *pCtx)
+{
+    /* Process any pending orders and empty the VBVA ring buffer. */
+    vbvaFlush (pVGAState, pCtx);
+
+    VBVAVIEW *pView = &pCtx->aViews[uScreenId];
+
+    if (pView->pVBVA)
+    {
+        pView->pVBVA->u32HostEvents = 0;
+        pView->pVBVA->u32SupportedOrders = 0;
+
+        pView->partialRecord.pu8 = NULL;
+        pView->partialRecord.cb = 0;
+
+        pView->pVBVA = NULL;
+        pView->u32VBVAOffset = HGSMIOFFSET_VOID;
+    }
+
+    pVGAState->pDrv->pfnVBVADisable (pVGAState->pDrv, uScreenId);
+    return VINF_SUCCESS;
+}
+
+static int vbvaMousePointerShape (PVGASTATE pVGAState, VBVACONTEXT *pCtx, const VBVAMOUSEPOINTERSHAPE *pShape, HGSMISIZE cbShape)
+{
+    bool fVisible = (pShape->fu32Flags & VBOX_MOUSE_POINTER_VISIBLE) != 0;
+    bool fAlpha =   (pShape->fu32Flags & VBOX_MOUSE_POINTER_ALPHA) != 0;
+    bool fShape =   (pShape->fu32Flags & VBOX_MOUSE_POINTER_SHAPE) != 0;
+
+    HGSMISIZE cbPointerData = 0;
+
+    if (fShape)
+    {
+         cbPointerData = ((((pShape->u32Width + 7) / 8) * pShape->u32Height + 3) & ~3)
+                         + pShape->u32Width * 4 * pShape->u32Height;
+    }
+    
+    if (cbPointerData > cbShape - RT_OFFSETOF(VBVAMOUSEPOINTERSHAPE, au8Data))
+    {
+        Log(("vbvaMousePointerShape: calculated pointer data size is too big (%d bytes, limit %d)\n",
+              cbPointerData, cbShape - RT_OFFSETOF(VBVAMOUSEPOINTERSHAPE, au8Data)));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    if (pVGAState->pDrv->pfnVBVAMousePointerShape == NULL)
+    {
+        return VERR_NOT_SUPPORTED;
+    }
+
+    int rc;
+
+    if (fShape)
+    {
+        rc = pVGAState->pDrv->pfnVBVAMousePointerShape (pVGAState->pDrv,
+                                                        fVisible,
+                                                        fAlpha,
+                                                        pShape->u32HotX, pShape->u32HotY,
+                                                        pShape->u32Width, pShape->u32Height,
+                                                        &pShape->au8Data[0]);
+    }
+    else
+    {
+        rc = pVGAState->pDrv->pfnVBVAMousePointerShape (pVGAState->pDrv,
+                                                        fVisible,
+                                                        false,
+                                                        0, 0,
+                                                        0, 0,
+                                                        NULL);
+    }
+
+    return rc;
+}
+
+static unsigned vbvaViewFromOffset (PHGSMIINSTANCE pIns, VBVACONTEXT *pCtx, const void *pvBuffer)
+{
+    /* Check which view contains the buffer. */
+    HGSMIOFFSET offBuffer = HGSMIPointerToOffsetHost (pIns, pvBuffer);
+
+    if (offBuffer != HGSMIOFFSET_VOID)
+    {
+        unsigned uScreenId;
+
+        for (uScreenId = 0; uScreenId < pCtx->cViews; uScreenId++)
+        {
+            VBVAINFOVIEW *pView = &pCtx->aViews[uScreenId].view;
+
+            if (   pView->u32ViewSize > 0
+                && pView->u32ViewOffset <= offBuffer
+                && offBuffer <= pView->u32ViewOffset + pView->u32ViewSize - 1)
+            {
+                return pView->u32ViewIndex;
+            }
+        }
+    }
+
+    return ~0;
+}
+
+#ifdef DEBUG_sunlover
+static void dumpctx(const VBVACONTEXT *pCtx)
+{
+    Log(("VBVACONTEXT dump: cViews %d\n", pCtx->cViews));
+
+    uint32_t iView;
+    for (iView = 0; iView < pCtx->cViews; iView++)
+    {
+        const VBVAVIEW *pView = &pCtx->aViews[iView];
+
+        Log(("                  view %d o 0x%x s 0x%x m 0x%x\n",
+              pView->view.u32ViewIndex,
+              pView->view.u32ViewOffset,
+              pView->view.u32ViewSize,
+              pView->view.u32MaxScreenSize));
+
+        Log(("                  screen %d @%d,%d s 0x%x l 0x%x %dx%d bpp %d f 0x%x\n",
+              pView->screen.u32ViewIndex,
+              pView->screen.i32OriginX,
+              pView->screen.i32OriginY,
+              pView->screen.u32StartOffset,
+              pView->screen.u32LineSize,
+              pView->screen.u32Width,
+              pView->screen.u32Height,
+              pView->screen.u16BitsPerPixel, 
+              pView->screen.u16Flags));
+
+        Log(("                  VBVA o 0x%x p %p\n",
+              pView->u32VBVAOffset,
+              pView->pVBVA));
+
+        Log(("                  PR cb 0x%x p %p\n",
+              pView->partialRecord.cb,
+              pView->partialRecord.pu8));
+    }
+}
+#endif /* DEBUG_sunlover */
+
+int vboxVBVASaveStateExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PVGASTATE pVGAState = PDMINS_2_DATA(pDevIns, PVGASTATE);
+    PHGSMIINSTANCE pIns = pVGAState->pHGSMI;
+
+    int rc = HGSMIHostSaveStateExec (pIns, pSSM);
+    if (RT_SUCCESS(rc))
+    {
+        /* Save VBVACONTEXT. */
+        VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pIns);
+
+        if (!pCtx)
+        {
+            AssertFailed();
+
+            /* Still write a valid value to the SSM. */
+            rc = SSMR3PutU32 (pSSM, 0);
+            AssertRCReturn(rc, rc);
+        }
+        else
+        {
+#ifdef DEBUG_sunlover
+            dumpctx(pCtx);
+#endif
+
+            rc = SSMR3PutU32 (pSSM, pCtx->cViews);
+            AssertRCReturn(rc, rc);
+
+            uint32_t iView;
+            for (iView = 0; iView < pCtx->cViews; iView++)
+            {
+                VBVAVIEW *pView = &pCtx->aViews[iView];
+
+                rc = SSMR3PutU32 (pSSM, pView->view.u32ViewIndex);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->view.u32ViewOffset);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->view.u32ViewSize);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->view.u32MaxScreenSize);
+                AssertRCReturn(rc, rc);
+
+                rc = SSMR3PutU32 (pSSM, pView->screen.u32ViewIndex);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutS32 (pSSM, pView->screen.i32OriginX);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutS32 (pSSM, pView->screen.i32OriginY);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->screen.u32StartOffset);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->screen.u32LineSize);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->screen.u32Width);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU32 (pSSM, pView->screen.u32Height);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU16 (pSSM, pView->screen.u16BitsPerPixel);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3PutU16 (pSSM, pView->screen.u16Flags);
+                AssertRCReturn(rc, rc);
+
+                rc = SSMR3PutU32 (pSSM, pView->u32VBVAOffset);
+                AssertRCReturn(rc, rc);
+
+                rc = SSMR3PutU32 (pSSM, pView->partialRecord.cb);
+                AssertRCReturn(rc, rc);
+
+                if (pView->partialRecord.cb > 0)
+                {
+                    rc = SSMR3PutMem (pSSM, pView->partialRecord.pu8, pView->partialRecord.cb);
+                    AssertRCReturn(rc, rc);
+                }
+            }
+        }
+    }
+
+    return rc;
+}
+
+int vboxVBVALoadStateExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t u32Version)
+{
+    if (u32Version < 3)
+    {
+        /* Nothing was saved. */
+        return VINF_SUCCESS;
+    }
+
+    PVGASTATE pVGAState = PDMINS_2_DATA(pDevIns, PVGASTATE);
+    PHGSMIINSTANCE pIns = pVGAState->pHGSMI;
+    int rc = HGSMIHostLoadStateExec (pIns, pSSM, u32Version);
+    if (RT_SUCCESS(rc))
+    {
+        /* Load VBVACONTEXT. */
+        VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pIns);
+
+        if (!pCtx)
+        {
+            /* This should not happen. */
+            AssertFailed();
+            rc = VERR_INVALID_PARAMETER;
+        }
+        else
+        {
+            uint32_t cViews = 0;
+            rc = SSMR3GetU32 (pSSM, &cViews);
+            AssertRCReturn(rc, rc);
+
+            uint32_t iView;
+            for (iView = 0; iView < cViews; iView++)
+            {
+                VBVAVIEW *pView = &pCtx->aViews[iView];
+
+                rc = SSMR3GetU32 (pSSM, &pView->view.u32ViewIndex);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->view.u32ViewOffset);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->view.u32ViewSize);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->view.u32MaxScreenSize);
+                AssertRCReturn(rc, rc);
+
+                rc = SSMR3GetU32 (pSSM, &pView->screen.u32ViewIndex);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetS32 (pSSM, &pView->screen.i32OriginX);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetS32 (pSSM, &pView->screen.i32OriginY);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->screen.u32StartOffset);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->screen.u32LineSize);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->screen.u32Width);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU32 (pSSM, &pView->screen.u32Height);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU16 (pSSM, &pView->screen.u16BitsPerPixel);
+                AssertRCReturn(rc, rc);
+                rc = SSMR3GetU16 (pSSM, &pView->screen.u16Flags);
+                AssertRCReturn(rc, rc);
+
+                rc = SSMR3GetU32 (pSSM, &pView->u32VBVAOffset);
+                AssertRCReturn(rc, rc);
+
+                rc = SSMR3GetU32 (pSSM, &pView->partialRecord.cb);
+                AssertRCReturn(rc, rc);
+
+                if (pView->partialRecord.cb == 0)
+                {
+                    pView->partialRecord.pu8 = NULL;
+                }
+                else
+                {
+                    Assert(pView->partialRecord.pu8 == NULL); /* Should be it. */
+
+                    uint8_t *pu8 = (uint8_t *)RTMemAlloc (pView->partialRecord.cb);
+
+                    if (!pu8)
+                    {
+                        return VERR_NO_MEMORY;
+                    }
+
+                    pView->partialRecord.pu8 = pu8;
+
+                    rc = SSMR3GetMem (pSSM, pView->partialRecord.pu8, pView->partialRecord.cb);
+                    AssertRCReturn(rc, rc);
+                }
+
+                if (pView->u32VBVAOffset == HGSMIOFFSET_VOID)
+                {
+                    pView->pVBVA = NULL;
+                }
+                else
+                {
+                    pView->pVBVA = (VBVABUFFER *)HGSMIOffsetToPointerHost (pIns, pView->u32VBVAOffset);
+                }
+            }
+
+            pCtx->cViews = iView;
+            LogFlowFunc(("%d views loaded\n", pCtx->cViews));
+
+#ifdef DEBUG_sunlover
+            dumpctx(pCtx);
+#endif
+        }
+    }
+
+    return rc;
+}
+
+int vboxVBVALoadStateDone (PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PVGASTATE pVGAState = PDMINS_2_DATA(pDevIns, PVGASTATE);
+    VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pVGAState->pHGSMI);
+
+    if (pCtx)
+    {
+        uint32_t iView;
+        for (iView = 0; iView < pCtx->cViews; iView++)
+        {
+            VBVAVIEW *pView = &pCtx->aViews[iView];
+
+            if (pView->pVBVA)
+            {
+                vbvaResize (pVGAState, pView, &pView->screen);
+            }
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+static VBOXVHWACMD* vbvaVHWAHHCommandCreate (PVGASTATE pVGAState, VBOXVHWACMD_TYPE enmCmd, VBOXVHWACMD_LENGTH cbCmd)
+{
+    VBOXVHWACMD* pHdr = (VBOXVHWACMD*)RTMemAlloc(cbCmd + VBOXVHWACMD_HEADSIZE());
+    Assert(pHdr);
+    if (pHdr)
+    {
+        memset(pHdr, 0, sizeof(VBOXVHWACMD));
+        pHdr->cRefs = 1;
+        pHdr->rc = VERR_GENERAL_FAILURE;
+        pHdr->enmCmd = enmCmd;
+        pHdr->Flags = VBOXVHWACMD_FLAG_HH_CMD;
+    }
+
+    return pHdr;
+}
+
+DECLINLINE(void) vbvaVHWAHHCommandRelease (VBOXVHWACMD* pCmd)
+{
+    uint32_t cRefs = ASMAtomicDecU32(&pCmd->cRefs);
+    if(!cRefs)
+    {
+        RTMemFree(pCmd);
+    }
+}
+
+DECLINLINE(void) vbvaVHWAHHCommandRetain (VBOXVHWACMD* pCmd)
+{
+    ASMAtomicIncU32(&pCmd->cRefs);
+}
+
+static unsigned vbvaVHWAHandleCommand (PVGASTATE pVGAState, VBVACONTEXT *pCtx, PVBOXVHWACMD pCmd)
+{
+    pVGAState->pDrv->pfnVHWACommandProcess(pVGAState->pDrv, pCmd);
+    return 0;
+}
+
+static int vbvaVHWAHHCommandPost(PVGASTATE pVGAState, VBOXVHWACMD* pCmd)
+{
+    RTSEMEVENT hComplEvent;
+    int rc = RTSemEventCreate(&hComplEvent);
+    Assert(RT_SUCCESS(rc));
+    if(RT_SUCCESS(rc))
+    {
+        /* ensure the cmd is not deleted until we process it */
+        vbvaVHWAHHCommandRetain (pCmd);
+        pCmd->GuestVBVAReserved1 = (uint64_t)hComplEvent;
+        vbvaVHWAHandleCommand(pVGAState, NULL, pCmd);
+        if((ASMAtomicReadU32((volatile uint32_t *)&pCmd->Flags)  & VBOXVHWACMD_FLAG_HG_ASYNCH) != 0)
+        {
+            rc = RTSemEventWaitNoResume(hComplEvent, RT_INDEFINITE_WAIT);
+        }
+        else
+        {
+            /* the command is completed */
+        }
+
+        Assert(RT_SUCCESS(rc));
+        if(RT_SUCCESS(rc))
+        {
+            RTSemEventDestroy(hComplEvent);
+        }
+        vbvaVHWAHHCommandRelease(pCmd);
+    }
+    return rc;
+}
+
+int vbvaVHWAConstruct (PVGASTATE pVGAState)
+{
+    VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(pVGAState, VBOXVHWACMD_TYPE_HH_CONSTRUCT, sizeof(VBOXVHWACMD_HH_CONSTRUCT));
+    Assert(pCmd);
+    if(pCmd)
+    {
+        VBOXVHWACMD_HH_CONSTRUCT * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_HH_CONSTRUCT);
+        memset(pBody, 0, sizeof(VBOXVHWACMD_HH_CONSTRUCT));
+
+        PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
+        PVM pVM = PDMDevHlpGetVM(pDevIns);
+
+        pBody->pVM = pVM;
+
+        int rc = vbvaVHWAHHCommandPost(pVGAState, pCmd);
+        Assert(RT_SUCCESS(rc));
+        if(RT_SUCCESS(rc))
+        {
+            rc = pCmd->rc;
+            Assert(RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED);
+            vbvaVHWAHHCommandRelease(pCmd);
+            if(rc == VERR_NOT_IMPLEMENTED)
+            {
+                /* @todo: set some flag in pVGAState indicating VHWA is not supported */
+                /* VERR_NOT_IMPLEMENTED is not a failure, we just do not support it */
+                rc = VINF_SUCCESS;
+            }
+        }
+        return rc;
+    }
+    return VERR_OUT_OF_RESOURCES;
+}
+
+/* @todo call this also on reset? */
+int vbvaVHWADisable (PVGASTATE pVGAState)
+{
+    VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(pVGAState, VBOXVHWACMD_TYPE_DISABLE, 0);
+    Assert(pCmd);
+    if(pCmd)
+    {
+        int rc = vbvaVHWAHHCommandPost(pVGAState, pCmd);
+        Assert(RT_SUCCESS(rc));
+        if(RT_SUCCESS(rc))
+        {
+            rc = pCmd->rc;
+            Assert(RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED);
+            vbvaVHWAHHCommandRelease(pCmd);
+            if(rc == VERR_NOT_IMPLEMENTED)
+            {
+                rc = VINF_SUCCESS;
+            }
+        }
+        return rc;
+    }
+    return VERR_OUT_OF_RESOURCES;
+}
+
+int vboxVBVASaveStatePrep (PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    /* ensure we have no pending commands */
+    return vbvaVHWADisable(PDMINS_2_DATA(pDevIns, PVGASTATE));
+}
+
+#define PPDMDDISPLAYVBVACALLBACKS_2_PVGASTATE(_pcb) ( (PVGASTATE)((uint8_t *)(_pcb) - RT_OFFSETOF(VGASTATE, VBVACallbacks)) )
+
+int vbvaVHWACommandCompleteAsynch(PPDMDDISPLAYVBVACALLBACKS pInterface, PVBOXVHWACMD pCmd)
+{
+    int rc;
+    if((pCmd->Flags & VBOXVHWACMD_FLAG_HH_CMD) == 0)
+    {
+        PVGASTATE pVGAState = PPDMDDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
+        PHGSMIINSTANCE pIns = pVGAState->pHGSMI;
+        VBVAHOSTCMD *pHostCmd;
+    //    Assert(0);
+
+        int32_t iDisplay = pCmd->iDisplay;
+        Assert(pCmd->Flags & VBOXVHWACMD_FLAG_HG_ASYNCH);
+        if(pCmd->Flags & VBOXVHWACMD_FLAG_GH_ASYNCH_EVENT)
+        {
+            rc = HGSMIHostCommandAlloc (pIns,
+                                          (void**)&pHostCmd,
+                                          VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDEVENT)),
+                                          HGSMI_CH_VBVA,
+                                          VBVAHG_EVENT);
+            Assert(RT_SUCCESS(rc));
+            if(RT_SUCCESS(rc))
+            {
+                memset(pHostCmd, 0 , VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDEVENT)));
+                pHostCmd->iDstID = pCmd->iDisplay;
+                pHostCmd->customOpCode = 0;
+                VBVAHOSTCMDEVENT *pBody = VBVAHOSTCMD_BODY(pHostCmd, VBVAHOSTCMDEVENT);
+                pBody->pEvent = pCmd->GuestVBVAReserved1;
+            }
+        }
+        else
+        {
+            HGSMIOFFSET offCmd = HGSMIPointerToOffsetHost (pIns, pCmd);
+            Assert(offCmd != HGSMIOFFSET_VOID);
+            if(offCmd != HGSMIOFFSET_VOID)
+            {
+                rc = HGSMIHostCommandAlloc (pIns,
+                                          (void**)&pHostCmd,
+                                          VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDVHWACMDCOMPLETE)),
+                                          HGSMI_CH_VBVA,
+                                          VBVAHG_DISPLAY_CUSTOM);
+                Assert(RT_SUCCESS(rc));
+                if(RT_SUCCESS(rc))
+                {
+                    memset(pHostCmd, 0 , VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDVHWACMDCOMPLETE)));
+                    pHostCmd->iDstID = pCmd->iDisplay;
+                    pHostCmd->customOpCode = VBVAHG_DCUSTOM_VHWA_CMDCOMPLETE;
+                    VBVAHOSTCMDVHWACMDCOMPLETE *pBody = VBVAHOSTCMD_BODY(pHostCmd, VBVAHOSTCMDVHWACMDCOMPLETE);
+                    pBody->offCmd = offCmd;
+                }
+            }
+            else
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+        }
+
+        if(RT_SUCCESS(rc))
+        {
+            rc = HGSMIHostCommandProcessAndFreeAsynch(pIns, pHostCmd, (pCmd->Flags & VBOXVHWACMD_FLAG_GH_ASYNCH_IRQ) != 0);
+            Assert(RT_SUCCESS(rc));
+            if(RT_SUCCESS(rc))
+            {
+                return rc;
+            }
+            HGSMIHostCommandFree (pIns, pHostCmd);
+        }
+    }
+    else
+    {
+        if(pCmd->GuestVBVAReserved1)
+        {
+            RTSEMEVENT hComplEvent = (RTSEMEVENT)pCmd->GuestVBVAReserved1;
+            RTSemEventSignal(hComplEvent);
+        }
+        rc = VINF_SUCCESS;
+    }
+    return rc;
+}
+#endif
+
+
+/*
+ *
+ * New VBVA uses a new interface id: #define VBE_DISPI_ID_VBOX_VIDEO         0xBE01
+ *
+ * VBVA uses two 32 bits PCI IO ports to write VRAM offsets of shared memory blocks for commands.
+ * IOPortBase+                     Read                        Write
+ * VGA_PORT_OFF_HGSMI_GUEST        control value?              to process
+ * VGA_PORT_OFF_HGSMI_HOST         to process                  completed
+ *
+ */
+
+static DECLCALLBACK(void) vbvaNotifyGuest (void *pvCallback)
+{
+#if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
+    PVGASTATE pVGAState = (PVGASTATE)pvCallback;
+    PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
+    PDMCritSectEnter(&pVGAState->lock, VERR_SEM_BUSY);
+    HGSMISetHostGuestFlags(pVGAState->pHGSMI, HGSMIHOSTFLAGS_IRQ);
+    PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
+    PDMCritSectLeave(&pVGAState->lock);
+#else
+    NOREF(pvCallback);
+    /* Do nothing. Later the VMMDev/VGA IRQ can be used for the notification. */
+#endif
+}
+
+/* The guest submitted a buffer. @todo Verify all guest data. */
+static DECLCALLBACK(int) vbvaChannelHandler (void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
+{
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pvHandler %p, u16ChannelInfo %d, pvBuffer %p, cbBuffer %u\n",
+            pvHandler, u16ChannelInfo, pvBuffer, cbBuffer));
+
+    PVGASTATE pVGAState = (PVGASTATE)pvHandler;
+    PHGSMIINSTANCE pIns = pVGAState->pHGSMI;
+    VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pIns);
+
+    switch (u16ChannelInfo)
+    {
+        case VBVA_QUERY_CONF32:
+        {
+            if (cbBuffer < sizeof (VBVACONF32))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVACONF32 *pConf32 = (VBVACONF32 *)pvBuffer;
+            LogFlowFunc(("VBVA_QUERY_CONF32: u32Index %d, u32Value 0x%x\n",
+                         pConf32->u32Index, pConf32->u32Value));
+
+            if (pConf32->u32Index == VBOX_VBVA_CONF32_MONITOR_COUNT)
+            {
+                pConf32->u32Value = pCtx->cViews;
+            }
+            else if (pConf32->u32Index == VBOX_VBVA_CONF32_HOST_HEAP_SIZE)
+            {
+                /* @todo a value caclucated from the vram size */
+                pConf32->u32Value = 64*_1K;
+            }
+            else
+            {
+                Log(("Unsupported VBVA_QUERY_CONF32 index %d!!!\n",
+                     pConf32->u32Index));
+                rc = VERR_INVALID_PARAMETER;
+            }
+        } break;
+
+        case VBVA_SET_CONF32:
+        {
+            if (cbBuffer < sizeof (VBVACONF32))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVACONF32 *pConf32 = (VBVACONF32 *)pvBuffer;
+            LogFlowFunc(("VBVA_SET_CONF32: u32Index %d, u32Value 0x%x\n",
+                         pConf32->u32Index, pConf32->u32Value));
+
+            if (pConf32->u32Index == VBOX_VBVA_CONF32_MONITOR_COUNT)
+            {
+                /* do nothing. this is a const. */
+            }
+            else if (pConf32->u32Index == VBOX_VBVA_CONF32_HOST_HEAP_SIZE)
+            {
+                /* do nothing. this is a const. */
+            }
+            else
+            {
+                Log(("Unsupported VBVA_SET_CONF32 index %d!!!\n",
+                     pConf32->u32Index));
+                rc = VERR_INVALID_PARAMETER;
+            }
+        } break;
+
+        case VBVA_INFO_VIEW:
+        {
+            if (cbBuffer < sizeof (VBVAINFOVIEW))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            /* Guest submits an array of VBVAINFOVIEW structures. */
+            VBVAINFOVIEW *pView = (VBVAINFOVIEW *)pvBuffer;
+            
+            for (;
+                 cbBuffer >= sizeof (VBVAINFOVIEW);
+                 pView++, cbBuffer -= sizeof (VBVAINFOVIEW))
+            {
+                LogFlowFunc(("VBVA_INFO_VIEW: index %d, offset 0x%x, size 0x%x, max screen size 0x%x\n",
+                             pView->u32ViewIndex, pView->u32ViewOffset, pView->u32ViewSize, pView->u32MaxScreenSize));
+
+                /* @todo verify view data. */
+                if (pView->u32ViewIndex >= pCtx->cViews)
+                {
+                    Log(("View index too large %d!!!\n",
+                         pView->u32ViewIndex));
+                    rc = VERR_INVALID_PARAMETER;
+                    break;
+                }
+
+                pCtx->aViews[pView->u32ViewIndex].view = *pView;
+            }
+        } break;
+
+        case VBVA_INFO_HEAP:
+        {
+            if (cbBuffer < sizeof (VBVAINFOHEAP))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVAINFOHEAP *pHeap = (VBVAINFOHEAP *)pvBuffer;
+            LogFlowFunc(("VBVA_INFO_HEAP: offset 0x%x, size 0x%x\n",
+                         pHeap->u32HeapOffset, pHeap->u32HeapSize));
+
+            rc = HGSMISetupHostHeap (pIns, pHeap->u32HeapOffset, pHeap->u32HeapSize);
+        } break;
+
+        case VBVA_FLUSH:
+        {
+            if (cbBuffer < sizeof (VBVAFLUSH))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVAFLUSH *pFlush = (VBVAFLUSH *)pvBuffer;
+            LogFlowFunc(("VBVA_FLUSH: u32Reserved 0x%x\n",
+                         pFlush->u32Reserved));
+
+            rc = vbvaFlush (pVGAState, pCtx);
+        } break;
+
+        case VBVA_INFO_SCREEN:
+        {
+            if (cbBuffer < sizeof (VBVAINFOSCREEN))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVAINFOSCREEN *pScreen = (VBVAINFOSCREEN *)pvBuffer;
+            LogFlowFunc(("VBVA_INFO_SCREEN: [%d] @%d,%d %dx%d, line 0x%x, BPP %d, flags 0x%x\n",
+                         pScreen->u32ViewIndex, pScreen->i32OriginX, pScreen->i32OriginY,
+                         pScreen->u32Width, pScreen->u32Height,
+                         pScreen->u32LineSize,  pScreen->u16BitsPerPixel, pScreen->u16Flags));
+
+            if (pScreen->u32ViewIndex < RT_ELEMENTS (pCtx->aViews))
+            {
+                vbvaResize (pVGAState, &pCtx->aViews[pScreen->u32ViewIndex], pScreen);
+            }
+            else
+            {
+                Log(("View index too large %d!!!\n",
+                     pScreen->u32ViewIndex));
+                rc = VERR_INVALID_PARAMETER;
+            }
+        } break;
+
+        case VBVA_ENABLE:
+        {
+            if (cbBuffer < sizeof (VBVAENABLE))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            unsigned uScreenId = vbvaViewFromOffset (pIns, pCtx, pvBuffer);
+
+            if (uScreenId == ~0)
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVAENABLE *pEnable = (VBVAENABLE *)pvBuffer;
+            LogFlowFunc(("VBVA_ENABLE[%d]: u32Flags 0x%x u32Offset 0x%x\n",
+                         uScreenId, pEnable->u32Flags, pEnable->u32Offset));
+
+            if ((pEnable->u32Flags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_ENABLE)
+            {
+                /* Guest reported offset relative to view. */
+                uint32_t u32Offset = pEnable->u32Offset + pCtx->aViews[uScreenId].view.u32ViewOffset;
+
+                VBVABUFFER *pVBVA = (VBVABUFFER *)HGSMIOffsetToPointerHost (pIns, u32Offset);
+
+                if (pVBVA)
+                {
+                    rc = vbvaEnable (uScreenId, pVGAState, pCtx, pVBVA, u32Offset);
+                }
+                else
+                {
+                    Log(("Invalid VBVABUFFER offset 0x%x!!!\n",
+                         pEnable->u32Offset));
+                    rc = VERR_INVALID_PARAMETER;
+                }
+            }
+            else if ((pEnable->u32Flags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_DISABLE)
+            {
+                rc = vbvaDisable (uScreenId, pVGAState, pCtx);
+            }
+            else
+            {
+                Log(("Invalid VBVA_ENABLE flags 0x%x!!!\n",
+                     pEnable->u32Flags));
+                rc = VERR_INVALID_PARAMETER;
+            }
+
+            pEnable->i32Result = rc;
+        } break;
+
+        case VBVA_MOUSE_POINTER_SHAPE:
+        {
+            if (cbBuffer < sizeof (VBVAMOUSEPOINTERSHAPE))
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            VBVAMOUSEPOINTERSHAPE *pShape = (VBVAMOUSEPOINTERSHAPE *)pvBuffer;
+
+            LogFlowFunc(("VBVA_MOUSE_POINTER_SHAPE: i32Result 0x%x, fu32Flags 0x%x, hot spot %d,%d, size %dx%d\n",
+                         pShape->i32Result,
+                         pShape->fu32Flags,
+                         pShape->u32HotX,
+                         pShape->u32HotY,
+                         pShape->u32Width,
+                         pShape->u32Height));
+
+            rc = vbvaMousePointerShape (pVGAState, pCtx, pShape, cbBuffer);
+
+            pShape->i32Result = rc;
+        } break;
+
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+        case VBVA_VHWA_CMD:
+        {
+            rc = vbvaVHWAHandleCommand (pVGAState, pCtx, (PVBOXVHWACMD)pvBuffer);
+        } break;
+#endif
+
+        default:
+            Log(("Unsupported VBVA guest command %d!!!\n",
+                 u16ChannelInfo));
+            break;
+    }
+
+    return rc;
+}
+
+void VBVAReset (PVGASTATE pVGAState)
+{
+    if (!pVGAState || !pVGAState->pHGSMI)
+    {
+        return;
+    }
+
+    VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pVGAState->pHGSMI);
+
+    if (pCtx)
+    {
+        vbvaFlush (pVGAState, pCtx);
+
+        unsigned uScreenId;
+
+        for (uScreenId = 0; uScreenId < pCtx->cViews; uScreenId++)
+        {
+            vbvaDisable (uScreenId, pVGAState, pCtx);
+        }
+    }
+}
+
+int VBVAUpdateDisplay (PVGASTATE pVGAState)
+{
+    int rc = VERR_NOT_SUPPORTED; /* Assuming that the VGA device will have to do updates. */
+
+    VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pVGAState->pHGSMI);
+
+    if (pCtx)
+    {
+        rc = vbvaFlush (pVGAState, pCtx);
+
+        if (RT_SUCCESS (rc))
+        {
+            if (!pCtx->aViews[0].pVBVA)
+            {
+                /* VBVA is not enabled for the first view, so VGA device must do updates. */
+                rc = VERR_NOT_SUPPORTED;
+            }
+        }
+    }
+
+    return rc;
+}
+
+static HGSMICHANNELHANDLER sOldChannelHandler;
+
+int VBVAInit (PVGASTATE pVGAState)
+{
+    PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
+
+    PVM pVM = PDMDevHlpGetVM(pDevIns);
+
+    int rc = HGSMICreate (&pVGAState->pHGSMI,
+                          pVM,
+                          "VBVA",
+                          0,
+                          pVGAState->vram_ptrR3,
+                          pVGAState->vram_size,
+                          vbvaNotifyGuest,
+                          pVGAState,
+                          sizeof (VBVACONTEXT));
+
+     if (RT_SUCCESS (rc))
+     {
+         rc = HGSMIHostChannelRegister (pVGAState->pHGSMI,
+                                    HGSMI_CH_VBVA,
+                                    vbvaChannelHandler,
+                                    pVGAState,
+                                    &sOldChannelHandler);
+         if (RT_SUCCESS (rc))
+         {
+             VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pVGAState->pHGSMI);
+             pCtx->cViews = pVGAState->cMonitors;
+         }
+     }
+
+     return rc;
+
+}
+
+void VBVADestroy (PVGASTATE pVGAState)
+{
+    HGSMIDestroy (pVGAState->pHGSMI);
+    pVGAState->pHGSMI = NULL;
+}
