DevVGA_VBVA.cpp revision bd88a03fe4f970611c171f081be318fcd74e85e8
/** @file
* VirtualBox Video Acceleration (VBVA).
*/
/*
* Copyright (C) 2006-2009 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;
* 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_VGA
#include <VBox/VBoxVideo.h>
#ifdef VBOX_WITH_VIDEOHWACCEL
#include <iprt/semaphore.h>
#endif
#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
{
typedef struct _VBVAVIEW
{
} VBVAVIEW;
typedef struct VBVAMOUSESHAPEINFO
{
bool fSet;
bool fVisible;
bool fAlpha;
/* @todo saved state: save and restore VBVACONTEXT */
typedef struct _VBVACONTEXT
{
} VBVACONTEXT;
/* Copies 'cb' bytes from the VBVA ring buffer to the 'pu8Dst'.
* Used for partial records or for records which cross the ring boundary.
*/
{
/* @todo replace the 'if' with an assert. The caller must ensure this condition. */
{
return;
}
if (i32Diff <= 0)
{
/* Chunk will not cross buffer boundary. */
}
else
{
/* Chunk crosses buffer boundary. */
}
/* Advance data offset. */
return;
}
static bool vbvaPartialRead (VBVAPARTIALRECORD *pPartialRecord, uint32_t cbRecord, VBVABUFFER *pVBVA)
{
LOGVBVABUFFER(("vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
if (pPartialRecord->pu8)
{
}
else
{
}
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)
{
}
pPartialRecord->cb = 0;
return false;
}
/* Fetch data from the ring buffer. */
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)
{
LOGVBVABUFFER(("first = %d, free = %d\n",
if (indexRecordFirst == indexRecordFree)
{
/* No records to process. Return without assigning output variables. */
return true;
}
if (pPartialRecord->cb)
{
/* There is a partial read in process. Continue with it. */
LOGVBVABUFFER(("continue partial record cb = %d cbRecord 0x%08X, first = %d, free = %d\n",
{
/* New data has been added to the record. */
{
return false;
}
}
{
/* The record is completed by guest. Return it to the caller. */
pPartialRecord->cb = 0;
/* Advance the record index. */
LOGVBVABUFFER(("partial done ok, data = %d, free = %d\n",
}
return true;
}
/* A new record need to be processed. */
{
/* 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.
*/
{
/* Partial read must be started. */
{
return false;
}
LOGVBVABUFFER(("started partial record cb = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
}
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. */
/* The pointer to data in the ring buffer. */
/* Fetch or point the data. */
if (u32BytesTillBoundary >= cbRecord)
{
/* The command does not cross buffer boundary. Return address in the buffer. */
/* Advance data offset. */
}
else
{
/* The command crosses buffer boundary. Rare case, so not optimized. */
if (!dst)
{
return false;
}
}
}
/* Advance the record index. */
LOGVBVABUFFER(("done ok, data = %d, free = %d\n",
return true;
}
static void vbvaReleaseCmd (VBVAPARTIALRECORD *pPartialRecord, VBVABUFFER *pVBVA, VBVACMDHDR *pHdr, uint32_t cbCmd)
{
{
/* The pointer is inside ring buffer. Must be continuous chunk. */
/* Do nothing. */
}
else
{
/* The pointer is outside. It is then an allocated copy. */
{
pPartialRecord->cb = 0;
}
else
{
}
}
return;
}
static int vbvaFlushProcess (unsigned uScreenId, PVGASTATE pVGAState, VBVAPARTIALRECORD *pPartialRecord, VBVABUFFER *pVBVA)
{
LOGVBVABUFFER(("uScreenId %d, indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
struct {
/* The rectangle that includes all dirty rectangles. */
} dirtyRect;
bool fUpdate = false; /* Whether there were any updates. */
for (;;)
{
/* Fetch the command data. */
{
LogFunc(("unable to fetch command. off32Data = %d, off32Free = %d!!!\n",
/* @todo old code disabled VBVA processing here. */
return VERR_NOT_SUPPORTED;
}
{
/* No more commands yet in the queue. */
break;
}
if (cbCmd != 0)
{
if (!fUpdate)
{
fUpdate = true;
}
/* Updates the rectangle and sends the command to the VRDP server. */
/* These are global coords, relative to the primary screen. */
LOGVBVABUFFER(("cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
/* Collect all rects into one. */
{
/* This is the first rectangle to be added. */
}
else
{
/* Adjust region coordinates. */
{
}
{
}
{
}
{
}
}
}
}
if (fUpdate)
{
{
}
else
{
}
}
return VINF_SUCCESS;
}
{
unsigned uScreenId;
{
if (pVBVA)
{
}
}
/* @todo rc */
return VINF_SUCCESS;
}
{
/* Verify pNewScreen. */
/* @todo */
/* Apply these changes. */
/* @todo process VINF_VGA_RESIZE_IN_PROGRESS? */
return rc;
}
static int vbvaEnable (unsigned uScreenId, PVGASTATE pVGAState, VBVACONTEXT *pCtx, VBVABUFFER *pVBVA, uint32_t u32Offset)
{
/* @todo old code did a UpdateDisplayAll at this place. */
int rc;
{
}
else
{
}
if (RT_SUCCESS (rc))
{
/* pVBVA->hostFlags has been set up by pfnVBVAEnable. */
LogFlowFunc(("u32HostEvents 0x%08X, u32SupportedOrders 0x%08X\n",
}
return rc;
}
{
/* Process any pending orders and empty the VBVA ring buffer. */
{
}
return VINF_SUCCESS;
}
#ifdef DEBUG_sunlover
{
LogFlow(("fSet = %d, fVisible %d, fAlpha %d, @%d,%d %dx%d (%p, %d/%d)\n",
));
}
#endif
static int vbvaUpdateMousePointerShape(PVGASTATE pVGAState, VBVAMOUSESHAPEINFO *pMouseShapeInfo, bool fShape, const uint8_t *pu8Shape)
{
int rc;
LogFlowFunc(("pVGAState %p, pMouseShapeInfo %p, fShape %d, pu8Shape %p\n",
#ifdef DEBUG_sunlover
#endif
{
pu8Shape);
}
else
{
false,
0, 0,
0, 0,
NULL);
}
return rc;
}
static int vbvaMousePointerShape (PVGASTATE pVGAState, VBVACONTEXT *pCtx, const VBVAMOUSEPOINTERSHAPE *pShape, HGSMISIZE cbShape)
{
HGSMISIZE cbPointerData = 0;
if (fShape)
{
}
{
Log(("vbvaMousePointerShape: calculated pointer data size is too big (%d bytes, limit %d)\n",
return VERR_INVALID_PARAMETER;
}
/* Save mouse info it will be used to restore mouse pointer after restoring saved state. */
if (fShape)
{
/* Data related to shape. */
/* Reallocate memory buffer if necessary. */
{
if (pu8Shape)
{
}
}
/* Copy shape bitmaps. */
{
}
}
{
return VERR_NOT_SUPPORTED;
}
int rc = vbvaUpdateMousePointerShape(pVGAState, &pCtx->mouseShapeInfo, fShape, &pShape->au8Data[0]);
return rc;
}
{
/* Check which view contains the buffer. */
if (offBuffer != HGSMIOFFSET_VOID)
{
unsigned uScreenId;
{
if ( pView->u32ViewSize > 0
{
return pView->u32ViewIndex;
}
}
}
return ~0U;
}
#ifdef DEBUG_sunlover
{
{
Log((" view %d o 0x%x s 0x%x m 0x%x\n",
Log((" screen %d @%d,%d s 0x%x l 0x%x %dx%d bpp %d f 0x%x\n",
Log((" VBVA o 0x%x p %p\n",
Log((" PR cb 0x%x p %p\n",
}
}
#endif /* DEBUG_sunlover */
{
if (RT_SUCCESS(rc))
{
/* Save VBVACONTEXT. */
if (!pCtx)
{
AssertFailed();
/* Still write a valid value to the SSM. */
}
else
{
#ifdef DEBUG_sunlover
#endif
{
{
}
}
/* Save mouse pointer shape information. */
{
}
/* Size of some additional data. For future extensions. */
}
}
return rc;
}
{
{
/* Nothing was saved. */
return VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
/* Load VBVACONTEXT. */
if (!pCtx)
{
/* This should not happen. */
AssertFailed();
}
else
{
{
{
}
else
{
if (!pu8)
{
return VERR_NO_MEMORY;
}
}
{
}
else
{
}
}
{
/* Read mouse pointer shape information. */
{
{
return VERR_NO_MEMORY;
}
}
else
{
}
/* Size of some additional data. For future extensions. */
if (cbExtra > 0)
{
}
}
#ifdef DEBUG_sunlover
#endif
}
}
return rc;
}
{
if (pCtx)
{
{
{
}
}
{
}
}
return VINF_SUCCESS;
}
#ifdef VBOX_WITH_VIDEOHWACCEL
{
}
static VBOXVHWACMD* vbvaVHWAHHCommandCreate (PVGASTATE pVGAState, VBOXVHWACMD_TYPE enmCmd, int32_t iDisplay, VBOXVHWACMD_LENGTH cbCmd)
{
if (pHdr)
return pHdr;
}
{
if(!cRefs)
{
}
}
{
}
{
#ifdef DEBUG_misha
else
AssertFailed();
#endif
return 0;
}
{
}
{
if(RT_SUCCESS(rc))
{
/* ensure the cmd is not deleted until we process it */
{
}
else
{
/* the command is completed */
}
if(RT_SUCCESS(rc))
{
}
}
return rc;
}
{
VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(pVGAState, VBOXVHWACMD_TYPE_HH_CONSTRUCT, 0, sizeof(VBOXVHWACMD_HH_CONSTRUCT));
if(pCmd)
{
int rc = VINF_SUCCESS;
do
{
if(RT_SUCCESS(rc))
{
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;
}
if (!RT_SUCCESS(rc))
break;
}
else
break;
++iDisplay;
break;
} while (true);
return rc;
}
return VERR_OUT_OF_RESOURCES;
}
{
/* ensure we have all pending cmds processed and h->g cmds disabled */
if(pCmd)
{
int rc = VINF_SUCCESS;
do
{
if(RT_SUCCESS(rc))
{
if (rc == VERR_NOT_IMPLEMENTED)
rc = VINF_SUCCESS;
}
if (!RT_SUCCESS(rc))
break;
++iDisplay;
break;
} while (true);
return rc;
}
return VERR_OUT_OF_RESOURCES;
}
/* @todo call this also on reset? */
{
if(pCmd)
{
int rc = VINF_SUCCESS;
do
{
if(RT_SUCCESS(rc))
{
if(rc == VERR_NOT_IMPLEMENTED)
{
rc = VINF_SUCCESS;
}
}
if (!RT_SUCCESS(rc))
break;
++iDisplay;
break;
} while (true);
return rc;
}
return VERR_OUT_OF_RESOURCES;
}
{
/* ensure we have no pending commands */
}
#define PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(_pcb) ( (PVGASTATE)((uint8_t *)(_pcb) - RT_OFFSETOF(VGASTATE, IVBVACallbacks)) )
{
int rc;
{
// Assert(0);
{
(void**)&pHostCmd,
VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDEVENT)),
if(RT_SUCCESS(rc))
{
pHostCmd->customOpCode = 0;
}
}
else
{
if(offCmd != HGSMIOFFSET_VOID)
{
(void**)&pHostCmd,
VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDVHWACMDCOMPLETE)),
if(RT_SUCCESS(rc))
{
}
}
else
{
}
}
if(RT_SUCCESS(rc))
{
rc = HGSMIHostCommandProcessAndFreeAsynch(pIns, pHostCmd, (pCmd->Flags & VBOXVHWACMD_FLAG_GH_ASYNCH_IRQ) != 0);
if(RT_SUCCESS(rc))
{
return rc;
}
}
}
else
{
if(pfn)
{
}
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 IO ports to write VRAM offsets of shared memory blocks for commands.
* Read Write
* Host port 0x3b0 to process completed
* Guest port 0x3d0 control value? to process
*
*/
{
#else
#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",
switch (u16ChannelInfo)
{
#ifdef VBOXVDMA
case VBVA_VDMA_CMD:
{
rc = VINF_SUCCESS;
break;
}
case VBVA_VDMA_CTL:
{
rc = VINF_SUCCESS;
break;
}
#endif
case VBVA_QUERY_CONF32:
{
if (cbBuffer < sizeof (VBVACONF32))
{
break;
}
LogFlowFunc(("VBVA_QUERY_CONF32: u32Index %d, u32Value 0x%x\n",
{
}
{
/* @todo a value caclucated from the vram size */
}
else
{
Log(("Unsupported VBVA_QUERY_CONF32 index %d!!!\n",
}
} break;
case VBVA_SET_CONF32:
{
if (cbBuffer < sizeof (VBVACONF32))
{
break;
}
LogFlowFunc(("VBVA_SET_CONF32: u32Index %d, u32Value 0x%x\n",
{
/* do nothing. this is a const. */
}
{
/* do nothing. this is a const. */
}
else
{
Log(("Unsupported VBVA_SET_CONF32 index %d!!!\n",
}
} break;
case VBVA_INFO_VIEW:
{
if (cbBuffer < sizeof (VBVAINFOVIEW))
{
break;
}
/* Guest submits an array of VBVAINFOVIEW structures. */
for (;
cbBuffer >= sizeof (VBVAINFOVIEW);
{
LogFlowFunc(("VBVA_INFO_VIEW: index %d, offset 0x%x, size 0x%x, max screen size 0x%x\n",
/* @todo verify view data. */
{
Log(("View index too large %d!!!\n",
pView->u32ViewIndex));
break;
}
}
} break;
case VBVA_INFO_HEAP:
{
if (cbBuffer < sizeof (VBVAINFOHEAP))
{
break;
}
LogFlowFunc(("VBVA_INFO_HEAP: offset 0x%x, size 0x%x\n",
} break;
case VBVA_FLUSH:
{
{
break;
}
LogFlowFunc(("VBVA_FLUSH: u32Reserved 0x%x\n",
pFlush->u32Reserved));
} break;
case VBVA_INFO_SCREEN:
{
if (cbBuffer < sizeof (VBVAINFOSCREEN))
{
break;
}
LogFlowFunc(("VBVA_INFO_SCREEN: [%d] @%d,%d %dx%d, line 0x%x, BPP %d, flags 0x%x\n",
{
}
else
{
Log(("View index too large %d!!!\n",
pScreen->u32ViewIndex));
}
} break;
case VBVA_ENABLE:
{
if (cbBuffer < sizeof (VBVAENABLE))
{
break;
}
unsigned uScreenId;
#ifdef VBOXWDDM_WITH_VBVA
{
if (cbBuffer < sizeof (VBVAENABLE_EX))
{
break;
}
}
else
#endif
{
}
if (uScreenId == ~0U)
{
break;
}
LogFlowFunc(("VBVA_ENABLE[%d]: u32Flags 0x%x u32Offset 0x%x\n",
{
/* Guest reported offset relative to view. */
if (pVBVA)
{
/* Process any pending orders and empty the VBVA ring buffer. */
}
else
{
Log(("Invalid VBVABUFFER offset 0x%x!!!\n",
}
}
{
}
else
{
Log(("Invalid VBVA_ENABLE flags 0x%x!!!\n",
}
} break;
case VBVA_MOUSE_POINTER_SHAPE:
{
if (cbBuffer < sizeof (VBVAMOUSEPOINTERSHAPE))
{
break;
}
LogFlowFunc(("VBVA_MOUSE_POINTER_SHAPE: i32Result 0x%x, fu32Flags 0x%x, hot spot %d,%d, size %dx%d\n",
} break;
#ifdef VBOX_WITH_VIDEOHWACCEL
case VBVA_VHWA_CMD:
{
} break;
#endif
default:
Log(("Unsupported VBVA guest command %d!!!\n",
break;
}
return rc;
}
{
{
return;
}
#ifdef VBOX_WITH_VIDEOHWACCEL
#endif
if(HgFlags & HGSMIHOSTFLAGS_IRQ)
{
/* this means the IRQ is LEVEL_HIGH, need to reset it */
}
if (pCtx)
{
unsigned uScreenId;
{
}
}
}
{
if (pCtx)
{
if (RT_SUCCESS (rc))
{
{
/* VBVA is not enabled for the first view, so VGA device must do updates. */
}
}
}
return rc;
}
{
pVM,
"VBVA",
0,
sizeof (VBVACONTEXT));
if (RT_SUCCESS (rc))
{
if (RT_SUCCESS (rc))
{
}
}
return rc;
}
{
if (pCtx)
{
}
}