diff options
Diffstat (limited to 'src/VBox/Devices/Graphics/DevVGA_VDMA.cpp')
-rw-r--r-- | src/VBox/Devices/Graphics/DevVGA_VDMA.cpp | 2028 |
1 files changed, 1235 insertions, 793 deletions
diff --git a/src/VBox/Devices/Graphics/DevVGA_VDMA.cpp b/src/VBox/Devices/Graphics/DevVGA_VDMA.cpp index bdec54a28..0e35fe5a3 100644 --- a/src/VBox/Devices/Graphics/DevVGA_VDMA.cpp +++ b/src/VBox/Devices/Graphics/DevVGA_VDMA.cpp @@ -20,12 +20,22 @@ #include <iprt/thread.h> #include <iprt/mem.h> #include <iprt/asm.h> +#include <iprt/list.h> #include "DevVGA.h" #include "HGSMI/SHGSMIHost.h" #include "HGSMI/HGSMIHostHlp.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) @@ -37,127 +47,733 @@ WARN_BP(); \ } while (0) -#ifdef VBOX_VDMA_WITH_WORKERTHREAD -typedef enum -{ - VBOXVDMAPIPE_STATE_CLOSED = 0, - VBOXVDMAPIPE_STATE_CREATED = 1, - VBOXVDMAPIPE_STATE_OPENNED = 2, - VBOXVDMAPIPE_STATE_CLOSING = 3 -} VBOXVDMAPIPE_STATE; +#define VBOXVDMATHREAD_STATE_TERMINATED 0 +#define VBOXVDMATHREAD_STATE_CREATED 1 +#define VBOXVDMATHREAD_STATE_TERMINATING 2 -typedef struct VBOXVDMAPIPE +typedef struct VBOXVDMATHREAD { + RTTHREAD hWorkerThread; RTSEMEVENT hEvent; - /* critical section for accessing pipe properties */ - RTCRITSECT hCritSect; - VBOXVDMAPIPE_STATE enmState; - /* true iff the other end needs Event notification */ - bool bNeedNotify; -} VBOXVDMAPIPE, *PVBOXVDMAPIPE; - -typedef enum -{ - VBOXVDMAPIPE_CMD_TYPE_UNDEFINED = 0, - VBOXVDMAPIPE_CMD_TYPE_DMACMD = 1, - VBOXVDMAPIPE_CMD_TYPE_DMACTL = 2 -} VBOXVDMAPIPE_CMD_TYPE; - -typedef struct VBOXVDMAPIPE_CMD_BODY -{ - VBOXVDMAPIPE_CMD_TYPE enmType; - union - { - PVBOXVDMACBUF_DR pDr; - PVBOXVDMA_CTL pCtl; - void *pvCmd; - } u; -}VBOXVDMAPIPE_CMD_BODY, *PVBOXVDMAPIPE_CMD_BODY; - -typedef struct VBOXVDMAPIPE_CMD -{ - HGSMILISTENTRY Entry; - VBOXVDMAPIPE_CMD_BODY Cmd; -} VBOXVDMAPIPE_CMD, *PVBOXVDMAPIPE_CMD; - -#define VBOXVDMAPIPE_CMD_FROM_ENTRY(_pE) ( (PVBOXVDMAPIPE_CMD)((uint8_t *)(_pE) - RT_OFFSETOF(VBOXVDMAPIPE_CMD, Entry)) ) - -typedef struct VBOXVDMAPIPE_CMD_POOL -{ - HGSMILIST List; - uint32_t cCmds; - VBOXVDMAPIPE_CMD aCmds[1]; -} VBOXVDMAPIPE_CMD_POOL, *PVBOXVDMAPIPE_CMD_POOL; -#endif + RTSEMEVENT hClientEvent; + volatile uint32_t u32State; +} VBOXVDMATHREAD, *PVBOXVDMATHREAD; /* state transformations: * * submitter | processor - * STOPPED - * | - * | - * > + * * LISTENING ---> PROCESSING - * ^ _/ - * | _/ - * | _/ - * | _/ - * | _/ - * | _/ - * | / - * < > - * PAUSED * * */ -#define VBVAEXHOSTCONTEXT_STATE_STOPPED 0 -#define VBVAEXHOSTCONTEXT_STATE_LISTENING 1 -#define VBVAEXHOSTCONTEXT_STATE_PROCESSING 2 -#define VBVAEXHOSTCONTEXT_STATE_PAUSED 3 +#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 { VBVABUFFER *pVBVA; - uint32_t cbCurData; - volatile uint32_t u32State; - volatile uint32_t u32Pause; - volatile uint32_t u32cOtherCommands; + volatile int32_t i32State; + volatile int32_t i32EnableState; + volatile uint32_t u32cCtls; + /* critical section for accessing ctl lists */ + RTCRITSECT CltCritSect; + RTLISTANCHOR GuestCtlList; + RTLISTANCHOR HostCtlList; +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMEMCACHE CtlCache; +#endif } VBVAEXHOSTCONTEXT; +typedef enum +{ + VBVAEXHOSTCTL_TYPE_UNDEFINED = 0, + VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE, + VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME, + VBVAEXHOSTCTL_TYPE_HH_ENABLE, + VBVAEXHOSTCTL_TYPE_HH_TERM, + VBVAEXHOSTCTL_TYPE_HH_RESET, + VBVAEXHOSTCTL_TYPE_HH_SAVESTATE, + VBVAEXHOSTCTL_TYPE_HH_LOADSTATE, + VBVAEXHOSTCTL_TYPE_HH_BE_OPAQUE, + VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE, + VBVAEXHOSTCTL_TYPE_GH_ENABLE_DISABLE +} VBVAEXHOSTCTL_TYPE; + +struct VBVAEXHOSTCTL; + +typedef DECLCALLBACKPTR(void, PFNVBVAEXHOSTCTL_COMPLETE)(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvComplete); + +typedef struct VBVAEXHOSTCTL +{ + RTLISTNODE Node; + VBVAEXHOSTCTL_TYPE enmType; + union + { + struct + { + uint8_t * pu8Cmd; + uint32_t cbCmd; + } cmd; + + struct + { + PSSMHANDLE pSSM; + uint32_t u32Version; + } state; + } u; + PFNVBVAEXHOSTCTL_COMPLETE pfnComplete; + void *pvComplete; +} VBVAEXHOSTCTL; + /* 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 */ -static bool VBoxVBVAExHPCmdCheckRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva); -static int VBoxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd); +typedef enum +{ + VBVAEXHOST_DATA_TYPE_NO_DATA = 0, + VBVAEXHOST_DATA_TYPE_CMD, + VBVAEXHOST_DATA_TYPE_HOSTCTL, + VBVAEXHOST_DATA_TYPE_GUESTCTL +} VBVAEXHOST_DATA_TYPE; +static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd); + +static void VBoxVBVAExHPDataCompleteCmd(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint32_t cbCmd); +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 VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva); -static void VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva); +static int VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva); static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA); static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva); static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva); 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); +static VBVAEXHOSTCTL* VBoxVBVAExHCtlAlloc(VBVAEXHOSTCONTEXT *pCmdVbva) +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (VBVAEXHOSTCTL*)RTMemCacheAlloc(pCmdVbva->CtlCache); +#else + return (VBVAEXHOSTCTL*)RTMemAlloc(sizeof (VBVAEXHOSTCTL)); +#endif +} + +static void VBoxVBVAExHCtlFree(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl) +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(pCmdVbva->CtlCache, pCtl); +#else + RTMemFree(pCtl); +#endif +} + +static VBVAEXHOSTCTL* VBoxVBVAExHCtlCreate(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL_TYPE enmType) +{ + VBVAEXHOSTCTL* pCtl = VBoxVBVAExHCtlAlloc(pCmdVbva); + if (!pCtl) + { + WARN(("VBoxVBVAExHCtlAlloc failed\n")); + return NULL; + } + + pCtl->enmType = enmType; + return pCtl; +} + +static int vboxVBVAExHSProcessorAcquire(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + Assert(pCmdVbva->i32State >= VBVAEXHOSTCONTEXT_STATE_LISTENING); + + 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) +{ + Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); + + if(!fHostOnlyMode && !ASMAtomicUoReadU32(&pCmdVbva->u32cCtls)) + return NULL; + + int rc = RTCritSectEnter(&pCmdVbva->CltCritSect); + if (RT_SUCCESS(rc)) + { + VBVAEXHOSTCTL* pCtl = RTListGetFirst(&pCmdVbva->HostCtlList, VBVAEXHOSTCTL, Node); + if (pCtl) + *pfHostCtl = true; + else if (!fHostOnlyMode) + { + if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) > VBVAEXHOSTCONTEXT_ESTATE_PAUSED) + { + pCtl = RTListGetFirst(&pCmdVbva->GuestCtlList, VBVAEXHOSTCTL, Node); + /* pCtl can not be null here since pCmdVbva->u32cCtls is not null, + * and there are no HostCtl commands*/ + Assert(pCtl); + *pfHostCtl = false; + } + } + + if (pCtl) + { + RTListNodeRemove(&pCtl->Node); + ASMAtomicDecU32(&pCmdVbva->u32cCtls); + } + + RTCritSectLeave(&pCmdVbva->CltCritSect); + + return pCtl; + } + else + WARN(("RTCritSectEnter failed %d\n", rc)); + + return NULL; +} + +static VBVAEXHOSTCTL* VBoxVBVAExHPCheckHostCtlOnDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + bool fHostCtl; + return vboxVBVAExHPCheckCtl(pCmdVbva, &fHostCtl, true); +} + + +static bool vboxVBVAExHPCheckProcessCtlInternal(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl) +{ + switch (pCtl->enmType) + { + case VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE: + if (pCmdVbva->i32EnableState > VBVAEXHOSTCONTEXT_ESTATE_PAUSED) + ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_PAUSED); + return true; + case VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME: + if (pCmdVbva->i32EnableState == VBVAEXHOSTCONTEXT_ESTATE_PAUSED) + ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_ENABLED); + return true; + default: + return false; + } +} + +static void vboxVBVAExHPProcessorRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); + + ASMAtomicWriteS32(&pCmdVbva->i32State, VBVAEXHOSTCONTEXT_STATE_LISTENING); +} + +static void vboxVBVAExHPHgEventSet(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); + if (pCmdVbva->pVBVA) + ASMAtomicOrU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, VBVA_F_STATE_PROCESSING); +} + +static void vboxVBVAExHPHgEventClear(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); + if (pCmdVbva->pVBVA) + ASMAtomicAndU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, ~VBVA_F_STATE_PROCESSING); +} + +static int vboxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd) +{ + Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); + Assert(pCmdVbva->i32EnableState > VBVAEXHOSTCONTEXT_ESTATE_PAUSED); + + VBVABUFFER *pVBVA = pCmdVbva->pVBVA; + + uint32_t indexRecordFirst = pVBVA->indexRecordFirst; + uint32_t indexRecordFree = pVBVA->indexRecordFree; + + Log(("first = %d, free = %d\n", + indexRecordFirst, indexRecordFree)); + + if (indexRecordFirst == indexRecordFree) + { + /* No records to process. Return without assigning output variables. */ + return VINF_EOF; + } + + uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVBVA->aRecords[indexRecordFirst].cbRecord); + + /* A new record need to be processed. */ + if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL) + { + /* the record is being recorded, try again */ + return VINF_TRY_AGAIN; + } + + uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL; + + if (!cbRecord) + { + /* the record is being recorded, try again */ + return VINF_TRY_AGAIN; + } + + /* we should not get partial commands here actually */ + Assert(cbRecord); + + /* The size of largest contiguous chunk in the ring biffer. */ + uint32_t u32BytesTillBoundary = pVBVA->cbData - pVBVA->off32Data; + + /* The pointer to data in the ring buffer. */ + uint8_t *pSrc = &pVBVA->au8Data[pVBVA->off32Data]; + + /* Fetch or point the data. */ + if (u32BytesTillBoundary >= cbRecord) + { + /* The command does not cross buffer boundary. Return address in the buffer. */ + *ppCmd = pSrc; + *pcbCmd = cbRecord; + return VINF_SUCCESS; + } + + LogRel(("CmdVbva: cross-bound writes unsupported\n")); + return VERR_INVALID_STATE; +} + +static void VBoxVBVAExHPDataCompleteCmd(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint32_t cbCmd) +{ + VBVABUFFER *pVBVA = pCmdVbva->pVBVA; + pVBVA->off32Data = (pVBVA->off32Data + cbCmd) % pVBVA->cbData; + + pVBVA->indexRecordFirst = (pVBVA->indexRecordFirst + 1) % RT_ELEMENTS(pVBVA->aRecords); +} + +static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc) +{ + if (pCtl->pfnComplete) + pCtl->pfnComplete(pCmdVbva, pCtl, rc, pCtl->pvComplete); + else + VBoxVBVAExHCtlFree(pCmdVbva, pCtl); +} + +static VBVAEXHOST_DATA_TYPE vboxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd) +{ + Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); + VBVAEXHOSTCTL*pCtl; + bool fHostClt; + + for(;;) + { + pCtl = vboxVBVAExHPCheckCtl(pCmdVbva, &fHostClt, false); + if (pCtl) + { + if (fHostClt) + { + if (!vboxVBVAExHPCheckProcessCtlInternal(pCmdVbva, pCtl)) + { + *ppCmd = (uint8_t*)pCtl; + *pcbCmd = sizeof (*pCtl); + return VBVAEXHOST_DATA_TYPE_HOSTCTL; + } + } + else + { + *ppCmd = (uint8_t*)pCtl; + *pcbCmd = sizeof (*pCtl); + return VBVAEXHOST_DATA_TYPE_GUESTCTL; + } + } + + if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) <= VBVAEXHOSTCONTEXT_ESTATE_PAUSED) + return VBVAEXHOST_DATA_TYPE_NO_DATA; + + int rc = vboxVBVAExHPCmdGet(pCmdVbva, ppCmd, pcbCmd); + 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 */ + WARN(("Warning: vboxVBVAExHCmdGet returned unexpected status %d\n", rc)); + 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) +{ + VBVAEXHOST_DATA_TYPE enmType = vboxVBVAExHPDataGet(pCmdVbva, ppCmd, pcbCmd); + if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA) + { + vboxVBVAExHPHgEventClear(pCmdVbva); + vboxVBVAExHPProcessorRelease(pCmdVbva); + /* 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" + **/ + int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva); + if (RT_SUCCESS(rc)) + { + /* we are the processor now */ + enmType = vboxVBVAExHPDataGet(pCmdVbva, ppCmd, pcbCmd); + if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA) + { + vboxVBVAExHPProcessorRelease(pCmdVbva); + return VBVAEXHOST_DATA_TYPE_NO_DATA; + } + + vboxVBVAExHPHgEventSet(pCmdVbva); + } + } + + return enmType; +} + +DECLINLINE(bool) vboxVBVAExHSHasCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + VBVABUFFER *pVBVA = pCmdVbva->pVBVA; + + if (pVBVA) + { + uint32_t indexRecordFirst = pVBVA->indexRecordFirst; + uint32_t indexRecordFree = pVBVA->indexRecordFree; + + if (indexRecordFirst != indexRecordFree) + return true; + } + + return !!ASMAtomicReadU32(&pCmdVbva->u32cCtls); +} + +/* 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 */ +static int VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva); + if (RT_SUCCESS(rc)) + { + /* we are the processor now */ + if (vboxVBVAExHSHasCommands(pCmdVbva)) + { + vboxVBVAExHPHgEventSet(pCmdVbva); + return VINF_SUCCESS; + } + + vboxVBVAExHPProcessorRelease(pCmdVbva); + return VINF_EOF; + } + if (rc == VERR_SEM_BUSY) + return VINF_ALREADY_INITIALIZED; + return VERR_INVALID_STATE; +} + +static int VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + memset(pCmdVbva, 0, sizeof (*pCmdVbva)); + int rc = RTCritSectInit(&pCmdVbva->CltCritSect); + if (RT_SUCCESS(rc)) + { +#ifndef VBOXVDBG_MEMCACHE_DISABLE + rc = RTMemCacheCreate(&pCmdVbva->CtlCache, sizeof (VBVAEXHOSTCTL), + 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 + { + RTListInit(&pCmdVbva->GuestCtlList); + RTListInit(&pCmdVbva->HostCtlList); + pCmdVbva->i32State = VBVAEXHOSTCONTEXT_STATE_PROCESSING; + pCmdVbva->i32EnableState = VBVAEXHOSTCONTEXT_ESTATE_DISABLED; + return VINF_SUCCESS; + } +#ifndef VBOXVDBG_MEMCACHE_DISABLE + else + WARN(("RTMemCacheCreate failed %d\n", rc)); +#endif + } + else + WARN(("RTCritSectInit failed %d\n", rc)); + + return rc; +} + +DECLINLINE(bool) VBoxVBVAExHSIsEnabled(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + return (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) >= VBVAEXHOSTCONTEXT_ESTATE_PAUSED); +} + +static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA) +{ + if (VBoxVBVAExHSIsEnabled(pCmdVbva)) + return VINF_ALREADY_INITIALIZED; + + pCmdVbva->pVBVA = pVBVA; + pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0; + ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_ENABLED); + return VINF_SUCCESS; +} + +static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + if (!VBoxVBVAExHSIsEnabled(pCmdVbva)) + return VINF_SUCCESS; + + ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_DISABLED); + return VINF_SUCCESS; +} + +static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva) +{ + /* ensure the processor is stopped */ + Assert(pCmdVbva->i32State >= VBVAEXHOSTCONTEXT_STATE_LISTENING); + + /* ensure no one tries to submit the command */ + if (pCmdVbva->pVBVA) + pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0; + + Assert(RTListIsEmpty(&pCmdVbva->GuestCtlList)); + Assert(RTListIsEmpty(&pCmdVbva->HostCtlList)); + + RTCritSectDelete(&pCmdVbva->CltCritSect); + +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheDestroy(pCmdVbva->CtlCache); +#endif + + memset(pCmdVbva, 0, sizeof (*pCmdVbva)); +} + +/* 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; + + int32_t i32EnableState = ASMAtomicUoReadS32(&pCmdVbva->i32EnableState); + if (i32EnableState >= VBVAEXHOSTCONTEXT_ESTATE_PAUSED) + { + if (i32EnableState != VBVAEXHOSTCONTEXT_ESTATE_PAUSED) + { + WARN(("vbva not paused\n")); + return VERR_INVALID_STATE; + } + + rc = SSMR3PutU32(pSSM, (uint32_t)(((uint8_t*)pCmdVbva->pVBVA) - pu8VramBase)); + AssertRCReturn(rc, rc); + return VINF_SUCCESS; + } + + rc = SSMR3PutU32(pSSM, 0xffffffff); + AssertRCReturn(rc, rc); + + return VINF_SUCCESS; +} + +typedef enum +{ + VBVAEXHOSTCTL_SOURCE_GUEST = 0, + VBVAEXHOSTCTL_SOURCE_HOST_ANY, + VBVAEXHOSTCTL_SOURCE_HOST_ENABLED +} VBVAEXHOSTCTL_SOURCE; + + +static int VBoxVBVAExHCtlSubmit(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete) +{ + if ((enmSource == VBVAEXHOSTCTL_SOURCE_HOST_ENABLED) && !VBoxVBVAExHSIsEnabled(pCmdVbva)) + { + WARN(("cmd vbva not enabled\n")); + return VERR_INVALID_STATE; + } + + pCtl->pfnComplete = pfnComplete; + pCtl->pvComplete = pvComplete; + + int rc = RTCritSectEnter(&pCmdVbva->CltCritSect); + if (RT_SUCCESS(rc)) + { + if (enmSource > VBVAEXHOSTCTL_SOURCE_GUEST) + { + if ((enmSource == VBVAEXHOSTCTL_SOURCE_HOST_ENABLED) && !VBoxVBVAExHSIsEnabled(pCmdVbva)) + { + WARN(("cmd vbva not enabled\n")); + RTCritSectLeave(&pCmdVbva->CltCritSect); + return VERR_INVALID_STATE; + } + RTListAppend(&pCmdVbva->HostCtlList, &pCtl->Node); + } + else + RTListAppend(&pCmdVbva->GuestCtlList, &pCtl->Node); + + ASMAtomicIncU32(&pCmdVbva->u32cCtls); + + RTCritSectLeave(&pCmdVbva->CltCritSect); + + rc = VBoxVBVAExHSCheckCommands(pCmdVbva); + } + else + WARN(("RTCritSectEnter failed %d\n", rc)); + + 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")); + uint32_t u32; + int rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + if (u32 != 0xffffffff) + { + VBVABUFFER *pVBVA = (VBVABUFFER*)pu8VramBase + u32; + rc = VBoxVBVAExHSEnable(pCmdVbva, pVBVA); + AssertRCReturn(rc, rc); + return VBoxVBVAExHSCheckCommands(pCmdVbva); + } + + return VINF_SUCCESS; +} + typedef struct VBOXVDMAHOST { PHGSMIINSTANCE pHgsmi; PVGASTATE pVGAState; VBVAEXHOSTCONTEXT CmdVbva; + VBOXVDMATHREAD Thread; + VBOXCRCMD_SVRINFO CrSrvInfo; + VBVAEXHOSTCTL* pCurRemainingHostCtl; #ifdef VBOX_VDMA_WITH_WATCHDOG PTMTIMERR3 WatchDogTimer; #endif -#ifdef VBOX_VDMA_WITH_WORKERTHREAD - VBOXVDMAPIPE Pipe; - HGSMILIST PendingList; - RTTHREAD hWorkerThread; - VBOXVDMAPIPE_CMD_POOL CmdPool; -#endif } VBOXVDMAHOST, *PVBOXVDMAHOST; +int VBoxVDMAThreadNotifyConstructSucceeded(PVBOXVDMATHREAD pThread) +{ + Assert(pThread->u32State == VBOXVDMATHREAD_STATE_TERMINATED); + pThread->u32State = VBOXVDMATHREAD_STATE_CREATED; + int rc = RTSemEventSignal(pThread->hClientEvent); + AssertRC(rc); + return VINF_SUCCESS; +} + +int VBoxVDMAThreadNotifyConstructFailed(PVBOXVDMATHREAD pThread) +{ + Assert(pThread->u32State == VBOXVDMATHREAD_STATE_TERMINATED); + int rc = RTSemEventSignal(pThread->hClientEvent); + AssertRC(rc); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + return rc; +} + +DECLINLINE(bool) VBoxVDMAThreadIsTerminating(PVBOXVDMATHREAD pThread) +{ + return ASMAtomicUoReadU32(&pThread->u32State) == VBOXVDMATHREAD_STATE_TERMINATING; +} + +int VBoxVDMAThreadCreate(PVBOXVDMATHREAD pThread, PFNRTTHREAD pfnThread, void *pvThread) +{ + int rc = RTSemEventCreate(&pThread->hEvent); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pThread->hClientEvent); + if (RT_SUCCESS(rc)) + { + pThread->u32State = VBOXVDMATHREAD_STATE_TERMINATED; + rc = RTThreadCreate(&pThread->hWorkerThread, pfnThread, pvThread, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "VDMA"); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(pThread->hClientEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + if (pThread->u32State == VBOXVDMATHREAD_STATE_CREATED) + return VINF_SUCCESS; + WARN(("thread routine failed the initialization\n")); + rc = VERR_INVALID_STATE; + } + else + WARN(("RTSemEventWait failed %d\n", rc)); + + RTThreadWait(pThread->hWorkerThread, RT_INDEFINITE_WAIT, NULL); + } + else + WARN(("RTThreadCreate failed %d\n", rc)); + + RTSemEventDestroy(pThread->hClientEvent); + } + else + WARN(("RTSemEventCreate failed %d\n", rc)); + + RTSemEventDestroy(pThread->hEvent); + } + else + WARN(("RTSemEventCreate failed %d\n", rc)); + + return rc; +} + +DECLINLINE(int) VBoxVDMAThreadEventNotify(PVBOXVDMATHREAD pThread) +{ + int rc = RTSemEventSignal(pThread->hEvent); + AssertRC(rc); + return rc; +} + +DECLINLINE(int) VBoxVDMAThreadEventWait(PVBOXVDMATHREAD pThread, RTMSINTERVAL cMillies) +{ + int rc = RTSemEventWait(pThread->hEvent, cMillies); + AssertRC(rc); + return rc; +} + +void VBoxVDMAThreadMarkTerminating(PVBOXVDMATHREAD pThread) +{ + Assert(pThread->u32State == VBOXVDMATHREAD_STATE_CREATED); + ASMAtomicWriteU32(&pThread->u32State, VBOXVDMATHREAD_STATE_TERMINATING); +} + +void VBoxVDMAThreadTerm(PVBOXVDMATHREAD pThread) +{ + int rc; + if (ASMAtomicReadU32(&pThread->u32State) != VBOXVDMATHREAD_STATE_TERMINATING) + { + VBoxVDMAThreadMarkTerminating(pThread); + rc = VBoxVDMAThreadEventNotify(pThread); + AssertRC(rc); + } + rc = RTThreadWait(pThread->hWorkerThread, RT_INDEFINITE_WAIT, NULL); + AssertRC(rc); + RTSemEventDestroy(pThread->hClientEvent); + RTSemEventDestroy(pThread->hEvent); +} + +static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource); #ifdef VBOX_WITH_CRHGSMI @@ -248,7 +864,7 @@ static int vboxVDMACrCtlPost(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd AssertRC(rc); if(RT_SUCCESS(rc)) { - rc = vboxVDMACrCtlPostAsync (pVGAState, pCmd, cbCmd, vboxVDMACrCtlCbSetEvent, (void*)hComplEvent); + rc = vboxVDMACrCtlPostAsync(pVGAState, pCmd, cbCmd, vboxVDMACrCtlCbSetEvent, (void*)hComplEvent); #ifdef DEBUG_misha AssertRC(rc); #endif @@ -270,17 +886,279 @@ static int vboxVDMACrCtlPost(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd return rc; } -static void vboxVDMACrCmdNotifyPerform(struct VBOXVDMAHOST *pVdma) +typedef struct VDMA_VBVA_CTL_CYNC_COMPLETION { + int rc; + RTSEMEVENT hEvent; +} VDMA_VBVA_CTL_CYNC_COMPLETION; + +static DECLCALLBACK(void) vboxVDMACrHgcmSubmitSyncCompletion(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion) +{ + VDMA_VBVA_CTL_CYNC_COMPLETION *pData = (VDMA_VBVA_CTL_CYNC_COMPLETION*)pvCompletion; + pData->rc = rc; + rc = RTSemEventSignal(pData->hEvent); + if (!RT_SUCCESS(rc)) + WARN(("RTSemEventSignal failed %d\n", rc)); +} + +static int vboxVDMACrHgcmSubmitSync(struct VBOXVDMAHOST *pVdma, VBOXCRCMDCTL* pCtl, uint32_t cbCtl) +{ + VDMA_VBVA_CTL_CYNC_COMPLETION Data; + Data.rc = VERR_NOT_IMPLEMENTED; + int rc = RTSemEventCreate(&Data.hEvent); + if (!RT_SUCCESS(rc)) + { + WARN(("RTSemEventCreate failed %d\n", rc)); + return rc; + } + PVGASTATE pVGAState = pVdma->pVGAState; - pVGAState->pDrv->pfnCrCmdNotifyCmds(pVGAState->pDrv); + rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCtl, cbCtl, vboxVDMACrHgcmSubmitSyncCompletion, &Data); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + rc = Data.rc; + if (!RT_SUCCESS(rc)) + { + WARN(("pfnCrHgcmCtlSubmit command failed %d\n", rc)); + } + + } + else + WARN(("RTSemEventWait failed %d\n", rc)); + } + else + WARN(("pfnCrHgcmCtlSubmit failed %d\n", rc)); + + + RTSemEventDestroy(Data.hEvent); + + return rc; +} + +static DECLCALLBACK(uint8_t*) vboxVDMACrHgcmHandleEnableRemainingHostCommand(HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hClient, uint32_t *pcbCtl, int prevCmdRc) +{ + struct VBOXVDMAHOST *pVdma = hClient; + if (!pVdma->pCurRemainingHostCtl) + { + /* disable VBVA, all subsequent host commands will go HGCM way */ + VBoxVBVAExHSDisable(&pVdma->CmdVbva); + } + else + { + VBoxVBVAExHPDataCompleteCtl(&pVdma->CmdVbva, pVdma->pCurRemainingHostCtl, prevCmdRc); + } + + pVdma->pCurRemainingHostCtl = VBoxVBVAExHPCheckHostCtlOnDisable(&pVdma->CmdVbva); + if (pVdma->pCurRemainingHostCtl) + { + *pcbCtl = pVdma->pCurRemainingHostCtl->u.cmd.cbCmd; + return pVdma->pCurRemainingHostCtl->u.cmd.pu8Cmd; + } + + *pcbCtl = 0; + return NULL; +} + +static int vboxVDMACrHgcmHandleEnable(struct VBOXVDMAHOST *pVdma) +{ + VBOXCRCMDCTL_ENABLE Enable; + Enable.Hdr.enmType = VBOXCRCMDCTL_TYPE_ENABLE; + Enable.hRHCmd = pVdma; + Enable.pfnRHCmd = vboxVDMACrHgcmHandleEnableRemainingHostCommand; + + int rc = vboxVDMACrHgcmSubmitSync(pVdma, &Enable.Hdr, sizeof (Enable)); + Assert(!pVdma->pCurRemainingHostCtl); + if (RT_SUCCESS(rc)) + { + Assert(!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)); + return VINF_SUCCESS; + } + + Assert(VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)); + WARN(("vboxVDMACrHgcmSubmitSync failed %d\n", rc)); + + return rc; } +static int vdmaVBVAEnableProcess(struct VBOXVDMAHOST *pVdma, uint32_t u32Offset) +{ + if (VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)) + { + WARN(("vdma VBVA is already enabled\n")); + return VERR_INVALID_STATE; + } + + VBVABUFFER *pVBVA = (VBVABUFFER *)HGSMIOffsetToPointerHost(pVdma->pHgsmi, u32Offset); + if (!pVBVA) + { + WARN(("invalid offset %d\n", u32Offset)); + return VERR_INVALID_PARAMETER; + } + + if (!pVdma->CrSrvInfo.pfnEnable) + { +#ifdef DEBUG_misha + WARN(("pfnEnable is NULL\n")); + return VERR_NOT_SUPPORTED; +#endif + } + + int rc = VBoxVBVAExHSEnable(&pVdma->CmdVbva, pVBVA); + if (RT_SUCCESS(rc)) + { + VBOXCRCMDCTL Ctl; + Ctl.enmType = VBOXCRCMDCTL_TYPE_DISABLE; + rc = vboxVDMACrHgcmSubmitSync(pVdma, &Ctl, sizeof (Ctl)); + if (RT_SUCCESS(rc)) + { + PVGASTATE pVGAState = pVdma->pVGAState; + VBOXCRCMD_SVRENABLE_INFO Info; + Info.hCltScr = pVGAState->pDrv; + Info.pfnCltScrUpdateBegin = pVGAState->pDrv->pfnVBVAUpdateBegin; + Info.pfnCltScrUpdateProcess = pVGAState->pDrv->pfnVBVAUpdateProcess; + Info.pfnCltScrUpdateEnd = pVGAState->pDrv->pfnVBVAUpdateEnd; + rc = pVdma->CrSrvInfo.pfnEnable(pVdma->CrSrvInfo.hSvr, &Info); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + else + WARN(("pfnEnable failed %d\n", rc)); + + vboxVDMACrHgcmHandleEnable(pVdma); + } + else + WARN(("vboxVDMACrHgcmSubmitSync failed %d\n", rc)); + + VBoxVBVAExHSDisable(&pVdma->CmdVbva); + } + else + WARN(("VBoxVBVAExHSEnable failed %d\n", rc)); + + return rc; +} + +static int vdmaVBVADisableProcess(struct VBOXVDMAHOST *pVdma) +{ + if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)) + { + Log(("vdma VBVA is already disabled\n")); + return VINF_SUCCESS; + } + + int rc = pVdma->CrSrvInfo.pfnDisable(pVdma->CrSrvInfo.hSvr); + 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 */ + rc = vboxVDMACrHgcmHandleEnable(pVdma); + if (RT_SUCCESS(rc)) + return rc; + + PVGASTATE pVGAState = pVdma->pVGAState; + VBOXCRCMD_SVRENABLE_INFO Info; + Info.hCltScr = pVGAState->pDrv; + Info.pfnCltScrUpdateBegin = pVGAState->pDrv->pfnVBVAUpdateBegin; + Info.pfnCltScrUpdateProcess = pVGAState->pDrv->pfnVBVAUpdateProcess; + Info.pfnCltScrUpdateEnd = pVGAState->pDrv->pfnVBVAUpdateEnd; + pVdma->CrSrvInfo.pfnEnable(pVdma->CrSrvInfo.hSvr, &Info); + } + else + WARN(("pfnDisable failed %d\n", rc)); + + return rc; +} + +static int vboxVDMACrHostCtlProcess(struct VBOXVDMAHOST *pVdma, VBVAEXHOSTCTL *pCmd) +{ + switch (pCmd->enmType) + { + case VBVAEXHOSTCTL_TYPE_HH_SAVESTATE: + if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)) + { + WARN(("VBVAEXHOSTCTL_TYPE_HH_SAVESTATE for disabled vdma VBVA\n")); + return VERR_INVALID_STATE; + } + return pVdma->CrSrvInfo.pfnSaveState(pVdma->CrSrvInfo.hSvr, pCmd->u.state.pSSM); + case VBVAEXHOSTCTL_TYPE_HH_LOADSTATE: + if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)) + { + 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); + case VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE: + if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)) + { + WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n")); + return VERR_INVALID_STATE; + } + return pVdma->CrSrvInfo.pfnHostCtl(pVdma->CrSrvInfo.hSvr, pCmd->u.cmd.pu8Cmd, pCmd->u.cmd.cbCmd); + case VBVAEXHOSTCTL_TYPE_HH_TERM: + { + int rc = vdmaVBVADisableProcess(pVdma); + if (!RT_SUCCESS(rc)) + { + WARN(("vdmaVBVADisableProcess failed %d\n", rc)); + return rc; + } + + VBoxVDMAThreadMarkTerminating(&pVdma->Thread); + return VINF_SUCCESS; + } + case VBVAEXHOSTCTL_TYPE_HH_RESET: + { + int rc = vdmaVBVADisableProcess(pVdma); + if (!RT_SUCCESS(rc)) + { + WARN(("vdmaVBVADisableProcess failed %d\n", rc)); + return rc; + } + return VINF_SUCCESS; + } + default: + WARN(("unexpected host ctl type %d\n", pCmd->enmType)); + return VERR_INVALID_PARAMETER; + } +} + +static int vboxVDMACrGuestCtlProcess(struct VBOXVDMAHOST *pVdma, VBVAEXHOSTCTL *pCmd) +{ + switch (pCmd->enmType) + { + case VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE: + if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva)) + { + WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n")); + return VERR_INVALID_STATE; + } + return pVdma->CrSrvInfo.pfnGuestCtl(pVdma->CrSrvInfo.hSvr, pCmd->u.cmd.pu8Cmd, pCmd->u.cmd.cbCmd); + case VBVAEXHOSTCTL_TYPE_GH_ENABLE_DISABLE: + { + VBVAENABLE *pEnable = (VBVAENABLE *)pCmd->u.cmd.pu8Cmd; + Assert(pCmd->u.cmd.cbCmd == sizeof (VBVAENABLE)); + if ((pEnable->u32Flags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_ENABLE) + { + uint32_t u32Offset = pEnable->u32Offset; + return vdmaVBVAEnableProcess(pVdma, u32Offset); + } + + return vdmaVBVADisableProcess(pVdma); + } + default: + WARN(("unexpected ctl type %d\n", pCmd->enmType)); + return VERR_INVALID_PARAMETER; + } +} + + /* * @returns * */ -static int vboxVDMACrCmdPreprocess(struct VBOXVDMAHOST *pVdma, uint8_t* pu8Cmd, uint32_t cbCmd) +static int vboxVDMACrCmdProcess(struct VBOXVDMAHOST *pVdma, uint8_t* pu8Cmd, uint32_t cbCmd) { if (*pu8Cmd == VBOXCMDVBVA_OPTYPE_NOP) return VINF_EOF; @@ -298,96 +1176,74 @@ static int vboxVDMACrCmdPreprocess(struct VBOXVDMAHOST *pVdma, uint8_t* pu8Cmd, switch (pCmd->u8OpCode) { case VBOXCMDVBVA_OPTYPE_NOPCMD: - pCmd->i8Result = 0; + pCmd->u.i8Result = 0; return VINF_EOF; default: return VINF_SUCCESS; } } -static DECLCALLBACK(int) vboxVDMACrCmdCltCmdGet(HVBOXCRCMDCLT hClt, PVBOXCMDVBVA_HDR *ppNextCmd, uint32_t *pcbNextCmd) +static DECLCALLBACK(int) vboxVDMACrCmdEnable(HVBOXCRCMDSVR hSvr, VBOXCRCMD_SVRENABLE_INFO *pInfo) { - struct VBOXVDMAHOST *pVdma = hClt; + return VINF_SUCCESS; +} - VBoxVBVAExHPCmdCheckRelease(&pVdma->CmdVbva); +static DECLCALLBACK(void) vboxVDMACrCmdDisable(HVBOXCRCMDSVR hSvr) +{ +} - uint32_t cbCmd; - uint8_t *pu8Cmd; +static DECLCALLBACK(int) vboxVDMACrCmdCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd) +{ + return VERR_NOT_SUPPORTED; +} - for(;;) +static DECLCALLBACK(int) vboxVDMACrCmdCmd(HVBOXCRCMDSVR hSvr, PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd) +{ + switch (pCmd->u8OpCode) { - int rc = VBoxVBVAExHPCmdGet(&pVdma->CmdVbva, &pu8Cmd, &cbCmd); - switch (rc) +#if 0 + case VBOXCMDVBVA_OPTYPE_BLT_OFFPRIMSZFMT_OR_ID: { - case VINF_SUCCESS: - { - rc = vboxVDMACrCmdPreprocess(pVdma, pu8Cmd, cbCmd); - switch (rc) - { - case VINF_SUCCESS: - *ppNextCmd = (PVBOXCMDVBVA_HDR)pu8Cmd; - *pcbNextCmd = cbCmd; - return VINF_SUCCESS; - case VINF_EOF: - continue; - default: - Assert(!RT_FAILURE(rc)); - return RT_FAILURE(rc) ? rc : VERR_INTERNAL_ERROR; - } - break; - } - case VINF_EOF: - return VINF_EOF; - case VINF_PERMISSION_DENIED: - /* processing was paused, processing state was released, only VBoxVBVAExHS*** calls are now allowed */ - return VINF_EOF; - case VINF_INTERRUPTED: - /* command processing was interrupted, processor state remains set. client can process any commands */ - vboxVDMACrCmdNotifyPerform(pVdma); - return VINF_EOF; - default: - Assert(!RT_FAILURE(rc)); - return RT_FAILURE(rc) ? rc : VERR_INTERNAL_ERROR; + crVBoxServerCrCmdBltProcess(pCmd, cbCmd); + break; } +#endif + default: + WARN(("unsupported command\n")); + pCmd->u.i8Result = -1; } - - WARN(("Warning: vboxVDMACrCmdCltCmdGet unexpected state\n")); - return VERR_INTERNAL_ERROR; -} - -static DECLCALLBACK(int) vboxVDMACrCmdCltDmGet(HVBOXCRCMDCLT hClt, uint32_t idScreen, struct VBVAINFOSCREEN *pScreen, void **ppvVram) -{ - struct VBOXVDMAHOST *pVdma = hClt; - PVGASTATE pVGAState = pVdma->pVGAState; - - return VBVAGetScreenInfo(pVGAState, idScreen, pScreen, ppvVram); + return VINF_SUCCESS; } static int vboxVDMACrCtlHgsmiSetup(struct VBOXVDMAHOST *pVdma) { - PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pCmd; - pCmd = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP) vboxVDMACrCtlCreate (VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP, - sizeof (*pCmd)); + PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pCmd = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP) + vboxVDMACrCtlCreate (VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP, sizeof (*pCmd)); + int rc = VERR_NO_MEMORY; if (pCmd) { - VBOXCRCMD_CLTINFO CltInfo; - CltInfo.hClient = pVdma; - CltInfo.pfnCmdGet = vboxVDMACrCmdCltCmdGet; - CltInfo.pfnDmGet = vboxVDMACrCmdCltDmGet; PVGASTATE pVGAState = pVdma->pVGAState; pCmd->pvVRamBase = pVGAState->vram_ptrR3; pCmd->cbVRam = pVGAState->vram_size; - pCmd->pCrCmdClientInfo = &CltInfo; - int rc = vboxVDMACrCtlPost(pVGAState, &pCmd->Hdr, sizeof (*pCmd)); - Assert(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED); + rc = vboxVDMACrCtlPost(pVGAState, &pCmd->Hdr, sizeof (*pCmd)); if (RT_SUCCESS(rc)) { rc = vboxVDMACrCtlGetRc(&pCmd->Hdr); + if (RT_SUCCESS(rc)) + pVdma->CrSrvInfo = pCmd->CrCmdServerInfo; + else if (rc != VERR_NOT_SUPPORTED) + WARN(("vboxVDMACrCtlGetRc returned %d\n", rc)); } + else + WARN(("vboxVDMACrCtlPost failed %d\n", rc)); + vboxVDMACrCtlRelease(&pCmd->Hdr); - return rc; } - return VERR_NO_MEMORY; + + if (!RT_SUCCESS(rc)) + memset(&pVdma->CrSrvInfo, 0, sizeof (pVdma->CrSrvInfo)); + + return rc; } static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer); @@ -883,236 +1739,52 @@ static int vboxVDMACmdExec(PVBOXVDMAHOST pVdma, const uint8_t *pvBuffer, uint32_ return VERR_INVALID_STATE; } -#ifdef VBOX_VDMA_WITH_WORKERTHREAD - -int vboxVDMAPipeConstruct(PVBOXVDMAPIPE pPipe) +static DECLCALLBACK(int) vboxVDMAWorkerThread(RTTHREAD ThreadSelf, void *pvUser) { - int rc = RTSemEventCreate(&pPipe->hEvent); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - rc = RTCritSectInit(&pPipe->hCritSect); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - pPipe->enmState = VBOXVDMAPIPE_STATE_CREATED; - pPipe->bNeedNotify = true; - return VINF_SUCCESS; -// RTCritSectDelete(pPipe->hCritSect); - } - RTSemEventDestroy(pPipe->hEvent); - } - return rc; -} + PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvUser; + PVGASTATE pVGAState = pVdma->pVGAState; + VBVAEXHOSTCONTEXT *pCmdVbva = &pVdma->CmdVbva; + PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi; + uint8_t *pCmd; + uint32_t cbCmd; -int vboxVDMAPipeOpenServer(PVBOXVDMAPIPE pPipe) -{ - int rc = RTCritSectEnter(&pPipe->hCritSect); - AssertRC(rc); - if (RT_SUCCESS(rc)) + int rc = VBoxVDMAThreadNotifyConstructSucceeded(&pVdma->Thread); + if (!RT_SUCCESS(rc)) { - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED); - switch (pPipe->enmState) - { - case VBOXVDMAPIPE_STATE_CREATED: - pPipe->enmState = VBOXVDMAPIPE_STATE_OPENNED; - pPipe->bNeedNotify = false; - rc = VINF_SUCCESS; - break; - case VBOXVDMAPIPE_STATE_OPENNED: - pPipe->bNeedNotify = false; - rc = VINF_ALREADY_INITIALIZED; - break; - default: - AssertBreakpoint(); - rc = VERR_INVALID_STATE; - break; - } - - RTCritSectLeave(&pPipe->hCritSect); + WARN(("VBoxVDMAThreadNotifyConstructSucceeded failed %d\n", rc)); + return rc; } - return rc; -} -int vboxVDMAPipeCloseServer(PVBOXVDMAPIPE pPipe) -{ - int rc = RTCritSectEnter(&pPipe->hCritSect); - AssertRC(rc); - if (RT_SUCCESS(rc)) + while (!VBoxVDMAThreadIsTerminating(&pVdma->Thread)) { - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED - || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING); - switch (pPipe->enmState) + VBVAEXHOST_DATA_TYPE enmType = VBoxVBVAExHPDataGet(pCmdVbva, &pCmd, &cbCmd); + switch (enmType) { - case VBOXVDMAPIPE_STATE_CLOSING: - pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED; - rc = VINF_SUCCESS; - break; - case VBOXVDMAPIPE_STATE_CLOSED: - rc = VINF_ALREADY_INITIALIZED; - break; - default: - AssertBreakpoint(); - rc = VERR_INVALID_STATE; + case VBVAEXHOST_DATA_TYPE_CMD: + vboxVDMACrCmdProcess(pVdma, pCmd, cbCmd); + VBoxVBVAExHPDataCompleteCmd(pCmdVbva, cbCmd); + VBVARaiseIrqNoWait(pVGAState, 0); break; - } - - RTCritSectLeave(&pPipe->hCritSect); - } - return rc; -} - -int vboxVDMAPipeCloseClient(PVBOXVDMAPIPE pPipe) -{ - int rc = RTCritSectEnter(&pPipe->hCritSect); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - bool bNeedNotify = false; - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED - || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED - || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED); - switch (pPipe->enmState) - { - case VBOXVDMAPIPE_STATE_OPENNED: - pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSING; - bNeedNotify = pPipe->bNeedNotify; - pPipe->bNeedNotify = false; + case VBVAEXHOST_DATA_TYPE_HOSTCTL: + rc = vboxVDMACrHostCtlProcess(pVdma, (VBVAEXHOSTCTL*)pCmd); + VBoxVBVAExHPDataCompleteCtl(pCmdVbva, (VBVAEXHOSTCTL*)pCmd, rc); break; - case VBOXVDMAPIPE_STATE_CREATED: - pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED; - pPipe->bNeedNotify = false; + case VBVAEXHOST_DATA_TYPE_GUESTCTL: + rc = vboxVDMACrGuestCtlProcess(pVdma, (VBVAEXHOSTCTL*)pCmd); + VBoxVBVAExHPDataCompleteCtl(pCmdVbva, (VBVAEXHOSTCTL*)pCmd, rc); break; - case VBOXVDMAPIPE_STATE_CLOSED: - rc = VINF_ALREADY_INITIALIZED; + case VBVAEXHOST_DATA_TYPE_NO_DATA: + rc = VBoxVDMAThreadEventWait(&pVdma->Thread, RT_INDEFINITE_WAIT); + AssertRC(rc); break; default: - AssertBreakpoint(); - rc = VERR_INVALID_STATE; + WARN(("unexpected type %d\n", enmType)); break; } - - RTCritSectLeave(&pPipe->hCritSect); - - if (bNeedNotify) - { - rc = RTSemEventSignal(pPipe->hEvent); - AssertRC(rc); - } } - return rc; -} - - -typedef DECLCALLBACK(bool) FNHVBOXVDMARWCB(PVBOXVDMAPIPE pPipe, void *pvCallback); -typedef FNHVBOXVDMARWCB *PFNHVBOXVDMARWCB; - -int vboxVDMAPipeModifyServer(PVBOXVDMAPIPE pPipe, PFNHVBOXVDMARWCB pfnCallback, void * pvCallback) -{ - int rc = RTCritSectEnter(&pPipe->hCritSect); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - do - { - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED - || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING); - - if (pPipe->enmState >= VBOXVDMAPIPE_STATE_OPENNED) - { - bool bProcessing = pfnCallback(pPipe, pvCallback); - pPipe->bNeedNotify = !bProcessing; - if (bProcessing) - { - RTCritSectLeave(&pPipe->hCritSect); - rc = VINF_SUCCESS; - break; - } - else if (pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING) - { - pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED; - RTCritSectLeave(&pPipe->hCritSect); - rc = VINF_EOF; - break; - } - } - else - { - AssertBreakpoint(); - rc = VERR_INVALID_STATE; - RTCritSectLeave(&pPipe->hCritSect); - break; - } - - RTCritSectLeave(&pPipe->hCritSect); - - rc = RTSemEventWait(pPipe->hEvent, RT_INDEFINITE_WAIT); - AssertRC(rc); - if (!RT_SUCCESS(rc)) - break; - - rc = RTCritSectEnter(&pPipe->hCritSect); - AssertRC(rc); - if (!RT_SUCCESS(rc)) - break; - } while (1); - } - - return rc; -} - -int vboxVDMAPipeModifyClient(PVBOXVDMAPIPE pPipe, PFNHVBOXVDMARWCB pfnCallback, void * pvCallback) -{ - int rc = RTCritSectEnter(&pPipe->hCritSect); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - bool bNeedNotify = false; - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED); - if (pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED) - { - bool bModified = pfnCallback(pPipe, pvCallback); - if (bModified) - { - bNeedNotify = pPipe->bNeedNotify; - pPipe->bNeedNotify = false; - } - } - else - rc = VERR_INVALID_STATE; - - RTCritSectLeave(&pPipe->hCritSect); - - if (bNeedNotify) - { - rc = RTSemEventSignal(pPipe->hEvent); - AssertRC(rc); - } - } - return rc; -} - -int vboxVDMAPipeDestruct(PVBOXVDMAPIPE pPipe) -{ - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED - || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED); - /* ensure the pipe is closed */ - vboxVDMAPipeCloseClient(pPipe); - - Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED); - - if (pPipe->enmState != VBOXVDMAPIPE_STATE_CLOSED) - return VERR_INVALID_STATE; - - int rc = RTCritSectDelete(&pPipe->hCritSect); - AssertRC(rc); - - rc = RTSemEventDestroy(pPipe->hEvent); - AssertRC(rc); return VINF_SUCCESS; } -#endif static void vboxVDMACommandProcess(PVBOXVDMAHOST pVdma, PVBOXVDMACBUF_DR pCmd, uint32_t cbCmd) { @@ -1181,87 +1853,6 @@ static void vboxVDMAControlProcess(PVBOXVDMAHOST pVdma, PVBOXVDMA_CTL pCmd) AssertRC(rc); } -#ifdef VBOX_VDMA_WITH_WORKERTHREAD -typedef struct -{ - struct VBOXVDMAHOST *pVdma; - VBOXVDMAPIPE_CMD_BODY Cmd; - bool bHasCmd; -} VBOXVDMACMD_PROCESS_CONTEXT, *PVBOXVDMACMD_PROCESS_CONTEXT; - -static DECLCALLBACK(bool) vboxVDMACommandProcessCb(PVBOXVDMAPIPE pPipe, void *pvCallback) -{ - PVBOXVDMACMD_PROCESS_CONTEXT pContext = (PVBOXVDMACMD_PROCESS_CONTEXT)pvCallback; - struct VBOXVDMAHOST *pVdma = pContext->pVdma; - HGSMILISTENTRY *pEntry = hgsmiListRemoveHead(&pVdma->PendingList); - if (pEntry) - { - PVBOXVDMAPIPE_CMD pPipeCmd = VBOXVDMAPIPE_CMD_FROM_ENTRY(pEntry); - Assert(pPipeCmd); - pContext->Cmd = pPipeCmd->Cmd; - hgsmiListPrepend(&pVdma->CmdPool.List, pEntry); - pContext->bHasCmd = true; - return true; - } - - pContext->bHasCmd = false; - return false; -} - -static DECLCALLBACK(int) vboxVDMAWorkerThread(RTTHREAD ThreadSelf, void *pvUser) -{ - PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvUser; - PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi; - VBOXVDMACMD_PROCESS_CONTEXT Context; - Context.pVdma = pVdma; - - int rc = vboxVDMAPipeOpenServer(&pVdma->Pipe); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - do - { - rc = vboxVDMAPipeModifyServer(&pVdma->Pipe, vboxVDMACommandProcessCb, &Context); - AssertRC(rc); - if (RT_SUCCESS(rc)) - { - switch (Context.Cmd.enmType) - { - case VBOXVDMAPIPE_CMD_TYPE_DMACMD: - { - PVBOXVDMACBUF_DR pDr = Context.Cmd.u.pDr; - vboxVDMACommandProcess(pVdma, pDr); - break; - } - case VBOXVDMAPIPE_CMD_TYPE_DMACTL: - { - PVBOXVDMA_CTL pCtl = Context.Cmd.u.pCtl; - vboxVDMAControlProcess(pVdma, pCtl); - break; - } - default: - AssertBreakpoint(); - break; - } - - if (rc == VINF_EOF) - { - rc = VINF_SUCCESS; - break; - } - } - else - break; - } while (1); - } - - /* always try to close the pipe to make sure the client side is notified */ - int tmpRc = vboxVDMAPipeCloseServer(&pVdma->Pipe); - AssertRC(tmpRc); - return rc; -} -#endif - #ifdef VBOX_VDMA_WITH_WATCHDOG static DECLCALLBACK(void) vboxVDMAWatchDogTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser) { @@ -1301,41 +1892,27 @@ int vboxVDMAConstruct(PVGASTATE pVGAState, uint32_t cPipeElements) "VDMA WatchDog Timer", &pVdma->WatchDogTimer); AssertRC(rc); #endif -#ifdef VBOX_VDMA_WITH_WORKERTHREAD - hgsmiListInit(&pVdma->PendingList); - rc = vboxVDMAPipeConstruct(&pVdma->Pipe); - AssertRC(rc); + rc = VBoxVBVAExHSInit(&pVdma->CmdVbva); if (RT_SUCCESS(rc)) { - rc = RTThreadCreate(&pVdma->hWorkerThread, vboxVDMAWorkerThread, pVdma, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "VDMA"); - AssertRC(rc); + rc = VBoxVDMAThreadCreate(&pVdma->Thread, vboxVDMAWorkerThread, pVdma); if (RT_SUCCESS(rc)) { - hgsmiListInit(&pVdma->CmdPool.List); - pVdma->CmdPool.cCmds = cPipeElements; - for (uint32_t i = 0; i < cPipeElements; ++i) - { - hgsmiListAppend(&pVdma->CmdPool.List, &pVdma->CmdPool.aCmds[i].Entry); - } -# if 0 //def VBOX_WITH_CRHGSMI - int tmpRc = vboxVDMACrCtlHgsmiSetup(pVdma); -# endif -#endif pVGAState->pVdma = pVdma; - VBoxVBVAExHSInit(&pVdma->CmdVbva); #ifdef VBOX_WITH_CRHGSMI int rcIgnored = vboxVDMACrCtlHgsmiSetup(pVdma); NOREF(rcIgnored); /** @todo is this ignoring intentional? */ #endif return VINF_SUCCESS; -#ifdef VBOX_VDMA_WITH_WORKERTHREAD } + else + WARN(("VBoxVDMAThreadCreate faile %d\n", rc)); - int tmpRc = vboxVDMAPipeDestruct(&pVdma->Pipe); - AssertRC(tmpRc); + VBoxVBVAExHSTerm(&pVdma->CmdVbva); } + else + WARN(("VBoxVBVAExHSInit faile %d\n", rc)); RTMemFree(pVdma); -#endif } else rc = VERR_OUT_OF_RESOURCES; @@ -1343,46 +1920,34 @@ int vboxVDMAConstruct(PVGASTATE pVGAState, uint32_t cPipeElements) return rc; } -int vboxVDMADestruct(struct VBOXVDMAHOST *pVdma) +int vboxVDMAReset(struct VBOXVDMAHOST *pVdma) { -#ifdef VBOX_VDMA_WITH_WORKERTHREAD - /* @todo: implement*/ - AssertBreakpoint(); -#endif - VBoxVBVAExHSTerm(&pVdma->CmdVbva); - RTMemFree(pVdma); + VBVAEXHOSTCTL Ctl; + Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_RESET; + int rc = vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY); + if (!RT_SUCCESS(rc)) + { + WARN(("vdmaVBVACtlSubmitSync failed %d\n", rc)); + return rc; + } return VINF_SUCCESS; } -#ifdef VBOX_VDMA_WITH_WORKERTHREAD -typedef struct -{ - struct VBOXVDMAHOST *pVdma; - VBOXVDMAPIPE_CMD_BODY Cmd; - bool bQueued; -} VBOXVDMACMD_SUBMIT_CONTEXT, *PVBOXVDMACMD_SUBMIT_CONTEXT; - -DECLCALLBACK(bool) vboxVDMACommandSubmitCb(PVBOXVDMAPIPE pPipe, void *pvCallback) +int vboxVDMADestruct(struct VBOXVDMAHOST *pVdma) { - PVBOXVDMACMD_SUBMIT_CONTEXT pContext = (PVBOXVDMACMD_SUBMIT_CONTEXT)pvCallback; - struct VBOXVDMAHOST *pVdma = pContext->pVdma; - HGSMILISTENTRY *pEntry = hgsmiListRemoveHead(&pVdma->CmdPool.List); - Assert(pEntry); - if (pEntry) + VBVAEXHOSTCTL Ctl; + Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_TERM; + int rc = vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY); + if (!RT_SUCCESS(rc)) { - PVBOXVDMAPIPE_CMD pPipeCmd = VBOXVDMAPIPE_CMD_FROM_ENTRY(pEntry); - pPipeCmd->Cmd = pContext->Cmd; - VBoxSHGSMICommandMarkAsynchCompletion(pContext->Cmd.u.pvCmd); - pContext->bQueued = true; - hgsmiListAppend(&pVdma->PendingList, pEntry); - return true; + WARN(("vdmaVBVACtlSubmitSync failed %d\n", rc)); + return rc; } - - /* @todo: should we try to flush some commands here? */ - pContext->bQueued = false; - return false; + VBoxVDMAThreadTerm(&pVdma->Thread); + VBoxVBVAExHSTerm(&pVdma->CmdVbva); + RTMemFree(pVdma); + return VINF_SUCCESS; } -#endif int vboxVDMASaveStateExecPrep(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM) { @@ -1545,377 +2110,201 @@ void vboxVDMACommand(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmd, uint32_t } /**/ -static int vboxVBVAExHSProcessorAcquire(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State != VBVAEXHOSTCONTEXT_STATE_STOPPED); - uint32_t oldState; - if (!ASMAtomicReadU32(&pCmdVbva->u32Pause)) +static int vdmaVBVACtlSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete) +{ + int rc = VBoxVBVAExHCtlSubmit(&pVdma->CmdVbva, pCtl, enmSource, pfnComplete, pvComplete); + if (RT_SUCCESS(rc)) { - if (ASMAtomicCmpXchgExU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_PROCESSING, VBVAEXHOSTCONTEXT_STATE_LISTENING, &oldState)) - return VINF_SUCCESS; - return oldState == VBVAEXHOSTCONTEXT_STATE_PROCESSING ? VERR_SEM_BUSY : VERR_INVALID_STATE; + if (rc == VINF_SUCCESS) + return VBoxVDMAThreadEventNotify(&pVdma->Thread); + else + Assert(rc == VINF_ALREADY_INITIALIZED); } - return VERR_INVALID_STATE; -} - -static bool vboxVBVAExHPCheckPause(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - if (!ASMAtomicReadU32(&pCmdVbva->u32Pause)) - return false; - - ASMAtomicWriteU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_PAUSED); - return true; -} - -static bool vboxVBVAExHPCheckOtherCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - return !!ASMAtomicUoReadU32(&pCmdVbva->u32cOtherCommands); -} - -static void vboxVBVAExHPProcessorRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - if (!vboxVBVAExHPCheckPause(pCmdVbva)) - ASMAtomicWriteU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_LISTENING); else - ASMAtomicWriteU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_PAUSED); -} + WARN(("VBoxVBVAExHCtlSubmit failed %d\n", rc)); -static void vboxVBVAExHPHgEventSet(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - ASMAtomicOrU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, VBVA_F_STATE_PROCESSING); -} - -static void vboxVBVAExHPHgEventClear(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - ASMAtomicAndU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, ~VBVA_F_STATE_PROCESSING); + return rc; } -static bool vboxVBVAExHPCmdCheckRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva) +static DECLCALLBACK(void) vboxCmdVBVACmdCtlGuestCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext) { - if (!pCmdVbva->cbCurData) - return false; - - VBVABUFFER *pVBVA = pCmdVbva->pVBVA; - pVBVA->off32Data = (pVBVA->off32Data + pCmdVbva->cbCurData) % pVBVA->cbData; - - pVBVA->indexRecordFirst = (pVBVA->indexRecordFirst + 1) % RT_ELEMENTS(pVBVA->aRecords); + PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvContext; + VBOXCMDVBVA_CTL *pGCtl = (VBOXCMDVBVA_CTL*)(pCtl->u.cmd.pu8Cmd - sizeof (VBOXCMDVBVA_CTL)); + AssertRC(rc); + pGCtl->i32Result = rc; - pCmdVbva->cbCurData = 0; + Assert(pVdma->pVGAState->fGuestCaps & VBVACAPS_COMPLETEGCMD_BY_IOREAD); + rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pGCtl); + AssertRC(rc); - return true; + VBoxVBVAExHCtlFree(pVbva, pCtl); } -static int vboxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd) +static int vdmaVBVACtlOpaqueSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL_SOURCE enmSource, uint8_t* pu8Cmd, uint32_t cbCmd, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete) { - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - VBVABUFFER *pVBVA = pCmdVbva->pVBVA; - - uint32_t indexRecordFirst = pVBVA->indexRecordFirst; - uint32_t indexRecordFree = pVBVA->indexRecordFree; - - Log(("first = %d, free = %d\n", - indexRecordFirst, indexRecordFree)); - - if (indexRecordFirst == indexRecordFree) - { - /* No records to process. Return without assigning output variables. */ - return VINF_EOF; - } - - uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVBVA->aRecords[indexRecordFirst].cbRecord); - - /* A new record need to be processed. */ - if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL) + VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE); + if (!pHCtl) { - /* the record is being recorded, try again */ - return VINF_TRY_AGAIN; + WARN(("VBoxVBVAExHCtlCreate failed\n")); + return VERR_NO_MEMORY; } - uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL; - - if (!cbRecord) - { - /* the record is being recorded, try again */ - return VINF_TRY_AGAIN; - } - - /* we should not get partial commands here actually */ - Assert(cbRecord); - - /* The size of largest contiguous chunk in the ring biffer. */ - uint32_t u32BytesTillBoundary = pVBVA->cbData - pVBVA->off32Data; - - /* The pointer to data in the ring buffer. */ - uint8_t *pSrc = &pVBVA->au8Data[pVBVA->off32Data]; - - /* Fetch or point the data. */ - if (u32BytesTillBoundary >= cbRecord) + pHCtl->u.cmd.pu8Cmd = pu8Cmd; + pHCtl->u.cmd.cbCmd = cbCmd; + int rc = vdmaVBVACtlSubmit(pVdma, pHCtl, enmSource, pfnComplete, pvComplete); + if (!RT_SUCCESS(rc)) { - /* The command does not cross buffer boundary. Return address in the buffer. */ - *ppCmd = pSrc; - *pcbCmd = cbRecord; - pCmdVbva->cbCurData = cbRecord; - return VINF_SUCCESS; + WARN(("vdmaVBVACtlSubmit failed rc %d\n", rc)); + return rc;; } - - LogRel(("CmdVbva: cross-bound writes unsupported\n")); - return VERR_INVALID_STATE; -} - -/* Resumes command processing - * @returns - same as VBoxVBVAExHSCheckCommands - */ -static int vboxVBVAExHSResume(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - Assert(pCmdVbva->u32State != VBVAEXHOSTCONTEXT_STATE_STOPPED); - - ASMAtomicWriteU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_LISTENING); - - return VBoxVBVAExHSCheckCommands(pCmdVbva); + return VINF_SUCCESS; } -/* pause the command processing. this will make the processor stop the command processing and release the processing state - * to resume the command processing the vboxVBVAExHSResume must be called */ -static void vboxVBVAExHSPause(struct VBVAEXHOSTCONTEXT *pCmdVbva) +static int vdmaVBVACtlOpaqueGuestSubmit(PVBOXVDMAHOST pVdma, VBOXCMDVBVA_CTL *pCtl, uint32_t cbCtl) { - Assert(pCmdVbva->u32State != VBVAEXHOSTCONTEXT_STATE_STOPPED); - - Assert(!pCmdVbva->u32Pause); - - ASMAtomicWriteU32(&pCmdVbva->u32Pause, 1); - - for(;;) - { - if (ASMAtomicCmpXchgU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_PAUSED, VBVAEXHOSTCONTEXT_STATE_LISTENING)) - break; - - if (ASMAtomicReadU32(&pCmdVbva->u32State) == VBVAEXHOSTCONTEXT_STATE_PAUSED) - break; - - RTThreadSleep(2); - } + Assert(cbCtl >= sizeof (VBOXCMDVBVA_CTL)); + VBoxSHGSMICommandMarkAsynchCompletion(pCtl); + int rc = vdmaVBVACtlOpaqueSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_GUEST, (uint8_t*)(pCtl+1), cbCtl - sizeof (VBOXCMDVBVA_CTL), vboxCmdVBVACmdCtlGuestCompletion, pVdma); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; - pCmdVbva->u32Pause = 0; + WARN(("vdmaVBVACtlOpaqueSubmit failed %d\n", rc)); + pCtl->i32Result = rc; + rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pCtl); + AssertRC(rc); + return VINF_SUCCESS; } -/* releases (completed) the command previously acquired by VBoxVBVAExHCmdGet - * for convenience can be called if no command is currently acquired - * in that case it will do nothing and return false. - * if the completion notification is needed returns true. */ -static bool VBoxVBVAExHPCmdCheckRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva) +static DECLCALLBACK(void) vboxCmdVBVACmdCtlHostCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvCompletion) { - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - return vboxVBVAExHPCmdCheckRelease(pCmdVbva); + VBOXCRCMDCTL* pVboxCtl = (VBOXCRCMDCTL*)pCtl->u.cmd.pu8Cmd; + if (pVboxCtl->pfnInternal) + ((PFNCRCTLCOMPLETION)pVboxCtl->pfnInternal)(pVboxCtl, pCtl->u.cmd.cbCmd, rc, pvCompletion); + VBoxVBVAExHCtlFree(pVbva, pCtl); } -/* - * @returns - * VINF_SUCCESS - new command is obtained - * VINF_EOF - processor has completed all commands and release the processing state, only VBoxVBVAExHS*** calls are now allowed - * VINF_PERMISSION_DENIED - processing was paused, processing state was released, only VBoxVBVAExHS*** calls are now allowed - * VINF_INTERRUPTED - command processing was interrupted, processor state remains set. client can process any commands, - * and call VBoxVBVAExHPCmdGet again for further processing - * VERR_** - error happened, most likely guest corrupted VBVA data - * - */ -static int VBoxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd) +static int vdmaVBVACtlOpaqueHostSubmit(PVBOXVDMAHOST pVdma, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, + PFNCRCTLCOMPLETION pfnCompletion, + void *pvCompletion) { - Assert(pCmdVbva->u32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING); - - for(;;) + pCmd->pfnInternal = (void(*)())pfnCompletion; + int rc = vdmaVBVACtlOpaqueSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_HOST_ENABLED, (uint8_t*)pCmd, cbCmd, vboxCmdVBVACmdCtlHostCompletion, pvCompletion); + if (!RT_SUCCESS(rc)) { - if (vboxVBVAExHPCheckPause(pCmdVbva)) - return VINF_PERMISSION_DENIED; - if (vboxVBVAExHPCheckOtherCommands(pCmdVbva)) - return VINF_INTERRUPTED; - - int rc = vboxVBVAExHPCmdGet(pCmdVbva, ppCmd, pcbCmd); - switch (rc) + if (rc == VERR_INVALID_STATE) { - case VINF_SUCCESS: - return VINF_SUCCESS; - case VINF_EOF: - vboxVBVAExHPHgEventClear(pCmdVbva); - vboxVBVAExHPProcessorRelease(pCmdVbva); - /* 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 (VBoxVBVAExHSCheckCommands(pCmdVbva) == VINF_SUCCESS) - continue; - return VINF_EOF; - 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 */ - if (RT_FAILURE(rc)) - return rc; + pCmd->pfnInternal = NULL; + PVGASTATE pVGAState = pVdma->pVGAState; + rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCmd, cbCmd, pfnCompletion, pvCompletion); + if (!RT_SUCCESS(rc)) + WARN(("pfnCrHgsmiControlProcess failed %d\n", rc)); - WARN(("Warning: vboxVBVAExHCmdGet returned unexpected success status %d\n", rc)); - return VERR_INTERNAL_ERROR; + return rc; } + WARN(("vdmaVBVACtlOpaqueSubmit failed %d\n", rc)); + return rc; } - WARN(("Warning: VBoxVBVAExHCmdGet unexpected state\n")); - return VERR_INTERNAL_ERROR; + return VINF_SUCCESS; } -/* 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 */ -static int VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva) +static int vdmaVBVACtlEnableDisableSubmitInternal(PVBOXVDMAHOST pVdma, VBVAENABLE *pEnable, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete) { - if (ASMAtomicUoReadU32(&pCmdVbva->u32State) == VBVAEXHOSTCONTEXT_STATE_STOPPED) - return VINF_EOF; - - int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva); - if (RT_SUCCESS(rc)) + VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, VBVAEXHOSTCTL_TYPE_GH_ENABLE_DISABLE); + if (!pHCtl) { - /* we are the processor now */ - VBVABUFFER *pVBVA = pCmdVbva->pVBVA; - - uint32_t indexRecordFirst = pVBVA->indexRecordFirst; - uint32_t indexRecordFree = pVBVA->indexRecordFree; - - if (indexRecordFirst != indexRecordFree) - { - vboxVBVAExHPHgEventSet(pCmdVbva); - return VINF_SUCCESS; - } - - vboxVBVAExHPProcessorRelease(pCmdVbva); - return VINF_EOF; + WARN(("VBoxVBVAExHCtlCreate failed\n")); + return VERR_NO_MEMORY; } - if (rc == VERR_SEM_BUSY) - return VINF_ALREADY_INITIALIZED; - Assert(rc == VERR_INVALID_STATE); - return VERR_INVALID_STATE; -} -static void VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva) -{ - memset(pCmdVbva, 0, sizeof (*pCmdVbva)); -} - -static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA) -{ - if (ASMAtomicUoReadU32(&pCmdVbva->u32State) != VBVAEXHOSTCONTEXT_STATE_STOPPED) - return VINF_ALREADY_INITIALIZED; - - pCmdVbva->pVBVA = pVBVA; - pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0; - ASMAtomicWriteU32(&pCmdVbva->u32State, VBVAEXHOSTCONTEXT_STATE_LISTENING); + pHCtl->u.cmd.pu8Cmd = (uint8_t*)pEnable; + pHCtl->u.cmd.cbCmd = sizeof (*pEnable); + int rc = vdmaVBVACtlSubmit(pVdma, pHCtl, VBVAEXHOSTCTL_SOURCE_GUEST, pfnComplete, pvComplete); + if (!RT_SUCCESS(rc)) + { + WARN(("vdmaVBVACtlSubmit failed rc %d\n", rc)); + return rc;; + } return VINF_SUCCESS; } -static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva) +static int vdmaVBVACtlEnableDisableSubmit(PVBOXVDMAHOST pVdma, VBOXCMDVBVA_CTL_ENABLE *pEnable) { - if (ASMAtomicUoReadU32(&pCmdVbva->u32State) == VBVAEXHOSTCONTEXT_STATE_STOPPED) - return VINF_SUCCESS; - - /* ensure no commands pending and one tries to submit them */ - int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva); + VBoxSHGSMICommandMarkAsynchCompletion(&pEnable->Hdr); + int rc = vdmaVBVACtlEnableDisableSubmitInternal(pVdma, &pEnable->Enable, vboxCmdVBVACmdCtlGuestCompletion, pVdma); if (RT_SUCCESS(rc)) - { - pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0; - memset(pCmdVbva, 0, sizeof (*pCmdVbva)); return VINF_SUCCESS; - } - return VERR_INVALID_STATE; + + WARN(("vdmaVBVACtlEnableDisableSubmitInternal failed %d\n", rc)); + pEnable->Hdr.i32Result = rc; + rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, &pEnable->Hdr); + AssertRC(rc); + return VINF_SUCCESS; } -static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva) +static DECLCALLBACK(void) vdmaVBVACtlSubmitSyncCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext) { - /* ensure the processor is stopped */ - if (ASMAtomicUoReadU32(&pCmdVbva->u32State) == VBVAEXHOSTCONTEXT_STATE_STOPPED) - return; - - /* ensure no one tries to submit the command */ - vboxVBVAExHSPause(pCmdVbva); - pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0; - memset(pCmdVbva, 0, sizeof (*pCmdVbva)); + VDMA_VBVA_CTL_CYNC_COMPLETION *pData = (VDMA_VBVA_CTL_CYNC_COMPLETION*)pvContext; + pData->rc = rc; + rc = RTSemEventSignal(pData->hEvent); + if (!RT_SUCCESS(rc)) + WARN(("RTSemEventSignal failed %d\n", rc)); } -/* Saves state - * @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail - */ -static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM) +static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource) { - int rc; - if (ASMAtomicUoReadU32(&pCmdVbva->u32State) != VBVAEXHOSTCONTEXT_STATE_STOPPED) + VDMA_VBVA_CTL_CYNC_COMPLETION Data; + Data.rc = VERR_NOT_IMPLEMENTED; + int rc = RTSemEventCreate(&Data.hEvent); + if (!RT_SUCCESS(rc)) { - vboxVBVAExHSPause(pCmdVbva); - rc = SSMR3PutU32(pSSM, (uint32_t)(((uint8_t*)pCmdVbva->pVBVA) - pu8VramBase)); - AssertRCReturn(rc, rc); - return vboxVBVAExHSResume(pCmdVbva); + WARN(("RTSemEventCreate failed %d\n", rc)); + return rc; } - rc = SSMR3PutU32(pSSM, 0xffffffff); - AssertRCReturn(rc, rc); - - return VINF_EOF; -} - -/* 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) -{ - uint32_t u32; - int rc = SSMR3GetU32(pSSM, &u32); - AssertRCReturn(rc, rc); - if (u32 != 0xffffffff) + rc = vdmaVBVACtlSubmit(pVdma, pCtl, enmSource, vdmaVBVACtlSubmitSyncCompletion, &Data); + if (RT_SUCCESS(rc)) { - VBVABUFFER *pVBVA = (VBVABUFFER*)pu8VramBase + u32; - rc = VBoxVBVAExHSEnable(pCmdVbva, pVBVA); - AssertRCReturn(rc, rc); - return VBoxVBVAExHSCheckCommands(pCmdVbva); + rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + rc = Data.rc; + if (!RT_SUCCESS(rc)) + WARN(("vdmaVBVACtlSubmitSyncCompletion returned %d\n", rc)); + } + else + WARN(("RTSemEventWait failed %d\n", rc)); } + else + WARN(("vdmaVBVACtlSubmit failed %d\n", rc)); - return VINF_EOF; + RTSemEventDestroy(Data.hEvent); + + return rc; } -int vboxCmdVBVAEnable(PVGASTATE pVGAState, VBVABUFFER *pVBVA) +static int vdmaVBVAPause(PVBOXVDMAHOST pVdma) { - struct VBOXVDMAHOST *pVdma = pVGAState->pVdma; - return VBoxVBVAExHSEnable(&pVdma->CmdVbva, pVBVA); + VBVAEXHOSTCTL Ctl; + Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE; + return vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY); } -int vboxCmdVBVADisable(PVGASTATE pVGAState) +static int vdmaVBVAResume(PVBOXVDMAHOST pVdma) { - struct VBOXVDMAHOST *pVdma = pVGAState->pVdma; - return VBoxVBVAExHSDisable(&pVdma->CmdVbva); + VBVAEXHOSTCTL Ctl; + Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME; + return vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY); } -static int vboxCmdVBVACmdSubmitPerform(PVGASTATE pVGAState) +static int vboxVDMACmdSubmitPerform(struct VBOXVDMAHOST *pVdma) { - struct VBOXVDMAHOST *pVdma = pVGAState->pVdma; int rc = VBoxVBVAExHSCheckCommands(&pVdma->CmdVbva); switch (rc) { case VINF_SUCCESS: - return pVGAState->pDrv->pfnCrCmdNotifyCmds(pVGAState->pDrv); + return VBoxVDMAThreadEventNotify(&pVdma->Thread); case VINF_ALREADY_INITIALIZED: case VINF_EOF: case VERR_INVALID_STATE: @@ -1926,17 +2315,70 @@ static int vboxCmdVBVACmdSubmitPerform(PVGASTATE pVGAState) } } + +int vboxCmdVBVACmdHostCtl(PPDMIDISPLAYVBVACALLBACKS pInterface, + struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, + PFNCRCTLCOMPLETION pfnCompletion, + void *pvCompletion) +{ + PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface); + struct VBOXVDMAHOST *pVdma = pVGAState->pVdma; + return vdmaVBVACtlOpaqueHostSubmit(pVdma, pCmd, cbCmd, pfnCompletion, pvCompletion); +} + +int vboxCmdVBVACmdCtl(PVGASTATE pVGAState, VBOXCMDVBVA_CTL *pCtl, uint32_t cbCtl) +{ + struct VBOXVDMAHOST *pVdma = pVGAState->pVdma; + int rc = VINF_SUCCESS; + switch (pCtl->u32Type) + { + case VBOXCMDVBVACTL_TYPE_3DCTL: + return vdmaVBVACtlOpaqueGuestSubmit(pVdma, pCtl, cbCtl); + case VBOXCMDVBVACTL_TYPE_ENABLE: + if (cbCtl != sizeof (VBOXCMDVBVA_CTL_ENABLE)) + { + WARN(("incorrect enable size\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + return vdmaVBVACtlEnableDisableSubmit(pVdma, (VBOXCMDVBVA_CTL_ENABLE*)pCtl); + default: + WARN(("unsupported type\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + pCtl->i32Result = rc; + rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pCtl); + AssertRC(rc); + return VINF_SUCCESS; +} + int vboxCmdVBVACmdSubmit(PVGASTATE pVGAState) { - return vboxCmdVBVACmdSubmitPerform(pVGAState); + if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva)) + { + WARN(("vdma VBVA is disabled\n")); + return VERR_INVALID_STATE; + } + + return vboxVDMACmdSubmitPerform(pVGAState->pVdma); } int vboxCmdVBVACmdFlush(PVGASTATE pVGAState) { - return vboxCmdVBVACmdSubmitPerform(pVGAState); + WARN(("flush\n")); + if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva)) + { + WARN(("vdma VBVA is disabled\n")); + return VERR_INVALID_STATE; + } + return vboxVDMACmdSubmitPerform(pVGAState->pVdma); } void vboxCmdVBVACmdTimer(PVGASTATE pVGAState) { - vboxCmdVBVACmdSubmitPerform(pVGAState); + if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva)) + return; + vboxVDMACmdSubmitPerform(pVGAState->pVdma); } |