VBoxMPIOCTL.cpp revision b0a3d0ec5780199a2f379da63c59ccf48f1a73b9
/* $Id$ */
/** @file
* VBox XPDM Miniport IOCTL handlers
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* 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 "VBoxMPInternal.h"
#include "common/VBoxMPCommon.h"
#include <VBox/Hardware/VBoxVideoVBE.h>
#include <VBox/VBoxGuestLib.h>
#include <VBox/VBoxVideo.h>
/* Note: in/out parameters passed to VBoxDrvStartIO point to the same memory location.
* That means we can't read anything from the input one after first write to the output.
* Defines below are somewhat silly way to catch possible misuse at compile time.
*/
#define VBOXMPIOCTL_HIDE(_var) \
{ \
PVOID (_var); \
(VOID)(_var)
#define VBOXMPIOCTL_UNHIDE() \
}
/* Called for IOCTL_VIDEO_RESET_DEVICE.
* Reset device to a state it comes at system boot time.
* @todo It doesn't do anythyng at the moment, but it looks like the same as VBoxDrvResetHW.
*/
BOOLEAN VBoxMPResetDevice(PVBOXMP_DEVEXT pExt, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
if (pExt->iDevice>0)
{
LOG(("skipping non-primary display %d", pExt->iDevice));
return TRUE;
}
#if 0
/* Don't disable the extended video mode. This would only switch the video mode
* to <current width> x <current height> x 0 bpp which is not what we want. And
* even worse, it causes an disturbing additional mode switch */
VideoPortWritePortUshort((PUSHORT)VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
VideoPortWritePortUshort((PUSHORT)VBE_DISPI_IOPORT_DATA, VBE_DISPI_DISABLED);
#endif
/* Tell the host that we no longer support graphics in the additions */
/* @todo VBoxMPSetGraphicsCap(FALSE); */
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_MAP_VIDEO_MEMORY.
* Maps FrameBuffer and video RAM to a caller's virtual adress space.
*/
BOOLEAN VBoxMPMapVideoMemory(PVBOXMP_DEVEXT pExt, PVIDEO_MEMORY pRequestedAddress,
PVIDEO_MEMORY_INFORMATION pMapInfo, PSTATUS_BLOCK pStatus)
{
PHYSICAL_ADDRESS framebuffer;
ULONG inIoSpace = 0;
LOGF(("framebuffer offset %#x", pExt->ulFrameBufferOffset));
framebuffer.QuadPart = VBE_DISPI_LFB_PHYSICAL_ADDRESS + pExt->ulFrameBufferOffset;
pMapInfo->VideoRamBase = pRequestedAddress->RequestedVirtualAddress;
VBOXMPIOCTL_HIDE(pRequestedAddress);
pMapInfo->VideoRamLength = pExt->pPrimary->u.primary.ulMaxFrameBufferSize;
pStatus->Status = VideoPortMapMemory(pExt, framebuffer, &pMapInfo->VideoRamLength,
&inIoSpace, &pMapInfo->VideoRamBase);
if (NO_ERROR == pStatus->Status)
{
pMapInfo->FrameBufferBase = (PUCHAR)pMapInfo->VideoRamBase;
pMapInfo->FrameBufferLength =
VBoxMPXpdmCurrentVideoMode(pExt)->VisScreenHeight*
VBoxMPXpdmCurrentVideoMode(pExt)->ScreenStride;
pStatus->Information = sizeof(VIDEO_MEMORY_INFORMATION);
/* Save the new framebuffer size */
pExt->ulFrameBufferSize = pMapInfo->FrameBufferLength;
HGSMIAreaInitialize(&pExt->areaDisplay, pMapInfo->FrameBufferBase,
pMapInfo->FrameBufferLength, pExt->ulFrameBufferOffset);
}
VBOXMPIOCTL_UNHIDE();
LOGF_LEAVE();
return NO_ERROR == pStatus->Status;
}
/* Called for IOCTL_VIDEO_UNMAP_VIDEO_MEMORY.
* Unmaps previously mapped FrameBuffer and video RAM from caller's virtual adress space.
*/
BOOLEAN VBoxMPUnmapVideoMemory(PVBOXMP_DEVEXT pExt, PVIDEO_MEMORY VideoMemory, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
HGSMIAreaClear(&pExt->areaDisplay);
pStatus->Status = VideoPortUnmapMemory(pExt, VideoMemory->RequestedVirtualAddress, NULL);
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_SHARE_VIDEO_MEMORY.
* Maps FrameBuffer as a linear frame buffer to a caller's virtual adress space. (obsolete).
*/
BOOLEAN VBoxMPShareVideoMemory(PVBOXMP_DEVEXT pExt, PVIDEO_SHARE_MEMORY pShareMem,
PVIDEO_SHARE_MEMORY_INFORMATION pShareMemInfo, PSTATUS_BLOCK pStatus)
{
PHYSICAL_ADDRESS shareAddress;
ULONG inIoSpace = 0;
ULONG offset, size;
PVOID virtualAddress;
ULONG ulMaxFBSize;
LOGF_ENTER();
ulMaxFBSize = pExt->pPrimary->u.primary.ulMaxFrameBufferSize;
offset = pShareMem->ViewOffset;
size = pShareMem->ViewSize;
virtualAddress = pShareMem->ProcessHandle;
VBOXMPIOCTL_HIDE(pShareMem);
if ((offset>ulMaxFBSize) || ((offset+size)>ulMaxFBSize))
{
WARN(("share failed offset:size(%#x:%#x) > %#x fb size.", offset, size, ulMaxFBSize));
pStatus->Status = ERROR_INVALID_PARAMETER;
return FALSE;
}
shareAddress.QuadPart = VBE_DISPI_LFB_PHYSICAL_ADDRESS + pExt->ulFrameBufferOffset;
pStatus->Status = VideoPortMapMemory(pExt, shareAddress, &size, &inIoSpace, &virtualAddress);
if (NO_ERROR == pStatus->Status)
{
pShareMemInfo->SharedViewOffset = offset;
pShareMemInfo->SharedViewSize = size;
pShareMemInfo->VirtualAddress = virtualAddress;
pStatus->Information = sizeof(VIDEO_SHARE_MEMORY_INFORMATION);
}
VBOXMPIOCTL_UNHIDE();
LOGF_LEAVE();
return NO_ERROR == pStatus->Status;
}
/* Called for IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY.
* Unmaps framebuffer previously mapped with IOCTL_VIDEO_SHARE_VIDEO_MEMORY.
*/
BOOLEAN VBoxMPUnshareVideoMemory(PVBOXMP_DEVEXT pExt, PVIDEO_SHARE_MEMORY pMem, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
pStatus->Status = VideoPortUnmapMemory(pExt, pMem->RequestedVirtualAddress, pMem->ProcessHandle);
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_SET_CURRENT_MODE.
* Sets adapter video mode.
*/
BOOLEAN VBoxMPSetCurrentMode(PVBOXMP_DEVEXT pExt, PVIDEO_MODE pMode, PSTATUS_BLOCK pStatus)
{
ULONG RequestedMode;
VIDEO_MODE_INFORMATION *pModeInfo;
LOGF(("mode=%#x", pMode->RequestedMode));
/* Get requested mode info */
RequestedMode = pMode->RequestedMode & ~(VIDEO_MODE_NO_ZERO_MEMORY|VIDEO_MODE_MAP_MEM_LINEAR);
if (RequestedMode!=pMode->RequestedMode)
{
WARN(("ignoring set VIDEO_MODE_NO_ZERO_MEMORY or VIDEO_MODE_MAP_MEM_LINEAR"));
}
pModeInfo = VBoxMPCmnGetVideoModeInfo(RequestedMode-1);
if (!pModeInfo)
{
pStatus->Status = ERROR_INVALID_PARAMETER;
return FALSE;
}
LOG(("width %d, height %d, bpp %d",
pModeInfo->VisScreenWidth, pModeInfo->VisScreenHeight, pModeInfo->BitsPerPlane));
/* Update device info */
pExt->CurrentMode = RequestedMode;
pExt->CurrentModeWidth = pModeInfo->VisScreenWidth;
pExt->CurrentModeHeight = pModeInfo->VisScreenHeight;
pExt->CurrentModeBPP = pModeInfo->BitsPerPlane;
if (pExt->iDevice>0)
{
LOG(("skipping non-primary display %d", pExt->iDevice));
return TRUE;
}
/* Perform actual mode switch */
VBoxVideoSetModeRegisters((USHORT)pModeInfo->VisScreenWidth, (USHORT)pModeInfo->VisScreenHeight,
(USHORT)pModeInfo->VisScreenWidth, (USHORT)pModeInfo->BitsPerPlane, 0, 0, 0);
/*@todo read back from port to check if mode switch was successful */
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_QUERY_CURRENT_MODE.
* Returns information about current video mode.
*/
BOOLEAN VBoxMPQueryCurrentMode(PVBOXMP_DEVEXT pExt, PVIDEO_MODE_INFORMATION pModeInfo, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
pStatus->Information = sizeof(VIDEO_MODE_INFORMATION);
VideoPortMoveMemory(pModeInfo, VBoxMPXpdmCurrentVideoMode(pExt), sizeof(VIDEO_MODE_INFORMATION));
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES.
* Returns count of supported video modes and structure size in bytes,
* used by the following IOCTL_VIDEO_QUERY_AVAIL_MODES.
*/
BOOLEAN VBoxMPQueryNumAvailModes(PVBOXMP_DEVEXT pExt, PVIDEO_NUM_MODES pNumModes, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
VBoxMPXpdmBuildVideoModesTable(pExt);
pNumModes->NumModes = VBoxMPXpdmGetVideoModesCount();
pNumModes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
pStatus->Information = sizeof(VIDEO_NUM_MODES);
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_QUERY_AVAIL_MODES.
* Returns information about supported video modes.
*/
BOOLEAN VBoxMPQueryAvailModes(PVBOXMP_DEVEXT pExt, PVIDEO_MODE_INFORMATION pModes, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
ULONG ulSize = VBoxMPXpdmGetVideoModesCount()*sizeof(VIDEO_MODE_INFORMATION);
pStatus->Information = ulSize;
VideoPortMoveMemory(pModes, VBoxMPCmnGetVideoModeInfo(0), ulSize);
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_SET_COLOR_REGISTERS.
* Sets adapter's color registers.
*/
BOOLEAN VBoxMPSetColorRegisters(PVBOXMP_DEVEXT pExt, PVIDEO_CLUT pClut, PSTATUS_BLOCK pStatus)
{
LONG entry;
LOGF_ENTER();
if (pClut->FirstEntry+pClut->NumEntries > 256)
{
pStatus->Status = ERROR_INVALID_PARAMETER;
return FALSE;
}
for (entry=pClut->FirstEntry; entry<pClut->FirstEntry+pClut->NumEntries; ++entry)
{
VBoxVideoCmnPortWriteUchar(VBE_DISPI_IOPORT_DAC_WRITE_INDEX, (UCHAR)entry);
VBoxVideoCmnPortWriteUchar(VBE_DISPI_IOPORT_DAC_DATA, pClut->LookupTable[entry].RgbArray.Red);
VBoxVideoCmnPortWriteUchar(VBE_DISPI_IOPORT_DAC_DATA, pClut->LookupTable[entry].RgbArray.Green);
VBoxVideoCmnPortWriteUchar(VBE_DISPI_IOPORT_DAC_DATA, pClut->LookupTable[entry].RgbArray.Blue);
}
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_SET_POINTER_ATTR.
* Sets pointer attributes.
*/
BOOLEAN VBoxMPSetPointerAttr(PVBOXMP_DEVEXT pExt, PVIDEO_POINTER_ATTRIBUTES pPointerAttrs, uint32_t cbLen, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC;
LOGF_ENTER();
if (VBoxQueryHostWantsAbsolute())
{
bRC = VBoxMPCmnUpdatePointerShape(VBoxCommonFromDeviceExt(pExt), pPointerAttrs, cbLen);
}
else
{
LOG(("Fallback to sw pointer."));
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC = FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_ENABLE_POINTER/IOCTL_VIDEO_DISABLE_POINTER.
* Hides pointer or makes it visible depending on bEnable value passed.
*/
BOOLEAN VBoxMPEnablePointer(PVBOXMP_DEVEXT pExt, BOOLEAN bEnable, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxQueryHostWantsAbsolute())
{
/* Check if it's not shown already. */
if (bEnable == pExt->pPrimary->u.primary.fMouseHidden)
{
VIDEO_POINTER_ATTRIBUTES attrs;
/* Visible and No Shape means show the pointer, 0 means hide pointer.
* It's enough to init only this field.
*/
attrs.Enable = bEnable ? VBOX_MOUSE_POINTER_VISIBLE:0;
/* Pass info to the host. */
bRC = VBoxMPCmnUpdatePointerShape(VBoxCommonFromDeviceExt(pExt), &attrs, sizeof(attrs));
if (bRC)
{
/* Update device state. */
pExt->pPrimary->u.primary.fMouseHidden = !bEnable;
}
}
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC = FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_QUERY_POINTER_POSITION.
* Query pointer position.
*/
BOOLEAN VBoxMPQueryPointerPosition(PVBOXMP_DEVEXT pExt, PVIDEO_POINTER_POSITION pPos, PSTATUS_BLOCK pStatus)
{
uint16_t PosX, PosY;
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxQueryPointerPos(&PosX, &PosY))
{
PVIDEO_MODE_INFORMATION pMode = VBoxMPXpdmCurrentVideoMode(pExt);
/* map from 0xFFFF to the current resolution */
pPos->Column = (SHORT)(PosX / (0xFFFF / pMode->VisScreenWidth));
pPos->Row = (SHORT)(PosY / (0xFFFF / pMode->VisScreenHeight));
pStatus->Information = sizeof(VIDEO_POINTER_POSITION);
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC = FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES.
* Query supported hardware pointer feaures.
* Note: we always return all caps we could ever support,
* related functions will return errors if host doesn't accept pointer integration
* and force display driver to enter software fallback codepath.
*/
BOOLEAN VBoxMPQueryPointerCapabilities(PVBOXMP_DEVEXT pExt, PVIDEO_POINTER_CAPABILITIES pCaps, PSTATUS_BLOCK pStatus)
{
LOGF_ENTER();
pStatus->Information = sizeof(VIDEO_POINTER_CAPABILITIES);
pCaps->Flags = VIDEO_MODE_ASYNC_POINTER | VIDEO_MODE_COLOR_POINTER | VIDEO_MODE_MONO_POINTER;
/* Up to 64x64 shapes */
pCaps->MaxWidth = 64;
pCaps->MaxHeight = 64;
/* Not used by our display driver */
pCaps->HWPtrBitmapStart = -1;
pCaps->HWPtrBitmapEnd = -1;
LOGF_LEAVE();
return TRUE;
}
/* Called for IOCTL_VIDEO_VBVA_ENABLE.
* Display driver is ready to switch to VBVA operation mode.
*/
BOOLEAN VBoxMPVBVAEnable(PVBOXMP_DEVEXT pExt, BOOLEAN bEnable, VBVAENABLERESULT *pResult, PSTATUS_BLOCK pStatus)
{
int rc;
BOOLEAN bRC = TRUE;
LOGF_ENTER();
rc = VBoxVbvaEnable(pExt, bEnable, pResult);
if (RT_SUCCESS(rc))
{
pStatus->Information = sizeof(VBVAENABLERESULT);
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC = FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_VBOX_SETVISIBLEREGION.
* Sends visible regions information to the host.
*/
BOOLEAN VBoxMPSetVisibleRegion(uint32_t cRects, RTRECT *pRects, PSTATUS_BLOCK pStatus)
{
int rc;
BOOLEAN bRC = FALSE;
LOGF_ENTER();
VMMDevVideoSetVisibleRegion *req = NULL;
rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevVideoSetVisibleRegion) + (cRects-1)*sizeof(RTRECT),
VMMDevReq_VideoSetVisibleRegion);
if (RT_SUCCESS(rc))
{
req->cRect = cRects;
memcpy(&req->Rect, pRects, cRects*sizeof(RTRECT));
rc = VbglGRPerform(&req->header);
if (RT_SUCCESS(rc))
{
bRC=TRUE;
}
VbglGRFree(&req->header);
}
else
{
WARN(("VbglGRAlloc rc = %#xrc", rc));
}
if (!bRC)
{
pStatus->Status = ERROR_INVALID_FUNCTION;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_HGSMI_QUERY_PORTPROCS.
* Returns video port api function pointers.
*/
BOOLEAN VBoxMPHGSMIQueryPortProcs(PVBOXMP_DEVEXT pExt, HGSMIQUERYCPORTPROCS *pProcs, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxCommonFromDeviceExt(pExt)->bHGSMI)
{
pProcs->pContext = pExt->pPrimary;
pProcs->VideoPortProcs = pExt->pPrimary->u.primary.VideoPortProcs;
pStatus->Information = sizeof(HGSMIQUERYCPORTPROCS);
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC=FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_HGSMI_QUERY_CALLBACKS.
* Returns HGSMI related callbacks.
*/
BOOLEAN VBoxMPHGSMIQueryCallbacks(PVBOXMP_DEVEXT pExt, HGSMIQUERYCALLBACKS *pCallbacks, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxCommonFromDeviceExt(pExt)->bHGSMI)
{
pCallbacks->hContext = VBoxCommonFromDeviceExt(pExt);
pCallbacks->pfnCompletionHandler = VBoxMPHGSMIHostCmdCompleteCB;
pCallbacks->pfnRequestCommandsHandler = VBoxMPHGSMIHostCmdRequestCB;
pStatus->Information = sizeof(HGSMIQUERYCALLBACKS);
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC=FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_QUERY_HGSMI_INFO.
* Returns hgsmi info for this adapter.
*/
BOOLEAN VBoxMPQueryHgsmiInfo(PVBOXMP_DEVEXT pExt, QUERYHGSMIRESULT *pResult, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxCommonFromDeviceExt(pExt)->bHGSMI)
{
pResult->iDevice = pExt->iDevice;
pResult->ulFlags = 0;
pResult->areaDisplay = pExt->areaDisplay;
pResult->u32DisplayInfoSize = VBVA_DISPLAY_INFORMATION_SIZE;
pResult->u32MinVBVABufferSize = VBVA_MIN_BUFFER_SIZE;
pResult->IOPortGuestCommand = VBoxCommonFromDeviceExt(pExt)->guestCtx.port;
pStatus->Information = sizeof(QUERYHGSMIRESULT);
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC=FALSE;
}
LOGF_LEAVE();
return bRC;
}
/* Called for IOCTL_VIDEO_HGSMI_HANDLER_ENABLE.
* Enables HGSMI miniport channel.
*/
BOOLEAN VBoxMPHgsmiHandlerEnable(PVBOXMP_DEVEXT pExt, HGSMIHANDLERENABLE *pChannel, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxCommonFromDeviceExt(pExt)->bHGSMI)
{
int rc = VBoxVbvaChannelDisplayEnable(VBoxCommonFromDeviceExt(pExt), pExt->iDevice, pChannel->u8Channel);
if (RT_FAILURE(rc))
{
pStatus->Status = ERROR_INVALID_NAME;
bRC=FALSE;
}
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC=FALSE;
}
LOGF_LEAVE();
return bRC;
}
#ifdef VBOX_WITH_VIDEOHWACCEL
/* Called for IOCTL_VIDEO_VHWA_QUERY_INFO.
* Returns framebuffer offset.
*/
BOOLEAN VBoxMPVhwaQueryInfo(PVBOXMP_DEVEXT pExt, VHWAQUERYINFO *pInfo, PSTATUS_BLOCK pStatus)
{
BOOLEAN bRC = TRUE;
LOGF_ENTER();
if (VBoxCommonFromDeviceExt(pExt)->bHGSMI)
{
pInfo->offVramBase = (ULONG_PTR)pExt->ulFrameBufferOffset;
pStatus->Information = sizeof (VHWAQUERYINFO);
}
else
{
pStatus->Status = ERROR_INVALID_FUNCTION;
bRC=FALSE;
}
LOGF_LEAVE();
return bRC;
}
#endif