DevVGA_VDMA.cpp revision 8bc59e8938a0c337714600cf0ed8124b702439c2
/** @file
* Video DMA (VDMA) support.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* 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.
*/
#include <VBox/VBoxVideo.h>
#include <iprt/semaphore.h>
#include "DevVGA.h"
#include "HGSMI/SHGSMIHost.h"
#include <VBox/VBoxVideo3D.h>
#include <VBox/VBoxVideoHost3D.h>
#ifdef DEBUG_misha
# define VBOXVDBG_MEMCACHE_DISABLE
#endif
#ifndef VBOXVDBG_MEMCACHE_DISABLE
# include <iprt/memcache.h>
#endif
#ifdef DEBUG_misha
#define WARN_BP() do { AssertFailed(); } while (0)
#else
#define WARN_BP() do { } while (0)
#endif
WARN_BP(); \
} while (0)
#define VBOXVDMATHREAD_STATE_TERMINATED 0
#define VBOXVDMATHREAD_STATE_CREATED 1
#define VBOXVDMATHREAD_STATE_TERMINATING 2
typedef struct VBOXVDMATHREAD
{
/* state transformations:
*
* submitter | processor
*
* LISTENING ---> PROCESSING
*
* */
#define VBVAEXHOSTCONTEXT_STATE_LISTENING 0
#define VBVAEXHOSTCONTEXT_STATE_PROCESSING 1
#define VBVAEXHOSTCONTEXT_ESTATE_DISABLED -1
#define VBVAEXHOSTCONTEXT_ESTATE_PAUSED 0
#define VBVAEXHOSTCONTEXT_ESTATE_ENABLED 1
typedef struct VBVAEXHOSTCONTEXT
{
volatile int32_t i32EnableState;
/* critical section for accessing ctl lists */
#ifndef VBOXVDBG_MEMCACHE_DISABLE
#endif
typedef enum
{
struct VBVAEXHOSTCTL;
typedef DECLCALLBACKPTR(void, PFNVBVAEXHOSTCTL_COMPLETE)(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvComplete);
typedef struct VBVAEXHOSTCTL
{
union
{
struct
{
} cmd;
struct
{
} state;
} u;
void *pvComplete;
/* VBoxVBVAExHP**, i.e. processor functions, can NOT be called concurrently with each other,
* but can be called with other VBoxVBVAExS** (submitter) functions except Init/Start/Term aparently.
* Can only be called be the processor, i.e. the entity that acquired the processor state by direct or indirect call to the VBoxVBVAExHSCheckCommands
* see mor edetailed comments in headers for function definitions */
typedef enum
{
static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd);
static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc);
/* VBoxVBVAExHP**, i.e. processor functions, can NOT be called concurrently with each other,
* can be called concurrently with istelf as well as with other VBoxVBVAEx** functions except Init/Start/Term aparently */
static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM);
static int VBoxVBVAExHSLoadState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version);
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
#else
#endif
}
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
#else
#endif
}
{
if (!pCtl)
{
WARN(("VBoxVBVAExHCtlAlloc failed\n"));
return NULL;
}
return pCtl;
}
{
if (ASMAtomicCmpXchgS32(&pCmdVbva->i32State, VBVAEXHOSTCONTEXT_STATE_PROCESSING, VBVAEXHOSTCONTEXT_STATE_LISTENING))
return VINF_SUCCESS;
return VERR_SEM_BUSY;
}
static VBVAEXHOSTCTL* vboxVBVAExHPCheckCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, bool *pfHostCtl, bool fHostOnlyMode)
{
return NULL;
if (RT_SUCCESS(rc))
{
if (pCtl)
*pfHostCtl = true;
else if (!fHostOnlyMode)
{
{
/* pCtl can not be null here since pCmdVbva->u32cCtls is not null,
* and there are no HostCtl commands*/
*pfHostCtl = false;
}
}
if (pCtl)
{
}
return pCtl;
}
else
return NULL;
}
{
bool fHostCtl;
}
static bool vboxVBVAExHPCheckProcessCtlInternal(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl)
{
{
return true;
return true;
default:
return false;
}
}
{
}
{
}
{
}
static int vboxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
{
Log(("first = %d, free = %d\n",
if (indexRecordFirst == indexRecordFree)
{
/* No records to process. Return without assigning output variables. */
return VINF_EOF;
}
/* A new record need to be processed. */
{
/* the record is being recorded, try again */
return VINF_TRY_AGAIN;
}
if (!cbRecord)
{
/* the record is being recorded, try again */
return VINF_TRY_AGAIN;
}
/* we should not get partial commands here actually */
/* The size of largest contiguous 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. */
return VINF_SUCCESS;
}
LogRel(("CmdVbva: cross-bound writes unsupported\n"));
return VERR_INVALID_STATE;
}
{
}
static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc)
{
if (pCtl->pfnComplete)
else
}
static VBVAEXHOST_DATA_TYPE vboxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
{
bool fHostClt;
for(;;)
{
if (pCtl)
{
if (fHostClt)
{
{
return VBVAEXHOST_DATA_TYPE_HOSTCTL;
}
}
else
{
return VBVAEXHOST_DATA_TYPE_GUESTCTL;
}
}
return VBVAEXHOST_DATA_TYPE_NO_DATA;
switch (rc)
{
case VINF_SUCCESS:
return VBVAEXHOST_DATA_TYPE_CMD;
case VINF_EOF:
return VBVAEXHOST_DATA_TYPE_NO_DATA;
case VINF_TRY_AGAIN:
RTThreadSleep(1);
continue;
default:
/* this is something really unexpected, i.e. most likely guest has written something incorrect to the VBVA buffer */
return VBVAEXHOST_DATA_TYPE_NO_DATA;
}
}
WARN(("Warning: VBoxVBVAExHCmdGet unexpected state\n"));
return VBVAEXHOST_DATA_TYPE_NO_DATA;
}
static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
{
if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA)
{
/* we need to prevent racing between us clearing the flag and command check/submission thread, i.e.
* 1. we check the queue -> and it is empty
* 2. submitter adds command to the queue
* 3. submitter checks the "processing" -> and it is true , thus it does not submit a notification
* 4. we clear the "processing" state
* 5. ->here we need to re-check the queue state to ensure we do not leak the notification of the above command
* 6. if the queue appears to be not-empty set the "processing" state back to "true"
**/
if (RT_SUCCESS(rc))
{
/* we are the processor now */
if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA)
{
return VBVAEXHOST_DATA_TYPE_NO_DATA;
}
}
}
return enmType;
}
{
if (pVBVA)
{
if (indexRecordFirst != indexRecordFree)
return true;
}
}
/* Checks whether the new commands are ready for processing
* @returns
* VINF_SUCCESS - there are commands are in a queue, and the given thread is now the processor (i.e. typically it would delegate processing to a worker thread)
* VINF_EOF - no commands in a queue
* VINF_ALREADY_INITIALIZED - another thread already processing the commands
* VERR_INVALID_STATE - the VBVA is paused or pausing */
{
if (RT_SUCCESS(rc))
{
/* we are the processor now */
{
return VINF_SUCCESS;
}
return VINF_EOF;
}
if (rc == VERR_SEM_BUSY)
return VINF_ALREADY_INITIALIZED;
return VERR_INVALID_STATE;
}
{
if (RT_SUCCESS(rc))
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
0, /* size_t cbAlignment */
UINT32_MAX, /* uint32_t cMaxObjects */
NULL, /* PFNMEMCACHECTOR pfnCtor*/
NULL, /* PFNMEMCACHEDTOR pfnDtor*/
NULL, /* void *pvUser*/
0 /* uint32_t fFlags*/
);
if (RT_SUCCESS(rc))
#endif
{
return VINF_SUCCESS;
}
#ifndef VBOXVDBG_MEMCACHE_DISABLE
else
#endif
}
else
return rc;
}
{
}
{
return VINF_ALREADY_INITIALIZED;
return VINF_SUCCESS;
}
{
if (!VBoxVBVAExHSIsEnabled(pCmdVbva))
return VINF_SUCCESS;
return VINF_SUCCESS;
}
{
/* ensure the processor is stopped */
/* ensure no one tries to submit the command */
#ifndef VBOXVDBG_MEMCACHE_DISABLE
#endif
}
/* Saves state
* @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail
*/
static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM)
{
int rc;
{
{
WARN(("vbva not paused\n"));
return VERR_INVALID_STATE;
}
return VINF_SUCCESS;
}
return VINF_SUCCESS;
}
typedef enum
{
static int VBoxVBVAExHCtlSubmit(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
{
{
Log(("cmd vbva not enabled\n"));
return VERR_INVALID_STATE;
}
if (RT_SUCCESS(rc))
{
{
{
Log(("cmd vbva not enabled\n"));
return VERR_INVALID_STATE;
}
}
else
}
else
return rc;
}
/* Loads state
* @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail
*/
static int VBoxVBVAExHSLoadState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version)
{
AssertMsgFailed(("implement!\n"));
if (u32 != 0xffffffff)
{
return VBoxVBVAExHSCheckCommands(pCmdVbva);
}
return VINF_SUCCESS;
}
typedef struct VBOXVDMAHOST
{
int32_t volatile i32cHostCrCtlCompleted;
#ifdef VBOX_VDMA_WITH_WATCHDOG
#endif
{
return VINF_SUCCESS;
}
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
return rc;
}
{
}
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
rc = RTThreadCreate(&pThread->hWorkerThread, pfnThread, pvThread, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "VDMA");
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
WARN(("thread routine failed the initialization\n"));
}
else
}
else
}
else
}
else
return rc;
}
{
return rc;
}
{
return rc;
}
{
}
{
int rc;
{
}
}
static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource);
#ifdef VBOX_WITH_CRHGSMI
typedef DECLCALLBACK(void) FNVBOXVDMACRCTL_CALLBACK(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext);
typedef struct VBOXVDMACMD_CHROMIUM_CTL_PRIVATE
{
void *pvCompletion;
#define VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(_p) ((PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE)(((uint8_t*)(_p)) - RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, Cmd)))
static PVBOXVDMACMD_CHROMIUM_CTL vboxVDMACrCtlCreate(VBOXVDMACMD_CHROMIUM_CTL_TYPE enmCmd, uint32_t cbCmd)
{
PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = (PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE)RTMemAllocZ(cbCmd + RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, Cmd));
if (pHdr)
{
}
return NULL;
}
{
if(!cRefs)
{
}
}
{
}
{
}
static DECLCALLBACK(void) vboxVDMACrCtlCbSetEvent(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext)
{
}
static DECLCALLBACK(void) vboxVDMACrCtlCbReleaseCmd(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext)
{
}
static int vboxVDMACrCtlPostAsync (PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd, PFNVBOXVDMACRCTL_CALLBACK pfnCompletion, void *pvCompletion)
{
{
return VINF_SUCCESS;
}
#ifdef DEBUG_misha
Assert(0);
#endif
return VERR_NOT_SUPPORTED;
}
{
if(RT_SUCCESS(rc))
{
#ifdef DEBUG_misha
#endif
if (RT_SUCCESS(rc))
{
if(RT_SUCCESS(rc))
{
}
}
else
{
/* the command is completed */
}
}
return rc;
}
typedef struct VDMA_VBVA_CTL_CYNC_COMPLETION
{
int rc;
static DECLCALLBACK(void) vboxVDMACrHgcmSubmitSyncCompletion(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
{
if (!RT_SUCCESS(rc))
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCtl, cbCtl, vboxVDMACrHgcmSubmitSyncCompletion, &Data);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (!RT_SUCCESS(rc))
{
}
}
else
}
else
return rc;
}
static DECLCALLBACK(uint8_t*) vboxVDMACrHgcmHandleEnableRemainingHostCommand(HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hClient, uint32_t *pcbCtl, int prevCmdRc)
{
if (!pVdma->pCurRemainingHostCtl)
{
/* disable VBVA, all subsequent host commands will go HGCM way */
}
else
{
}
if (pVdma->pCurRemainingHostCtl)
{
}
*pcbCtl = 0;
return NULL;
}
{
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
return rc;
}
{
{
WARN(("vdma VBVA is already enabled\n"));
return VERR_INVALID_STATE;
}
if (!pVBVA)
{
return VERR_INVALID_PARAMETER;
}
{
#ifdef DEBUG_misha
WARN(("pfnEnable is NULL\n"));
return VERR_NOT_SUPPORTED;
#endif
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
else
}
else
}
else
return rc;
}
{
{
Log(("vdma VBVA is already disabled\n"));
return VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
/* disable is a bit tricky
* we need to ensure the host ctl commands do not come out of order
* and do not come over HGCM channel until after it is enabled */
if (RT_SUCCESS(rc))
return rc;
}
else
return rc;
}
{
{
{
WARN(("VBVAEXHOSTCTL_TYPE_HH_SAVESTATE for disabled vdma VBVA\n"));
return VERR_INVALID_STATE;
}
{
WARN(("VBVAEXHOSTCTL_TYPE_HH_LOADSTATE for disabled vdma VBVA\n"));
return VERR_INVALID_STATE;
}
return pVdma->CrSrvInfo.pfnLoadState(pVdma->CrSrvInfo.hSvr, pCmd->u.state.pSSM, pCmd->u.state.u32Version);
{
WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
return VERR_INVALID_STATE;
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
return VINF_SUCCESS;
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
return VINF_SUCCESS;
}
default:
return VERR_INVALID_PARAMETER;
}
}
{
{
{
WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
return VERR_INVALID_STATE;
}
{
{
}
return vdmaVBVADisableProcess(pVdma);
}
default:
return VERR_INVALID_PARAMETER;
}
}
/**
* @param fIn - whether this is a page in or out op.
* the direction is VRA#M - related, so fIn == true - transfer to VRAM); false - transfer from VRAM
*/
static int vboxVDMACrCmdVbvaProcessPagingEl(PPDMDEVINS pDevIns, const VBOXCMDVBVA_SYSMEMEL *pMemEl, uint8_t *pu8Vram, uint8_t *pu8VramMax, uint8_t **ppu8VramNext, bool fIn)
{
{
WARN(("invalid copy size"));
return VERR_INVALID_PARAMETER;
}
int rc;
if (fIn)
{
{
const void * pvPage;
if (!RT_SUCCESS(rc))
{
return rc;
}
}
}
else
{
{
void * pvPage;
if (!RT_SUCCESS(rc))
{
return rc;
}
}
}
if (ppu8VramNext)
return VINF_SUCCESS;
}
static int vboxVDMACrCmdVbvaProcessPagingEls(PPDMDEVINS pDevIns, const VBOXCMDVBVA_SYSMEMEL *pMemEl, uint32_t cMemEls, uint8_t *pu8Vram, uint8_t *pu8VramMax, uint8_t **ppu8VramNext, bool fIn)
{
{
if (!RT_SUCCESS(rc))
{
return rc;
}
}
if (ppu8VramNext)
return VINF_SUCCESS;
}
static int8_t vboxVDMACrCmdVbvaPagingDataInit(PVGASTATE pVGAState, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd,
{
if (cbCmd < sizeof (VBOXCMDVBVA_PAGING_TRANSFER))
{
WARN(("cmd too small"));
return -1;
}
if (cSysMem % sizeof (VBOXCMDVBVA_SYSMEMEL))
{
WARN(("invalid cmd size"));
return -1;
}
cSysMem /= sizeof (VBOXCMDVBVA_SYSMEMEL);
if (offVRAM & PAGE_OFFSET_MASK)
{
WARN(("offVRAM address is not on page boundary\n"));
return -1;
}
{
WARN(("invalid vram offset"));
return -1;
}
return 0;
}
static int8_t vboxVDMACrCmdVbvaProcessCmdData(struct VBOXVDMAHOST *pVdma, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd)
{
{
return 0;
{
const VBOXCMDVBVA_SYSMEMEL *pSysMem;
bool fIn;
if (i8Result < 0)
{
return i8Result;
}
int rc = vboxVDMACrCmdVbvaProcessPagingEls(pDevIns, pSysMem, cSysMem, pu8Vram, pu8VramMax, &pu8Vram, fIn);
if (!RT_SUCCESS(rc))
{
return -1;
}
return 0;
}
WARN(("VBOXCMDVBVA_OPTYPE_PAGING_FILL not implemented"));
return -1;
default:
}
}
#if 0
typedef struct VBOXCMDVBVA_PAGING_TRANSFER
{
/* for now can only contain offVRAM.
* paging transfer can NOT be initiated for allocations having host 3D object (hostID) associated */
#endif
static int8_t vboxVDMACrCmdVbvaProcess(struct VBOXVDMAHOST *pVdma, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd)
{
{
{
const VBOXCMDVBVA_HDR *pRealCmd;
if (cbRealCmd < sizeof (VBOXCMDVBVA_HDR))
{
WARN(("invalid sysmem cmd size"));
return -1;
}
if (phPage & PAGE_OFFSET_MASK)
{
WARN(("cmd address is not on page boundary\n"));
return -1;
}
const void * pvCmd;
if (!RT_SUCCESS(rc))
{
return -1;
}
{
return i8Result;
}
{
{
const VBOXCMDVBVA_SYSMEMEL *pSysMem;
bool fIn;
if (i8Result < 0)
{
return i8Result;
}
cCurSysMem /= sizeof (VBOXCMDVBVA_SYSMEMEL);
do
{
rc = vboxVDMACrCmdVbvaProcessPagingEls(pDevIns, pSysMem, cCurSysMem, pu8Vram, pu8VramMax, &pu8Vram, fIn);
if (!RT_SUCCESS(rc))
{
i8Result = -1;
break;
}
cSysMem -= cCurSysMem;
if (!cSysMem)
break;
if (!RT_SUCCESS(rc))
{
return -1;
}
else
} while (1);
break;
}
default:
WARN(("command can not be splitted"));
i8Result = -1;
break;
}
return i8Result;
}
default:
}
}
{
if (*pu8Cmd == VBOXCMDVBVA_OPTYPE_NOP)
return;
if (cbCmd < sizeof (VBOXCMDVBVA_HDR))
{
WARN(("invalid command size"));
return;
}
/* check if the command is cancelled */
if (!ASMAtomicCmpXchgU8(&pCmd->u8State, VBOXCMDVBVA_STATE_IN_PROGRESS, VBOXCMDVBVA_STATE_SUBMITTED))
{
return;
}
}
{
int rc = VERR_NO_MEMORY;
if (pCmd)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
else if (rc != VERR_NOT_SUPPORTED)
}
else
}
if (!RT_SUCCESS(rc))
return rc;
}
static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer);
/* check if this is external cmd to be passed to chromium backend */
static int vboxVDMACmdCheckCrCmd(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmdDr, uint32_t cbCmdDr)
{
int rc = VINF_NOT_SUPPORTED;
{
{
AssertMsgFailed(("invalid buffer data!"));
return VERR_INVALID_PARAMETER;
}
{
AssertMsgFailed(("invalid command buffer data!"));
return VERR_INVALID_PARAMETER;
}
}
{
{
AssertMsgFailed(("invalid command buffer data from offset!"));
return VERR_INVALID_PARAMETER;
}
}
if (pDmaCmd)
{
{
{
{
AssertMsgFailed(("invalid chromium command buffer size!"));
return VERR_INVALID_PARAMETER;
}
rc = VINF_SUCCESS;
{
break;
}
else
{
Assert(0);
}
break;
}
{
{
AssertMsgFailed(("invalid bpb transfer buffer size!"));
return VERR_INVALID_PARAMETER;
}
if (RT_SUCCESS(rc))
{
rc = VINF_SUCCESS;
}
break;
}
default:
break;
}
}
return rc;
}
int vboxVDMACrHgsmiCommandCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, int rc)
{
return rc;
}
int vboxVDMACrHgsmiControlCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd, int rc)
{
if (pCmdPrivate->pfnCompletion)
{
}
return VINF_SUCCESS;
}
#endif
#ifdef VBOX_VDMA_WITH_WORKERTHREAD
/* to simplify things and to avoid extra backend if modifications we assume the VBOXVDMA_RECTL is the same as VBVACMDHDR */
static int vboxVDMANotifyPrimaryUpdate (PVGASTATE pVGAState, unsigned uScreenId, const VBOXVDMA_RECTL * pRectl)
{
/* Updates the rectangle and sends the command to the VRDP server. */
sizeof (VBOXVDMA_RECTL));
return VINF_SUCCESS;
}
#endif
{
/* we do not support color conversion */
/* we do not support stretching */
return VERR_INVALID_FUNCTION;
{
}
else
{
uint32_t offDstLineEnd = ((pDstRectl->left * pDstDesc->bpp + 7) >> 3) + ((pDstDesc->bpp * pDstRectl->width + 7) >> 3);
uint32_t offSrcLineEnd = ((pSrcRectl->left * pSrcDesc->bpp + 7) >> 3) + ((pSrcDesc->bpp * pSrcRectl->width + 7) >> 3);
for (uint32_t i = 0; ; ++i)
{
break;
pvDstStart += cbDstSkip;
pvSrcStart += cbSrcSkip;
}
}
return VINF_SUCCESS;
}
{
else
{
{
}
{
}
}
}
/*
* @return on success the number of bytes the command contained, otherwise - VERR_xxx error code
*/
static int vboxVDMACmdExecBlt(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt, uint32_t cbBuffer)
{
const uint32_t cbBlt = VBOXVDMACMD_BODY_FIELD_OFFSET(uint32_t, VBOXVDMACMD_DMA_PRESENT_BLT, aDstSubRects[pBlt->cDstSubRects]);
return VERR_INVALID_FUNCTION;
/* we do not support stretching for now */
return VERR_INVALID_FUNCTION;
return VERR_INVALID_FUNCTION;
VBOXVDMA_RECTL updateRectl = {0, 0, 0, 0};
if (pBlt->cDstSubRects)
{
{
{
}
{
}
if (!RT_SUCCESS(rc))
return rc;
}
}
else
{
if (!RT_SUCCESS(rc))
return rc;
}
#ifdef VBOX_VDMA_WITH_WORKERTHREAD
int iView = 0;
/* @todo: fixme: check if update is needed and get iView */
#endif
return cbBlt;
}
static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer)
{
return VERR_INVALID_PARAMETER;
const void * pvSrc;
void * pvDst;
int rc = VINF_SUCCESS;
uint32_t cbTransfered = 0;
bool bSrcLocked = false;
bool bDstLocked = false;
do
{
{
}
else
{
phPage += cbTransfered;
if (RT_SUCCESS(rc))
{
bSrcLocked = true;
}
else
{
break;
}
}
{
}
else
{
phPage += cbTransfered;
if (RT_SUCCESS(rc))
{
bDstLocked = true;
}
else
{
break;
}
}
if (RT_SUCCESS(rc))
{
}
else
{
cbTransfer = 0; /* to break */
}
if (bSrcLocked)
if (bDstLocked)
} while (cbTransfer);
if (RT_SUCCESS(rc))
return sizeof (*pTransfer);
return rc;
}
{
do
{
if (!pvBuffer)
return VERR_INVALID_PARAMETER;
if (cbBuffer < VBOXVDMACMD_HEADER_SIZE())
return VERR_INVALID_PARAMETER;
{
{
#ifdef VBOXWDDM_TEST_UHGSMI
static int count = 0;
if (count==0)
{
start = RTTimeNanoTS();
}
++count;
if (count==100000)
{
end = RTTimeNanoTS();
}
#endif
/* todo: post the buffer to chromium */
return VINF_SUCCESS;
}
{
if (cbBlt >= 0)
{
return VINF_SUCCESS;
else
{
}
}
else
return cbBlt; /* error */
break;
}
{
const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer = VBOXVDMACMD_BODY(pCmd, VBOXVDMACMD_DMA_BPB_TRANSFER);
Assert(cbTransfer >= 0);
if (cbTransfer >= 0)
{
return VINF_SUCCESS;
else
{
pvBuffer -= cbTransfer;
}
}
else
return cbTransfer; /* error */
break;
}
case VBOXVDMACMD_TYPE_DMA_NOP:
return VINF_SUCCESS;
return VINF_SUCCESS;
default:
return VERR_INVALID_FUNCTION;
}
} while (1);
/* we should not be here */
return VERR_INVALID_STATE;
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
{
switch (enmType)
{
case VBVAEXHOST_DATA_TYPE_CMD:
break;
break;
break;
break;
default:
break;
}
}
return VINF_SUCCESS;
}
{
int rc;
bool bReleaseLocked = false;
do
{
{
}
else
{
{
/* @todo: more advanced mechanism of command buffer proc is actually needed */
break;
}
const void * pvPageBuf;
if (!RT_SUCCESS(rc))
{
/* @todo: if (rc == VERR_PGM_PHYS_PAGE_RESERVED) -> fall back on using PGMPhysRead ?? */
break;
}
bReleaseLocked = true;
}
if (bReleaseLocked)
} while (0);
}
{
}
#ifdef VBOX_VDMA_WITH_WATCHDOG
{
}
{
if (cMillis)
else
return VINF_SUCCESS;
}
#endif
{
int rc;
#ifdef VBOX_VDMA_WITH_WORKERTHREAD
PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)RTMemAllocZ(RT_OFFSETOF(VBOXVDMAHOST, CmdPool.aCmds[cPipeElements]));
#else
#endif
if (pVdma)
{
if (RT_SUCCESS(rc))
{
#ifdef VBOX_VDMA_WITH_WATCHDOG
#endif
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
#ifdef VBOX_WITH_CRHGSMI
int rcIgnored = vboxVDMACrCtlHgsmiSetup(pVdma); NOREF(rcIgnored); /** @todo is this ignoring intentional? */
#endif
return VINF_SUCCESS;
}
else
}
else
}
else
}
else
return rc;
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
return VINF_SUCCESS;
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
return VINF_SUCCESS;
}
{
#ifdef VBOX_WITH_CRHGSMI
VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN, sizeof (*pCmd));
if (pCmd)
{
if (RT_SUCCESS(rc))
{
}
return rc;
}
return VERR_NO_MEMORY;
#else
return VINF_SUCCESS;
#endif
}
{
#ifdef VBOX_WITH_CRHGSMI
VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END, sizeof (*pCmd));
if (pCmd)
{
if (RT_SUCCESS(rc))
{
}
return rc;
}
return VERR_NO_MEMORY;
#else
return VINF_SUCCESS;
#endif
}
{
#if 1
{
case VBOXVDMA_CTL_TYPE_ENABLE:
break;
break;
case VBOXVDMA_CTL_TYPE_FLUSH:
break;
#ifdef VBOX_VDMA_WITH_WATCHDOG
break;
#endif
default:
}
#else
/* test asinch completion */
if (RT_SUCCESS(rc))
{
{
/* success */
return;
}
}
/* failure */
#endif
}
{
int rc = VERR_NOT_IMPLEMENTED;
#ifdef VBOX_WITH_CRHGSMI
/* chromium commands are processed by crhomium hgcm thread independently from our internal cmd processing pipeline
* this is why we process them specially */
if (rc == VINF_SUCCESS)
return;
if (RT_FAILURE(rc))
{
return;
}
#endif
#ifndef VBOX_VDMA_WITH_WORKERTHREAD
#else
# ifdef DEBUG_misha
Assert(0);
# endif
if (RT_SUCCESS(rc))
{
{
/* success */
return;
}
}
/* failure */
#endif
}
/**/
static int vdmaVBVACtlSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
{
if (RT_SUCCESS(rc))
{
if (rc == VINF_SUCCESS)
else
}
else
return rc;
}
static DECLCALLBACK(void) vboxCmdVBVACmdCtlGuestCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext)
{
}
static int vdmaVBVACtlOpaqueSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL_SOURCE enmSource, uint8_t* pu8Cmd, uint32_t cbCmd, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
{
if (!pHCtl)
{
WARN(("VBoxVBVAExHCtlCreate failed\n"));
return VERR_NO_MEMORY;
}
if (!RT_SUCCESS(rc))
{
return rc;;
}
return VINF_SUCCESS;
}
{
int rc = vdmaVBVACtlOpaqueSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_GUEST, (uint8_t*)(pCtl+1), cbCtl - sizeof (VBOXCMDVBVA_CTL), vboxCmdVBVACmdCtlGuestCompletion, pVdma);
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
return VINF_SUCCESS;
}
static DECLCALLBACK(void) vboxCmdVBVACmdCtlHostCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvCompletion)
{
if (pVboxCtl->u.pfnInternal)
}
static int vdmaVBVACtlOpaqueHostSubmit(PVBOXVDMAHOST pVdma, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
void *pvCompletion)
{
int rc = vdmaVBVACtlOpaqueSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_HOST_ENABLED, (uint8_t*)pCmd, cbCmd, vboxCmdVBVACmdCtlHostCompletion, pvCompletion);
if (!RT_SUCCESS(rc))
{
if (rc == VERR_INVALID_STATE)
{
rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCmd, cbCmd, pfnCompletion, pvCompletion);
if (!RT_SUCCESS(rc))
return rc;
}
return rc;
}
return VINF_SUCCESS;
}
static int vdmaVBVACtlEnableDisableSubmitInternal(PVBOXVDMAHOST pVdma, VBVAENABLE *pEnable, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
{
if (!pHCtl)
{
WARN(("VBoxVBVAExHCtlCreate failed\n"));
return VERR_NO_MEMORY;
}
if (!RT_SUCCESS(rc))
{
return rc;;
}
return VINF_SUCCESS;
}
{
int rc = vdmaVBVACtlEnableDisableSubmitInternal(pVdma, &pEnable->Enable, vboxCmdVBVACmdCtlGuestCompletion, pVdma);
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
return VINF_SUCCESS;
}
static DECLCALLBACK(void) vdmaVBVACtlSubmitSyncCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext)
{
if (!RT_SUCCESS(rc))
}
static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource)
{
if (!RT_SUCCESS(rc))
{
return rc;
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (!RT_SUCCESS(rc))
}
else
}
else
return rc;
}
{
}
{
}
{
switch (rc)
{
case VINF_SUCCESS:
case VINF_ALREADY_INITIALIZED:
case VINF_EOF:
case VERR_INVALID_STATE:
return VINF_SUCCESS;
default:
}
}
void *pvCompletion)
{
}
typedef struct VBOXCMDVBVA_CMDHOSTCTL_SYNC
{
struct VBOXVDMAHOST *pVdma;
int rc;
static DECLCALLBACK(void) vboxCmdVBVACmdHostCtlSyncCb(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
{
pData->fProcessing = 0;
}
{
if (!RT_SUCCESS(rc))
{
return rc;
}
while (Data.fProcessing)
{
/* Poll infrequently to make sure no completed message has been missed. */
if (Data.fProcessing)
}
/* 'Our' message has been processed, so should reset the semaphore.
* There is still possible that another message has been processed
* and the semaphore has been signalled again.
* Reset only if there are no other messages completed.
*/
Assert(c >= 0);
if (!c)
if (!RT_SUCCESS(rc))
return rc;
}
{
int rc = VINF_SUCCESS;
{
if (cbCtl != sizeof (VBOXCMDVBVA_CTL_ENABLE))
{
WARN(("incorrect enable size\n"));
break;
}
default:
WARN(("unsupported type\n"));
break;
}
return VINF_SUCCESS;
}
{
{
WARN(("vdma VBVA is disabled\n"));
return VERR_INVALID_STATE;
}
}
{
WARN(("flush\n"));
{
WARN(("vdma VBVA is disabled\n"));
return VERR_INVALID_STATE;
}
}
{
return;
}