VBoxVideoHGSMI.cpp revision e64031e20c39650a7bc902a3e1aba613b9415dee
/* $Id$ */
/** @file
*/
/*
* Copyright (C) 2006-2009 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "VBoxVideo.h"
#include "Helper.h"
#include <VBox/VBoxGuest.h>
#include <VBox/VBoxVideo.h>
#include <VBoxDisplay.h>
#include "vboxioctl.h"
#define MEM_TAG 'HVBV'
{
}
{
}
{
HGSMIOFFSET offMem = HGSMIPointerToOffset (&PrimaryExtension->u.primary.areaHostHeap, HGSMIBufferHeaderFromData (pvMem));
if(offMem != HGSMIOFFSET_VOID)
{
}
}
{
if(RT_FAILURE(rc))
{
/* failure means the command was not submitted to the handler for some reason
* it's our responsibility to notify its completion in this case */
}
/* if the cmd succeeded it's responsibility of the callback to complete it */
}
{
return VBoxHGSMIHostRead(PrimaryExtension);
}
{
if(offset != HGSMIOFFSET_VOID)
{
}
}
do { \
if(_dpc) \
{ \
} \
else \
{\
}\
} while(0)
do { \
if(_dpc) \
{ \
} \
else \
{\
}\
} while(0)
)
{
bool bProcessing = false;
/* we check if another thread is processing the queue and exit if so */
do
{
bool bLock = false;
{
if(!bProcessing)
{
break;
}
{
break;
}
}
else
{
if(!bProcessing)
{
{
break;
}
bProcessing = true;
}
}
Assert((PrimaryExtension->u.primary.pHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING) != 0);
bProcessing = true;
} while(true/*!PrimaryExtension->u.primary.bPollingStop*/);
}
/* Detect whether HGSMI is supported by the host. */
{
return (DispiId == VBE_DISPI_ID_HGSMI);
}
typedef FNHGSMICALLINIT *PFNHGSMICALLINIT;
typedef int FNHGSMICALLFINALIZE (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData);
typedef FNHGSMICALLFINALIZE *PFNHGSMICALLFINALIZE;
{
#ifdef VBOXWDDM
/* @todo: add synchronization */
#endif
}
{
#ifdef VBOXWDDM
/* @todo: add synchronization */
#endif
}
{
/* Initialize the buffer and get the offset for port IO. */
HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&PrimaryExtension->u.primary.hgsmiAdapterHeap, pvBuffer);
if (offBuffer != HGSMIOFFSET_VOID)
{
/* Submit the buffer to the host. */
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
void *pvContext)
{
int rc = VINF_SUCCESS;
/* Allocate the IO buffer. */
#ifndef VBOXWDDM
{
return VERR_INVALID_PARAMETER;
}
#endif
if (!p)
{
rc = VERR_NO_MEMORY;
}
else
{
/* Prepare data to be sent to the host. */
if (pfnInit)
{
}
if (RT_SUCCESS (rc))
{
/* Initialize the buffer and get the offset for port IO. */
p);
/* Submit the buffer to the host. */
if (pfnFinalize)
{
}
}
else
{
AssertFailed ();
}
/* Free the IO buffer. */
}
return rc;
}
void *pvContext)
{
return vboxCallChannel (PrimaryExtension,
}
typedef struct _QUERYCONFCTX
{
} QUERYCONFCTX;
{
p->u32Value = 0;
return VINF_SUCCESS;
}
static int vbvaFinalizeQueryConf (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
{
{
}
return VINF_SUCCESS;
}
static int vboxQueryConfHGSMI (PDEVICE_EXTENSION PrimaryExtension, uint32_t u32Index, ULONG *pulValue)
{
sizeof (VBVACONF32),
&context);
return rc;
}
#ifndef VBOXWDDM
{
int i;
for (i = 0, Extension = PrimaryExtension;
{
/* How much VRAM should be reserved for the guest drivers to use VBVA. */
p[i].u32ViewSize - cbReservedVRAM:
0;
}
{
return VINF_SUCCESS;
}
AssertFailed ();
return VERR_INTERNAL_ERROR;
}
#endif
{
return VINF_SUCCESS;
}
static int hgsmiInitFlagsLocation (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
{
p->cbLocation = sizeof (HGSMIHOSTFLAGS);
return VINF_SUCCESS;
}
{
dprintf(("VBoxVideo::vboxSetupAdapterInfo\n"));
/* setup the flags first to ensure they are initialized by the time the host heap is ready */
sizeof (HGSMIBUFFERLOCATION),
NULL,
NULL);
if(RT_SUCCESS (rc))
{
#ifndef VBOXWDDM
NULL,
NULL);
if (RT_SUCCESS (rc))
#else
/* in case of WDDM we do not control the framebuffer location,
* i.e. it is assigned by Video Memory Manager,
* The FB information should be passed to guest from our DxgkDdiSetVidPnSourceAddress callback */
#endif
{
/* Report the host heap location. */
sizeof (VBVAINFOHEAP),
NULL,
NULL);
}
}
return rc;
}
#ifndef VBOXWDDM
VP_STATUS vboxWaitForSingleObjectVoid(IN PVOID HwDeviceExtension, IN PVOID Object, IN PLARGE_INTEGER Timeout OPTIONAL)
{
return ERROR_INVALID_FUNCTION;
}
{
return 0;
}
{
}
VP_STATUS vboxCreateEventVoid(IN PVOID HwDeviceExtension, IN ULONG EventFlag, IN PVOID Unused, OUT PEVENT *ppEvent)
{
return ERROR_INVALID_FUNCTION;
}
{
return ERROR_INVALID_FUNCTION;
}
{
return ERROR_INVALID_FUNCTION;
}
{
return ERROR_INVALID_FUNCTION;
}
VOID vboxAcquireSpinLockVoid (IN PVOID HwDeviceExtension, IN PSPIN_LOCK SpinLock, OUT PUCHAR OldIrql)
{
}
{
}
{
}
{
}
PVOID vboxAllocatePoolVoid(IN PVOID HwDeviceExtension, IN VBOXVP_POOL_TYPE PoolType, IN size_t NumberOfBytes, IN ULONG Tag)
{
return NULL;
}
{
}
BOOLEAN vboxQueueDpcVoid(IN PVOID HwDeviceExtension, IN PMINIPORT_DPC_ROUTINE CallbackRoutine, IN PVOID Context)
{
return FALSE;
}
void VBoxSetupVideoPortFunctions(PDEVICE_EXTENSION PrimaryExtension, VBOXVIDEOPORTPROCS *pCallbacks, PVIDEO_PORT_CONFIG_INFO pConfigInfo)
{
if (vboxQueryWinVersion() <= WINNT4)
{
/* VideoPortGetProcAddress is available for >= win2k */
return;
}
(PUCHAR)"VideoPortWaitForSingleObject");
(PUCHAR)"VideoPortSetEvent");
(PUCHAR)"VideoPortClearEvent");
(PUCHAR)"VideoPortCreateEvent");
(PUCHAR)"VideoPortDeleteEvent");
&& pCallbacks->pfnDeleteEvent)
{
}
else
{
}
(PUCHAR)"VideoPortCreateSpinLock");
(PUCHAR)"VideoPortDeleteSpinLock");
(PUCHAR)"VideoPortAcquireSpinLock");
(PUCHAR)"VideoPortReleaseSpinLock");
pCallbacks->pfnAcquireSpinLockAtDpcLevel = (PFNACQUIRESPINLOCKATDPCLEVEL)(pConfigInfo->VideoPortGetProcAddress)
(PUCHAR)"VideoPortAcquireSpinLockAtDpcLevel");
pCallbacks->pfnReleaseSpinLockFromDpcLevel = (PFNRELEASESPINLOCKFROMDPCLEVEL)(pConfigInfo->VideoPortGetProcAddress)
(PUCHAR)"VideoPortReleaseSpinLockFromDpcLevel");
{
}
else
{
}
(PUCHAR)"VideoPortAllocatePool");
(PUCHAR)"VideoPortFreePool");
&& pCallbacks->pfnFreePool)
{
}
else
{
}
(PUCHAR)"VideoPortQueueDpc");
if(pCallbacks->pfnQueueDpc)
{
}
else
{
}
#ifdef DEBUG_misha
#endif
}
#endif
/**
* Helper function to register secondary displays (DualView). Note that this will not
* be available on pre-XP versions, and some editions on XP will fail because they are
* intentionally crippled.
*
* HGSMI variant is a bit different because it uses only HGSMI interface (VBVA channel)
* to talk to the host.
*/
#ifndef VBOXWDDM
#endif
{
dprintf(("VBoxVideo::VBoxSetupDisplays: PrimaryExtension = %p\n",
/* Preinitialize the primary extension.
* Note: bVBoxVideoSupported is set to FALSE, because HGSMI is active instead.
*/
#ifndef VBOXWDDM
PrimaryExtension->iDevice = 0;
#endif
#ifndef VBOXWDDM
#endif
{
}
{
/* Map the adapter information. It will be needed for HGSMI IO. */
);
{
dprintf(("VBoxVideo::VBoxSetupDisplays: VBoxMapAdapterMemory pvAdapterInfoirrmation failed rc = %d\n",
rc));
}
else
{
/* Setup a HGSMI heap within the adapter information area. */
VBVA_ADAPTER_INFORMATION_SIZE - sizeof(HGSMIHOSTFLAGS),
false /*fOffsetBased*/);
if (RT_FAILURE (rc))
{
dprintf(("VBoxVideo::VBoxSetupDisplays: HGSMIHeapSetup failed rc = %d\n",
rc));
}
else
{
PrimaryExtension->u.primary.pHostFlags = (HGSMIHOSTFLAGS*)(((uint8_t*)PrimaryExtension->u.primary.pvAdapterInformation)
+ VBVA_ADAPTER_INFORMATION_SIZE - sizeof(HGSMIHOSTFLAGS));
}
}
}
/* Setup the host heap and the adapter memory. */
{
/* The miniport heap is used for the host buffers. */
ULONG cbMiniportHeap = 0;
if (cbMiniportHeap != 0)
{
/* Do not allow too big heap. No more than 25% of VRAM is allowed. */
{
}
{
}
/* Round up to 4096 bytes. */
dprintf(("VBoxVideo::VBoxSetupDisplays: cbMiniportHeap = 0x%08X, PrimaryExtension->u.primary.cbMiniportHeap = 0x%08X, cbMiniportHeapMaxSize = 0x%08X\n",
/* Map the heap region.
*
* Note: the heap will be used for the host buffers submitted to the guest.
* The miniport driver is responsible for reading FIFO and notifying
* display drivers.
*/
);
{
}
else
{
/* Init the host hap area. Buffers from the host will be placed there. */
offBase);
}
}
else
{
/* Host has not requested a heap. */
}
}
/* Check whether the guest supports multimonitors. */
{
#ifndef VBOXWDDM
/* Dynamically query the VideoPort import to be binary compatible across Windows versions */
if (vboxQueryWinVersion() > WINNT4)
{
/* This bluescreens on NT4, hence the above version check */
(PUCHAR)"VideoPortCreateSecondaryDisplay");
}
if (pfnCreateSecondaryDisplay != NULL)
#endif
{
/* Query the configured number of displays. */
dprintf(("VBoxVideo::VBoxSetupDisplays: cDisplays = %d\n",
cDisplays));
{
/* Host reported some bad value. Continue in the 1 screen mode. */
cDisplays = 1;
}
#ifndef VBOXWDDM
{
rc = pfnCreateSecondaryDisplay (PrimaryExtension, (PVOID*)&SecondaryExtension, VIDEO_DUALVIEW_REMOVABLE);
dprintf(("VBoxVideo::VBoxSetupDisplays: VideoPortCreateSecondaryDisplay returned %#x, SecondaryExtension = %p\n",
rc, SecondaryExtension));
{
break;
}
/* Update the list pointers. */
/* Take the successfully created display into account. */
}
#else
/* simply store the number of monitors, we will deal with VidPN stuff later */
#endif
}
/* Failure to create secondary displays is not fatal */
}
#ifndef VBOXWDDM
/* Now when the number of monitors is known and extensions are created,
* calculate the layout of framebuffers.
*/
#endif
{
/* Setup the information for the host. */
if (RT_FAILURE (rc))
{
}
}
#ifdef VBOXWDDM
{
/* Align down to 4096 bytes. */
ulSize &= ~0xFFF;
if (RT_SUCCESS(rc))
{
* for basic DMA functionality */
if (RT_FAILURE(rc))
}
#ifdef VBOXWDDM_RENDER_FROM_SHADOW
if (RT_SUCCESS(rc))
{
if (ulSize > VBVA_MIN_BUFFER_SIZE)
{
if (ulRatio)
else
}
else
{
/* todo: ?? */
}
ulSize &= ~0xFFF;
{
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
{
/* @todo: de-initialize */
}
}
}
}
#endif
if (RT_FAILURE(rc))
}
#endif
{
/* Unmap the memory if VBoxVideo is not supported. */
VBoxUnmapAdapterMemory (PrimaryExtension, &PrimaryExtension->u.primary.pvMiniportHeap, PrimaryExtension->u.primary.cbMiniportHeap);
VBoxUnmapAdapterMemory (PrimaryExtension, &PrimaryExtension->u.primary.pvAdapterInformation, VBVA_ADAPTER_INFORMATION_SIZE);
}
{
}
dprintf(("VBoxVideo::VBoxSetupDisplays: finished\n"));
}
/*
* Send the pointer shape to the host.
*/
typedef struct _MOUSEPOINTERSHAPECTX
{
static int vbvaInitMousePointerShape (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
{
/* Will be updated by the host. */
p->i32Result = VINF_SUCCESS;
/* We have our custom flags in the field */
if (p->fu32Flags & VBOX_MOUSE_POINTER_SHAPE)
{
/* If shape is supplied, then alway create the pointer visible.
* See comments in 'vboxUpdatePointerShape'
*/
/* Copy the actual pointer data. */
}
return VINF_SUCCESS;
}
static int vbvaFinalizeMousePointerShape (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
{
return VINF_SUCCESS;
}
{
#ifndef VBOXWDDM
/* In multimonitor case the HW mouse pointer is the same on all screens,
* and Windows calls each display driver with the same pointer data: visible for
* the screen where the pointer is and invisible for other screens.
*
* This driver passes the shape to the host only from primary screen and makes
* the pointer always visible (in vbvaInitMousePointerShape).
*
* The simple solution makes it impossible to create the shape and keep the mouse
* pointer invisible. New shapes will be created visible.
* But:
* 1) VBox frontends actually ignore the visibility flag if VBOX_MOUSE_POINTER_SHAPE
* is set and always create new pointers visible.
* 2) Windows uses DrvMovePointer to hide the pointer, which will still work.
*/
{
dprintf(("vboxUpdatePointerShape: ignore non primary device %d(%d)\n",
/* Success. */
return TRUE;
}
#else
#endif
{
/* Size of the pointer data: sizeof (AND mask) + sizeof (XOR_MASK) */
}
#ifndef DEBUG_misha
dprintf(("vboxUpdatePointerShape: cbData %d, %dx%d\n",
#endif
{
dprintf(("vboxUpdatePointerShape: calculated pointer data size is too big (%d bytes, limit %d)\n",
return FALSE;
}
sizeof (VBVAMOUSEPOINTERSHAPE) + cbData,
&ctx);
#ifndef DEBUG_misha
#endif
}
typedef struct _VBVAMINIPORT_CHANNELCONTEXT
{
void *pvChannelHandler;
typedef struct _VBVADISP_CHANNELCONTEXT
{
struct _VBVAHOSTCMD * pFirstCmd;
struct _VBVAHOSTCMD * pLastCmd;
#ifdef DEBUG
int cCmds;
#endif
bool bValid;
#ifdef DEBUG
{
int counter = 0;
{
{
}
{
}
counter++;
}
}
void dbgCheckList(PDEVICE_EXTENSION PrimaryExtension, VBVADISP_CHANNELCONTEXT *pList, struct _VBVAHOSTCMD * pCmd)
{
}
#else
#endif
typedef struct _VBVA_CHANNELCONTEXTS
{
static int vboxVBVADeleteChannelContexts(PDEVICE_EXTENSION PrimaryExtension, VBVA_CHANNELCONTEXTS * pContext)
{
return VINF_SUCCESS;
}
static int vboxVBVACreateChannelContexts(PDEVICE_EXTENSION PrimaryExtension, VBVA_CHANNELCONTEXTS ** ppContext)
{
#ifndef VBOXWDDM
#else
#endif
VBVA_CHANNELCONTEXTS * pContext = (VBVA_CHANNELCONTEXTS*)VBoxVideoCmnMemAllocNonPaged(PrimaryExtension, size, MEM_TAG);
if(pContext)
{
return VINF_SUCCESS;
}
return VERR_GENERAL_FAILURE;
}
{
if(iId < 0)
{
return NULL;
}
{
}
return NULL;
}
#ifndef VBOXWDDM
{
}
DECLCALLBACK(int) hgsmiHostCmdRequest (HVBOXVIDEOHGSMI hHGSMI, uint8_t u8Channel, struct _VBVAHOSTCMD ** ppCmd)
{
// if(display < 0)
// return VERR_INVALID_PARAMETER;
if(!ppCmd)
return VERR_INVALID_PARAMETER;
/* pick up the host commands */
if(pChannel)
{
if(pDispContext)
{
#ifdef DEBUG
pDispContext->cCmds = 0;
#endif
return VINF_SUCCESS;
}
}
return VERR_INVALID_PARAMETER;
}
#endif
static DECLCALLBACK(int) vboxVBVAChannelGenericHandler(void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
{
// Assert(0);
if(cbBuffer > VBVAHOSTCMD_HDRSIZE)
{
{
{
{
switch(u16ChannelInfo)
{
case VBVAHG_DISPLAY_CUSTOM:
{
if(pLast)
{
}
else
{
}
//TODO: use offset here
break;
}
case VBVAHG_EVENT:
{
#ifndef VBOXWDDM
pEvent);
#else
#endif
}
default:
{
//TODO: use offset here
if(pLast)
break;
}
}
}
/* we do not support lists currently */
if(pLast)
{
}
if(pFirst)
{
&oldIrql);
{
}
else
{
}
#ifdef DEBUG
#endif
oldIrql);
}
else
{
}
return VINF_SUCCESS;
}
}
else
{
//TODO: impl
// HGSMIMINIPORT_CHANNELCONTEXT *pHandler = vboxVideoHGSMIFindHandler;
// if(pHandler && pHandler->pfnChannelHandler)
// {
// pHandler->pfnChannelHandler(pHandler->pvChannelHandler, u16ChannelInfo, pHdr, cbBuffer);
//
// return VINF_SUCCESS;
// }
}
}
/* no handlers were found, need to complete the command here */
return VINF_SUCCESS;
}
static HGSMICHANNELHANDLER g_OldHandler;
int iDisplay, /* negative would mean this is a miniport handler */
{
if(!pChannel)
{
if(RT_FAILURE(rc))
{
return rc;
}
}
else
{
}
if(pDispContext)
{
#ifdef DEBUGVHWASTRICT
#endif
if(!pDispContext->bValid)
{
pDispContext->bValid = true;
#ifdef DEBUG
pDispContext->cCmds = 0;
#endif
int rc = VINF_SUCCESS;
if(!pChannel)
{
"VGA Miniport HGSMI channel",
&g_OldHandler);
}
if(RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
}
}
if(!pChannel)
{
}
return VERR_GENERAL_FAILURE;
}
/** @todo Mouse pointer position to be read from VMMDev memory, address of the memory region
* can be queried from VMMDev via an IOCTL. This VMMDev memory region will contain
* host information which is needed by the guest.
*
* Reading will not cause a switch to the host.
*
* Have to take into account:
* * synchronization: host must write to the memory only from EMT,
* large structures must be read under flag, which tells the host
* that the guest is currently reading the memory (OWNER flag?).
* * guest writes: may be allocate a page for the host info and make
* the page readonly for the guest.
* * the information should be available only for additions drivers.
* * VMMDev additions driver will inform the host which version of the info it expects,
* host must support all versions.
*
*/