summaryrefslogtreecommitdiff
path: root/src/VBox/Additions/common/VBoxVideo
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/common/VBoxVideo')
-rw-r--r--src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp579
-rw-r--r--src/VBox/Additions/common/VBoxVideo/Modesetting.cpp171
-rw-r--r--src/VBox/Additions/common/VBoxVideo/VBVABase.cpp390
3 files changed, 1140 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp
new file mode 100644
index 000000000..4ab42f26a
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp
@@ -0,0 +1,579 @@
+/* $Id: HGSMIBase.cpp 35118 2010-12-15 10:48:29Z vboxsync $ */
+/** @file
+ * VirtualBox Video driver, common code - HGSMI initialisation and helper
+ * functions.
+ */
+
+/*
+ * Copyright (C) 2006-2010 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 <VBox/VBoxVideoGuest.h>
+#include <VBox/VBoxVideo.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/Hardware/VBoxVideoVBE.h>
+#include <VBox/VMMDev.h>
+
+#include <iprt/asm.h>
+#include <iprt/log.h>
+
+#include <string.h>
+
+/** Send completion notification to the host for the command located at offset
+ * @a offt into the host command buffer. */
+static void HGSMINotifyHostCmdComplete(PHGSMIHOSTCOMMANDCONTEXT pCtx, HGSMIOFFSET offt)
+{
+ VBoxVideoCmnPortWriteUlong(pCtx->port, offt);
+}
+
+
+/**
+ * Inform the host that a command has been handled.
+ *
+ * @param pCtx the context containing the heap to be used
+ * @param pvMem pointer into the heap as mapped in @a pCtx to the command to
+ * be completed
+ */
+RTDECL(void) VBoxHGSMIHostCmdComplete(PHGSMIHOSTCOMMANDCONTEXT pCtx,
+ void *pvMem)
+{
+ HGSMIBUFFERHEADER *pHdr = HGSMIBufferHeaderFromData(pvMem);
+ HGSMIOFFSET offMem = HGSMIPointerToOffset(&pCtx->areaCtx, pHdr);
+ Assert(offMem != HGSMIOFFSET_VOID);
+ if(offMem != HGSMIOFFSET_VOID)
+ {
+ HGSMINotifyHostCmdComplete(pCtx, offMem);
+ }
+}
+
+
+/** Submit an incoming host command to the appropriate handler. */
+static void hgsmiHostCmdProcess(PHGSMIHOSTCOMMANDCONTEXT pCtx,
+ HGSMIOFFSET offBuffer)
+{
+ int rc = HGSMIBufferProcess(&pCtx->areaCtx, &pCtx->channels, offBuffer);
+ Assert(!RT_FAILURE(rc));
+ 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 */
+ HGSMINotifyHostCmdComplete(pCtx, offBuffer);
+ }
+ /* if the cmd succeeded it's responsibility of the callback to complete it */
+}
+
+/** Get the next command from the host. */
+static HGSMIOFFSET hgsmiGetHostBuffer(PHGSMIHOSTCOMMANDCONTEXT pCtx)
+{
+ return VBoxVideoCmnPortReadUlong(pCtx->port);
+}
+
+
+/** Get and handle the next command from the host. */
+static void hgsmiHostCommandQueryProcess(PHGSMIHOSTCOMMANDCONTEXT pCtx)
+{
+ HGSMIOFFSET offset = hgsmiGetHostBuffer(pCtx);
+ AssertReturnVoid(offset != HGSMIOFFSET_VOID);
+ hgsmiHostCmdProcess(pCtx, offset);
+}
+
+
+/** Drain the host command queue. */
+RTDECL(void) VBoxHGSMIProcessHostQueue(PHGSMIHOSTCOMMANDCONTEXT pCtx)
+{
+ while (pCtx->pfHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING)
+ {
+ if (!ASMAtomicCmpXchgBool(&pCtx->fHostCmdProcessing, true, false))
+ return;
+ hgsmiHostCommandQueryProcess(pCtx);
+ ASMAtomicWriteBool(&pCtx->fHostCmdProcessing, false);
+ }
+}
+
+
+/** Detect whether HGSMI is supported by the host. */
+RTDECL(bool) VBoxHGSMIIsSupported(void)
+{
+ uint16_t DispiId;
+
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI);
+
+ DispiId = VBoxVideoCmnPortReadUshort(VBE_DISPI_IOPORT_DATA);
+
+ return (DispiId == VBE_DISPI_ID_HGSMI);
+}
+
+
+/**
+ * Allocate and initialise a command descriptor in the guest heap for a
+ * guest-to-host command.
+ *
+ * @returns pointer to the descriptor's command data buffer
+ * @param pCtx the context containing the heap to be used
+ * @param cbData the size of the command data to go into the descriptor
+ * @param u8Ch the HGSMI channel to be used, set to the descriptor
+ * @param u16Op the HGSMI command to be sent, set to the descriptor
+ */
+RTDECL(void *) VBoxHGSMIBufferAlloc(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ HGSMISIZE cbData,
+ uint8_t u8Ch,
+ uint16_t u16Op)
+{
+#ifdef VBOX_WITH_WDDM
+ /* @todo: add synchronization */
+#endif
+ return HGSMIHeapAlloc (&pCtx->heapCtx, cbData, u8Ch, u16Op);
+}
+
+
+/**
+ * Free a descriptor allocated by @a VBoxHGSMIBufferAlloc.
+ *
+ * @param pCtx the context containing the heap used
+ * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc
+ */
+RTDECL(void) VBoxHGSMIBufferFree(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ void *pvBuffer)
+{
+#ifdef VBOX_WITH_WDDM
+ /* @todo: add synchronization */
+#endif
+ HGSMIHeapFree (&pCtx->heapCtx, pvBuffer);
+}
+
+
+/**
+ * Submit a command descriptor allocated by @a VBoxHGSMIBufferAlloc.
+ *
+ * @param pCtx the context containing the heap used
+ * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc
+ */
+RTDECL(int) VBoxHGSMIBufferSubmit(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ void *pvBuffer)
+{
+ /* Initialize the buffer and get the offset for port IO. */
+ HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&pCtx->heapCtx, pvBuffer);
+
+ Assert(offBuffer != HGSMIOFFSET_VOID);
+ if (offBuffer != HGSMIOFFSET_VOID)
+ {
+ /* Submit the buffer to the host. */
+ VBoxVideoCmnPortWriteUlong(pCtx->port, offBuffer);
+ return VINF_SUCCESS;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/** Inform the host of the location of the host flags in VRAM via an HGSMI
+ * command. */
+static int vboxHGSMIReportFlagsLocation(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ HGSMIOFFSET offLocation)
+{
+ HGSMIBUFFERLOCATION *p;
+ int rc = VINF_SUCCESS;
+
+ /* Allocate the IO buffer. */
+ p = (HGSMIBUFFERLOCATION *)HGSMIHeapAlloc(&pCtx->heapCtx,
+ sizeof(HGSMIBUFFERLOCATION),
+ HGSMI_CH_HGSMI,
+ HGSMI_CC_HOST_FLAGS_LOCATION);
+ if (p)
+ {
+ /* Prepare data to be sent to the host. */
+ p->offLocation = offLocation;
+ p->cbLocation = sizeof(HGSMIHOSTFLAGS);
+ rc = VBoxHGSMIBufferSubmit(pCtx, p);
+ /* Free the IO buffer. */
+ HGSMIHeapFree (&pCtx->heapCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/** Notify the host of HGSMI-related guest capabilities via an HGSMI command.
+ */
+static int vboxHGSMISendCapsInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t fCaps)
+{
+ VBVACAPS *pCaps;
+ int rc = VINF_SUCCESS;
+
+ /* Allocate the IO buffer. */
+ pCaps = (VBVACAPS *)HGSMIHeapAlloc(&pCtx->heapCtx,
+ sizeof(VBVACAPS), HGSMI_CH_VBVA,
+ VBVA_INFO_CAPS);
+
+ if (pCaps)
+ {
+ /* Prepare data to be sent to the host. */
+ pCaps->rc = VERR_NOT_IMPLEMENTED;
+ pCaps->fCaps = fCaps;
+ rc = VBoxHGSMIBufferSubmit(pCtx, pCaps);
+ if (RT_SUCCESS(rc))
+ {
+ AssertRC(pCaps->rc);
+ rc = pCaps->rc;
+ }
+ /* Free the IO buffer. */
+ HGSMIHeapFree(&pCtx->heapCtx, pCaps);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/** Tell the host about the location of the area of VRAM set aside for the host
+ * heap. */
+static int vboxHGSMIReportHostArea(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t u32AreaOffset, uint32_t u32AreaSize)
+{
+ VBVAINFOHEAP *p;
+ int rc = VINF_SUCCESS;
+
+ /* Allocate the IO buffer. */
+ p = (VBVAINFOHEAP *)HGSMIHeapAlloc(&pCtx->heapCtx,
+ sizeof (VBVAINFOHEAP), HGSMI_CH_VBVA,
+ VBVA_INFO_HEAP);
+ if (p)
+ {
+ /* Prepare data to be sent to the host. */
+ p->u32HeapOffset = u32AreaOffset;
+ p->u32HeapSize = u32AreaSize;
+ rc = VBoxHGSMIBufferSubmit(pCtx, p);
+ /* Free the IO buffer. */
+ HGSMIHeapFree(&pCtx->heapCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Get the information needed to map the basic communication structures in
+ * device memory into our address space. All pointer parameters are optional.
+ *
+ * @param cbVRAM how much video RAM is allocated to the device
+ * @param poffVRAMBaseMapping where to save the offset from the start of the
+ * device VRAM of the whole area to map
+ * @param pcbMapping where to save the mapping size
+ * @param poffGuestHeapMemory where to save the offset into the mapped area
+ * of the guest heap backing memory
+ * @param pcbGuestHeapMemory where to save the size of the guest heap
+ * backing memory
+ * @param poffHostFlags where to save the offset into the mapped area
+ * of the host flags
+ */
+RTDECL(void) VBoxHGSMIGetBaseMappingInfo(uint32_t cbVRAM,
+ uint32_t *poffVRAMBaseMapping,
+ uint32_t *pcbMapping,
+ uint32_t *poffGuestHeapMemory,
+ uint32_t *pcbGuestHeapMemory,
+ uint32_t *poffHostFlags)
+{
+ AssertPtrNullReturnVoid(poffVRAMBaseMapping);
+ AssertPtrNullReturnVoid(pcbMapping);
+ AssertPtrNullReturnVoid(poffGuestHeapMemory);
+ AssertPtrNullReturnVoid(pcbGuestHeapMemory);
+ AssertPtrNullReturnVoid(poffHostFlags);
+ if (poffVRAMBaseMapping)
+ *poffVRAMBaseMapping = cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE;
+ if (pcbMapping)
+ *pcbMapping = VBVA_ADAPTER_INFORMATION_SIZE;
+ if (poffGuestHeapMemory)
+ *poffGuestHeapMemory = 0;
+ if (pcbGuestHeapMemory)
+ *pcbGuestHeapMemory = VBVA_ADAPTER_INFORMATION_SIZE
+ - sizeof(HGSMIHOSTFLAGS);
+ if (poffHostFlags)
+ *poffHostFlags = VBVA_ADAPTER_INFORMATION_SIZE
+ - sizeof(HGSMIHOSTFLAGS);
+}
+
+
+/**
+ * Set up the HGSMI guest-to-host command context.
+ * @returns iprt status value
+ * @param pCtx the context to set up
+ * @param pvGuestHeapMemory a pointer to the mapped backing memory for
+ * the guest heap
+ * @param cbGuestHeapMemory the size of the backing memory area
+ * @param offVRAMGuestHeapMemory the offset of the memory pointed to by
+ * @a pvGuestHeapMemory within the video RAM
+ */
+RTDECL(int) VBoxHGSMISetupGuestContext(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ void *pvGuestHeapMemory,
+ uint32_t cbGuestHeapMemory,
+ uint32_t offVRAMGuestHeapMemory)
+{
+ /** @todo should we be using a fixed ISA port value here? */
+ pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_GUEST;
+ return HGSMIHeapSetup(&pCtx->heapCtx, pvGuestHeapMemory,
+ cbGuestHeapMemory, offVRAMGuestHeapMemory,
+ false /*fOffsetBased*/);
+}
+
+
+/**
+ * Get the information needed to map the area used by the host to send back
+ * requests.
+ *
+ * @param pCtx the context containing the heap to use
+ * @param cbVRAM how much video RAM is allocated to the device
+ * @param offVRAMBaseMapping the offset of the basic communication structures
+ * into the guest's VRAM
+ * @param poffVRAMHostArea where to store the offset into VRAM of the host
+ * heap area
+ * @param pcbHostArea where to store the size of the host heap area
+ */
+RTDECL(void) VBoxHGSMIGetHostAreaMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t cbVRAM,
+ uint32_t offVRAMBaseMapping,
+ uint32_t *poffVRAMHostArea,
+ uint32_t *pcbHostArea)
+{
+ uint32_t offVRAMHostArea = offVRAMBaseMapping, cbHostArea = 0;
+
+ AssertPtrReturnVoid(poffVRAMHostArea);
+ AssertPtrReturnVoid(pcbHostArea);
+ VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_HOST_HEAP_SIZE, &cbHostArea);
+ if (cbHostArea != 0)
+ {
+ uint32_t cbHostAreaMaxSize = cbVRAM / 4;
+ /** @todo what is the idea of this? */
+ if (cbHostAreaMaxSize >= VBVA_ADAPTER_INFORMATION_SIZE)
+ {
+ cbHostAreaMaxSize -= VBVA_ADAPTER_INFORMATION_SIZE;
+ }
+ if (cbHostArea > cbHostAreaMaxSize)
+ {
+ cbHostArea = cbHostAreaMaxSize;
+ }
+ /* Round up to 4096 bytes. */
+ cbHostArea = (cbHostArea + 0xFFF) & ~0xFFF;
+ offVRAMHostArea = offVRAMBaseMapping - cbHostArea;
+ }
+
+ *pcbHostArea = cbHostArea;
+ *poffVRAMHostArea = offVRAMHostArea;
+ LogFunc(("offVRAMHostArea = 0x%08X, cbHostArea = 0x%08X\n",
+ offVRAMHostArea, cbHostArea));
+}
+
+
+/**
+ * Initialise the host context structure.
+ *
+ * @param pCtx the context structure to initialise
+ * @param pvBaseMapping where the basic HGSMI structures are mapped at
+ * @param offHostFlags the offset of the host flags into the basic HGSMI
+ * structures
+ * @param pvHostAreaMapping where the area for the host heap is mapped at
+ * @param offVRAMHostArea offset of the host heap area into VRAM
+ * @param cbHostArea size in bytes of the host heap area
+ */
+RTDECL(void) VBoxHGSMISetupHostContext(PHGSMIHOSTCOMMANDCONTEXT pCtx,
+ void *pvBaseMapping,
+ uint32_t offHostFlags,
+ void *pvHostAreaMapping,
+ uint32_t offVRAMHostArea,
+ uint32_t cbHostArea)
+{
+ uint8_t *pu8HostFlags = ((uint8_t *)pvBaseMapping) + offHostFlags;
+ pCtx->pfHostFlags = (HGSMIHOSTFLAGS *)pu8HostFlags;
+ /** @todo should we really be using a fixed ISA port value here? */
+ pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_HOST;
+ HGSMIAreaInitialize(&pCtx->areaCtx, pvHostAreaMapping, cbHostArea,
+ offVRAMHostArea);
+}
+
+
+/**
+ * Tell the host about the ways it can use to communicate back to us via an
+ * HGSMI command
+ *
+ * @returns iprt status value
+ * @param pCtx the context containing the heap to use
+ * @param offVRAMFlagsLocation where we wish the host to place its flags
+ * relative to the start of the VRAM
+ * @param fCaps additions HGSMI capabilities the guest
+ * supports
+ * @param offVRAMHostArea offset into VRAM of the host heap area
+ * @param cbHostArea size in bytes of the host heap area
+ */
+RTDECL(int) VBoxHGSMISendHostCtxInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ HGSMIOFFSET offVRAMFlagsLocation,
+ uint32_t fCaps,
+ uint32_t offVRAMHostArea,
+ uint32_t cbHostArea)
+{
+ Log(("VBoxVideo::vboxSetupAdapterInfo\n"));
+
+ /* setup the flags first to ensure they are initialized by the time the
+ * host heap is ready */
+ int rc = vboxHGSMIReportFlagsLocation(pCtx, offVRAMFlagsLocation);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc) && fCaps)
+ {
+ /* Inform about caps */
+ rc = vboxHGSMISendCapsInfo(pCtx, fCaps);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS (rc))
+ {
+ /* Report the host heap location. */
+ rc = vboxHGSMIReportHostArea(pCtx, offVRAMHostArea, cbHostArea);
+ AssertRC(rc);
+ }
+ Log(("VBoxVideo::vboxSetupAdapterInfo finished rc = %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Query the host for an HGSMI configuration parameter via an HGSMI command.
+ * @returns iprt status value
+ * @param pCtx the context containing the heap used
+ * @param u32Index the index of the parameter to query,
+ * @see VBVACONF32::u32Index
+ * @param pulValue where to store the value of the parameter on success
+ */
+RTDECL(int) VBoxQueryConfHGSMI(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t u32Index, uint32_t *pulValue)
+{
+ int rc = VINF_SUCCESS;
+ VBVACONF32 *p;
+ LogFunc(("u32Index = %d\n", u32Index));
+
+ /* Allocate the IO buffer. */
+ p = (VBVACONF32 *)HGSMIHeapAlloc(&pCtx->heapCtx,
+ sizeof(VBVACONF32), HGSMI_CH_VBVA,
+ VBVA_QUERY_CONF32);
+ if (p)
+ {
+ /* Prepare data to be sent to the host. */
+ p->u32Index = u32Index;
+ p->u32Value = 0;
+ rc = VBoxHGSMIBufferSubmit(pCtx, p);
+ if (RT_SUCCESS(rc))
+ {
+ *pulValue = p->u32Value;
+ LogFunc(("u32Value = %d\n", p->u32Value));
+ }
+ /* Free the IO buffer. */
+ HGSMIHeapFree(&pCtx->heapCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ LogFunc(("rc = %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Pass the host a new mouse pointer shape via an HGSMI command.
+ *
+ * @returns success or failure
+ * @todo why not return an iprt status code?
+ * @param fFlags cursor flags, @see VMMDevReqMousePointer::fFlags
+ * @param cHotX horizontal position of the hot spot
+ * @param cHotY vertical position of the hot spot
+ * @param cWidth width in pixels of the cursor
+ * @param cHeight height in pixels of the cursor
+ * @param pPixels pixel data, @see VMMDevReqMousePointer for the format
+ * @param cbLength size in bytes of the pixel data
+ */
+RTDECL(bool) VBoxHGSMIUpdatePointerShape(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t fFlags,
+ uint32_t cHotX,
+ uint32_t cHotY,
+ uint32_t cWidth,
+ uint32_t cHeight,
+ uint8_t *pPixels,
+ uint32_t cbLength)
+{
+ VBVAMOUSEPOINTERSHAPE *p;
+ uint32_t cbData = 0;
+ int rc = VINF_SUCCESS;
+
+ if (fFlags & VBOX_MOUSE_POINTER_SHAPE)
+ {
+ /* Size of the pointer data: sizeof (AND mask) + sizeof (XOR_MASK) */
+ cbData = ((((cWidth + 7) / 8) * cHeight + 3) & ~3)
+ + cWidth * 4 * cHeight;
+ /* If shape is supplied, then always create the pointer visible.
+ * See comments in 'vboxUpdatePointerShape'
+ */
+ fFlags |= VBOX_MOUSE_POINTER_VISIBLE;
+ }
+ LogFlowFunc(("cbData %d, %dx%d\n", cbData, cWidth, cHeight));
+ if (cbData > cbLength)
+ {
+ LogFunc(("calculated pointer data size is too big (%d bytes, limit %d)\n",
+ cbData, cbLength));
+ return false;
+ }
+ /* Allocate the IO buffer. */
+ p = (VBVAMOUSEPOINTERSHAPE *)HGSMIHeapAlloc(&pCtx->heapCtx,
+ sizeof(VBVAMOUSEPOINTERSHAPE)
+ + cbData,
+ HGSMI_CH_VBVA,
+ VBVA_MOUSE_POINTER_SHAPE);
+ if (p)
+ {
+ /* Prepare data to be sent to the host. */
+ /* Will be updated by the host. */
+ p->i32Result = VINF_SUCCESS;
+ /* We have our custom flags in the field */
+ p->fu32Flags = fFlags;
+ p->u32HotX = cHotX;
+ p->u32HotY = cHotY;
+ p->u32Width = cWidth;
+ p->u32Height = cHeight;
+ if (p->fu32Flags & VBOX_MOUSE_POINTER_SHAPE)
+ /* Copy the actual pointer data. */
+ memcpy (p->au8Data, pPixels, cbData);
+ rc = VBoxHGSMIBufferSubmit(pCtx, p);
+ if (RT_SUCCESS(rc))
+ rc = p->i32Result;
+ /* Free the IO buffer. */
+ HGSMIHeapFree(&pCtx->heapCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ LogFlowFunc(("rc %d\n", rc));
+ return RT_SUCCESS(rc);
+}
+
+
+/** @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.
+ *
+ */
diff --git a/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp b/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp
new file mode 100644
index 000000000..a61fa79eb
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp
@@ -0,0 +1,171 @@
+/* $Id: Modesetting.cpp 35150 2010-12-15 16:33:59Z vboxsync $ */
+/** @file
+ * VirtualBox Video driver, common code - HGSMI initialisation and helper
+ * functions.
+ */
+
+/*
+ * Copyright (C) 2006-2010 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 <VBox/VBoxVideoGuest.h>
+#include <VBox/VBoxVideo.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/Hardware/VBoxVideoVBE.h>
+#include <VBox/VMMDev.h>
+
+#include <iprt/asm.h>
+#include <iprt/log.h>
+
+/**
+ * Gets the count of virtual monitors attached to the guest via an HGSMI
+ * command
+ *
+ * @returns the right count on success or 1 on failure.
+ * @param pCtx the context containing the heap to use
+ */
+RTDECL(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx)
+{
+ /* Query the configured number of displays. */
+ uint32_t cDisplays = 0;
+ VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
+ LogFunc(("cDisplays = %d\n", cDisplays));
+ if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS)
+ /* Host reported some bad value. Continue in the 1 screen mode. */
+ cDisplays = 1;
+ return cDisplays;
+}
+
+
+/**
+ * Tell the host about how VRAM is divided up between each screen via an HGSMI
+ * command. It is acceptable to specifiy identical data for each screen if
+ * they share a single framebuffer.
+ *
+ * @returns iprt status code, either VERR_NO_MEMORY or the status returned by
+ * @a pfnFill
+ * @param pCtx the context containing the heap to use
+ * @param u32Count the number of screens we are activating
+ * @param pfnFill a callback which initialises the VBVAINFOVIEW structures
+ * for all screens
+ * @param pvData context data for @a pfnFill
+ */
+RTDECL(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t u32Count,
+ PFNHGSMIFILLVIEWINFO pfnFill,
+ void *pvData)
+{
+ int rc;
+ /* Issue the screen info command. */
+ void *p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOVIEW) * u32Count,
+ HGSMI_CH_VBVA, VBVA_INFO_VIEW);
+ if (p)
+ {
+ VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *)p;
+ rc = pfnFill(pvData, pInfo, u32Count);
+ if (RT_SUCCESS(rc))
+ VBoxHGSMIBufferSubmit (pCtx, p);
+ VBoxHGSMIBufferFree(pCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Set a video mode using port registers. This must be done for the first
+ * screen before every HGSMI modeset and also works when HGSM is not enabled.
+ * @param cWidth the mode width
+ * @param cHeight the mode height
+ * @param cBPP the colour depth of the mode
+ * @param cx the horizontal panning offset
+ * @param cy the vertical panning offset
+ */
+RTDECL(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight,
+ uint16_t cVirtWidth, uint16_t cBPP,
+ uint16_t cx, uint16_t cy)
+{
+ /* set the mode characteristics */
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cWidth);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cHeight);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cVirtWidth);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cBPP);
+ /* enable the mode */
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
+ /* Panning registers */
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_X_OFFSET);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cx);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_Y_OFFSET);
+ VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cy);
+ /** @todo read from the port to see if the mode switch was successful */
+}
+
+
+/**
+ * Set a video mode via an HGSMI request. The views must have been
+ * initialised first using @a VBoxHGSMISendViewInfo and if the mode is being
+ * set on the first display then it must be set first using registers.
+ * @param cDisplay the screen number
+ * @param cOriginX the horizontal displacement relative to the first screen
+ * @param cOriginY the vertical displacement relative to the first screen
+ * @param offStart the offset of the visible area of the framebuffer
+ * relative to the framebuffer start
+ * @param cbPitch the offset in bytes between the starts of two adjecent
+ * scan lines in video RAM
+ * @param cWidth the mode width
+ * @param cHeight the mode height
+ * @param cBPP the colour depth of the mode
+ */
+RTDECL(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t cDisplay,
+ int32_t cOriginX,
+ int32_t cOriginY,
+ uint32_t offStart,
+ uint32_t cbPitch,
+ uint32_t cWidth,
+ uint32_t cHeight,
+ uint16_t cBPP,
+ uint16_t fFlags)
+{
+ /* Issue the screen info command. */
+ void *p = VBoxHGSMIBufferAlloc(pCtx,
+ sizeof (VBVAINFOSCREEN),
+ HGSMI_CH_VBVA,
+ VBVA_INFO_SCREEN);
+ if (!p)
+ {
+ LogFunc(("HGSMIHeapAlloc failed\n"));
+ }
+ else
+ {
+ VBVAINFOSCREEN *pScreen = (VBVAINFOSCREEN *)p;
+
+ pScreen->u32ViewIndex = cDisplay;
+ pScreen->i32OriginX = cOriginX;
+ pScreen->i32OriginY = cOriginY;
+ pScreen->u32StartOffset = offStart;
+ pScreen->u32LineSize = cbPitch;
+ pScreen->u32Width = cWidth;
+ pScreen->u32Height = cHeight;
+ pScreen->u16BitsPerPixel = cBPP;
+ pScreen->u16Flags = fFlags;
+
+ VBoxHGSMIBufferSubmit(pCtx, p);
+
+ VBoxHGSMIBufferFree(pCtx, p);
+ }
+}
diff --git a/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp
new file mode 100644
index 000000000..6f8398ae8
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp
@@ -0,0 +1,390 @@
+/* $Id: VBVABase.cpp 35292 2010-12-22 11:56:59Z vboxsync $ */
+/** @file
+ * VirtualBox Video driver, common code - VBVA initialisation and helper
+ * functions.
+ */
+
+/*
+ * Copyright (C) 2006-2010 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 <VBox/VBoxVideoGuest.h>
+#include <VBox/VBoxVideo.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+
+#include <string.h>
+
+/*
+ * There is a hardware ring buffer in the graphics device video RAM, formerly
+ * in the VBox VMMDev PCI memory space.
+ * All graphics commands go there serialized by VBoxVBVABufferBeginUpdate.
+ * and vboxHwBufferEndUpdate.
+ *
+ * off32Free is writing position. off32Data is reading position.
+ * off32Free == off32Data means buffer is empty.
+ * There must be always gap between off32Data and off32Free when data
+ * are in the buffer.
+ * Guest only changes off32Free, host changes off32Data.
+ */
+
+/* Forward declarations of internal functions. */
+static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx);
+static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
+ uint32_t cb, uint32_t offset);
+static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ const void *p, uint32_t cb);
+
+
+static bool vboxVBVAInformHost(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ int32_t cScreen, bool bEnable)
+{
+ bool bRc = false;
+
+#if 0 /* All callers check this */
+ if (ppdev->bHGSMISupported)
+#endif
+ {
+ void *p = VBoxHGSMIBufferAlloc(pHGSMICtx,
+ sizeof (VBVAENABLE_EX),
+ HGSMI_CH_VBVA,
+ VBVA_ENABLE);
+ if (!p)
+ {
+ LogFunc(("HGSMIHeapAlloc failed\n"));
+ }
+ else
+ {
+ VBVAENABLE_EX *pEnable = (VBVAENABLE_EX *)p;
+
+ pEnable->Base.u32Flags = bEnable? VBVA_F_ENABLE: VBVA_F_DISABLE;
+ pEnable->Base.u32Offset = pCtx->offVRAMBuffer;
+ pEnable->Base.i32Result = VERR_NOT_SUPPORTED;
+ if (cScreen >= 0)
+ {
+ pEnable->Base.u32Flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET;
+ pEnable->u32ScreenId = cScreen;
+ }
+
+ VBoxHGSMIBufferSubmit(pHGSMICtx, p);
+
+ if (bEnable)
+ {
+ bRc = RT_SUCCESS(pEnable->Base.i32Result);
+ }
+ else
+ {
+ bRc = true;
+ }
+
+ VBoxHGSMIBufferFree(pHGSMICtx, p);
+ }
+ }
+
+ return bRc;
+}
+
+/*
+ * Public hardware buffer methods.
+ */
+RTDECL(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ VBVABUFFER *pVBVA, int32_t cScreen)
+{
+ bool bRc = false;
+
+ LogFlowFunc(("pVBVA %p\n", pVBVA));
+
+#if 0 /* All callers check this */
+ if (ppdev->bHGSMISupported)
+#endif
+ {
+ LogFunc(("pVBVA %p vbva off 0x%x\n", pVBVA, pCtx->offVRAMBuffer));
+
+ pVBVA->hostFlags.u32HostEvents = 0;
+ pVBVA->hostFlags.u32SupportedOrders = 0;
+ pVBVA->off32Data = 0;
+ pVBVA->off32Free = 0;
+ memset(pVBVA->aRecords, 0, sizeof (pVBVA->aRecords));
+ pVBVA->indexRecordFirst = 0;
+ pVBVA->indexRecordFree = 0;
+ pVBVA->cbPartialWriteThreshold = 256;
+ pVBVA->cbData = pCtx->cbBuffer - sizeof (VBVABUFFER) + sizeof (pVBVA->au8Data);
+
+ pCtx->fHwBufferOverflow = false;
+ pCtx->pRecord = NULL;
+ pCtx->pVBVA = pVBVA;
+
+ bRc = vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, true);
+ }
+
+ if (!bRc)
+ {
+ VBoxVBVADisable(pCtx, pHGSMICtx, cScreen);
+ }
+
+ return bRc;
+}
+
+RTDECL(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ int32_t cScreen)
+{
+ LogFlowFunc(("\n"));
+
+ pCtx->fHwBufferOverflow = false;
+ pCtx->pRecord = NULL;
+ pCtx->pVBVA = NULL;
+
+ vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, false);
+
+ return;
+}
+
+RTDECL(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx)
+{
+ bool bRc = false;
+
+ // LogFunc(("flags = 0x%08X\n", pCtx->pVBVA? pCtx->pVBVA->u32HostEvents: -1));
+
+ if ( pCtx->pVBVA
+ && (pCtx->pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED))
+ {
+ uint32_t indexRecordNext;
+
+ Assert(!pCtx->fHwBufferOverflow);
+ Assert(pCtx->pRecord == NULL);
+
+ indexRecordNext = (pCtx->pVBVA->indexRecordFree + 1) % VBVA_MAX_RECORDS;
+
+ if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
+ {
+ /* All slots in the records queue are used. */
+ vboxHwBufferFlush (pHGSMICtx);
+ }
+
+ if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
+ {
+ /* Even after flush there is no place. Fail the request. */
+ LogFunc(("no space in the queue of records!!! first %d, last %d\n",
+ pCtx->pVBVA->indexRecordFirst, pCtx->pVBVA->indexRecordFree));
+ }
+ else
+ {
+ /* Initialize the record. */
+ VBVARECORD *pRecord = &pCtx->pVBVA->aRecords[pCtx->pVBVA->indexRecordFree];
+
+ pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
+
+ pCtx->pVBVA->indexRecordFree = indexRecordNext;
+
+ // LogFunc(("indexRecordNext = %d\n", indexRecordNext));
+
+ /* Remember which record we are using. */
+ pCtx->pRecord = pRecord;
+
+ bRc = true;
+ }
+ }
+
+ return bRc;
+}
+
+RTDECL(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx)
+{
+ VBVARECORD *pRecord;
+
+ // LogFunc(("\n"));
+
+ Assert(pCtx->pVBVA);
+
+ pRecord = pCtx->pRecord;
+ Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
+
+ /* Mark the record completed. */
+ pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
+
+ pCtx->fHwBufferOverflow = false;
+ pCtx->pRecord = NULL;
+
+ return;
+}
+
+/*
+ * Private operations.
+ */
+static uint32_t vboxHwBufferAvail (const VBVABUFFER *pVBVA)
+{
+ int32_t i32Diff = pVBVA->off32Data - pVBVA->off32Free;
+
+ return i32Diff > 0? i32Diff: pVBVA->cbData + i32Diff;
+}
+
+static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx)
+{
+ /* Issue the flush command. */
+ void *p = VBoxHGSMIBufferAlloc(pCtx,
+ sizeof (VBVAFLUSH),
+ HGSMI_CH_VBVA,
+ VBVA_FLUSH);
+ if (!p)
+ {
+ LogFunc(("HGSMIHeapAlloc failed\n"));
+ }
+ else
+ {
+ VBVAFLUSH *pFlush = (VBVAFLUSH *)p;
+
+ pFlush->u32Reserved = 0;
+
+ VBoxHGSMIBufferSubmit(pCtx, p);
+
+ VBoxHGSMIBufferFree(pCtx, p);
+ }
+
+ return;
+}
+
+static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
+ uint32_t cb, uint32_t offset)
+{
+ VBVABUFFER *pVBVA = pCtx->pVBVA;
+ uint32_t u32BytesTillBoundary = pVBVA->cbData - offset;
+ uint8_t *dst = &pVBVA->au8Data[offset];
+ int32_t i32Diff = cb - u32BytesTillBoundary;
+
+ if (i32Diff <= 0)
+ {
+ /* Chunk will not cross buffer boundary. */
+ memcpy (dst, p, cb);
+ }
+ else
+ {
+ /* Chunk crosses buffer boundary. */
+ memcpy (dst, p, u32BytesTillBoundary);
+ memcpy (&pVBVA->au8Data[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff);
+ }
+
+ return;
+}
+
+static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ const void *p, uint32_t cb)
+{
+ VBVARECORD *pRecord;
+ uint32_t cbHwBufferAvail;
+
+ uint32_t cbWritten = 0;
+
+ VBVABUFFER *pVBVA = pCtx->pVBVA;
+ Assert(pVBVA);
+
+ if (!pVBVA || pCtx->fHwBufferOverflow)
+ {
+ return false;
+ }
+
+ Assert(pVBVA->indexRecordFirst != pVBVA->indexRecordFree);
+
+ pRecord = pCtx->pRecord;
+ Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
+
+ LogFunc(("%d\n", cb));
+
+ cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
+
+ while (cb > 0)
+ {
+ uint32_t cbChunk = cb;
+
+ // LogFunc(("pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n",
+ // pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
+
+ if (cbChunk >= cbHwBufferAvail)
+ {
+ LogFunc(("1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk));
+
+ vboxHwBufferFlush (pHGSMICtx);
+
+ cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
+
+ if (cbChunk >= cbHwBufferAvail)
+ {
+ LogFunc(("no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n",
+ cb, cbHwBufferAvail));
+
+ if (cbHwBufferAvail <= pVBVA->cbPartialWriteThreshold)
+ {
+ LogFunc(("Buffer overflow!!!\n"));
+ pCtx->fHwBufferOverflow = true;
+ Assert(false);
+ return false;
+ }
+
+ cbChunk = cbHwBufferAvail - pVBVA->cbPartialWriteThreshold;
+ }
+ }
+
+ Assert(cbChunk <= cb);
+ Assert(cbChunk <= vboxHwBufferAvail (pVBVA));
+
+ vboxHwBufferPlaceDataAt (pCtx, (uint8_t *)p + cbWritten, cbChunk, pVBVA->off32Free);
+
+ pVBVA->off32Free = (pVBVA->off32Free + cbChunk) % pVBVA->cbData;
+ pRecord->cbRecord += cbChunk;
+ cbHwBufferAvail -= cbChunk;
+
+ cb -= cbChunk;
+ cbWritten += cbChunk;
+ }
+
+ return true;
+}
+
+/*
+ * Public writer to the hardware buffer.
+ */
+RTDECL(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ const void *pv, uint32_t cb)
+{
+ return vboxHwBufferWrite (pCtx, pHGSMICtx, pv, cb);
+}
+
+RTDECL(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code)
+{
+ VBVABUFFER *pVBVA = pCtx->pVBVA;
+
+ if (!pVBVA)
+ {
+ return false;
+ }
+
+ if (pVBVA->hostFlags.u32SupportedOrders & (1 << code))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+RTDECL(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx,
+ uint32_t offVRAMBuffer,
+ uint32_t cbBuffer)
+{
+ pCtx->offVRAMBuffer = offVRAMBuffer;
+ pCtx->cbBuffer = cbBuffer;
+}