VBoxVideoHGSMI.cpp revision 759c605ff712b0d178548cee2e3e41035b8d731c
/* $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 (&pCommon->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(pCommon);
}
{
if(offset != HGSMIOFFSET_VOID)
{
}
}
)
{
{
->bHostCmdProcessing, true, false))
return;
->bHostCmdProcessing, false);
}
}
/* Detect whether HGSMI is supported by the host. */
BOOLEAN VBoxHGSMIIsSupported (void)
{
return (DispiId == VBE_DISPI_ID_HGSMI);
}
typedef FNHGSMICALLINIT *PFNHGSMICALLINIT;
typedef FNHGSMICALLFINALIZE *PFNHGSMICALLFINALIZE;
{
#ifdef VBOX_WITH_WDDM
/* @todo: add synchronization */
#endif
}
{
#ifdef VBOX_WITH_WDDM
/* @todo: add synchronization */
#endif
}
{
/* Initialize the buffer and get the offset for port IO. */
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. */
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 (pCommon,
}
typedef struct _QUERYCONFCTX
{
} QUERYCONFCTX;
{
p->u32Value = 0;
return VINF_SUCCESS;
}
{
{
}
return VINF_SUCCESS;
}
{
sizeof (VBVACONF32),
&context);
return rc;
}
int VBoxHGSMISendViewInfo(PVBOXVIDEO_COMMON pCommon, uint32_t u32Count, PFNHGSMIFILLVIEWINFO pfnFill, void *pvData)
{
int rc;
/* Issue the screen info command. */
Assert(p);
if (p)
{
if (RT_SUCCESS(rc))
vboxHGSMIBufferSubmit (pCommon, p);
vboxHGSMIBufferFree (pCommon, p);
}
else
rc = VERR_NO_MEMORY;
return rc;
}
#ifndef VBOX_WITH_WDDM
{
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;
}
{
}
{
return VINF_SUCCESS;
}
{
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);
{
/* Inform about caps */
sizeof (VBVACAPS),
NULL);
}
if (RT_SUCCESS (rc))
{
/* Report the host heap location. */
sizeof (VBVAINFOHEAP),
NULL,
NULL);
}
return rc;
}
#ifndef VBOX_WITH_WDDM
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 VBOX_WITH_WDDM
#endif
{
dprintf(("VBoxVideo::VBoxSetupDisplays: PrimaryExtension = %p\n",
sizeof(*commonFromDeviceExt(PrimaryExtension)));
/* Why does this use VBoxVideoCmnMemZero? The MSDN docs say that it should
* only be used on mapped display adapter memory. Done with memset above. */
// VBoxVideoCmnMemZero(&commonFromDeviceExt(PrimaryExtension)->areaHostHeap, sizeof(HGSMIAREA));
{
/** @note (michael) moved this here as it is done unconditionally in both
* driver branches. Feel free to fix if that is ever changed. */
/* 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
{
commonFromDeviceExt(PrimaryExtension)->pHostFlags = (HGSMIHOSTFLAGS*)(((uint8_t*)commonFromDeviceExt(PrimaryExtension)->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;
vboxQueryConfHGSMI (commonFromDeviceExt(PrimaryExtension), VBOX_VBVA_CONF32_HOST_HEAP_SIZE, &cbMiniportHeap);
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. */
{
/* Query the configured number of displays. */
vboxQueryConfHGSMI (commonFromDeviceExt(PrimaryExtension), VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
dprintf(("VBoxVideo::VBoxSetupDisplays: cDisplays = %d\n",
cDisplays));
{
/* Host reported some bad value. Continue in the 1 screen mode. */
cDisplays = 1;
}
}
{
/* Setup the information for the host. */
if (RT_FAILURE (rc))
{
}
}
#ifndef VBOX_WITH_WDDM
/* For WDDM, we simply store the number of monitors as we will deal with
* VidPN stuff later */
{
/* 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");
}
else
{
{
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. */
}
}
/* Failure to create secondary displays is not fatal */
}
/* Now when the number of monitors is known and extensions are created,
* calculate the layout of framebuffers.
*/
/* 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 */
{
if (RT_SUCCESS(rc))
{
(void *) PrimaryExtension);
}
if (RT_FAILURE (rc))
{
}
}
#endif
#ifdef VBOX_WITH_WDDM
{
#ifdef VBOX_WITH_VDMA
/* Align down to 4096 bytes. */
ulSize &= ~0xFFF;
#else
#endif
#ifdef VBOX_WITH_VDMA
#endif
);
if (RT_SUCCESS(rc))
{
* for basic DMA functionality */
if (RT_FAILURE(rc))
}
NTSTATUS Status = vboxVideoAMgrCreate(PrimaryExtension, &PrimaryExtension->AllocMgr, offset, ulSize);
if (Status != STATUS_SUCCESS)
{
}
#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
rc = VBoxMapAdapterMemory(commonFromDeviceExt(PrimaryExtension), (void**)&PrimaryExtension->pvVisibleVram,
0,
if (rc != VINF_SUCCESS)
if (RT_FAILURE(rc))
}
#endif
{
/* Unmap the memory if VBoxVideo is not supported. */
VBoxUnmapAdapterMemory (PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap, commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap);
}
dprintf(("VBoxVideo::VBoxSetupDisplays: finished\n"));
}
#ifdef VBOX_WITH_WDDM
{
int rc = VINF_SUCCESS;
VBoxUnmapAdapterMemory(PrimaryExtension, (void**)&PrimaryExtension->pvVisibleVram, vboxWddmVramCpuVisibleSize(PrimaryExtension));
{
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
{
/* @todo: */
}
}
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*rc = */VBoxUnmapAdapterMemory(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap, commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap);
/*
AssertRC(rc);
if (RT_SUCCESS(rc))
*/
{
/* Map the adapter information. It will be needed for HGSMI IO. */
/*
AssertRC(rc);
if (RT_FAILURE(rc))
drprintf((__FUNCTION__"VBoxUnmapAdapterMemory PrimaryExtension->u.primary.pvAdapterInformation failed, rc(%d)\n", rc));
*/
}
}
}
return rc;
}
#endif
/*
* Send the pointer shape to the host.
*/
typedef struct _MOUSEPOINTERSHAPECTX
{
{
/* 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;
}
{
return VINF_SUCCESS;
}
{
#ifndef VBOX_WITH_WDDM
/* 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
}
#ifndef VBOX_WITH_WDDM
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)
{
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;
}
{
}
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 */
HGSMICHANNEL * pChannel = HGSMIChannelFindById (&commonFromDeviceExt(PrimaryExtension)->channels, u8Channel);
if(pChannel)
{
if(pDispContext)
{
#ifdef DEBUG
pDispContext->cCmds = 0;
#endif
return VINF_SUCCESS;
}
}
return VERR_INVALID_PARAMETER;
}
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 VBOX_WITH_WDDM
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 */
{
HGSMICHANNEL * pChannel = HGSMIChannelFindById (&commonFromDeviceExt(PrimaryExtension)->channels, u8Channel);
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;
}
#endif /* !VBOX_WITH_WDDM */
/** @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.
*
*/