VBoxMPCm.cpp revision 3c5c04d7b0973be0757addef8ba44b9352b38386
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync/* $Id$ */
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync/** @file
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync * VBox WDDM Miniport driver
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync */
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync/*
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync * Copyright (C) 2011 Oracle Corporation
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync *
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync * available from http://www.virtualbox.org. This file is free software;
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync */
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync#include "VBoxMPWddm.h"
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsynctypedef struct VBOXVIDEOCM_CMD_DR
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync{
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync LIST_ENTRY QueueList;
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync PVBOXVIDEOCM_CTX pContext;
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync uint32_t cbMaxCmdSize;
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync volatile uint32_t cRefs;
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync VBOXVIDEOCM_CMD_HDR CmdHdr;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync} VBOXVIDEOCM_CMD_DR, *PVBOXVIDEOCM_CMD_DR;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsynctypedef enum
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync{
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync VBOXVIDEOCM_CMD_CTL_KM_TYPE_POST_INVOKE = 1,
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync VBOXVIDEOCM_CMD_CTL_KM_TYPE_PRE_INVOKE,
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync VBOXVIDEOCM_CMD_CTL_KM_TYPE_DUMMY_32BIT = 0x7fffffff
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync} VBOXVIDEOCM_CMD_CTL_KM_TYPE;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsynctypedef DECLCALLBACK(VOID) FNVBOXVIDEOCM_CMD_CB(PVBOXVIDEOCM_CTX pContext, struct VBOXVIDEOCM_CMD_CTL_KM *pCmd, PVOID pvContext);
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsynctypedef FNVBOXVIDEOCM_CMD_CB *PFNVBOXVIDEOCM_CMD_CB;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsynctypedef struct VBOXVIDEOCM_CMD_CTL_KM
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync{
8e034cd2a6b0c86697554278e970163287003bb9vboxsync VBOXVIDEOCM_CMD_CTL_KM_TYPE enmType;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync uint32_t u32Reserved;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync PFNVBOXVIDEOCM_CMD_CB pfnCb;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync PVOID pvCb;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync} VBOXVIDEOCM_CMD_CTL_KM, *PVBOXVIDEOCM_CMD_CTL_KM;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
8e034cd2a6b0c86697554278e970163287003bb9vboxsyncAssertCompile(VBOXWDDM_ROUNDBOUND(RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, CmdHdr), 8) == RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, CmdHdr));
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
8e034cd2a6b0c86697554278e970163287003bb9vboxsync#define VBOXVIDEOCM_HEADER_SIZE() (VBOXWDDM_ROUNDBOUND(sizeof (VBOXVIDEOCM_CMD_DR), 8))
8e034cd2a6b0c86697554278e970163287003bb9vboxsync#define VBOXVIDEOCM_SIZE_FROMBODYSIZE(_s) (VBOXVIDEOCM_HEADER_SIZE() + (_s))
8e034cd2a6b0c86697554278e970163287003bb9vboxsync//#define VBOXVIDEOCM_SIZE(_t) (VBOXVIDEOCM_SIZE_FROMBODYSIZE(sizeof (_t)))
8e034cd2a6b0c86697554278e970163287003bb9vboxsync#define VBOXVIDEOCM_BODY(_pCmd, _t) ( (_t*)(((uint8_t*)(_pCmd)) + VBOXVIDEOCM_HEADER_SIZE()) )
8e034cd2a6b0c86697554278e970163287003bb9vboxsync#define VBOXVIDEOCM_HEAD(_pCmd) ( (PVBOXVIDEOCM_CMD_DR)(((uint8_t*)(_pCmd)) - VBOXVIDEOCM_HEADER_SIZE()) )
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
8e034cd2a6b0c86697554278e970163287003bb9vboxsync#define VBOXVIDEOCM_SENDSIZE_FROMBODYSIZE(_s) ( VBOXVIDEOCM_SIZE_FROMBODYSIZE(_s) - RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, CmdHdr))
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync//#define VBOXVIDEOCM_BODY_FIELD_OFFSET(_ot, _t, _f) ( (_ot)( VBOXVIDEOCM_BODY(0, uint8_t) + RT_OFFSETOF(_t, _f) ) )
2c203bb219df5391ed7f3888c85e12a4e47817davboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsynctypedef struct VBOXVIDEOCM_SESSION
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync{
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync /* contexts in this session */
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync LIST_ENTRY QueueEntry;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync /* contexts in this session */
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync LIST_ENTRY ContextList;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync /* commands list */
089c21a647af46044cad04a78cfdcfae814d2105vboxsync LIST_ENTRY CommandsList;
089c21a647af46044cad04a78cfdcfae814d2105vboxsync /* post process commands list */
089c21a647af46044cad04a78cfdcfae814d2105vboxsync LIST_ENTRY PpCommandsList;
089c21a647af46044cad04a78cfdcfae814d2105vboxsync /* event used to notify UMD about pending commands */
8e034cd2a6b0c86697554278e970163287003bb9vboxsync PKEVENT pUmEvent;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync /* sync lock */
8e034cd2a6b0c86697554278e970163287003bb9vboxsync FAST_MUTEX Mutex;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync /* indicates whether event signaling is needed on cmd add */
8e034cd2a6b0c86697554278e970163287003bb9vboxsync bool bEventNeeded;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync} VBOXVIDEOCM_SESSION, *PVBOXVIDEOCM_SESSION;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
8e034cd2a6b0c86697554278e970163287003bb9vboxsync#define VBOXCMENTRY_2_CMD(_pE) ((PVBOXVIDEOCM_CMD_DR)((uint8_t*)(_pE) - RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, QueueList)))
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsyncvoid* vboxVideoCmCmdReinitForContext(void *pvCmd, PVBOXVIDEOCM_CTX pContext)
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync{
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync pHdr->pContext = pContext;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync pHdr->CmdHdr.u64UmData = pContext->u64UmData;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync return pvCmd;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync}
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsyncvoid* vboxVideoCmCmdCreate(PVBOXVIDEOCM_CTX pContext, uint32_t cbSize)
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync{
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync Assert(cbSize);
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync if (!cbSize)
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync return NULL;
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync Assert(VBOXWDDM_ROUNDBOUND(cbSize, 8) == cbSize);
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync cbSize = VBOXWDDM_ROUNDBOUND(cbSize, 8);
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync Assert(pContext->pSession);
089c21a647af46044cad04a78cfdcfae814d2105vboxsync if (!pContext->pSession)
8e034cd2a6b0c86697554278e970163287003bb9vboxsync return NULL;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
8e034cd2a6b0c86697554278e970163287003bb9vboxsync uint32_t cbCmd = VBOXVIDEOCM_SIZE_FROMBODYSIZE(cbSize);
8e034cd2a6b0c86697554278e970163287003bb9vboxsync PVBOXVIDEOCM_CMD_DR pCmd = (PVBOXVIDEOCM_CMD_DR)vboxWddmMemAllocZero(cbCmd);
8e034cd2a6b0c86697554278e970163287003bb9vboxsync Assert(pCmd);
8e034cd2a6b0c86697554278e970163287003bb9vboxsync if (pCmd)
8e034cd2a6b0c86697554278e970163287003bb9vboxsync {
8e034cd2a6b0c86697554278e970163287003bb9vboxsync InitializeListHead(&pCmd->QueueList);
8e034cd2a6b0c86697554278e970163287003bb9vboxsync pCmd->pContext = pContext;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync pCmd->cbMaxCmdSize = VBOXVIDEOCM_SENDSIZE_FROMBODYSIZE(cbSize);
8e034cd2a6b0c86697554278e970163287003bb9vboxsync pCmd->cRefs = 1;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync pCmd->CmdHdr.u64UmData = pContext->u64UmData;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync pCmd->CmdHdr.cbCmd = pCmd->cbMaxCmdSize;
8e034cd2a6b0c86697554278e970163287003bb9vboxsync }
8e034cd2a6b0c86697554278e970163287003bb9vboxsync return VBOXVIDEOCM_BODY(pCmd, void);
8e034cd2a6b0c86697554278e970163287003bb9vboxsync}
8e034cd2a6b0c86697554278e970163287003bb9vboxsync
8e034cd2a6b0c86697554278e970163287003bb9vboxsyncstatic PVBOXVIDEOCM_CMD_CTL_KM vboxVideoCmCmdCreateKm(PVBOXVIDEOCM_CTX pContext, VBOXVIDEOCM_CMD_CTL_KM_TYPE enmType,
2c203bb219df5391ed7f3888c85e12a4e47817davboxsync PFNVBOXVIDEOCM_CMD_CB pfnCb, PVOID pvCb,
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync uint32_t cbSize)
29bdc01040c07a3dd482a94a2cb8f0a90f8587a7vboxsync{
PVBOXVIDEOCM_CMD_CTL_KM pCmd = (PVBOXVIDEOCM_CMD_CTL_KM)vboxVideoCmCmdCreate(pContext, cbSize + sizeof (*pCmd));
pCmd->enmType = enmType;
pCmd->pfnCb = pfnCb;
pCmd->pvCb = pvCb;
PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pCmd);
pHdr->CmdHdr.enmType = VBOXVIDEOCM_CMD_TYPE_CTL_KM;
return pCmd;
}
static DECLCALLBACK(VOID) vboxVideoCmCmdCbSetEventAndDereference(PVBOXVIDEOCM_CTX pContext, PVBOXVIDEOCM_CMD_CTL_KM pCmd, PVOID pvContext)
{
PKEVENT pEvent = (PKEVENT)pvContext;
KeSetEvent(pEvent, 0, FALSE);
ObDereferenceObject(pEvent);
vboxVideoCmCmdRelease(pCmd);
}
NTSTATUS vboxVideoCmCmdSubmitCompleteEvent(PVBOXVIDEOCM_CTX pContext, PKEVENT pEvent)
{
Assert(pEvent);
PVBOXVIDEOCM_CMD_CTL_KM pCmd = vboxVideoCmCmdCreateKm(pContext, VBOXVIDEOCM_CMD_CTL_KM_TYPE_POST_INVOKE,
vboxVideoCmCmdCbSetEventAndDereference, pEvent, 0);
if (!pCmd)
{
WARN(("vboxVideoCmCmdCreateKm failed"));
return STATUS_NO_MEMORY;
}
vboxVideoCmCmdSubmit(pCmd, VBOXVIDEOCM_SUBMITSIZE_DEFAULT);
return STATUS_SUCCESS;
}
DECLINLINE(void) vboxVideoCmCmdRetainByHdr(PVBOXVIDEOCM_CMD_DR pHdr)
{
ASMAtomicIncU32(&pHdr->cRefs);
}
DECLINLINE(void) vboxVideoCmCmdReleaseByHdr(PVBOXVIDEOCM_CMD_DR pHdr)
{
uint32_t cRefs = ASMAtomicDecU32(&pHdr->cRefs);
Assert(cRefs < UINT32_MAX/2);
if (!cRefs)
vboxWddmMemFree(pHdr);
}
static void vboxVideoCmCmdCancel(PVBOXVIDEOCM_CMD_DR pHdr)
{
InitializeListHead(&pHdr->QueueList);
vboxVideoCmCmdReleaseByHdr(pHdr);
}
static void vboxVideoCmCmdPostByHdr(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CMD_DR pHdr, uint32_t cbSize)
{
bool bSignalEvent = false;
if (cbSize != VBOXVIDEOCM_SUBMITSIZE_DEFAULT)
{
cbSize = VBOXVIDEOCM_SENDSIZE_FROMBODYSIZE(cbSize);
Assert(cbSize <= pHdr->cbMaxCmdSize);
pHdr->CmdHdr.cbCmd = cbSize;
}
Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
ExAcquireFastMutex(&pSession->Mutex);
InsertHeadList(&pSession->CommandsList, &pHdr->QueueList);
if (pSession->bEventNeeded)
{
pSession->bEventNeeded = false;
bSignalEvent = true;
}
ExReleaseFastMutex(&pSession->Mutex);
if (bSignalEvent)
KeSetEvent(pSession->pUmEvent, 0, FALSE);
}
void vboxVideoCmCmdRetain(void *pvCmd)
{
PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
vboxVideoCmCmdRetainByHdr(pHdr);
}
void vboxVideoCmCmdRelease(void *pvCmd)
{
PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
vboxVideoCmCmdReleaseByHdr(pHdr);
}
/**
* @param pvCmd memory buffer returned by vboxVideoCmCmdCreate
* @param cbSize should be <= cbSize posted to vboxVideoCmCmdCreate on command creation
*/
void vboxVideoCmCmdSubmit(void *pvCmd, uint32_t cbSize)
{
PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
vboxVideoCmCmdPostByHdr(pHdr->pContext->pSession, pHdr, cbSize);
}
NTSTATUS vboxVideoCmCmdVisit(PVBOXVIDEOCM_CTX pContext, BOOLEAN bEntireSession, PFNVBOXVIDEOCMCMDVISITOR pfnVisitor, PVOID pvVisitor)
{
PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
PLIST_ENTRY pCurEntry = NULL;
PVBOXVIDEOCM_CMD_DR pHdr;
ExAcquireFastMutex(&pSession->Mutex);
pCurEntry = pSession->CommandsList.Blink;
do
{
if (pCurEntry != &pSession->CommandsList)
{
pHdr = VBOXCMENTRY_2_CMD(pCurEntry);
pCurEntry = pHdr->QueueList.Blink;
if (bEntireSession || pHdr->pContext == pContext)
{
if (pHdr->CmdHdr.enmType == VBOXVIDEOCM_CMD_TYPE_UM)
{
void * pvBody = VBOXVIDEOCM_BODY(pHdr, void);
UINT fRet = pfnVisitor(pHdr->pContext, pvBody, pHdr->CmdHdr.cbCmd, pvVisitor);
if (fRet & VBOXVIDEOCMCMDVISITOR_RETURN_RMCMD)
{
RemoveEntryList(&pHdr->QueueList);
}
if ((fRet & VBOXVIDEOCMCMDVISITOR_RETURN_BREAK))
break;
}
else
{
WARN(("non-um cmd on visit, skipping"));
}
}
}
else
{
break;
}
} while (1);
ExReleaseFastMutex(&pSession->Mutex);
return STATUS_SUCCESS;
}
void vboxVideoCmCtxInitEmpty(PVBOXVIDEOCM_CTX pContext)
{
InitializeListHead(&pContext->SessionEntry);
pContext->pSession = NULL;
pContext->u64UmData = 0ULL;
}
static void vboxVideoCmSessionCtxAddLocked(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CTX pContext)
{
InsertHeadList(&pSession->ContextList, &pContext->SessionEntry);
pContext->pSession = pSession;
}
void vboxVideoCmSessionCtxAdd(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CTX pContext)
{
Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
ExAcquireFastMutex(&pSession->Mutex);
vboxVideoCmSessionCtxAddLocked(pSession, pContext);
ExReleaseFastMutex(&pSession->Mutex);
}
static void vboxVideoCmSessionDestroyLocked(PVBOXVIDEOCM_SESSION pSession)
{
/* signal event so that user-space client can figure out the context is destroyed
* in case the context destroyal is caused by Graphics device reset or miniport driver update */
KeSetEvent(pSession->pUmEvent, 0, FALSE);
ObDereferenceObject(pSession->pUmEvent);
Assert(IsListEmpty(&pSession->ContextList));
Assert(IsListEmpty(&pSession->CommandsList));
Assert(IsListEmpty(&pSession->PpCommandsList));
RemoveEntryList(&pSession->QueueEntry);
vboxWddmMemFree(pSession);
}
static void vboxVideoCmSessionCtxPpList(PVBOXVIDEOCM_CTX pContext, PLIST_ENTRY pHead)
{
LIST_ENTRY *pCur;
for (pCur = pHead->Flink; pCur != pHead; pCur = pHead->Flink)
{
RemoveEntryList(pCur);
PVBOXVIDEOCM_CMD_DR pHdr = VBOXCMENTRY_2_CMD(pCur);
PVBOXVIDEOCM_CMD_CTL_KM pCmd = VBOXVIDEOCM_BODY(pHdr, VBOXVIDEOCM_CMD_CTL_KM);
pCmd->pfnCb(pContext, pCmd, pCmd->pvCb);
}
}
static void vboxVideoCmSessionCtxDetachCmdsLocked(PLIST_ENTRY pEntriesHead, PVBOXVIDEOCM_CTX pContext, PLIST_ENTRY pDstHead)
{
LIST_ENTRY *pCur;
LIST_ENTRY *pPrev;
pCur = pEntriesHead->Flink;
pPrev = pEntriesHead;
while (pCur != pEntriesHead)
{
PVBOXVIDEOCM_CMD_DR pCmd = VBOXCMENTRY_2_CMD(pCur);
if (pCmd->pContext == pContext)
{
RemoveEntryList(pCur);
InsertTailList(pDstHead, pCur);
pCur = pPrev;
/* pPrev - remains unchanged */
}
else
{
pPrev = pCur;
}
pCur = pCur->Flink;
}
}
/**
* @return true iff the given session is destroyed
*/
bool vboxVideoCmSessionCtxRemoveLocked(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CTX pContext)
{
bool bDestroy;
LIST_ENTRY RemainedList;
LIST_ENTRY RemainedPpList;
LIST_ENTRY *pCur;
InitializeListHead(&RemainedList);
InitializeListHead(&RemainedPpList);
Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
ExAcquireFastMutex(&pSession->Mutex);
pContext->pSession = NULL;
RemoveEntryList(&pContext->SessionEntry);
bDestroy = !!(IsListEmpty(&pSession->ContextList));
/* ensure there are no commands left for the given context */
if (bDestroy)
{
vboxVideoLeDetach(&pSession->CommandsList, &RemainedList);
vboxVideoLeDetach(&pSession->PpCommandsList, &RemainedPpList);
}
else
{
vboxVideoCmSessionCtxDetachCmdsLocked(&pSession->CommandsList, pContext, &RemainedList);
vboxVideoCmSessionCtxDetachCmdsLocked(&pSession->PpCommandsList, pContext, &RemainedPpList);
}
ExReleaseFastMutex(&pSession->Mutex);
for (pCur = RemainedList.Flink; pCur != &RemainedList; pCur = RemainedList.Flink)
{
RemoveEntryList(pCur);
PVBOXVIDEOCM_CMD_DR pCmd = VBOXCMENTRY_2_CMD(pCur);
vboxVideoCmCmdCancel(pCmd);
}
vboxVideoCmSessionCtxPpList(pContext, &RemainedPpList);
if (bDestroy)
{
vboxVideoCmSessionDestroyLocked(pSession);
}
return bDestroy;
}
/* the session gets destroyed once the last context is removed from it */
NTSTATUS vboxVideoCmSessionCreateLocked(PVBOXVIDEOCM_MGR pMgr, PVBOXVIDEOCM_SESSION *ppSession, PKEVENT pUmEvent, PVBOXVIDEOCM_CTX pContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PVBOXVIDEOCM_SESSION pSession = (PVBOXVIDEOCM_SESSION)vboxWddmMemAllocZero(sizeof (VBOXVIDEOCM_SESSION));
Assert(pSession);
if (pSession)
{
InitializeListHead(&pSession->ContextList);
InitializeListHead(&pSession->CommandsList);
InitializeListHead(&pSession->PpCommandsList);
pSession->pUmEvent = pUmEvent;
Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
ExInitializeFastMutex(&pSession->Mutex);
pSession->bEventNeeded = true;
vboxVideoCmSessionCtxAddLocked(pSession, pContext);
InsertHeadList(&pMgr->SessionList, &pSession->QueueEntry);
*ppSession = pSession;
return STATUS_SUCCESS;
// vboxWddmMemFree(pSession);
}
else
{
Status = STATUS_NO_MEMORY;
}
return Status;
}
#define VBOXCMENTRY_2_SESSION(_pE) ((PVBOXVIDEOCM_SESSION)((uint8_t*)(_pE) - RT_OFFSETOF(VBOXVIDEOCM_SESSION, QueueEntry)))
NTSTATUS vboxVideoCmCtxAdd(PVBOXVIDEOCM_MGR pMgr, PVBOXVIDEOCM_CTX pContext, HANDLE hUmEvent, uint64_t u64UmData)
{
PKEVENT pUmEvent = NULL;
Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
NTSTATUS Status = ObReferenceObjectByHandle(hUmEvent, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode,
(PVOID*)&pUmEvent,
NULL);
Assert(Status == STATUS_SUCCESS);
if (Status == STATUS_SUCCESS)
{
Status = KeWaitForSingleObject(&pMgr->SynchEvent, Executive, KernelMode,
FALSE, /* BOOLEAN Alertable */
NULL /* PLARGE_INTEGER Timeout */
);
Assert(Status == STATUS_SUCCESS);
if (Status == STATUS_SUCCESS)
{
bool bFound = false;
PVBOXVIDEOCM_SESSION pSession = NULL;
for (PLIST_ENTRY pEntry = pMgr->SessionList.Flink; pEntry != &pMgr->SessionList; pEntry = pEntry->Flink)
{
pSession = VBOXCMENTRY_2_SESSION(pEntry);
if (pSession->pUmEvent == pUmEvent)
{
bFound = true;
break;
}
}
pContext->u64UmData = u64UmData;
if (!bFound)
{
Status = vboxVideoCmSessionCreateLocked(pMgr, &pSession, pUmEvent, pContext);
Assert(Status == STATUS_SUCCESS);
}
else
{
/* Status = */vboxVideoCmSessionCtxAdd(pSession, pContext);
/*Assert(Status == STATUS_SUCCESS);*/
}
LONG tstL = KeSetEvent(&pMgr->SynchEvent, 0, FALSE);
Assert(!tstL);
if (Status == STATUS_SUCCESS)
{
return STATUS_SUCCESS;
}
}
ObDereferenceObject(pUmEvent);
}
return Status;
}
NTSTATUS vboxVideoCmCtxRemove(PVBOXVIDEOCM_MGR pMgr, PVBOXVIDEOCM_CTX pContext)
{
PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
if (!pSession)
return STATUS_SUCCESS;
NTSTATUS Status = KeWaitForSingleObject(&pMgr->SynchEvent, Executive, KernelMode,
FALSE, /* BOOLEAN Alertable */
NULL /* PLARGE_INTEGER Timeout */
);
Assert(Status == STATUS_SUCCESS);
if (Status == STATUS_SUCCESS)
{
vboxVideoCmSessionCtxRemoveLocked(pSession, pContext);
LONG tstL = KeSetEvent(&pMgr->SynchEvent, 0, FALSE);
Assert(!tstL);
}
return Status;
}
NTSTATUS vboxVideoCmInit(PVBOXVIDEOCM_MGR pMgr)
{
KeInitializeEvent(&pMgr->SynchEvent, SynchronizationEvent, TRUE);
InitializeListHead(&pMgr->SessionList);
return STATUS_SUCCESS;
}
NTSTATUS vboxVideoCmTerm(PVBOXVIDEOCM_MGR pMgr)
{
Assert(IsListEmpty(&pMgr->SessionList));
return STATUS_SUCCESS;
}
VOID vboxVideoCmProcessKm(PVBOXVIDEOCM_CTX pContext, PVBOXVIDEOCM_CMD_CTL_KM pCmd)
{
PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
switch (pCmd->enmType)
{
case VBOXVIDEOCM_CMD_CTL_KM_TYPE_PRE_INVOKE:
{
pCmd->pfnCb(pContext, pCmd, pCmd->pvCb);
break;
}
case VBOXVIDEOCM_CMD_CTL_KM_TYPE_POST_INVOKE:
{
PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pCmd);
ExAcquireFastMutex(&pSession->Mutex);
InsertTailList(&pSession->PpCommandsList, &pHdr->QueueList);
ExReleaseFastMutex(&pSession->Mutex);
break;
}
default:
{
WARN(("unsupported cmd type %d", pCmd->enmType));
break;
}
}
}
NTSTATUS vboxVideoCmEscape(PVBOXVIDEOCM_CTX pContext, PVBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD pCmd, uint32_t cbCmd)
{
Assert(cbCmd >= sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD));
if (cbCmd < sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD))
return STATUS_BUFFER_TOO_SMALL;
PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
PVBOXVIDEOCM_CMD_DR pHdr;
LIST_ENTRY DetachedList;
LIST_ENTRY DetachedPpList;
PLIST_ENTRY pCurEntry = NULL;
uint32_t cbCmdsReturned = 0;
uint32_t cbRemainingCmds = 0;
uint32_t cbRemainingFirstCmd = 0;
uint32_t cbData = cbCmd - sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD);
uint8_t * pvData = ((uint8_t *)pCmd) + sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD);
bool bDetachMode = true;
InitializeListHead(&DetachedList);
InitializeListHead(&DetachedPpList);
// PVBOXWDDM_GETVBOXVIDEOCMCMD_HDR *pvCmd
Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
ExAcquireFastMutex(&pSession->Mutex);
vboxVideoCmSessionCtxDetachCmdsLocked(&pSession->PpCommandsList, pContext, &DetachedPpList);
do
{
if (bDetachMode)
{
if (!IsListEmpty(&pSession->CommandsList))
{
Assert(!pCurEntry);
pHdr = VBOXCMENTRY_2_CMD(pSession->CommandsList.Blink);
Assert(pHdr->CmdHdr.cbCmd);
uint32_t cbUserCmd = pHdr->CmdHdr.enmType == VBOXVIDEOCM_CMD_TYPE_UM ? pHdr->CmdHdr.cbCmd : 0;
if (cbData >= cbUserCmd)
{
RemoveEntryList(&pHdr->QueueList);
InsertHeadList(&DetachedList, &pHdr->QueueList);
cbData -= cbUserCmd;
}
else
{
Assert(cbUserCmd);
cbRemainingFirstCmd = cbUserCmd;
cbRemainingCmds = cbUserCmd;
pCurEntry = pHdr->QueueList.Blink;
bDetachMode = false;
}
}
else
{
pSession->bEventNeeded = true;
break;
}
}
else
{
Assert(pCurEntry);
if (pCurEntry != &pSession->CommandsList)
{
pHdr = VBOXCMENTRY_2_CMD(pCurEntry);
uint32_t cbUserCmd = pHdr->CmdHdr.enmType == VBOXVIDEOCM_CMD_TYPE_UM ? pHdr->CmdHdr.cbCmd : 0;
Assert(cbRemainingFirstCmd);
cbRemainingCmds += cbUserCmd;
pCurEntry = pHdr->QueueList.Blink;
}
else
{
Assert(cbRemainingFirstCmd);
Assert(cbRemainingCmds);
break;
}
}
} while (1);
ExReleaseFastMutex(&pSession->Mutex);
vboxVideoCmSessionCtxPpList(pContext, &DetachedPpList);
pCmd->Hdr.cbCmdsReturned = 0;
for (pCurEntry = DetachedList.Blink; pCurEntry != &DetachedList; pCurEntry = DetachedList.Blink)
{
pHdr = VBOXCMENTRY_2_CMD(pCurEntry);
RemoveEntryList(pCurEntry);
switch (pHdr->CmdHdr.enmType)
{
case VBOXVIDEOCM_CMD_TYPE_UM:
{
memcpy(pvData, &pHdr->CmdHdr, pHdr->CmdHdr.cbCmd);
pvData += pHdr->CmdHdr.cbCmd;
pCmd->Hdr.cbCmdsReturned += pHdr->CmdHdr.cbCmd;
vboxVideoCmCmdReleaseByHdr(pHdr);
break;
}
case VBOXVIDEOCM_CMD_TYPE_CTL_KM:
{
vboxVideoCmProcessKm(pContext, VBOXVIDEOCM_BODY(pHdr, VBOXVIDEOCM_CMD_CTL_KM));
break;
}
default:
{
WARN(("unsupported cmd type %d", pHdr->CmdHdr.enmType));
break;
}
}
}
pCmd->Hdr.cbRemainingCmds = cbRemainingCmds;
pCmd->Hdr.cbRemainingFirstCmd = cbRemainingFirstCmd;
pCmd->Hdr.u32Reserved = 0;
return STATUS_SUCCESS;
}
VOID vboxVideoCmLock(PVBOXVIDEOCM_CTX pContext)
{
ExAcquireFastMutex(&pContext->pSession->Mutex);
}
VOID vboxVideoCmUnlock(PVBOXVIDEOCM_CTX pContext)
{
ExReleaseFastMutex(&pContext->pSession->Mutex);
}
static BOOLEAN vboxVideoCmHasUncompletedCmdsLocked(PVBOXVIDEOCM_MGR pMgr)
{
PVBOXVIDEOCM_SESSION pSession = NULL;
for (PLIST_ENTRY pEntry = pMgr->SessionList.Flink; pEntry != &pMgr->SessionList; pEntry = pEntry->Flink)
{
pSession = VBOXCMENTRY_2_SESSION(pEntry);
ExAcquireFastMutex(&pSession->Mutex);
if (pSession->bEventNeeded)
{
/* commands still being processed */
ExReleaseFastMutex(&pSession->Mutex);
return TRUE;
}
ExReleaseFastMutex(&pSession->Mutex);
}
return FALSE;
}
/* waits for all outstanding commands to completed by client
* assumptions here are:
* 1. no new commands are submitted while we are waiting
* 2. it is assumed that a client completes all previously received commands
* once it queries for the new set of commands */
NTSTATUS vboxVideoCmWaitCompletedCmds(PVBOXVIDEOCM_MGR pMgr, uint32_t msTimeout)
{
LARGE_INTEGER Timeout;
PLARGE_INTEGER pTimeout;
uint32_t cIters;
if (msTimeout != RT_INDEFINITE_WAIT)
{
uint32_t msIter = 2;
cIters = msTimeout/msIter;
if (!cIters)
{
msIter = msTimeout;
cIters = 1;
}
Timeout.QuadPart = -(int64_t) msIter /* ms */ * 10000;
pTimeout = &Timeout;
}
else
{
pTimeout = NULL;
cIters = 1;
}
Assert(cIters);
do
{
NTSTATUS Status = KeWaitForSingleObject(&pMgr->SynchEvent, Executive, KernelMode,
FALSE, /* BOOLEAN Alertable */
pTimeout /* PLARGE_INTEGER Timeout */
);
if (Status == STATUS_TIMEOUT)
{
--cIters;
}
else
{
if (!NT_SUCCESS(Status))
{
WARN(("KeWaitForSingleObject failed with Status (0x%x)", Status));
return Status;
}
/* succeeded */
if (!vboxVideoCmHasUncompletedCmdsLocked(pMgr))
{
LONG tstL = KeSetEvent(&pMgr->SynchEvent, 0, FALSE);
Assert(!tstL);
return STATUS_SUCCESS;
}
LONG tstL = KeSetEvent(&pMgr->SynchEvent, 0, FALSE);
Assert(!tstL);
}
if (!cIters)
break;
KeDelayExecutionThread(KernelMode, FALSE, pTimeout);
--cIters;
if (!cIters)
break;
} while (0);
return STATUS_TIMEOUT;
}