summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Graphics/DevVGA-SVGA.cpp')
-rw-r--r--src/VBox/Devices/Graphics/DevVGA-SVGA.cpp7149
1 files changed, 7149 insertions, 0 deletions
diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp b/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp
new file mode 100644
index 00000000..c199b618
--- /dev/null
+++ b/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp
@@ -0,0 +1,7149 @@
+/* $Id: DevVGA-SVGA.cpp $ */
+/** @file
+ * VMware SVGA device.
+ *
+ * Logging levels guidelines for this and related files:
+ * - Log() for normal bits.
+ * - LogFlow() for more info.
+ * - Log2 for hex dump of cursor data.
+ * - Log3 for hex dump of shader code.
+ * - Log4 for hex dumps of 3D data.
+ * - Log5 for info about GMR pages.
+ * - Log6 for DX shaders.
+ * - Log7 for SVGA command dump.
+ * - Log8 for content of constant and vertex buffers.
+ * - LogRel for the usual important stuff.
+ * - LogRel2 for cursor.
+ * - LogRel3 for 3D performance data.
+ * - LogRel4 for HW accelerated graphics output.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/** @page pg_dev_vmsvga VMSVGA - VMware SVGA II Device Emulation
+ *
+ * This device emulation was contributed by trivirt AG. It offers an
+ * alternative to our Bochs based VGA graphics and 3d emulations. This is
+ * valuable for Xorg based guests, as there is driver support shipping with Xorg
+ * since it forked from XFree86.
+ *
+ *
+ * @section sec_dev_vmsvga_sdk The VMware SDK
+ *
+ * This is officially deprecated now, however it's still quite useful,
+ * especially for getting the old features working:
+ * http://vmware-svga.sourceforge.net/
+ *
+ * They currently point developers at the following resources.
+ * - http://cgit.freedesktop.org/xorg/driver/xf86-video-vmware/
+ * - http://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/svga/
+ * - http://cgit.freedesktop.org/mesa/vmwgfx/
+ *
+ * @subsection subsec_dev_vmsvga_sdk_results Test results
+ *
+ * Test results:
+ * - 2dmark.img:
+ * + todo
+ * - backdoor-tclo.img:
+ * + todo
+ * - blit-cube.img:
+ * + todo
+ * - bunnies.img:
+ * + todo
+ * - cube.img:
+ * + todo
+ * - cubemark.img:
+ * + todo
+ * - dynamic-vertex-stress.img:
+ * + todo
+ * - dynamic-vertex.img:
+ * + todo
+ * - fence-stress.img:
+ * + todo
+ * - gmr-test.img:
+ * + todo
+ * - half-float-test.img:
+ * + todo
+ * - noscreen-cursor.img:
+ * - The CURSOR I/O and FIFO registers are not implemented, so the mouse
+ * cursor doesn't show. (Hacking the GUI a little, would make the cursor
+ * visible though.)
+ * - Cursor animation via the palette doesn't work.
+ * - During debugging, it turns out that the framebuffer content seems to
+ * be halfways ignore or something (memset(fb, 0xcc, lots)).
+ * - Trouble with way to small FIFO and the 256x256 cursor fails. Need to
+ * grow it 0x10 fold (128KB -> 2MB like in WS10).
+ * - null.img:
+ * + todo
+ * - pong.img:
+ * + todo
+ * - presentReadback.img:
+ * + todo
+ * - resolution-set.img:
+ * + todo
+ * - rt-gamma-test.img:
+ * + todo
+ * - screen-annotation.img:
+ * + todo
+ * - screen-cursor.img:
+ * + todo
+ * - screen-dma-coalesce.img:
+ * + todo
+ * - screen-gmr-discontig.img:
+ * + todo
+ * - screen-gmr-remap.img:
+ * + todo
+ * - screen-multimon.img:
+ * + todo
+ * - screen-present-clip.img:
+ * + todo
+ * - screen-render-test.img:
+ * + todo
+ * - screen-simple.img:
+ * + todo
+ * - screen-text.img:
+ * + todo
+ * - simple-shaders.img:
+ * + todo
+ * - simple_blit.img:
+ * + todo
+ * - tiny-2d-updates.img:
+ * + todo
+ * - video-formats.img:
+ * + todo
+ * - video-sync.img:
+ * + todo
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_VMSVGA
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/version.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/sup.h>
+
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/uuid.h>
+#ifdef IN_RING3
+# include <iprt/ctype.h>
+# include <iprt/mem.h>
+# ifdef VBOX_STRICT
+# include <iprt/time.h>
+# endif
+#endif
+
+#include <VBox/AssertGuest.h>
+#include <VBox/VMMDev.h>
+#include <VBoxVideo.h>
+#include <VBox/bioslogo.h>
+
+#ifdef LOG_ENABLED
+#include "svgadump/svga_dump.h"
+#endif
+
+/* should go BEFORE any other DevVGA include to make all DevVGA.h config defines be visible */
+#include "DevVGA.h"
+
+/* Should be included after DevVGA.h/DevVGA-SVGA.h to pick all defines. */
+#ifdef VBOX_WITH_VMSVGA3D
+# include "DevVGA-SVGA3d.h"
+# ifdef RT_OS_DARWIN
+# include "DevVGA-SVGA3d-cocoa.h"
+# endif
+# ifdef RT_OS_LINUX
+# ifdef IN_RING3
+# include "DevVGA-SVGA3d-glLdr.h"
+# endif
+# endif
+#endif
+#ifdef IN_RING3
+#include "DevVGA-SVGA-internal.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/**
+ * Macro for checking if a fixed FIFO register is valid according to the
+ * current FIFO configuration.
+ *
+ * @returns true / false.
+ * @param a_iIndex The fifo register index (like SVGA_FIFO_CAPABILITIES).
+ * @param a_offFifoMin A valid SVGA_FIFO_MIN value.
+ */
+#define VMSVGA_IS_VALID_FIFO_REG(a_iIndex, a_offFifoMin) ( ((a_iIndex) + 1) * sizeof(uint32_t) <= (a_offFifoMin) )
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+static FNPGMPHYSHANDLER vmsvgaR3FifoAccessHandler;
+# endif
+# ifdef DEBUG_GMR_ACCESS
+static FNPGMPHYSHANDLER vmsvgaR3GmrAccessHandler;
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+
+/**
+ * SSM descriptor table for the VMSVGAGMRDESCRIPTOR structure.
+ */
+static SSMFIELD const g_aVMSVGAGMRDESCRIPTORFields[] =
+{
+ SSMFIELD_ENTRY_GCPHYS( VMSVGAGMRDESCRIPTOR, GCPhys),
+ SSMFIELD_ENTRY( VMSVGAGMRDESCRIPTOR, numPages),
+ SSMFIELD_ENTRY_TERM()
+};
+
+/**
+ * SSM descriptor table for the GMR structure.
+ */
+static SSMFIELD const g_aGMRFields[] =
+{
+ SSMFIELD_ENTRY( GMR, cMaxPages),
+ SSMFIELD_ENTRY( GMR, cbTotal),
+ SSMFIELD_ENTRY( GMR, numDescriptors),
+ SSMFIELD_ENTRY_IGN_HCPTR( GMR, paDesc),
+ SSMFIELD_ENTRY_TERM()
+};
+
+/**
+ * SSM descriptor table for the VMSVGASCREENOBJECT structure.
+ */
+static SSMFIELD const g_aVMSVGASCREENOBJECTFields[] =
+{
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, fuScreen),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, idScreen),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, xOrigin),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, yOrigin),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, cWidth),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, cHeight),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, offVRAM),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, cbPitch),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, cBpp),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, fDefined),
+ SSMFIELD_ENTRY( VMSVGASCREENOBJECT, fModified),
+ SSMFIELD_ENTRY_VER( VMSVGASCREENOBJECT, cDpi, VGA_SAVEDSTATE_VERSION_VMSVGA_MIPLEVELS),
+ SSMFIELD_ENTRY_TERM()
+};
+
+/**
+ * SSM descriptor table for the VMSVGAR3STATE structure.
+ */
+static SSMFIELD const g_aVMSVGAR3STATEFields[] =
+{
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, paGMR),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, GMRFB),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, Cursor.fActive),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, Cursor.xHotspot),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, Cursor.yHotspot),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, Cursor.width),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, Cursor.height),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, Cursor.cbData),
+ SSMFIELD_ENTRY_IGN_HCPTR( VMSVGAR3STATE, Cursor.pData),
+ SSMFIELD_ENTRY( VMSVGAR3STATE, colorAnnotation),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, cBusyDelayedEmts),
+#ifdef VMSVGA_USE_EMT_HALT_CODE
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, BusyDelayedEmts),
+#else
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, hBusyDelayedEmts),
+#endif
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatBusyDelayEmts),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dPresentProf),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dDrawPrimitivesProf),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceDmaProf),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dBlitSurfaceToScreenProf),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineGmr2),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineGmr2Free),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineGmr2Modify),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdRemapGmr2),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdRemapGmr2Modify),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdInvalidCmd),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdFence),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdUpdate),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdUpdateVerbose),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineCursor),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineAlphaCursor),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdMoveCursor),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDisplayCursor),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdRectFill),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdRectCopy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdRectRopCopy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdEscape),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineScreen),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDestroyScreen),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdDefineGmrFb),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdBlitGmrFbToScreen),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdBlitScreentoGmrFb),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdAnnotationFill),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3CmdAnnotationCopy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceDefine),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceDefineV2),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceDestroy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceCopy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceStretchBlt),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceDma),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSurfaceScreen),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dContextDefine),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dContextDestroy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetTransform),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetZRange),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetRenderState),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetRenderTarget),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetTextureState),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetMaterial),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetLightData),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetLightEnable),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetViewPort),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetClipPlane),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dClear),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dPresent),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dPresentReadBack),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dShaderDefine),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dShaderDestroy),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetShader),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetShaderConst),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dDrawPrimitives),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dSetScissorRect),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dBeginQuery),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dEndQuery),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dWaitForQuery),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dGenerateMipmaps),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dActivateSurface),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3Cmd3dDeactivateSurface),
+
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3RegConfigDoneWr),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3RegGmrDescriptorWr),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3RegGmrDescriptorWrErrors),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatR3RegGmrDescriptorWrFree),
+
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoCommands),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoErrors),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoUnkCmds),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoTodoTimeout),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoTodoWoken),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoStalls),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoExtendedSleep),
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoAccessHandler),
+# endif
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoCursorFetchAgain),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoCursorNoChange),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoCursorPosition),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAR3STATE, StatFifoCursorVisiblity),
+
+ SSMFIELD_ENTRY_TERM()
+};
+
+/**
+ * SSM descriptor table for the VGAState.svga structure.
+ */
+static SSMFIELD const g_aVGAStateSVGAFields[] =
+{
+ SSMFIELD_ENTRY_IGN_GCPHYS( VMSVGAState, GCPhysFIFO),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, cbFIFO),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, cbFIFOConfig),
+ SSMFIELD_ENTRY( VMSVGAState, u32SVGAId),
+ SSMFIELD_ENTRY( VMSVGAState, fEnabled),
+ SSMFIELD_ENTRY( VMSVGAState, fConfigured),
+ SSMFIELD_ENTRY( VMSVGAState, fBusy),
+ SSMFIELD_ENTRY( VMSVGAState, fTraces),
+ SSMFIELD_ENTRY( VMSVGAState, u32GuestId),
+ SSMFIELD_ENTRY( VMSVGAState, cScratchRegion),
+ SSMFIELD_ENTRY( VMSVGAState, au32ScratchRegion),
+ SSMFIELD_ENTRY( VMSVGAState, u32IrqStatus),
+ SSMFIELD_ENTRY( VMSVGAState, u32IrqMask),
+ SSMFIELD_ENTRY( VMSVGAState, u32PitchLock),
+ SSMFIELD_ENTRY( VMSVGAState, u32CurrentGMRId),
+ SSMFIELD_ENTRY( VMSVGAState, u32DeviceCaps),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32DeviceCaps2, VGA_SAVEDSTATE_VERSION_VMSVGA_REG_CAP2),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32GuestDriverId, VGA_SAVEDSTATE_VERSION_VMSVGA_REG_CAP2),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32GuestDriverVer1, VGA_SAVEDSTATE_VERSION_VMSVGA_REG_CAP2),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32GuestDriverVer2, VGA_SAVEDSTATE_VERSION_VMSVGA_REG_CAP2),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32GuestDriverVer3, VGA_SAVEDSTATE_VERSION_VMSVGA_REG_CAP2),
+ SSMFIELD_ENTRY( VMSVGAState, u32IndexReg),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, hFIFORequestSem),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, uLastCursorUpdateCount),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, fFIFOThreadSleeping),
+ SSMFIELD_ENTRY_VER( VMSVGAState, fGFBRegisters, VGA_SAVEDSTATE_VERSION_VMSVGA_SCREENS),
+ SSMFIELD_ENTRY( VMSVGAState, uWidth),
+ SSMFIELD_ENTRY( VMSVGAState, uHeight),
+ SSMFIELD_ENTRY( VMSVGAState, uBpp),
+ SSMFIELD_ENTRY( VMSVGAState, cbScanline),
+ SSMFIELD_ENTRY_VER( VMSVGAState, uScreenOffset, VGA_SAVEDSTATE_VERSION_VMSVGA),
+ SSMFIELD_ENTRY_VER( VMSVGAState, uCursorX, VGA_SAVEDSTATE_VERSION_VMSVGA_CURSOR),
+ SSMFIELD_ENTRY_VER( VMSVGAState, uCursorY, VGA_SAVEDSTATE_VERSION_VMSVGA_CURSOR),
+ SSMFIELD_ENTRY_VER( VMSVGAState, uCursorID, VGA_SAVEDSTATE_VERSION_VMSVGA_CURSOR),
+ SSMFIELD_ENTRY_VER( VMSVGAState, uCursorOn, VGA_SAVEDSTATE_VERSION_VMSVGA_CURSOR),
+ SSMFIELD_ENTRY( VMSVGAState, u32MaxWidth),
+ SSMFIELD_ENTRY( VMSVGAState, u32MaxHeight),
+ SSMFIELD_ENTRY( VMSVGAState, u32ActionFlags),
+ SSMFIELD_ENTRY( VMSVGAState, f3DEnabled),
+ SSMFIELD_ENTRY( VMSVGAState, fVRAMTracking),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, u8FIFOExtCommand),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, fFifoExtCommandWakeup),
+ SSMFIELD_ENTRY_IGNORE( VMSVGAState, cGMR),
+ SSMFIELD_ENTRY_VER( VMSVGAState, au32DevCaps, VGA_SAVEDSTATE_VERSION_VMSVGA_DX),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32DevCapIndex, VGA_SAVEDSTATE_VERSION_VMSVGA_DX),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32RegCommandLow, VGA_SAVEDSTATE_VERSION_VMSVGA_DX),
+ SSMFIELD_ENTRY_VER( VMSVGAState, u32RegCommandHigh, VGA_SAVEDSTATE_VERSION_VMSVGA_DX),
+
+ SSMFIELD_ENTRY_TERM()
+};
+#endif /* IN_RING3 */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+static void vmsvgaR3SetTraces(PPDMDEVINS pDevIns, PVGASTATE pThis, bool fTraces);
+static int vmsvgaR3LoadExecFifo(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PVGASTATECC pThisCC, PSSMHANDLE pSSM,
+ uint32_t uVersion, uint32_t uPass);
+static int vmsvgaR3SaveExecFifo(PCPDMDEVHLPR3 pHlp, PVGASTATECC pThisCC, PSSMHANDLE pSSM);
+static void vmsvgaR3CmdBufSubmit(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS GCPhysCB, SVGACBContext CBCtx);
+static void vmsvgaR3PowerOnDevice(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool fLoadState);
+#endif /* IN_RING3 */
+
+
+#define SVGA_CASE_ID2STR(idx) case idx: return #idx
+#if defined(LOG_ENABLED)
+/**
+ * Index register string name lookup
+ *
+ * @returns Index register string or "UNKNOWN"
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param idxReg The index register.
+ */
+static const char *vmsvgaIndexToString(PVGASTATE pThis, uint32_t idxReg)
+{
+ AssertCompile(SVGA_REG_TOP == 77); /* Ensure that the correct headers are used. */
+ switch (idxReg)
+ {
+ SVGA_CASE_ID2STR(SVGA_REG_ID);
+ SVGA_CASE_ID2STR(SVGA_REG_ENABLE);
+ SVGA_CASE_ID2STR(SVGA_REG_WIDTH);
+ SVGA_CASE_ID2STR(SVGA_REG_HEIGHT);
+ SVGA_CASE_ID2STR(SVGA_REG_MAX_WIDTH);
+ SVGA_CASE_ID2STR(SVGA_REG_MAX_HEIGHT);
+ SVGA_CASE_ID2STR(SVGA_REG_DEPTH);
+ SVGA_CASE_ID2STR(SVGA_REG_BITS_PER_PIXEL); /* Current bpp in the guest */
+ SVGA_CASE_ID2STR(SVGA_REG_PSEUDOCOLOR);
+ SVGA_CASE_ID2STR(SVGA_REG_RED_MASK);
+ SVGA_CASE_ID2STR(SVGA_REG_GREEN_MASK);
+ SVGA_CASE_ID2STR(SVGA_REG_BLUE_MASK);
+ SVGA_CASE_ID2STR(SVGA_REG_BYTES_PER_LINE);
+ SVGA_CASE_ID2STR(SVGA_REG_FB_START); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_FB_OFFSET);
+ SVGA_CASE_ID2STR(SVGA_REG_VRAM_SIZE);
+ SVGA_CASE_ID2STR(SVGA_REG_FB_SIZE);
+
+ /* ID 0 implementation only had the above registers, then the palette */
+ SVGA_CASE_ID2STR(SVGA_REG_CAPABILITIES);
+ SVGA_CASE_ID2STR(SVGA_REG_MEM_START); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_MEM_SIZE);
+ SVGA_CASE_ID2STR(SVGA_REG_CONFIG_DONE); /* Set when memory area configured */
+ SVGA_CASE_ID2STR(SVGA_REG_SYNC); /* See "FIFO Synchronization Registers" */
+ SVGA_CASE_ID2STR(SVGA_REG_BUSY); /* See "FIFO Synchronization Registers" */
+ SVGA_CASE_ID2STR(SVGA_REG_GUEST_ID); /* Set guest OS identifier */
+ SVGA_CASE_ID2STR(SVGA_REG_DEAD); /* (Deprecated) SVGA_REG_CURSOR_ID. */
+ SVGA_CASE_ID2STR(SVGA_REG_CURSOR_X); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_CURSOR_Y); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_CURSOR_ON); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_HOST_BITS_PER_PIXEL); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_SCRATCH_SIZE); /* Number of scratch registers */
+ SVGA_CASE_ID2STR(SVGA_REG_MEM_REGS); /* Number of FIFO registers */
+ SVGA_CASE_ID2STR(SVGA_REG_NUM_DISPLAYS); /* (Deprecated) */
+ SVGA_CASE_ID2STR(SVGA_REG_PITCHLOCK); /* Fixed pitch for all modes */
+ SVGA_CASE_ID2STR(SVGA_REG_IRQMASK); /* Interrupt mask */
+
+ /* Legacy multi-monitor support */
+ SVGA_CASE_ID2STR(SVGA_REG_NUM_GUEST_DISPLAYS); /* Number of guest displays in X/Y direction */
+ SVGA_CASE_ID2STR(SVGA_REG_DISPLAY_ID); /* Display ID for the following display attributes */
+ SVGA_CASE_ID2STR(SVGA_REG_DISPLAY_IS_PRIMARY); /* Whether this is a primary display */
+ SVGA_CASE_ID2STR(SVGA_REG_DISPLAY_POSITION_X); /* The display position x */
+ SVGA_CASE_ID2STR(SVGA_REG_DISPLAY_POSITION_Y); /* The display position y */
+ SVGA_CASE_ID2STR(SVGA_REG_DISPLAY_WIDTH); /* The display's width */
+ SVGA_CASE_ID2STR(SVGA_REG_DISPLAY_HEIGHT); /* The display's height */
+
+ SVGA_CASE_ID2STR(SVGA_REG_GMR_ID);
+ SVGA_CASE_ID2STR(SVGA_REG_GMR_DESCRIPTOR);
+ SVGA_CASE_ID2STR(SVGA_REG_GMR_MAX_IDS);
+ SVGA_CASE_ID2STR(SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH);
+
+ SVGA_CASE_ID2STR(SVGA_REG_TRACES); /* Enable trace-based updates even when FIFO is on */
+ SVGA_CASE_ID2STR(SVGA_REG_GMRS_MAX_PAGES); /* Maximum number of 4KB pages for all GMRs */
+ SVGA_CASE_ID2STR(SVGA_REG_MEMORY_SIZE); /* Total dedicated device memory excluding FIFO */
+ SVGA_CASE_ID2STR(SVGA_REG_COMMAND_LOW); /* Lower 32 bits and submits commands */
+ SVGA_CASE_ID2STR(SVGA_REG_COMMAND_HIGH); /* Upper 32 bits of command buffer PA */
+ SVGA_CASE_ID2STR(SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM); /* Max primary memory */
+ SVGA_CASE_ID2STR(SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); /* Suggested limit on mob mem */
+ SVGA_CASE_ID2STR(SVGA_REG_DEV_CAP); /* Write dev cap index, read value */
+ SVGA_CASE_ID2STR(SVGA_REG_CMD_PREPEND_LOW);
+ SVGA_CASE_ID2STR(SVGA_REG_CMD_PREPEND_HIGH);
+ SVGA_CASE_ID2STR(SVGA_REG_SCREENTARGET_MAX_WIDTH);
+ SVGA_CASE_ID2STR(SVGA_REG_SCREENTARGET_MAX_HEIGHT);
+ SVGA_CASE_ID2STR(SVGA_REG_MOB_MAX_SIZE);
+ SVGA_CASE_ID2STR(SVGA_REG_BLANK_SCREEN_TARGETS);
+ SVGA_CASE_ID2STR(SVGA_REG_CAP2);
+ SVGA_CASE_ID2STR(SVGA_REG_DEVEL_CAP);
+ SVGA_CASE_ID2STR(SVGA_REG_GUEST_DRIVER_ID);
+ SVGA_CASE_ID2STR(SVGA_REG_GUEST_DRIVER_VERSION1);
+ SVGA_CASE_ID2STR(SVGA_REG_GUEST_DRIVER_VERSION2);
+ SVGA_CASE_ID2STR(SVGA_REG_GUEST_DRIVER_VERSION3);
+ SVGA_CASE_ID2STR(SVGA_REG_CURSOR_MOBID);
+ SVGA_CASE_ID2STR(SVGA_REG_CURSOR_MAX_BYTE_SIZE);
+ SVGA_CASE_ID2STR(SVGA_REG_CURSOR_MAX_DIMENSION);
+ SVGA_CASE_ID2STR(SVGA_REG_FIFO_CAPS);
+ SVGA_CASE_ID2STR(SVGA_REG_FENCE);
+ SVGA_CASE_ID2STR(SVGA_REG_RESERVED1);
+ SVGA_CASE_ID2STR(SVGA_REG_RESERVED2);
+ SVGA_CASE_ID2STR(SVGA_REG_RESERVED3);
+ SVGA_CASE_ID2STR(SVGA_REG_RESERVED4);
+ SVGA_CASE_ID2STR(SVGA_REG_RESERVED5);
+ SVGA_CASE_ID2STR(SVGA_REG_SCREENDMA);
+ SVGA_CASE_ID2STR(SVGA_REG_GBOBJECT_MEM_SIZE_KB);
+ SVGA_CASE_ID2STR(SVGA_REG_TOP); /* Must be 1 more than the last register */
+
+ default:
+ if (idxReg - (uint32_t)SVGA_SCRATCH_BASE < pThis->svga.cScratchRegion)
+ return "SVGA_SCRATCH_BASE reg";
+ if (idxReg - (uint32_t)SVGA_PALETTE_BASE < (uint32_t)SVGA_NUM_PALETTE_REGS)
+ return "SVGA_PALETTE_BASE reg";
+ return "UNKNOWN";
+ }
+}
+#endif /* LOG_ENABLED */
+
+#if defined(LOG_ENABLED) || (defined(IN_RING3) && defined(VBOX_WITH_VMSVGA3D))
+static const char *vmsvgaDevCapIndexToString(SVGA3dDevCapIndex idxDevCap)
+{
+ AssertCompile(SVGA3D_DEVCAP_MAX == 260);
+ switch (idxDevCap)
+ {
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_INVALID);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_3D);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_LIGHTS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_TEXTURES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_CLIP_PLANES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_VERTEX_SHADER_VERSION);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_VERTEX_SHADER);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_FRAGMENT_SHADER);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_RENDER_TARGETS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_S23E8_TEXTURES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_S10E5_TEXTURES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_D16_BUFFER_FORMAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_QUERY_TYPES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_POINT_SIZE);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_SHADER_TEXTURES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_VOLUME_EXTENT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_VERTEX_INDEX);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_TEXTURE_OPS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_R5G6B5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_ALPHA8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Z_D16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_DXT1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_DXT2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_DXT3);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_DXT4);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_DXT5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_CxV8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_R_S10E5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_R_S23E8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MISSING62);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_V16U16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_G16R16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_UYVY);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_YUY2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD4); /* SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD5); /* SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD7); /* SVGA3D_DEVCAP_ALPHATOCOVERAGE */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD6); /* SVGA3D_DEVCAP_SUPERSAMPLE */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_AUTOGENMIPMAPS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_NV12);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD10); /* SVGA3D_DEVCAP_SURFACEFMT_AYUV */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_CONTEXT_IDS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_SURFACE_IDS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Z_DF16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Z_DF24);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_ATI1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_ATI2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD8); /* SVGA3D_DEVCAP_VIDEO_DECODE */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD9); /* SVGA3D_DEVCAP_VIDEO_PROCESS */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_LINE_AA);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_LINE_STIPPLE);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_LINE_WIDTH);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX_AA_LINE_WIDTH);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SURFACEFMT_YV12);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD3); /* Old SVGA3D_DEVCAP_LOGICOPS */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_TS_COLOR_KEY);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXCONTEXT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DEAD11); /* SVGA3D_DEVCAP_MAX_TEXTURE_ARRAY_SIZE */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DX_MAX_VERTEXBUFFERS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DX_MAX_CONSTANT_BUFFERS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DX_PROVOKING_VERTEX);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_X8R8G8B8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A8R8G8B8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R5G6B5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_X1R5G5B5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A1R5G5B5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A4R4G4B4);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_D32);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_D16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_D24S8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_D15S1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_LUMINANCE8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_LUMINANCE4_ALPHA4);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_LUMINANCE16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_LUMINANCE8_ALPHA8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_DXT1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_DXT2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_DXT3);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_DXT4);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_DXT5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BUMPU8V8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BUMPL6V5U5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BUMPX8L8V8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_FORMAT_DEAD1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_ARGB_S10E5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_ARGB_S23E8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A2R10G10B10);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_V8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Q8W8V8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_CxV8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_X8L8V8U8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A2W10V10U10);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_ALPHA8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R_S10E5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R_S23E8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_RG_S10E5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_RG_S23E8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BUFFER);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_D24X8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_V16U16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_G16R16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A16B16G16R16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_UYVY);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_YUY2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_NV12);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_FORMAT_DEAD2); /* SVGA3D_DEVCAP_DXFMT_AYUV */
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32A32_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32A32_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32A32_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16B16A16_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16B16A16_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16B16A16_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16B16A16_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G8X24_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_D32_FLOAT_S8X24_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32_FLOAT_X8X24);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_X32_G8X24_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R10G10B10A2_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R10G10B10A2_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R11G11B10_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8B8A8_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8B8A8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8B8A8_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8B8A8_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8B8A8_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_D32_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R24G8_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_D24_UNORM_S8_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R24_UNORM_X8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_X24_G8_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8_UINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8_SINT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_P8);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R9G9B9E5_SHAREDEXP);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8_B8G8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_G8R8_G8B8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC1_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC1_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC2_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC2_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC3_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC3_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC4_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_ATI1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC4_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC5_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_ATI2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC5_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R10G10B10_XR_BIAS_A2_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B8G8R8A8_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B8G8R8A8_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B8G8R8X8_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B8G8R8X8_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_DF16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_DF24);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_Z_D24S8_INT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_YV12);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32B32A32_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16B16A16_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16B16A16_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32G32_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R10G10B10A2_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8B8A8_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16G16_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R32_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R8G8_SNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_R16_FLOAT);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_D16_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_A8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC1_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC2_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC3_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B5G6R5_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B5G5R5A1_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B8G8R8A8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_B8G8R8X8_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC4_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC5_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SM41);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MULTISAMPLE_2X);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MULTISAMPLE_4X);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MS_FULL_QUALITY);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_LOGICOPS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_LOGIC_BLENDOPS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_RESERVED_1);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC6H_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC6H_UF16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC6H_SF16);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC7_TYPELESS);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC7_UNORM);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_DXFMT_BC7_UNORM_SRGB);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_RESERVED_2);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_SM5);
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MULTISAMPLE_8X);
+
+ SVGA_CASE_ID2STR(SVGA3D_DEVCAP_MAX);
+
+ default:
+ break;
+ }
+ return "UNKNOWN";
+}
+#endif /* defined(LOG_ENABLED) || (defined(IN_RING3) && defined(VBOX_WITH_VMSVGA3D)) */
+#undef SVGA_CASE_ID2STR
+
+
+#ifdef IN_RING3
+
+/**
+ * @interface_method_impl{PDMIDISPLAYPORT,pfnSetViewport}
+ */
+DECLCALLBACK(void) vmsvgaR3PortSetViewport(PPDMIDISPLAYPORT pInterface, uint32_t idScreen, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
+{
+ PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVGASTATE);
+
+ Log(("vmsvgaPortSetViewPort: screen %d (%d,%d)(%d,%d)\n", idScreen, x, y, cx, cy));
+ VMSVGAVIEWPORT const OldViewport = pThis->svga.viewport;
+
+ /** @todo Test how it interacts with multiple screen objects. */
+ VMSVGASCREENOBJECT *pScreen = vmsvgaR3GetScreenObject(pThisCC, idScreen);
+ uint32_t const uWidth = pScreen ? pScreen->cWidth : 0;
+ uint32_t const uHeight = pScreen ? pScreen->cHeight : 0;
+
+ if (x < uWidth)
+ {
+ pThis->svga.viewport.x = x;
+ pThis->svga.viewport.cx = RT_MIN(cx, uWidth - x);
+ pThis->svga.viewport.xRight = x + pThis->svga.viewport.cx;
+ }
+ else
+ {
+ pThis->svga.viewport.x = uWidth;
+ pThis->svga.viewport.cx = 0;
+ pThis->svga.viewport.xRight = uWidth;
+ }
+ if (y < uHeight)
+ {
+ pThis->svga.viewport.y = y;
+ pThis->svga.viewport.cy = RT_MIN(cy, uHeight - y);
+ pThis->svga.viewport.yLowWC = uHeight - y - pThis->svga.viewport.cy;
+ pThis->svga.viewport.yHighWC = uHeight - y;
+ }
+ else
+ {
+ pThis->svga.viewport.y = uHeight;
+ pThis->svga.viewport.cy = 0;
+ pThis->svga.viewport.yLowWC = 0;
+ pThis->svga.viewport.yHighWC = 0;
+ }
+
+# ifdef VBOX_WITH_VMSVGA3D
+ /*
+ * Now inform the 3D backend.
+ */
+ if (pThis->svga.f3DEnabled)
+ vmsvga3dUpdateHostScreenViewport(pThisCC, idScreen, &OldViewport);
+# else
+ RT_NOREF(OldViewport);
+# endif
+}
+
+
+/**
+ * Updating screen information in API
+ *
+ * @param pThis The The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+void vmsvgaR3VBVAResize(PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ int rc;
+
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ for (unsigned iScreen = 0; iScreen < RT_ELEMENTS(pSVGAState->aScreens); ++iScreen)
+ {
+ VMSVGASCREENOBJECT *pScreen = &pSVGAState->aScreens[iScreen];
+ if (!pScreen->fModified)
+ continue;
+
+ pScreen->fModified = false;
+
+ VBVAINFOVIEW view;
+ RT_ZERO(view);
+ view.u32ViewIndex = pScreen->idScreen;
+ // view.u32ViewOffset = 0;
+ view.u32ViewSize = pThis->vram_size;
+ view.u32MaxScreenSize = pThis->vram_size;
+
+ VBVAINFOSCREEN screen;
+ RT_ZERO(screen);
+ screen.u32ViewIndex = pScreen->idScreen;
+
+ if (pScreen->fDefined)
+ {
+ if ( pScreen->cWidth == VMSVGA_VAL_UNINITIALIZED
+ || pScreen->cHeight == VMSVGA_VAL_UNINITIALIZED
+ || pScreen->cBpp == VMSVGA_VAL_UNINITIALIZED)
+ {
+ Assert(pThis->svga.fGFBRegisters);
+ continue;
+ }
+
+ screen.i32OriginX = pScreen->xOrigin;
+ screen.i32OriginY = pScreen->yOrigin;
+ screen.u32StartOffset = pScreen->offVRAM;
+ screen.u32LineSize = pScreen->cbPitch;
+ screen.u32Width = pScreen->cWidth;
+ screen.u32Height = pScreen->cHeight;
+ screen.u16BitsPerPixel = pScreen->cBpp;
+ if (!(pScreen->fuScreen & SVGA_SCREEN_DEACTIVATE))
+ screen.u16Flags = VBVA_SCREEN_F_ACTIVE;
+ if (pScreen->fuScreen & SVGA_SCREEN_BLANKING)
+ screen.u16Flags |= VBVA_SCREEN_F_BLANK2;
+ }
+ else
+ {
+ /* Screen is destroyed. */
+ screen.u16Flags = VBVA_SCREEN_F_DISABLED;
+ }
+
+ void *pvVRAM = pScreen->pvScreenBitmap ? pScreen->pvScreenBitmap : pThisCC->pbVRam;
+ rc = pThisCC->pDrv->pfnVBVAResize(pThisCC->pDrv, &view, &screen, pvVRAM, /*fResetInputMapping=*/ true);
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMIDISPLAYPORT,pfnReportMonitorPositions}
+ *
+ * Used to update screen offsets (positions) since appearently vmwgfx fails to
+ * pass correct offsets thru FIFO.
+ */
+DECLCALLBACK(void) vmsvgaR3PortReportMonitorPositions(PPDMIDISPLAYPORT pInterface, uint32_t cPositions, PCRTPOINT paPositions)
+{
+ PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVGASTATE);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ AssertReturnVoid(pSVGAState);
+
+ /* We assume cPositions is the # of outputs Xserver reports and paPositions is (-1, -1) for disabled monitors. */
+ cPositions = RT_MIN(cPositions, RT_ELEMENTS(pSVGAState->aScreens));
+ for (uint32_t i = 0; i < cPositions; ++i)
+ {
+ if ( pSVGAState->aScreens[i].xOrigin == paPositions[i].x
+ && pSVGAState->aScreens[i].yOrigin == paPositions[i].y)
+ continue;
+
+ if (paPositions[i].x == -1)
+ continue;
+ if (paPositions[i].y == -1)
+ continue;
+
+ pSVGAState->aScreens[i].xOrigin = paPositions[i].x;
+ pSVGAState->aScreens[i].yOrigin = paPositions[i].y;
+ pSVGAState->aScreens[i].fModified = true;
+ }
+
+ vmsvgaR3VBVAResize(pThis, pThisCC);
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Read port register
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pu32 Where to store the read value
+ */
+static int vmsvgaReadPort(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t *pu32)
+{
+#ifdef IN_RING3
+ PVGASTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+#endif
+ int rc = VINF_SUCCESS;
+ *pu32 = 0;
+
+ /* Rough index register validation. */
+ uint32_t idxReg = pThis->svga.u32IndexReg;
+#if !defined(IN_RING3) && defined(VBOX_STRICT)
+ ASSERT_GUEST_MSG_RETURN(idxReg < SVGA_SCRATCH_BASE + pThis->svga.cScratchRegion, ("idxReg=%#x\n", idxReg),
+ VINF_IOM_R3_IOPORT_READ);
+#else
+ ASSERT_GUEST_MSG_STMT_RETURN(idxReg < SVGA_SCRATCH_BASE + pThis->svga.cScratchRegion, ("idxReg=%#x\n", idxReg),
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegUnknownRd),
+ VINF_SUCCESS);
+#endif
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /* We must adjust the register number if we're in SVGA_ID_0 mode because the PALETTE range moved. */
+ if ( idxReg >= SVGA_REG_ID_0_TOP
+ && pThis->svga.u32SVGAId == SVGA_ID_0)
+ {
+ idxReg += SVGA_PALETTE_BASE - SVGA_REG_ID_0_TOP;
+ Log(("vmsvgaWritePort: SVGA_ID_0 reg adj %#x -> %#x\n", pThis->svga.u32IndexReg, idxReg));
+ }
+
+ switch (idxReg)
+ {
+ case SVGA_REG_ID:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegIdRd);
+ *pu32 = pThis->svga.u32SVGAId;
+ break;
+
+ case SVGA_REG_ENABLE:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegEnableRd);
+ *pu32 = pThis->svga.fEnabled;
+ break;
+
+ case SVGA_REG_WIDTH:
+ {
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegWidthRd);
+ if ( pThis->svga.fEnabled
+ && pThis->svga.uWidth != VMSVGA_VAL_UNINITIALIZED)
+ *pu32 = pThis->svga.uWidth;
+ else
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_READ;
+#else
+ *pu32 = pThisCC->pDrv->cx;
+#endif
+ }
+ break;
+ }
+
+ case SVGA_REG_HEIGHT:
+ {
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegHeightRd);
+ if ( pThis->svga.fEnabled
+ && pThis->svga.uHeight != VMSVGA_VAL_UNINITIALIZED)
+ *pu32 = pThis->svga.uHeight;
+ else
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_READ;
+#else
+ *pu32 = pThisCC->pDrv->cy;
+#endif
+ }
+ break;
+ }
+
+ case SVGA_REG_MAX_WIDTH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMaxWidthRd);
+ *pu32 = pThis->svga.u32MaxWidth;
+ break;
+
+ case SVGA_REG_MAX_HEIGHT:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMaxHeightRd);
+ *pu32 = pThis->svga.u32MaxHeight;
+ break;
+
+ case SVGA_REG_DEPTH:
+ /* This returns the color depth of the current mode. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDepthRd);
+ switch (pThis->svga.uBpp)
+ {
+ case 15:
+ case 16:
+ case 24:
+ *pu32 = pThis->svga.uBpp;
+ break;
+
+ default:
+ case 32:
+ *pu32 = 24; /* The upper 8 bits are either alpha bits or not used. */
+ break;
+ }
+ break;
+
+ case SVGA_REG_HOST_BITS_PER_PIXEL: /* (Deprecated) */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegHostBitsPerPixelRd);
+ *pu32 = pThis->svga.uHostBpp;
+ break;
+
+ case SVGA_REG_BITS_PER_PIXEL: /* Current bpp in the guest */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegBitsPerPixelRd);
+ *pu32 = pThis->svga.uBpp;
+ break;
+
+ case SVGA_REG_PSEUDOCOLOR:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegPsuedoColorRd);
+ *pu32 = pThis->svga.uBpp == 8; /* See section 6 "Pseudocolor" in svga_interface.txt. */
+ break;
+
+ case SVGA_REG_RED_MASK:
+ case SVGA_REG_GREEN_MASK:
+ case SVGA_REG_BLUE_MASK:
+ {
+ uint32_t uBpp;
+
+ if (pThis->svga.fEnabled)
+ uBpp = pThis->svga.uBpp;
+ else
+ uBpp = pThis->svga.uHostBpp;
+
+ uint32_t u32RedMask, u32GreenMask, u32BlueMask;
+ switch (uBpp)
+ {
+ case 8:
+ u32RedMask = 0x07;
+ u32GreenMask = 0x38;
+ u32BlueMask = 0xc0;
+ break;
+
+ case 15:
+ u32RedMask = 0x0000001f;
+ u32GreenMask = 0x000003e0;
+ u32BlueMask = 0x00007c00;
+ break;
+
+ case 16:
+ u32RedMask = 0x0000001f;
+ u32GreenMask = 0x000007e0;
+ u32BlueMask = 0x0000f800;
+ break;
+
+ case 24:
+ case 32:
+ default:
+ u32RedMask = 0x00ff0000;
+ u32GreenMask = 0x0000ff00;
+ u32BlueMask = 0x000000ff;
+ break;
+ }
+ switch (idxReg)
+ {
+ case SVGA_REG_RED_MASK:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegRedMaskRd);
+ *pu32 = u32RedMask;
+ break;
+
+ case SVGA_REG_GREEN_MASK:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGreenMaskRd);
+ *pu32 = u32GreenMask;
+ break;
+
+ case SVGA_REG_BLUE_MASK:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegBlueMaskRd);
+ *pu32 = u32BlueMask;
+ break;
+ }
+ break;
+ }
+
+ case SVGA_REG_BYTES_PER_LINE:
+ {
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegBytesPerLineRd);
+ if ( pThis->svga.fEnabled
+ && pThis->svga.cbScanline)
+ *pu32 = pThis->svga.cbScanline;
+ else
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_READ;
+#else
+ *pu32 = pThisCC->pDrv->cbScanline;
+#endif
+ }
+ break;
+ }
+
+ case SVGA_REG_VRAM_SIZE: /* VRAM size */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegVramSizeRd);
+ *pu32 = pThis->vram_size;
+ break;
+
+ case SVGA_REG_FB_START: /* Frame buffer physical address. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegFbStartRd);
+ Assert(pThis->GCPhysVRAM <= 0xffffffff);
+ *pu32 = pThis->GCPhysVRAM;
+ break;
+
+ case SVGA_REG_FB_OFFSET: /* Offset of the frame buffer in VRAM */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegFbOffsetRd);
+ /* Always zero in our case. */
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_FB_SIZE: /* Frame buffer size */
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_READ;
+#else
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegFbSizeRd);
+
+ /* VMWare testcases want at least 4 MB in case the hardware is disabled. */
+ if ( pThis->svga.fEnabled
+ && pThis->svga.uHeight != VMSVGA_VAL_UNINITIALIZED)
+ {
+ /* Hardware enabled; return real framebuffer size .*/
+ *pu32 = (uint32_t)pThis->svga.uHeight * pThis->svga.cbScanline;
+ }
+ else
+ *pu32 = RT_MAX(0x100000, (uint32_t)pThisCC->pDrv->cy * pThisCC->pDrv->cbScanline);
+
+ *pu32 = RT_MIN(pThis->vram_size, *pu32);
+ Log(("h=%d w=%d bpp=%d\n", pThisCC->pDrv->cy, pThisCC->pDrv->cx, pThisCC->pDrv->cBits));
+#endif
+ break;
+ }
+
+ case SVGA_REG_CAPABILITIES:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCapabilitesRd);
+ *pu32 = pThis->svga.u32DeviceCaps;
+ break;
+
+ case SVGA_REG_MEM_START: /* FIFO start */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMemStartRd);
+ Assert(pThis->svga.GCPhysFIFO <= 0xffffffff);
+ *pu32 = pThis->svga.GCPhysFIFO;
+ break;
+
+ case SVGA_REG_MEM_SIZE: /* FIFO size */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMemSizeRd);
+ *pu32 = pThis->svga.cbFIFO;
+ break;
+
+ case SVGA_REG_CONFIG_DONE: /* Set when memory area configured */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegConfigDoneRd);
+ *pu32 = pThis->svga.fConfigured;
+ break;
+
+ case SVGA_REG_SYNC: /* See "FIFO Synchronization Registers" */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegSyncRd);
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_BUSY: /* See "FIFO Synchronization Registers" */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegBusyRd);
+ if (pThis->svga.fBusy)
+ {
+#ifndef IN_RING3
+ /* Go to ring-3 and halt the CPU. */
+ rc = VINF_IOM_R3_IOPORT_READ;
+ RT_NOREF(pDevIns);
+ break;
+#else /* IN_RING3 */
+# if defined(VMSVGA_USE_EMT_HALT_CODE)
+ /* The guest is basically doing a HLT via the device here, but with
+ a special wake up condition on FIFO completion. */
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ STAM_REL_PROFILE_START(&pSVGAState->StatBusyDelayEmts, EmtDelay);
+ VMCPUID idCpu = PDMDevHlpGetCurrentCpuId(pDevIns);
+ VMCPUSET_ATOMIC_ADD(&pSVGAState->BusyDelayedEmts, idCpu);
+ ASMAtomicIncU32(&pSVGAState->cBusyDelayedEmts);
+ if (pThis->svga.fBusy)
+ {
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* hack around lock order issue. */
+ rc = PDMDevHlpVMWaitForDeviceReady(pDevIns, idCpu);
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+ }
+ ASMAtomicDecU32(&pSVGAState->cBusyDelayedEmts);
+ VMCPUSET_ATOMIC_DEL(&pSVGAState->BusyDelayedEmts, idCpu);
+# else
+
+ /* Delay the EMT a bit so the FIFO and others can get some work done.
+ This used to be a crude 50 ms sleep. The current code tries to be
+ more efficient, but the consept is still very crude. */
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ STAM_REL_PROFILE_START(&pSVGAState->StatBusyDelayEmts, EmtDelay);
+ RTThreadYield();
+ if (pThis->svga.fBusy)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pSVGAState->cBusyDelayedEmts);
+
+ if (pThis->svga.fBusy && cRefs == 1)
+ RTSemEventMultiReset(pSVGAState->hBusyDelayedEmts);
+ if (pThis->svga.fBusy)
+ {
+ /** @todo If this code is going to stay, we need to call into the halt/wait
+ * code in VMEmt.cpp here, otherwise all kind of EMT interaction will
+ * suffer when the guest is polling on a busy FIFO. */
+ uint64_t uIgnored1, uIgnored2;
+ uint64_t cNsMaxWait = TMVirtualSyncGetNsToDeadline(PDMDevHlpGetVM(pDevIns), &uIgnored1, &uIgnored2);
+ if (cNsMaxWait >= RT_NS_100US)
+ RTSemEventMultiWaitEx(pSVGAState->hBusyDelayedEmts,
+ RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NORESUME,
+ RT_MIN(cNsMaxWait, RT_NS_10MS));
+ }
+
+ ASMAtomicDecU32(&pSVGAState->cBusyDelayedEmts);
+ }
+ STAM_REL_PROFILE_STOP(&pSVGAState->StatBusyDelayEmts, EmtDelay);
+# endif
+ *pu32 = pThis->svga.fBusy != 0;
+#endif /* IN_RING3 */
+ }
+ else
+ *pu32 = false;
+ break;
+
+ case SVGA_REG_GUEST_ID: /* Set guest OS identifier */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGuestIdRd);
+ *pu32 = pThis->svga.u32GuestId;
+ break;
+
+ case SVGA_REG_SCRATCH_SIZE: /* Number of scratch registers */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegScratchSizeRd);
+ *pu32 = pThis->svga.cScratchRegion;
+ break;
+
+ case SVGA_REG_MEM_REGS: /* Number of FIFO registers */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMemRegsRd);
+ *pu32 = SVGA_FIFO_NUM_REGS;
+ break;
+
+ case SVGA_REG_PITCHLOCK: /* Fixed pitch for all modes */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegPitchLockRd);
+ *pu32 = pThis->svga.u32PitchLock;
+ break;
+
+ case SVGA_REG_IRQMASK: /* Interrupt mask */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegIrqMaskRd);
+ *pu32 = pThis->svga.u32IrqMask;
+ break;
+
+ /* See "Guest memory regions" below. */
+ case SVGA_REG_GMR_ID:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGmrIdRd);
+ *pu32 = pThis->svga.u32CurrentGMRId;
+ break;
+
+ case SVGA_REG_GMR_DESCRIPTOR:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegWriteOnlyRd);
+ /* Write only */
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_GMR_MAX_IDS:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGmrMaxIdsRd);
+ *pu32 = pThis->svga.cGMR;
+ break;
+
+ case SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGmrMaxDescriptorLengthRd);
+ *pu32 = VMSVGA_MAX_GMR_PAGES;
+ break;
+
+ case SVGA_REG_TRACES: /* Enable trace-based updates even when FIFO is on */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegTracesRd);
+ *pu32 = pThis->svga.fTraces;
+ break;
+
+ case SVGA_REG_GMRS_MAX_PAGES: /* Maximum number of 4KB pages for all GMRs */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGmrsMaxPagesRd);
+ *pu32 = VMSVGA_MAX_GMR_PAGES;
+ break;
+
+ case SVGA_REG_MEMORY_SIZE: /* Total dedicated device memory excluding FIFO */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMemorySizeRd);
+ *pu32 = VMSVGA_SURFACE_SIZE;
+ break;
+
+ case SVGA_REG_TOP: /* Must be 1 more than the last register */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegTopRd);
+ break;
+
+ /* Mouse cursor support. */
+ case SVGA_REG_DEAD: /* SVGA_REG_CURSOR_ID */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorIdRd);
+ *pu32 = pThis->svga.uCursorID;
+ break;
+
+ case SVGA_REG_CURSOR_X:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorXRd);
+ *pu32 = pThis->svga.uCursorX;
+ break;
+
+ case SVGA_REG_CURSOR_Y:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorYRd);
+ *pu32 = pThis->svga.uCursorY;
+ break;
+
+ case SVGA_REG_CURSOR_ON:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorOnRd);
+ *pu32 = pThis->svga.uCursorOn;
+ break;
+
+ /* Legacy multi-monitor support */
+ case SVGA_REG_NUM_GUEST_DISPLAYS:/* Number of guest displays in X/Y direction */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegNumGuestDisplaysRd);
+ *pu32 = 1;
+ break;
+
+ case SVGA_REG_DISPLAY_ID: /* Display ID for the following display attributes */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayIdRd);
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_DISPLAY_IS_PRIMARY:/* Whether this is a primary display */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayIsPrimaryRd);
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_DISPLAY_POSITION_X:/* The display position x */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayPositionXRd);
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_DISPLAY_POSITION_Y:/* The display position y */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayPositionYRd);
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_DISPLAY_WIDTH: /* The display's width */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayWidthRd);
+ *pu32 = pThis->svga.uWidth;
+ break;
+
+ case SVGA_REG_DISPLAY_HEIGHT: /* The display's height */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayHeightRd);
+ *pu32 = pThis->svga.uHeight;
+ break;
+
+ case SVGA_REG_NUM_DISPLAYS: /* (Deprecated) */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegNumDisplaysRd);
+ /* We must return something sensible here otherwise the Linux driver
+ will take a legacy code path without 3d support. This number also
+ limits how many screens Linux guests will allow. */
+ *pu32 = pThis->cMonitors;
+ break;
+
+ /*
+ * SVGA_CAP_GBOBJECTS+ registers.
+ */
+ case SVGA_REG_COMMAND_LOW:
+ /* Lower 32 bits of command buffer physical address. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCommandLowRd);
+ *pu32 = pThis->svga.u32RegCommandLow;
+ break;
+
+ case SVGA_REG_COMMAND_HIGH:
+ /* Upper 32 bits of command buffer PA. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCommandHighRd);
+ *pu32 = pThis->svga.u32RegCommandHigh;
+ break;
+
+ case SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM:
+ /* Max primary (screen) memory. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMaxPrimBBMemRd);
+ *pu32 = pThis->vram_size; /** @todo Maybe half VRAM? */
+ break;
+
+ case SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB:
+ /* Suggested limit on mob mem (i.e. size of the guest mapped VRAM in KB) */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGBMemSizeRd);
+ *pu32 = pThis->vram_size / 1024;
+ break;
+
+ case SVGA_REG_DEV_CAP:
+ /* Write dev cap index, read value */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDevCapRd);
+ if (pThis->svga.u32DevCapIndex < RT_ELEMENTS(pThis->svga.au32DevCaps))
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ *pu32 = pThis->svga.au32DevCaps[pThis->svga.u32DevCapIndex];
+ }
+ else
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_CMD_PREPEND_LOW:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCmdPrependLowRd);
+ *pu32 = 0; /* Not supported. */
+ break;
+
+ case SVGA_REG_CMD_PREPEND_HIGH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCmdPrependHighRd);
+ *pu32 = 0; /* Not supported. */
+ break;
+
+ case SVGA_REG_SCREENTARGET_MAX_WIDTH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegScrnTgtMaxWidthRd);
+ *pu32 = pThis->svga.u32MaxWidth;
+ break;
+
+ case SVGA_REG_SCREENTARGET_MAX_HEIGHT:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegScrnTgtMaxHeightRd);
+ *pu32 = pThis->svga.u32MaxHeight;
+ break;
+
+ case SVGA_REG_MOB_MAX_SIZE:
+ /* Essentially the max texture size */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegMobMaxSizeRd);
+ *pu32 = _128M; /** @todo Some actual value. Probably the mapped VRAM size. */
+ break;
+
+ case SVGA_REG_BLANK_SCREEN_TARGETS:
+ /// @todo STAM_REL_COUNTER_INC(&pThis->svga.aStatRegRd[idxReg]);
+ *pu32 = 0; /* Not supported. */
+ break;
+
+ case SVGA_REG_CAP2:
+ *pu32 = pThis->svga.u32DeviceCaps2;
+ break;
+
+ case SVGA_REG_DEVEL_CAP:
+ *pu32 = 0; /* Not supported. */
+ break;
+
+ /*
+ * SVGA_REG_GUEST_DRIVER_* registers require SVGA_CAP2_DX2.
+ */
+ case SVGA_REG_GUEST_DRIVER_ID:
+ *pu32 = pThis->svga.u32GuestDriverId;
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_VERSION1:
+ *pu32 = pThis->svga.u32GuestDriverVer1;
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_VERSION2:
+ *pu32 = pThis->svga.u32GuestDriverVer2;
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_VERSION3:
+ *pu32 = pThis->svga.u32GuestDriverVer3;
+ break;
+
+ /*
+ * SVGA_REG_CURSOR_ registers require SVGA_CAP2_CURSOR_MOB which the device does not support currently.
+ */
+ case SVGA_REG_CURSOR_MOBID:
+ *pu32 = SVGA_ID_INVALID;
+ break;
+
+ case SVGA_REG_CURSOR_MAX_BYTE_SIZE:
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_CURSOR_MAX_DIMENSION:
+ *pu32 = 0;
+ break;
+
+ case SVGA_REG_FIFO_CAPS:
+ case SVGA_REG_FENCE: /* Same as SVGA_FIFO_FENCE for PCI_ID_SVGA3. Our device is PCI_ID_SVGA2 so not supported. */
+ case SVGA_REG_RESERVED1: /* SVGA_REG_RESERVED* correspond to SVGA_REG_CURSOR4_*. Require SVGA_CAP2_EXTRA_REGS. */
+ case SVGA_REG_RESERVED2:
+ case SVGA_REG_RESERVED3:
+ case SVGA_REG_RESERVED4:
+ case SVGA_REG_RESERVED5:
+ case SVGA_REG_SCREENDMA:
+ *pu32 = 0; /* Not supported. */
+ break;
+
+ case SVGA_REG_GBOBJECT_MEM_SIZE_KB:
+ /** @todo "The maximum amount of guest-backed objects that the device can have resident at a time" */
+ *pu32 = _1G / _1K;
+ break;
+
+ default:
+ {
+ uint32_t offReg;
+ if ((offReg = idxReg - SVGA_SCRATCH_BASE) < pThis->svga.cScratchRegion)
+ {
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegScratchRd);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ *pu32 = pThis->svga.au32ScratchRegion[offReg];
+ }
+ else if ((offReg = idxReg - SVGA_PALETTE_BASE) < (uint32_t)SVGA_NUM_PALETTE_REGS)
+ {
+ /* Note! Using last_palette rather than palette here to preserve the VGA one. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegPaletteRd);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ uint32_t u32 = pThis->last_palette[offReg / 3];
+ switch (offReg % 3)
+ {
+ case 0: *pu32 = (u32 >> 16) & 0xff; break; /* red */
+ case 1: *pu32 = (u32 >> 8) & 0xff; break; /* green */
+ case 2: *pu32 = u32 & 0xff; break; /* blue */
+ }
+ }
+ else
+ {
+#if !defined(IN_RING3) && defined(VBOX_STRICT)
+ rc = VINF_IOM_R3_IOPORT_READ;
+#else
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegUnknownRd);
+
+ /* Do not assert. The guest might be reading all registers. */
+ LogFunc(("Unknown reg=%#x\n", idxReg));
+#endif
+ }
+ break;
+ }
+ }
+ LogFlow(("vmsvgaReadPort index=%s (%d) val=%#x rc=%x\n", vmsvgaIndexToString(pThis, idxReg), idxReg, *pu32, rc));
+ return rc;
+}
+
+#ifdef IN_RING3
+/**
+ * Apply the current resolution settings to change the video mode.
+ *
+ * @returns VBox status code.
+ * @param pThis The shared VGA state.
+ * @param pThisCC The ring-3 VGA state.
+ */
+int vmsvgaR3ChangeMode(PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ /* Always do changemode on FIFO thread. */
+ Assert(RTThreadSelf() == pThisCC->svga.pFIFOIOThread->Thread);
+
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ pThisCC->pDrv->pfnLFBModeChange(pThisCC->pDrv, true);
+
+ if (pThis->svga.fGFBRegisters)
+ {
+ /* "For backwards compatibility, when the GFB mode registers (WIDTH,
+ * HEIGHT, PITCHLOCK, BITS_PER_PIXEL) are modified, the SVGA device
+ * deletes all screens other than screen #0, and redefines screen
+ * #0 according to the specified mode. Drivers that use
+ * SVGA_CMD_DEFINE_SCREEN should destroy or redefine screen #0."
+ */
+
+ VMSVGASCREENOBJECT *pScreen = &pSVGAState->aScreens[0];
+ Assert(pScreen->idScreen == 0);
+ pScreen->fDefined = true;
+ pScreen->fModified = true;
+ pScreen->fuScreen = SVGA_SCREEN_MUST_BE_SET | SVGA_SCREEN_IS_PRIMARY;
+ pScreen->xOrigin = 0;
+ pScreen->yOrigin = 0;
+ pScreen->offVRAM = 0;
+ pScreen->cbPitch = pThis->svga.cbScanline;
+ pScreen->cWidth = pThis->svga.uWidth;
+ pScreen->cHeight = pThis->svga.uHeight;
+ pScreen->cBpp = pThis->svga.uBpp;
+
+ for (unsigned iScreen = 1; iScreen < RT_ELEMENTS(pSVGAState->aScreens); ++iScreen)
+ {
+ /* Delete screen. */
+ pScreen = &pSVGAState->aScreens[iScreen];
+ if (pScreen->fDefined)
+ {
+ pScreen->fModified = true;
+ pScreen->fDefined = false;
+ }
+ }
+ }
+ else
+ {
+ /* "If Screen Objects are supported, they can be used to fully
+ * replace the functionality provided by the framebuffer registers
+ * (SVGA_REG_WIDTH, HEIGHT, etc.) and by SVGA_CAP_DISPLAY_TOPOLOGY."
+ */
+ pThis->svga.uWidth = VMSVGA_VAL_UNINITIALIZED;
+ pThis->svga.uHeight = VMSVGA_VAL_UNINITIALIZED;
+ pThis->svga.uBpp = pThis->svga.uHostBpp;
+ }
+
+ vmsvgaR3VBVAResize(pThis, pThisCC);
+
+ /* Last stuff. For the VGA device screenshot. */
+ pThis->last_bpp = pSVGAState->aScreens[0].cBpp;
+ pThis->last_scr_width = pSVGAState->aScreens[0].cWidth;
+ pThis->last_scr_height = pSVGAState->aScreens[0].cHeight;
+ pThis->last_width = pSVGAState->aScreens[0].cWidth;
+ pThis->last_height = pSVGAState->aScreens[0].cHeight;
+
+ /* vmsvgaPortSetViewPort not called after state load; set sensible defaults. */
+ if ( pThis->svga.viewport.cx == 0
+ && pThis->svga.viewport.cy == 0)
+ {
+ pThis->svga.viewport.cx = pSVGAState->aScreens[0].cWidth;
+ pThis->svga.viewport.xRight = pSVGAState->aScreens[0].cWidth;
+ pThis->svga.viewport.cy = pSVGAState->aScreens[0].cHeight;
+ pThis->svga.viewport.yHighWC = pSVGAState->aScreens[0].cHeight;
+ pThis->svga.viewport.yLowWC = 0;
+ }
+
+ return VINF_SUCCESS;
+}
+
+int vmsvgaR3UpdateScreen(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, int x, int y, int w, int h)
+{
+ ASSERT_GUEST_LOGREL_MSG_RETURN(w > 0 && h > 0,
+ ("vmsvgaR3UpdateScreen: screen %d (%d,%d) %dx%d: Invalid height and/or width supplied.\n",
+ pScreen->idScreen, x, y, w, h),
+ VERR_INVALID_PARAMETER);
+
+ VBVACMDHDR cmd;
+ cmd.x = (int16_t)(pScreen->xOrigin + x);
+ cmd.y = (int16_t)(pScreen->yOrigin + y);
+ cmd.w = (uint16_t)w;
+ cmd.h = (uint16_t)h;
+
+ pThisCC->pDrv->pfnVBVAUpdateBegin(pThisCC->pDrv, pScreen->idScreen);
+ pThisCC->pDrv->pfnVBVAUpdateProcess(pThisCC->pDrv, pScreen->idScreen, &cmd, sizeof(cmd));
+ pThisCC->pDrv->pfnVBVAUpdateEnd(pThisCC->pDrv, pScreen->idScreen,
+ pScreen->xOrigin + x, pScreen->yOrigin + y, w, h);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+#if defined(IN_RING0) || defined(IN_RING3)
+
+/**
+ * Safely updates the SVGA_FIFO_BUSY register (in shared memory).
+ *
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param fState The busy state.
+ */
+DECLINLINE(void) vmsvgaHCSafeFifoBusyRegUpdate(PVGASTATE pThis, PVGASTATECC pThisCC, bool fState)
+{
+ ASMAtomicWriteU32(&pThisCC->svga.pau32FIFO[SVGA_FIFO_BUSY], fState);
+
+ if (RT_UNLIKELY(fState != (pThis->svga.fBusy != 0)))
+ {
+ /* Race / unfortunately scheduling. Highly unlikly. */
+ uint32_t cLoops = 64;
+ do
+ {
+ ASMNopPause();
+ fState = (pThis->svga.fBusy != 0);
+ ASMAtomicWriteU32(&pThisCC->svga.pau32FIFO[SVGA_FIFO_BUSY], fState != 0);
+ } while (cLoops-- > 0 && fState != (pThis->svga.fBusy != 0));
+ }
+}
+
+
+/**
+ * Update the scanline pitch in response to the guest changing mode
+ * width/bpp.
+ *
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ */
+DECLINLINE(void) vmsvgaHCUpdatePitch(PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ uint32_t RT_UNTRUSTED_VOLATILE_GUEST *pFIFO = pThisCC->svga.pau32FIFO;
+ uint32_t uFifoPitchLock = pFIFO[SVGA_FIFO_PITCHLOCK];
+ uint32_t uRegPitchLock = pThis->svga.u32PitchLock;
+ uint32_t uFifoMin = pFIFO[SVGA_FIFO_MIN];
+
+ /* The SVGA_FIFO_PITCHLOCK register is only valid if SVGA_FIFO_MIN points past
+ * it. If SVGA_FIFO_MIN is small, there may well be data at the SVGA_FIFO_PITCHLOCK
+ * location but it has a different meaning.
+ */
+ if ((uFifoMin / sizeof(uint32_t)) <= SVGA_FIFO_PITCHLOCK)
+ uFifoPitchLock = 0;
+
+ /* Sanitize values. */
+ if ((uFifoPitchLock < 200) || (uFifoPitchLock > 32768))
+ uFifoPitchLock = 0;
+ if ((uRegPitchLock < 200) || (uRegPitchLock > 32768))
+ uRegPitchLock = 0;
+
+ /* Prefer the register value to the FIFO value.*/
+ if (uRegPitchLock)
+ pThis->svga.cbScanline = uRegPitchLock;
+ else if (uFifoPitchLock)
+ pThis->svga.cbScanline = uFifoPitchLock;
+ else
+ pThis->svga.cbScanline = (uint32_t)pThis->svga.uWidth * (RT_ALIGN(pThis->svga.uBpp, 8) / 8);
+
+ if ((uFifoMin / sizeof(uint32_t)) <= SVGA_FIFO_PITCHLOCK)
+ pThis->svga.u32PitchLock = pThis->svga.cbScanline;
+}
+
+#endif /* IN_RING0 || IN_RING3 */
+
+#ifdef IN_RING3
+
+/**
+ * Sends cursor position and visibility information from legacy
+ * SVGA registers to the front-end.
+ */
+static void vmsvgaR3RegUpdateCursor(PVGASTATECC pThisCC, PVGASTATE pThis, uint32_t uCursorOn)
+{
+ /*
+ * Writing the X/Y/ID registers does not trigger changes; only writing the
+ * SVGA_REG_CURSOR_ON register does. That minimizes the overhead.
+ * We boldly assume that guests aren't stupid and aren't writing the CURSOR_ON
+ * register if they don't have to.
+ */
+ uint32_t x, y, idScreen;
+ uint32_t fFlags = VBVA_CURSOR_VALID_DATA;
+
+ x = pThis->svga.uCursorX;
+ y = pThis->svga.uCursorY;
+ idScreen = SVGA_ID_INVALID; /* The old register interface is single screen only. */
+
+ /* The original values for SVGA_REG_CURSOR_ON were off (0) and on (1); later, the values
+ * were extended as follows:
+ *
+ * SVGA_CURSOR_ON_HIDE 0
+ * SVGA_CURSOR_ON_SHOW 1
+ * SVGA_CURSOR_ON_REMOVE_FROM_FB 2 - cursor on but not in the framebuffer
+ * SVGA_CURSOR_ON_RESTORE_TO_FB 3 - cursor on, possibly in the framebuffer
+ *
+ * Since we never draw the cursor into the guest's framebuffer, we do not need to
+ * distinguish between the non-zero values but still remember them.
+ */
+ if (RT_BOOL(pThis->svga.uCursorOn) != RT_BOOL(uCursorOn))
+ {
+ LogRel2(("vmsvgaR3RegUpdateCursor: uCursorOn %d prev CursorOn %d (%d,%d)\n", uCursorOn, pThis->svga.uCursorOn, x, y));
+ pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, RT_BOOL(uCursorOn), false, 0, 0, 0, 0, NULL);
+ }
+ pThis->svga.uCursorOn = uCursorOn;
+ pThisCC->pDrv->pfnVBVAReportCursorPosition(pThisCC->pDrv, fFlags, idScreen, x, y);
+}
+
+#endif /* IN_RING3 */
+
+
+/**
+ * Write port register
+ *
+ * @returns Strict VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param u32 Value to write
+ */
+static VBOXSTRICTRC vmsvgaWritePort(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t u32)
+{
+#ifdef IN_RING3
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+#endif
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ RT_NOREF(pThisCC);
+
+ /* Rough index register validation. */
+ uint32_t idxReg = pThis->svga.u32IndexReg;
+#if !defined(IN_RING3) && defined(VBOX_STRICT)
+ ASSERT_GUEST_MSG_RETURN(idxReg < SVGA_SCRATCH_BASE + pThis->svga.cScratchRegion, ("idxReg=%#x\n", idxReg),
+ VINF_IOM_R3_IOPORT_WRITE);
+#else
+ ASSERT_GUEST_MSG_STMT_RETURN(idxReg < SVGA_SCRATCH_BASE + pThis->svga.cScratchRegion, ("idxReg=%#x\n", idxReg),
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegUnknownWr),
+ VINF_SUCCESS);
+#endif
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /* We must adjust the register number if we're in SVGA_ID_0 mode because the PALETTE range moved. */
+ if ( idxReg >= SVGA_REG_ID_0_TOP
+ && pThis->svga.u32SVGAId == SVGA_ID_0)
+ {
+ idxReg += SVGA_PALETTE_BASE - SVGA_REG_ID_0_TOP;
+ Log(("vmsvgaWritePort: SVGA_ID_0 reg adj %#x -> %#x\n", pThis->svga.u32IndexReg, idxReg));
+ }
+#ifdef LOG_ENABLED
+ if (idxReg != SVGA_REG_DEV_CAP)
+ LogFlow(("vmsvgaWritePort index=%s (%d) val=%#x\n", vmsvgaIndexToString(pThis, idxReg), idxReg, u32));
+ else
+ LogFlow(("vmsvgaWritePort index=%s (%d) val=%s (%d)\n", vmsvgaIndexToString(pThis, idxReg), idxReg, vmsvgaDevCapIndexToString((SVGA3dDevCapIndex)u32), u32));
+#endif
+ /* Check if the guest uses legacy registers. See vmsvgaR3ChangeMode */
+ switch (idxReg)
+ {
+ case SVGA_REG_WIDTH:
+ case SVGA_REG_HEIGHT:
+ case SVGA_REG_PITCHLOCK:
+ case SVGA_REG_BITS_PER_PIXEL:
+ pThis->svga.fGFBRegisters = true;
+ break;
+ default:
+ break;
+ }
+
+ switch (idxReg)
+ {
+ case SVGA_REG_ID:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegIdWr);
+ if ( u32 == SVGA_ID_0
+ || u32 == SVGA_ID_1
+ || u32 == SVGA_ID_2)
+ pThis->svga.u32SVGAId = u32;
+ else
+ PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Trying to set SVGA_REG_ID to %#x (%d)\n", u32, u32);
+ break;
+
+ case SVGA_REG_ENABLE:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegEnableWr);
+#ifdef IN_RING3
+ if ( (u32 & SVGA_REG_ENABLE_ENABLE)
+ && pThis->svga.fEnabled == false)
+ {
+ /* Make a backup copy of the first 512kb in order to save font data etc. */
+ /** @todo should probably swap here, rather than copy + zero */
+ memcpy(pThisCC->svga.pbVgaFrameBufferR3, pThisCC->pbVRam, VMSVGA_VGA_FB_BACKUP_SIZE);
+ memset(pThisCC->pbVRam, 0, VMSVGA_VGA_FB_BACKUP_SIZE);
+ }
+
+ pThis->svga.fEnabled = u32;
+ if (pThis->svga.fEnabled)
+ {
+ if ( pThis->svga.uWidth == VMSVGA_VAL_UNINITIALIZED
+ && pThis->svga.uHeight == VMSVGA_VAL_UNINITIALIZED)
+ {
+ /* Keep the current mode. */
+ pThis->svga.uWidth = pThisCC->pDrv->cx;
+ pThis->svga.uHeight = pThisCC->pDrv->cy;
+ pThis->svga.uBpp = (pThisCC->pDrv->cBits + 7) & ~7;
+ vmsvgaHCUpdatePitch(pThis, pThisCC);
+ }
+
+ if ( pThis->svga.uWidth != VMSVGA_VAL_UNINITIALIZED
+ && pThis->svga.uHeight != VMSVGA_VAL_UNINITIALIZED)
+ ASMAtomicOrU32(&pThis->svga.u32ActionFlags, VMSVGA_ACTION_CHANGEMODE);
+# ifdef LOG_ENABLED
+ uint32_t *pFIFO = pThisCC->svga.pau32FIFO;
+ Log(("configured=%d busy=%d\n", pThis->svga.fConfigured, pFIFO[SVGA_FIFO_BUSY]));
+ Log(("next %x stop %x\n", pFIFO[SVGA_FIFO_NEXT_CMD], pFIFO[SVGA_FIFO_STOP]));
+# endif
+
+ /* Disable or enable dirty page tracking according to the current fTraces value. */
+ vmsvgaR3SetTraces(pDevIns, pThis, !!pThis->svga.fTraces);
+
+ /* bird: Whatever this is was added to make screenshot work, ask sunlover should explain... */
+ for (uint32_t idScreen = 0; idScreen < pThis->cMonitors; ++idScreen)
+ pThisCC->pDrv->pfnVBVAEnable(pThisCC->pDrv, idScreen, NULL /*pHostFlags*/);
+
+ /* Make the cursor visible again as needed. */
+ if (pSVGAState->Cursor.fActive)
+ pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, true /*fVisible*/, false, 0, 0, 0, 0, NULL);
+ }
+ else
+ {
+ /* Make sure the cursor is off. */
+ if (pSVGAState->Cursor.fActive)
+ pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, false /*fVisible*/, false, 0, 0, 0, 0, NULL);
+
+ /* Restore the text mode backup. */
+ memcpy(pThisCC->pbVRam, pThisCC->svga.pbVgaFrameBufferR3, VMSVGA_VGA_FB_BACKUP_SIZE);
+
+ pThisCC->pDrv->pfnLFBModeChange(pThisCC->pDrv, false);
+
+ /* Enable dirty page tracking again when going into legacy mode. */
+ vmsvgaR3SetTraces(pDevIns, pThis, true);
+
+ /* bird: Whatever this is was added to make screenshot work, ask sunlover should explain... */
+ for (uint32_t idScreen = 0; idScreen < pThis->cMonitors; ++idScreen)
+ pThisCC->pDrv->pfnVBVADisable(pThisCC->pDrv, idScreen);
+
+ /* Clear the pitch lock. */
+ pThis->svga.u32PitchLock = 0;
+ }
+#else /* !IN_RING3 */
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif /* !IN_RING3 */
+ break;
+
+ case SVGA_REG_WIDTH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegWidthWr);
+ if (u32 != pThis->svga.uWidth)
+ {
+ if (u32 <= pThis->svga.u32MaxWidth)
+ {
+#if defined(IN_RING3) || defined(IN_RING0)
+ pThis->svga.uWidth = u32;
+ vmsvgaHCUpdatePitch(pThis, pThisCC);
+ if (pThis->svga.fEnabled)
+ ASMAtomicOrU32(&pThis->svga.u32ActionFlags, VMSVGA_ACTION_CHANGEMODE);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ }
+ else
+ Log(("SVGA_REG_WIDTH: New value is out of bounds: %u, max %u\n", u32, pThis->svga.u32MaxWidth));
+ }
+ /* else: nop */
+ break;
+
+ case SVGA_REG_HEIGHT:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegHeightWr);
+ if (u32 != pThis->svga.uHeight)
+ {
+ if (u32 <= pThis->svga.u32MaxHeight)
+ {
+ pThis->svga.uHeight = u32;
+ if (pThis->svga.fEnabled)
+ ASMAtomicOrU32(&pThis->svga.u32ActionFlags, VMSVGA_ACTION_CHANGEMODE);
+ }
+ else
+ Log(("SVGA_REG_HEIGHT: New value is out of bounds: %u, max %u\n", u32, pThis->svga.u32MaxHeight));
+ }
+ /* else: nop */
+ break;
+
+ case SVGA_REG_DEPTH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDepthWr);
+ /** @todo read-only?? */
+ break;
+
+ case SVGA_REG_BITS_PER_PIXEL: /* Current bpp in the guest */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegBitsPerPixelWr);
+ if (pThis->svga.uBpp != u32)
+ {
+ if (u32 <= 32)
+ {
+#if defined(IN_RING3) || defined(IN_RING0)
+ pThis->svga.uBpp = u32;
+ vmsvgaHCUpdatePitch(pThis, pThisCC);
+ if (pThis->svga.fEnabled)
+ ASMAtomicOrU32(&pThis->svga.u32ActionFlags, VMSVGA_ACTION_CHANGEMODE);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ }
+ else
+ Log(("SVGA_REG_BITS_PER_PIXEL: New value is out of bounds: %u, max 32\n", u32));
+ }
+ /* else: nop */
+ break;
+
+ case SVGA_REG_PSEUDOCOLOR:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegPseudoColorWr);
+ break;
+
+ case SVGA_REG_CONFIG_DONE: /* Set when memory area configured */
+#ifdef IN_RING3
+ STAM_REL_COUNTER_INC(&pSVGAState->StatR3RegConfigDoneWr);
+ pThis->svga.fConfigured = u32;
+ /* Disabling the FIFO enables tracing (dirty page detection) by default. */
+ if (!pThis->svga.fConfigured)
+ pThis->svga.fTraces = true;
+ vmsvgaR3SetTraces(pDevIns, pThis, !!pThis->svga.fTraces);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ break;
+
+ case SVGA_REG_SYNC: /* See "FIFO Synchronization Registers" */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegSyncWr);
+ if ( pThis->svga.fEnabled
+ && pThis->svga.fConfigured)
+ {
+#if defined(IN_RING3) || defined(IN_RING0)
+ Log(("SVGA_REG_SYNC: SVGA_FIFO_BUSY=%d\n", pThisCC->svga.pau32FIFO[SVGA_FIFO_BUSY]));
+ /*
+ * The VMSVGA_BUSY_F_EMT_FORCE flag makes sure we will check if the FIFO is empty
+ * at least once; VMSVGA_BUSY_F_FIFO alone does not ensure that.
+ */
+ ASMAtomicWriteU32(&pThis->svga.fBusy, VMSVGA_BUSY_F_EMT_FORCE | VMSVGA_BUSY_F_FIFO);
+ if (VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_BUSY, pThisCC->svga.pau32FIFO[SVGA_FIFO_MIN]))
+ vmsvgaHCSafeFifoBusyRegUpdate(pThis, pThisCC, true);
+
+ /* Kick the FIFO thread to start processing commands again. */
+ PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ }
+ /* else nothing to do. */
+ else
+ Log(("Sync ignored enabled=%d configured=%d\n", pThis->svga.fEnabled, pThis->svga.fConfigured));
+
+ break;
+
+ case SVGA_REG_BUSY: /* See "FIFO Synchronization Registers" (read-only) */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegBusyWr);
+ break;
+
+ case SVGA_REG_GUEST_ID: /* Set guest OS identifier */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGuestIdWr);
+ pThis->svga.u32GuestId = u32;
+ break;
+
+ case SVGA_REG_PITCHLOCK: /* Fixed pitch for all modes */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegPitchLockWr);
+ pThis->svga.u32PitchLock = u32;
+ /* Should this also update the FIFO pitch lock? Unclear. */
+ break;
+
+ case SVGA_REG_IRQMASK: /* Interrupt mask */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegIrqMaskWr);
+ pThis->svga.u32IrqMask = u32;
+
+ /* Irq pending after the above change? */
+ if (pThis->svga.u32IrqStatus & u32)
+ {
+ Log(("SVGA_REG_IRQMASK: Trigger interrupt with status %x\n", pThis->svga.u32IrqStatus));
+ PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 1);
+ }
+ else
+ PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 0);
+ break;
+
+ /* Mouse cursor support */
+ case SVGA_REG_DEAD: /* SVGA_REG_CURSOR_ID */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorIdWr);
+ pThis->svga.uCursorID = u32;
+ break;
+
+ case SVGA_REG_CURSOR_X:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorXWr);
+ pThis->svga.uCursorX = u32;
+ break;
+
+ case SVGA_REG_CURSOR_Y:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorYWr);
+ pThis->svga.uCursorY = u32;
+ break;
+
+ case SVGA_REG_CURSOR_ON:
+#ifdef IN_RING3
+ /* The cursor is only updated when SVGA_REG_CURSOR_ON is written. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCursorOnWr);
+ vmsvgaR3RegUpdateCursor(pThisCC, pThis, u32);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ break;
+
+ /* Legacy multi-monitor support */
+ case SVGA_REG_NUM_GUEST_DISPLAYS:/* Number of guest displays in X/Y direction */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegNumGuestDisplaysWr);
+ break;
+ case SVGA_REG_DISPLAY_ID: /* Display ID for the following display attributes */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayIdWr);
+ break;
+ case SVGA_REG_DISPLAY_IS_PRIMARY:/* Whether this is a primary display */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayIsPrimaryWr);
+ break;
+ case SVGA_REG_DISPLAY_POSITION_X:/* The display position x */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayPositionXWr);
+ break;
+ case SVGA_REG_DISPLAY_POSITION_Y:/* The display position y */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayPositionYWr);
+ break;
+ case SVGA_REG_DISPLAY_WIDTH: /* The display's width */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayWidthWr);
+ break;
+ case SVGA_REG_DISPLAY_HEIGHT: /* The display's height */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDisplayHeightWr);
+ break;
+#ifdef VBOX_WITH_VMSVGA3D
+ /* See "Guest memory regions" below. */
+ case SVGA_REG_GMR_ID:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegGmrIdWr);
+ pThis->svga.u32CurrentGMRId = u32;
+ break;
+
+ case SVGA_REG_GMR_DESCRIPTOR:
+# ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+ break;
+# else /* IN_RING3 */
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatR3RegGmrDescriptorWr);
+
+ /* Validate current GMR id. */
+ uint32_t idGMR = pThis->svga.u32CurrentGMRId;
+ AssertBreak(idGMR < pThis->svga.cGMR);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /* Free the old GMR if present. */
+ vmsvgaR3GmrFree(pThisCC, idGMR);
+
+ /* Just undefine the GMR? */
+ RTGCPHYS GCPhys = (RTGCPHYS)u32 << GUEST_PAGE_SHIFT;
+ if (GCPhys == 0)
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatR3RegGmrDescriptorWrFree);
+ break;
+ }
+
+
+ /* Never cross a page boundary automatically. */
+ const uint32_t cMaxPages = RT_MIN(VMSVGA_MAX_GMR_PAGES, UINT32_MAX / X86_PAGE_SIZE);
+ uint32_t cPagesTotal = 0;
+ uint32_t iDesc = 0;
+ PVMSVGAGMRDESCRIPTOR paDescs = NULL;
+ uint32_t cLoops = 0;
+ RTGCPHYS GCPhysBase = GCPhys;
+ while ((GCPhys >> GUEST_PAGE_SHIFT) == (GCPhysBase >> GUEST_PAGE_SHIFT))
+ {
+ /* Read descriptor. */
+ SVGAGuestMemDescriptor desc;
+ rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, &desc, sizeof(desc));
+ AssertRCBreak(VBOXSTRICTRC_VAL(rc));
+
+ if (desc.numPages != 0)
+ {
+ AssertBreakStmt(desc.numPages <= cMaxPages, rc = VERR_OUT_OF_RANGE);
+ cPagesTotal += desc.numPages;
+ AssertBreakStmt(cPagesTotal <= cMaxPages, rc = VERR_OUT_OF_RANGE);
+
+ if ((iDesc & 15) == 0)
+ {
+ void *pvNew = RTMemRealloc(paDescs, (iDesc + 16) * sizeof(VMSVGAGMRDESCRIPTOR));
+ AssertBreakStmt(pvNew, rc = VERR_NO_MEMORY);
+ paDescs = (PVMSVGAGMRDESCRIPTOR)pvNew;
+ }
+
+ paDescs[iDesc].GCPhys = (RTGCPHYS)desc.ppn << GUEST_PAGE_SHIFT;
+ paDescs[iDesc++].numPages = desc.numPages;
+
+ /* Continue with the next descriptor. */
+ GCPhys += sizeof(desc);
+ }
+ else if (desc.ppn == 0)
+ break; /* terminator */
+ else /* Pointer to the next physical page of descriptors. */
+ GCPhys = GCPhysBase = (RTGCPHYS)desc.ppn << GUEST_PAGE_SHIFT;
+
+ cLoops++;
+ AssertBreakStmt(cLoops < VMSVGA_MAX_GMR_DESC_LOOP_COUNT, rc = VERR_OUT_OF_RANGE);
+ }
+
+ AssertStmt(iDesc > 0 || RT_FAILURE_NP(rc), rc = VERR_OUT_OF_RANGE);
+ if (RT_SUCCESS(rc))
+ {
+ /* Commit the GMR. */
+ pSVGAState->paGMR[idGMR].paDesc = paDescs;
+ pSVGAState->paGMR[idGMR].numDescriptors = iDesc;
+ pSVGAState->paGMR[idGMR].cMaxPages = cPagesTotal;
+ pSVGAState->paGMR[idGMR].cbTotal = cPagesTotal * GUEST_PAGE_SIZE;
+ Assert((pSVGAState->paGMR[idGMR].cbTotal >> GUEST_PAGE_SHIFT) == cPagesTotal);
+ Log(("Defined new gmr %x numDescriptors=%d cbTotal=%x (%#x pages)\n",
+ idGMR, iDesc, pSVGAState->paGMR[idGMR].cbTotal, cPagesTotal));
+ }
+ else
+ {
+ RTMemFree(paDescs);
+ STAM_REL_COUNTER_INC(&pSVGAState->StatR3RegGmrDescriptorWrErrors);
+ }
+ break;
+ }
+# endif /* IN_RING3 */
+#endif // VBOX_WITH_VMSVGA3D
+
+ case SVGA_REG_TRACES: /* Enable trace-based updates even when FIFO is on */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegTracesWr);
+ if (pThis->svga.fTraces == u32)
+ break; /* nothing to do */
+
+#ifdef IN_RING3
+ vmsvgaR3SetTraces(pDevIns, pThis, !!u32);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ break;
+
+ case SVGA_REG_TOP: /* Must be 1 more than the last register */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegTopWr);
+ break;
+
+ case SVGA_REG_NUM_DISPLAYS: /* (Deprecated) */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegNumDisplaysWr);
+ Log(("Write to deprecated register %x - val %x ignored\n", idxReg, u32));
+ break;
+
+ /*
+ * SVGA_CAP_GBOBJECTS+ registers.
+ */
+ case SVGA_REG_COMMAND_LOW:
+ {
+ /* Lower 32 bits of command buffer physical address and submit the command buffer. */
+#ifdef IN_RING3
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCommandLowWr);
+ pThis->svga.u32RegCommandLow = u32;
+
+ /* "lower 6 bits are used for the SVGACBContext" */
+ RTGCPHYS GCPhysCB = pThis->svga.u32RegCommandHigh;
+ GCPhysCB <<= 32;
+ GCPhysCB |= pThis->svga.u32RegCommandLow & ~SVGA_CB_CONTEXT_MASK;
+ SVGACBContext const CBCtx = (SVGACBContext)(pThis->svga.u32RegCommandLow & SVGA_CB_CONTEXT_MASK);
+ vmsvgaR3CmdBufSubmit(pDevIns, pThis, pThisCC, GCPhysCB, CBCtx);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ break;
+ }
+
+ case SVGA_REG_COMMAND_HIGH:
+ /* Upper 32 bits of command buffer PA. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCommandHighWr);
+ pThis->svga.u32RegCommandHigh = u32;
+ break;
+
+ case SVGA_REG_DEV_CAP:
+ /* Write dev cap index, read value */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegDevCapWr);
+ pThis->svga.u32DevCapIndex = u32;
+ break;
+
+ case SVGA_REG_CMD_PREPEND_LOW:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCmdPrependLowWr);
+ /* Not supported. */
+ break;
+
+ case SVGA_REG_CMD_PREPEND_HIGH:
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegCmdPrependHighWr);
+ /* Not supported. */
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_ID:
+ if (u32 != SVGA_REG_GUEST_DRIVER_ID_SUBMIT)
+ pThis->svga.u32GuestDriverId = u32;
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_VERSION1:
+ pThis->svga.u32GuestDriverVer1 = u32;
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_VERSION2:
+ pThis->svga.u32GuestDriverVer2 = u32;
+ break;
+
+ case SVGA_REG_GUEST_DRIVER_VERSION3:
+ pThis->svga.u32GuestDriverVer3 = u32;
+ break;
+
+ case SVGA_REG_CURSOR_MOBID:
+ /* Not supported, ignore. See correspondent comments in vmsvgaReadPort. */
+ break;
+
+ case SVGA_REG_FB_START:
+ case SVGA_REG_MEM_START:
+ case SVGA_REG_HOST_BITS_PER_PIXEL:
+ case SVGA_REG_MAX_WIDTH:
+ case SVGA_REG_MAX_HEIGHT:
+ case SVGA_REG_VRAM_SIZE:
+ case SVGA_REG_FB_SIZE:
+ case SVGA_REG_CAPABILITIES:
+ case SVGA_REG_MEM_SIZE:
+ case SVGA_REG_SCRATCH_SIZE: /* Number of scratch registers */
+ case SVGA_REG_MEM_REGS: /* Number of FIFO registers */
+ case SVGA_REG_BYTES_PER_LINE:
+ case SVGA_REG_FB_OFFSET:
+ case SVGA_REG_RED_MASK:
+ case SVGA_REG_GREEN_MASK:
+ case SVGA_REG_BLUE_MASK:
+ case SVGA_REG_GMRS_MAX_PAGES: /* Maximum number of 4KB pages for all GMRs */
+ case SVGA_REG_MEMORY_SIZE: /* Total dedicated device memory excluding FIFO */
+ case SVGA_REG_GMR_MAX_IDS:
+ case SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH:
+ case SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM:
+ case SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB:
+ case SVGA_REG_SCREENTARGET_MAX_WIDTH:
+ case SVGA_REG_SCREENTARGET_MAX_HEIGHT:
+ case SVGA_REG_MOB_MAX_SIZE:
+ case SVGA_REG_BLANK_SCREEN_TARGETS:
+ case SVGA_REG_CAP2:
+ case SVGA_REG_DEVEL_CAP:
+ case SVGA_REG_CURSOR_MAX_BYTE_SIZE:
+ case SVGA_REG_CURSOR_MAX_DIMENSION:
+ case SVGA_REG_FIFO_CAPS:
+ case SVGA_REG_FENCE:
+ case SVGA_REG_RESERVED1:
+ case SVGA_REG_RESERVED2:
+ case SVGA_REG_RESERVED3:
+ case SVGA_REG_RESERVED4:
+ case SVGA_REG_RESERVED5:
+ case SVGA_REG_SCREENDMA:
+ case SVGA_REG_GBOBJECT_MEM_SIZE_KB:
+ /* Read only - ignore. */
+ Log(("Write to R/O register %x - val %x ignored\n", idxReg, u32));
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegReadOnlyWr);
+ break;
+
+ default:
+ {
+ uint32_t offReg;
+ if ((offReg = idxReg - SVGA_SCRATCH_BASE) < pThis->svga.cScratchRegion)
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ pThis->svga.au32ScratchRegion[offReg] = u32;
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegScratchWr);
+ }
+ else if ((offReg = idxReg - SVGA_PALETTE_BASE) < (uint32_t)SVGA_NUM_PALETTE_REGS)
+ {
+ /* Note! Using last_palette rather than palette here to preserve the VGA one.
+ Btw, see rgb_to_pixel32. */
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegPaletteWr);
+ u32 &= 0xff;
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ uint32_t uRgb = pThis->last_palette[offReg / 3];
+ switch (offReg % 3)
+ {
+ case 0: uRgb = (uRgb & UINT32_C(0x0000ffff)) | (u32 << 16); break; /* red */
+ case 1: uRgb = (uRgb & UINT32_C(0x00ff00ff)) | (u32 << 8); break; /* green */
+ case 2: uRgb = (uRgb & UINT32_C(0x00ffff00)) | u32 ; break; /* blue */
+ }
+ pThis->last_palette[offReg / 3] = uRgb;
+ }
+ else
+ {
+#if !defined(IN_RING3) && defined(VBOX_STRICT)
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+ STAM_REL_COUNTER_INC(&pThis->svga.StatRegUnknownWr);
+ AssertMsgFailed(("reg=%#x u32=%#x\n", idxReg, u32));
+#endif
+ }
+ break;
+ }
+ }
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN}
+ */
+DECLCALLBACK(VBOXSTRICTRC) vmsvgaIORead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ RT_NOREF_PV(pvUser);
+
+ /* Only dword accesses. */
+ if (cb == 4)
+ {
+ switch (offPort)
+ {
+ case SVGA_INDEX_PORT:
+ *pu32 = pThis->svga.u32IndexReg;
+ break;
+
+ case SVGA_VALUE_PORT:
+ return vmsvgaReadPort(pDevIns, pThis, pu32);
+
+ case SVGA_BIOS_PORT:
+ Log(("Ignoring BIOS port read\n"));
+ *pu32 = 0;
+ break;
+
+ case SVGA_IRQSTATUS_PORT:
+ LogFlow(("vmsvgaIORead: SVGA_IRQSTATUS_PORT %x\n", pThis->svga.u32IrqStatus));
+ *pu32 = pThis->svga.u32IrqStatus;
+ break;
+
+ default:
+ ASSERT_GUEST_MSG_FAILED(("vmsvgaIORead: Unknown register %u was read from.\n", offPort));
+ *pu32 = UINT32_MAX;
+ break;
+ }
+ }
+ else
+ {
+ Log(("Ignoring non-dword I/O port read at %x cb=%d\n", offPort, cb));
+ *pu32 = UINT32_MAX;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWOUT}
+ */
+DECLCALLBACK(VBOXSTRICTRC) vmsvgaIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ RT_NOREF_PV(pvUser);
+
+ /* Only dword accesses. */
+ if (cb == 4)
+ switch (offPort)
+ {
+ case SVGA_INDEX_PORT:
+ pThis->svga.u32IndexReg = u32;
+ break;
+
+ case SVGA_VALUE_PORT:
+ return vmsvgaWritePort(pDevIns, pThis, pThisCC, u32);
+
+ case SVGA_BIOS_PORT:
+ Log(("Ignoring BIOS port write (val=%x)\n", u32));
+ break;
+
+ case SVGA_IRQSTATUS_PORT:
+ LogFlow(("vmsvgaIOWrite SVGA_IRQSTATUS_PORT %x: status %x -> %x\n", u32, pThis->svga.u32IrqStatus, pThis->svga.u32IrqStatus & ~u32));
+ ASMAtomicAndU32(&pThis->svga.u32IrqStatus, ~u32);
+ /* Clear the irq in case all events have been cleared. */
+ if (!(pThis->svga.u32IrqStatus & pThis->svga.u32IrqMask))
+ {
+ Log(("vmsvgaIOWrite SVGA_IRQSTATUS_PORT: clearing IRQ\n"));
+ PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 0);
+ }
+ break;
+
+ default:
+ ASSERT_GUEST_MSG_FAILED(("vmsvgaIOWrite: Unknown register %u was written to, value %#x LB %u.\n", offPort, u32, cb));
+ break;
+ }
+ else
+ Log(("Ignoring non-dword write at %x val=%x cb=%d\n", offPort, u32, cb));
+
+ return VINF_SUCCESS;
+}
+
+#ifdef IN_RING3
+
+# ifdef DEBUG_FIFO_ACCESS
+/**
+ * Handle FIFO memory access.
+ * @returns VBox status code.
+ * @param pVM VM handle.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param GCPhys The access physical address.
+ * @param fWriteAccess Read or write access
+ */
+static int vmsvgaR3DebugFifoAccess(PVM pVM, PVGASTATE pThis, RTGCPHYS GCPhys, bool fWriteAccess)
+{
+ RT_NOREF(pVM);
+ RTGCPHYS GCPhysOffset = GCPhys - pThis->svga.GCPhysFIFO;
+ uint32_t *pFIFO = pThisCC->svga.pau32FIFO;
+
+ switch (GCPhysOffset >> 2)
+ {
+ case SVGA_FIFO_MIN:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_MIN = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_MAX:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_MAX = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_NEXT_CMD:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_NEXT_CMD = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_STOP:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_STOP = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CAPABILITIES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CAPABILITIES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_FLAGS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_FLAGS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_FENCE:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_FENCE = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_HWVERSION:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_HWVERSION = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_PITCHLOCK:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_PITCHLOCK = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CURSOR_ON:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CURSOR_ON = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CURSOR_X:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CURSOR_X = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CURSOR_Y:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CURSOR_Y = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CURSOR_COUNT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CURSOR_COUNT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CURSOR_LAST_UPDATED:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CURSOR_LAST_UPDATED = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_RESERVED:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_RESERVED = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_CURSOR_SCREEN_ID:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_CURSOR_SCREEN_ID = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_DEAD:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_DEAD = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_HWVERSION_REVISED:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_HWVERSION_REVISED = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_3D:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_3D = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_LIGHTS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_LIGHTS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_TEXTURES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_TEXTURES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_CLIP_PLANES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_CLIP_PLANES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_VERTEX_SHADER_VERSION:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_VERTEX_SHADER_VERSION = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_VERTEX_SHADER:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_VERTEX_SHADER = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_FRAGMENT_SHADER:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_FRAGMENT_SHADER = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_RENDER_TARGETS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_RENDER_TARGETS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_S23E8_TEXTURES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_S23E8_TEXTURES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_S10E5_TEXTURES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_S10E5_TEXTURES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_D16_BUFFER_FORMAT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_D16_BUFFER_FORMAT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_QUERY_TYPES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_QUERY_TYPES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_POINT_SIZE:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_POINT_SIZE = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_SHADER_TEXTURES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_SHADER_TEXTURES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_VOLUME_EXTENT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_VOLUME_EXTENT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_VERTEX_INDEX:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_VERTEX_INDEX = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_TEXTURE_OPS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_TEXTURE_OPS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_R5G6B5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_R5G6B5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_ALPHA8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_ALPHA8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Z_D16:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Z_D16 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_DXT1:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_DXT1 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_DXT2:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_DXT2 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_DXT3:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_DXT3 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_DXT4:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_DXT4 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_DXT5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_DXT5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_CxV8U8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_CxV8U8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_R_S10E5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_R_S10E5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_R_S23E8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_R_S23E8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_V16U16:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_V16U16 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_G16R16:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_G16R16 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_UYVY:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_UYVY = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_YUY2:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_YUY2 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_DEAD4: /* SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES */
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_DEAD4 (SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES) = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_DEAD5: /* SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES */
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_DEAD5 (SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES) = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_DEAD7: /* SVGA3D_DEVCAP_ALPHATOCOVERAGE */
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_DEAD7 (SVGA3D_DEVCAP_ALPHATOCOVERAGE) = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_DEAD6: /* SVGA3D_DEVCAP_SUPERSAMPLE */
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_DEAD6 (SVGA3D_DEVCAP_SUPERSAMPLE) = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_AUTOGENMIPMAPS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_AUTOGENMIPMAPS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_NV12:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_NV12 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_DEAD10: /* SVGA3D_DEVCAP_SURFACEFMT_AYUV */
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_DEAD10 (SVGA3D_DEVCAP_SURFACEFMT_AYUV) = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_CONTEXT_IDS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_CONTEXT_IDS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_MAX_SURFACE_IDS:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_MAX_SURFACE_IDS = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Z_DF16:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Z_DF16 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Z_DF24:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Z_DF24 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_ATI1:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_ATI1 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS + SVGA3D_DEVCAP_SURFACEFMT_ATI2:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS SVGA3D_DEVCAP_SURFACEFMT_ATI2 = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_3D_CAPS_LAST:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_3D_CAPS_LAST = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_GUEST_3D_HWVERSION:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_GUEST_3D_HWVERSION = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_FENCE_GOAL:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_FENCE_GOAL = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ case SVGA_FIFO_BUSY:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s SVGA_FIFO_BUSY = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", pFIFO[GCPhysOffset >> 2]));
+ break;
+ default:
+ Log(("vmsvgaFIFOAccess [0x%x]: %s access at offset %x = %x\n", GCPhysOffset >> 2, (fWriteAccess) ? "WRITE" : "READ", GCPhysOffset, pFIFO[GCPhysOffset >> 2]));
+ break;
+ }
+
+ return VINF_EM_RAW_EMULATE_INSTR;
+}
+# endif /* DEBUG_FIFO_ACCESS */
+
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+/**
+ * HC access handler for the FIFO.
+ *
+ * @returns VINF_SUCCESS if the handler have carried out the operation.
+ * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
+ * @param pVM VM Handle.
+ * @param pVCpu The cross context CPU structure for the calling EMT.
+ * @param GCPhys The physical address the guest is writing to.
+ * @param pvPhys The HC mapping of that address.
+ * @param pvBuf What the guest is reading/writing.
+ * @param cbBuf How much it's reading/writing.
+ * @param enmAccessType The access type.
+ * @param enmOrigin Who is making the access.
+ * @param pvUser User argument.
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+vmsvgaR3FifoAccessHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf,
+ PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
+{
+ NOREF(pVCpu); NOREF(pvPhys); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmOrigin); NOREF(enmAccessType); NOREF(GCPhys);
+ PVGASTATE pThis = (PVGASTATE)pvUser;
+ AssertPtr(pThis);
+
+# ifdef VMSVGA_USE_FIFO_ACCESS_HANDLER
+ /*
+ * Wake up the FIFO thread as it might have work to do now.
+ */
+ int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+ AssertLogRelRC(rc);
+# endif
+
+# ifdef DEBUG_FIFO_ACCESS
+ /*
+ * When in debug-fifo-access mode, we do not disable the access handler,
+ * but leave it on as we wish to catch all access.
+ */
+ Assert(GCPhys >= pThis->svga.GCPhysFIFO);
+ rc = vmsvgaR3DebugFifoAccess(pVM, pThis, GCPhys, enmAccessType == PGMACCESSTYPE_WRITE);
+# elif defined(VMSVGA_USE_FIFO_ACCESS_HANDLER)
+ /*
+ * Temporarily disable the access handler now that we've kicked the FIFO thread.
+ */
+ STAM_REL_COUNTER_INC(&pThisCC->svga.pSvgaR3State->StatFifoAccessHandler);
+ rc = PGMHandlerPhysicalPageTempOff(pVM, pThis->svga.GCPhysFIFO, pThis->svga.GCPhysFIFO);
+# endif
+ if (RT_SUCCESS(rc))
+ return VINF_PGM_HANDLER_DO_DEFAULT;
+ AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
+ return rc;
+}
+# endif /* VMSVGA_USE_FIFO_ACCESS_HANDLER || DEBUG_FIFO_ACCESS */
+
+#endif /* IN_RING3 */
+
+#ifdef DEBUG_GMR_ACCESS
+# ifdef IN_RING3
+
+/**
+ * HC access handler for GMRs.
+ *
+ * @returns VINF_SUCCESS if the handler have carried out the operation.
+ * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
+ * @param pVM VM Handle.
+ * @param pVCpu The cross context CPU structure for the calling EMT.
+ * @param GCPhys The physical address the guest is writing to.
+ * @param pvPhys The HC mapping of that address.
+ * @param pvBuf What the guest is reading/writing.
+ * @param cbBuf How much it's reading/writing.
+ * @param enmAccessType The access type.
+ * @param enmOrigin Who is making the access.
+ * @param pvUser User argument.
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+vmsvgaR3GmrAccessHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf,
+ PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
+{
+ PVGASTATE pThis = (PVGASTATE)pvUser;
+ Assert(pThis);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ NOREF(pVCpu); NOREF(pvPhys); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmAccessType); NOREF(enmOrigin);
+
+ Log(("vmsvgaR3GmrAccessHandler: GMR access to page %RGp\n", GCPhys));
+
+ for (uint32_t i = 0; i < pThis->svga.cGMR; ++i)
+ {
+ PGMR pGMR = &pSVGAState->paGMR[i];
+
+ if (pGMR->numDescriptors)
+ {
+ for (uint32_t j = 0; j < pGMR->numDescriptors; j++)
+ {
+ if ( GCPhys >= pGMR->paDesc[j].GCPhys
+ && GCPhys < pGMR->paDesc[j].GCPhys + pGMR->paDesc[j].numPages * GUEST_PAGE_SIZE)
+ {
+ /*
+ * Turn off the write handler for this particular page and make it R/W.
+ * Then return telling the caller to restart the guest instruction.
+ */
+ int rc = PGMHandlerPhysicalPageTempOff(pVM, pGMR->paDesc[j].GCPhys, GCPhys);
+ AssertRC(rc);
+ return VINF_PGM_HANDLER_DO_DEFAULT;
+ }
+ }
+ }
+ }
+
+ return VINF_PGM_HANDLER_DO_DEFAULT;
+}
+
+/** Callback handler for VMR3ReqCallWaitU */
+static DECLCALLBACK(int) vmsvgaR3RegisterGmr(PPDMDEVINS pDevIns, uint32_t gmrId)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ PGMR pGMR = &pSVGAState->paGMR[gmrId];
+ int rc;
+
+ for (uint32_t i = 0; i < pGMR->numDescriptors; i++)
+ {
+ rc = PDMDevHlpPGMHandlerPhysicalRegister(pDevIns, pGMR->paDesc[i].GCPhys,
+ pGMR->paDesc[i].GCPhys + pGMR->paDesc[i].numPages * GUEST_PAGE_SIZE - 1,
+ pThis->svga.hGmrAccessHandlerType, pThis, NIL_RTR0PTR, NIL_RTRCPTR, "VMSVGA GMR");
+ AssertRC(rc);
+ }
+ return VINF_SUCCESS;
+}
+
+/** Callback handler for VMR3ReqCallWaitU */
+static DECLCALLBACK(int) vmsvgaR3DeregisterGmr(PPDMDEVINS pDevIns, uint32_t gmrId)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ PGMR pGMR = &pSVGAState->paGMR[gmrId];
+
+ for (uint32_t i = 0; i < pGMR->numDescriptors; i++)
+ {
+ int rc = PDMDevHlpPGMHandlerPhysicalDeregister(pDevIns, pGMR->paDesc[i].GCPhys);
+ AssertRC(rc);
+ }
+ return VINF_SUCCESS;
+}
+
+/** Callback handler for VMR3ReqCallWaitU */
+static DECLCALLBACK(int) vmsvgaR3ResetGmrHandlers(PVGASTATE pThis)
+{
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ for (uint32_t i = 0; i < pThis->svga.cGMR; ++i)
+ {
+ PGMR pGMR = &pSVGAState->paGMR[i];
+
+ if (pGMR->numDescriptors)
+ {
+ for (uint32_t j = 0; j < pGMR->numDescriptors; j++)
+ {
+ int rc = PDMDevHlpPGMHandlerPhysicalReset(pDevIns, pGMR->paDesc[j].GCPhys);
+ AssertRC(rc);
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+# endif /* IN_RING3 */
+#endif /* DEBUG_GMR_ACCESS */
+
+/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
+
+#ifdef IN_RING3
+
+
+/*
+ *
+ * Command buffer submission.
+ *
+ * Guest submits a buffer by writing to SVGA_REG_COMMAND_LOW register.
+ *
+ * EMT thread appends a command buffer to the context queue (VMSVGACMDBUFCTX::listSubmitted)
+ * and wakes up the FIFO thread.
+ *
+ * FIFO thread fetches the command buffer from the queue, processes the commands and writes
+ * the buffer header back to the guest memory.
+ *
+ * If buffers are preempted, then the EMT thread removes all buffers from the context queue.
+ *
+ */
+
+
+/** Update a command buffer header 'status' and 'errorOffset' fields in the guest memory.
+ *
+ * @param pDevIns The device instance.
+ * @param GCPhysCB Guest physical address of the command buffer header.
+ * @param status Command buffer status (SVGA_CB_STATUS_*).
+ * @param errorOffset Offset to the first byte of the failing command for SVGA_CB_STATUS_COMMAND_ERROR.
+ * errorOffset is ignored if the status is not SVGA_CB_STATUS_COMMAND_ERROR.
+ * @thread FIFO or EMT.
+ */
+static void vmsvgaR3CmdBufWriteStatus(PPDMDEVINS pDevIns, RTGCPHYS GCPhysCB, SVGACBStatus status, uint32_t errorOffset)
+{
+ SVGACBHeader hdr;
+ hdr.status = status;
+ hdr.errorOffset = errorOffset;
+ AssertCompile( RT_OFFSETOF(SVGACBHeader, status) == 0
+ && RT_OFFSETOF(SVGACBHeader, errorOffset) == 4
+ && RT_OFFSETOF(SVGACBHeader, id) == 8);
+ size_t const cbWrite = status == SVGA_CB_STATUS_COMMAND_ERROR
+ ? RT_UOFFSET_AFTER(SVGACBHeader, errorOffset) /* Both 'status' and 'errorOffset' fields. */
+ : RT_UOFFSET_AFTER(SVGACBHeader, status); /* Only 'status' field. */
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhysCB, &hdr, cbWrite);
+}
+
+
+/** Raise an IRQ.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param u32IrqStatus SVGA_IRQFLAG_* bits.
+ * @thread FIFO or EMT.
+ */
+static void vmsvgaR3CmdBufRaiseIRQ(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t u32IrqStatus)
+{
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ if (pThis->svga.u32IrqMask & u32IrqStatus)
+ {
+ LogFunc(("Trigger interrupt with status %#x\n", u32IrqStatus));
+ ASMAtomicOrU32(&pThis->svga.u32IrqStatus, u32IrqStatus);
+ PDMDevHlpPCISetIrq(pDevIns, 0, 1);
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/** Allocate a command buffer structure.
+ *
+ * @param pCmdBufCtx The command buffer context which must allocate the buffer.
+ * @return Pointer to the allocated command buffer structure.
+ */
+static PVMSVGACMDBUF vmsvgaR3CmdBufAlloc(PVMSVGACMDBUFCTX pCmdBufCtx)
+{
+ if (!pCmdBufCtx)
+ return NULL;
+
+ PVMSVGACMDBUF pCmdBuf = (PVMSVGACMDBUF)RTMemAllocZ(sizeof(*pCmdBuf));
+ if (pCmdBuf)
+ {
+ // RT_ZERO(pCmdBuf->nodeBuffer);
+ pCmdBuf->pCmdBufCtx = pCmdBufCtx;
+ // pCmdBuf->GCPhysCB = 0;
+ // RT_ZERO(pCmdBuf->hdr);
+ // pCmdBuf->pvCommands = NULL;
+ }
+
+ return pCmdBuf;
+}
+
+
+/** Free a command buffer structure.
+ *
+ * @param pCmdBuf The command buffer pointer.
+ */
+static void vmsvgaR3CmdBufFree(PVMSVGACMDBUF pCmdBuf)
+{
+ if (pCmdBuf)
+ RTMemFree(pCmdBuf->pvCommands);
+ RTMemFree(pCmdBuf);
+}
+
+
+/** Initialize a command buffer context.
+ *
+ * @param pCmdBufCtx The command buffer context.
+ */
+static void vmsvgaR3CmdBufCtxInit(PVMSVGACMDBUFCTX pCmdBufCtx)
+{
+ RTListInit(&pCmdBufCtx->listSubmitted);
+ pCmdBufCtx->cSubmitted = 0;
+}
+
+
+/** Destroy a command buffer context.
+ *
+ * @param pCmdBufCtx The command buffer context pointer.
+ */
+static void vmsvgaR3CmdBufCtxTerm(PVMSVGACMDBUFCTX pCmdBufCtx)
+{
+ if (!pCmdBufCtx)
+ return;
+
+ if (pCmdBufCtx->listSubmitted.pNext)
+ {
+ /* If the list has been initialized. */
+ PVMSVGACMDBUF pIter, pNext;
+ RTListForEachSafe(&pCmdBufCtx->listSubmitted, pIter, pNext, VMSVGACMDBUF, nodeBuffer)
+ {
+ RTListNodeRemove(&pIter->nodeBuffer);
+ --pCmdBufCtx->cSubmitted;
+ vmsvgaR3CmdBufFree(pIter);
+ }
+ }
+ Assert(pCmdBufCtx->cSubmitted == 0);
+ pCmdBufCtx->cSubmitted = 0;
+}
+
+
+/** Handles SVGA_DC_CMD_START_STOP_CONTEXT command.
+ *
+ * @param pSvgaR3State VMSVGA R3 state.
+ * @param pCmd The command data.
+ * @return SVGACBStatus code.
+ * @thread EMT
+ */
+static SVGACBStatus vmsvgaR3CmdBufDCStartStop(PVMSVGAR3STATE pSvgaR3State, SVGADCCmdStartStop const *pCmd)
+{
+ /* Create or destroy a regular command buffer context. */
+ if (pCmd->context >= RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs))
+ return SVGA_CB_STATUS_COMMAND_ERROR;
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ SVGACBStatus CBStatus = SVGA_CB_STATUS_COMPLETED;
+
+ int rc = RTCritSectEnter(&pSvgaR3State->CritSectCmdBuf);
+ AssertRC(rc);
+ if (pCmd->enable)
+ {
+ pSvgaR3State->apCmdBufCtxs[pCmd->context] = (PVMSVGACMDBUFCTX)RTMemAlloc(sizeof(VMSVGACMDBUFCTX));
+ if (pSvgaR3State->apCmdBufCtxs[pCmd->context])
+ vmsvgaR3CmdBufCtxInit(pSvgaR3State->apCmdBufCtxs[pCmd->context]);
+ else
+ CBStatus = SVGA_CB_STATUS_QUEUE_FULL;
+ }
+ else
+ {
+ vmsvgaR3CmdBufCtxTerm(pSvgaR3State->apCmdBufCtxs[pCmd->context]);
+ RTMemFree(pSvgaR3State->apCmdBufCtxs[pCmd->context]);
+ pSvgaR3State->apCmdBufCtxs[pCmd->context] = NULL;
+ }
+ RTCritSectLeave(&pSvgaR3State->CritSectCmdBuf);
+
+ return CBStatus;
+}
+
+
+/** Handles SVGA_DC_CMD_PREEMPT command.
+ *
+ * @param pDevIns The device instance.
+ * @param pSvgaR3State VMSVGA R3 state.
+ * @param pCmd The command data.
+ * @return SVGACBStatus code.
+ * @thread EMT
+ */
+static SVGACBStatus vmsvgaR3CmdBufDCPreempt(PPDMDEVINS pDevIns, PVMSVGAR3STATE pSvgaR3State, SVGADCCmdPreempt const *pCmd)
+{
+ /* Remove buffers from the processing queue of the specified context. */
+ if (pCmd->context >= RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs))
+ return SVGA_CB_STATUS_COMMAND_ERROR;
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ PVMSVGACMDBUFCTX const pCmdBufCtx = pSvgaR3State->apCmdBufCtxs[pCmd->context];
+ RTLISTANCHOR listPreempted;
+
+ int rc = RTCritSectEnter(&pSvgaR3State->CritSectCmdBuf);
+ AssertRC(rc);
+ if (pCmd->ignoreIDZero)
+ {
+ RTListInit(&listPreempted);
+
+ PVMSVGACMDBUF pIter, pNext;
+ RTListForEachSafe(&pCmdBufCtx->listSubmitted, pIter, pNext, VMSVGACMDBUF, nodeBuffer)
+ {
+ if (pIter->hdr.id == 0)
+ continue;
+
+ RTListNodeRemove(&pIter->nodeBuffer);
+ --pCmdBufCtx->cSubmitted;
+ RTListAppend(&listPreempted, &pIter->nodeBuffer);
+ }
+ }
+ else
+ {
+ RTListMove(&listPreempted, &pCmdBufCtx->listSubmitted);
+ pCmdBufCtx->cSubmitted = 0;
+ }
+ RTCritSectLeave(&pSvgaR3State->CritSectCmdBuf);
+
+ PVMSVGACMDBUF pIter, pNext;
+ RTListForEachSafe(&listPreempted, pIter, pNext, VMSVGACMDBUF, nodeBuffer)
+ {
+ RTListNodeRemove(&pIter->nodeBuffer);
+ vmsvgaR3CmdBufWriteStatus(pDevIns, pIter->GCPhysCB, SVGA_CB_STATUS_PREEMPTED, 0);
+ LogFunc(("Preempted %RX64\n", pIter->GCPhysCB));
+ vmsvgaR3CmdBufFree(pIter);
+ }
+
+ return SVGA_CB_STATUS_COMPLETED;
+}
+
+
+/** @def VMSVGA_INC_CMD_SIZE_BREAK
+ * Increments the size of the command cbCmd by a_cbMore.
+ * Checks that the command buffer has at least cbCmd bytes. Will break out of the switch if it doesn't.
+ * Used by vmsvgaR3CmdBufProcessDC and vmsvgaR3CmdBufProcessCommands.
+ */
+#define VMSVGA_INC_CMD_SIZE_BREAK(a_cbMore) \
+ if (1) { \
+ cbCmd += (a_cbMore); \
+ ASSERT_GUEST_MSG_STMT_BREAK(cbRemain >= cbCmd, ("size=%#x remain=%#zx\n", cbCmd, (size_t)cbRemain), CBstatus = SVGA_CB_STATUS_COMMAND_ERROR); \
+ RT_UNTRUSTED_VALIDATED_FENCE(); \
+ } else do {} while (0)
+
+
+/** Processes Device Context command buffer.
+ *
+ * @param pDevIns The device instance.
+ * @param pSvgaR3State VMSVGA R3 state.
+ * @param pvCommands Pointer to the command buffer.
+ * @param cbCommands Size of the command buffer.
+ * @param poffNextCmd Where to store the offset of the first unprocessed command.
+ * @return SVGACBStatus code.
+ * @thread EMT
+ */
+static SVGACBStatus vmsvgaR3CmdBufProcessDC(PPDMDEVINS pDevIns, PVMSVGAR3STATE pSvgaR3State, void const *pvCommands, uint32_t cbCommands, uint32_t *poffNextCmd)
+{
+ SVGACBStatus CBstatus = SVGA_CB_STATUS_COMPLETED;
+
+ uint8_t const *pu8Cmd = (uint8_t *)pvCommands;
+ uint32_t cbRemain = cbCommands;
+ while (cbRemain)
+ {
+ /* Command identifier is a 32 bit value. */
+ if (cbRemain < sizeof(uint32_t))
+ {
+ CBstatus = SVGA_CB_STATUS_COMMAND_ERROR;
+ break;
+ }
+
+ /* Fetch the command id. */
+ uint32_t const cmdId = *(uint32_t *)pu8Cmd;
+ uint32_t cbCmd = sizeof(uint32_t);
+ switch (cmdId)
+ {
+ case SVGA_DC_CMD_NOP:
+ {
+ /* NOP */
+ break;
+ }
+
+ case SVGA_DC_CMD_START_STOP_CONTEXT:
+ {
+ SVGADCCmdStartStop *pCmd = (SVGADCCmdStartStop *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ CBstatus = vmsvgaR3CmdBufDCStartStop(pSvgaR3State, pCmd);
+ break;
+ }
+
+ case SVGA_DC_CMD_PREEMPT:
+ {
+ SVGADCCmdPreempt *pCmd = (SVGADCCmdPreempt *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ CBstatus = vmsvgaR3CmdBufDCPreempt(pDevIns, pSvgaR3State, pCmd);
+ break;
+ }
+
+ default:
+ {
+ /* Unsupported command. */
+ CBstatus = SVGA_CB_STATUS_COMMAND_ERROR;
+ break;
+ }
+ }
+
+ if (CBstatus != SVGA_CB_STATUS_COMPLETED)
+ break;
+
+ pu8Cmd += cbCmd;
+ cbRemain -= cbCmd;
+ }
+
+ Assert(cbRemain <= cbCommands);
+ *poffNextCmd = cbCommands - cbRemain;
+ return CBstatus;
+}
+
+
+/** Submits a device context command buffer for synchronous processing.
+ *
+ * @param pDevIns The device instance.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param ppCmdBuf Pointer to the command buffer pointer.
+ * The function can set the command buffer pointer to NULL to prevent deallocation by the caller.
+ * @param poffNextCmd Where to store the offset of the first unprocessed command.
+ * @return SVGACBStatus code.
+ * @thread EMT
+ */
+static SVGACBStatus vmsvgaR3CmdBufSubmitDC(PPDMDEVINS pDevIns, PVGASTATECC pThisCC, PVMSVGACMDBUF *ppCmdBuf, uint32_t *poffNextCmd)
+{
+ /* Synchronously process the device context commands. */
+ PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+ return vmsvgaR3CmdBufProcessDC(pDevIns, pSvgaR3State, (*ppCmdBuf)->pvCommands, (*ppCmdBuf)->hdr.length, poffNextCmd);
+}
+
+/** Submits a command buffer for asynchronous processing by the FIFO thread.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param ppCmdBuf Pointer to the command buffer pointer.
+ * The function can set the command buffer pointer to NULL to prevent deallocation by the caller.
+ * @return SVGACBStatus code.
+ * @thread EMT
+ */
+static SVGACBStatus vmsvgaR3CmdBufSubmitCtx(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, PVMSVGACMDBUF *ppCmdBuf)
+{
+ /* Command buffer submission. */
+ PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+ SVGACBStatus CBstatus = SVGA_CB_STATUS_NONE;
+
+ PVMSVGACMDBUF const pCmdBuf = *ppCmdBuf;
+ PVMSVGACMDBUFCTX const pCmdBufCtx = pCmdBuf->pCmdBufCtx;
+
+ int rc = RTCritSectEnter(&pSvgaR3State->CritSectCmdBuf);
+ AssertRC(rc);
+
+ if (RT_LIKELY(pCmdBufCtx->cSubmitted < SVGA_CB_MAX_QUEUED_PER_CONTEXT))
+ {
+ RTListAppend(&pCmdBufCtx->listSubmitted, &pCmdBuf->nodeBuffer);
+ ++pCmdBufCtx->cSubmitted;
+ *ppCmdBuf = NULL; /* Consume the buffer. */
+ ASMAtomicWriteU32(&pThisCC->svga.pSvgaR3State->fCmdBuf, 1);
+ }
+ else
+ CBstatus = SVGA_CB_STATUS_QUEUE_FULL;
+
+ RTCritSectLeave(&pSvgaR3State->CritSectCmdBuf);
+
+ /* Inform the FIFO thread. */
+ if (*ppCmdBuf == NULL)
+ PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+
+ return CBstatus;
+}
+
+
+/** SVGA_REG_COMMAND_LOW write handler.
+ * Submits a command buffer to the FIFO thread or processes a device context command.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param GCPhysCB Guest physical address of the command buffer header.
+ * @param CBCtx Context the command buffer is submitted to.
+ * @thread EMT
+ */
+static void vmsvgaR3CmdBufSubmit(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS GCPhysCB, SVGACBContext CBCtx)
+{
+ PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+ SVGACBStatus CBstatus = SVGA_CB_STATUS_NONE;
+ uint32_t offNextCmd = 0;
+ uint32_t fIRQ = 0;
+
+ /* Get the context if the device has the capability. */
+ PVMSVGACMDBUFCTX pCmdBufCtx = NULL;
+ if (pThis->svga.u32DeviceCaps & SVGA_CAP_COMMAND_BUFFERS)
+ {
+ if (RT_LIKELY(CBCtx < RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs)))
+ pCmdBufCtx = pSvgaR3State->apCmdBufCtxs[CBCtx];
+ else if (CBCtx == SVGA_CB_CONTEXT_DEVICE)
+ pCmdBufCtx = &pSvgaR3State->CmdBufCtxDC;
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ }
+
+ /* Allocate a new command buffer. */
+ PVMSVGACMDBUF pCmdBuf = vmsvgaR3CmdBufAlloc(pCmdBufCtx);
+ if (RT_LIKELY(pCmdBuf))
+ {
+ pCmdBuf->GCPhysCB = GCPhysCB;
+
+ int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhysCB, &pCmdBuf->hdr, sizeof(pCmdBuf->hdr));
+ if (RT_SUCCESS(rc))
+ {
+ LogFunc(("status %RX32 errorOffset %RX32 id %RX64 flags %RX32 length %RX32 ptr %RX64 offset %RX32 dxContext %RX32 (%RX32 %RX32 %RX32 %RX32 %RX32 %RX32)\n",
+ pCmdBuf->hdr.status,
+ pCmdBuf->hdr.errorOffset,
+ pCmdBuf->hdr.id,
+ pCmdBuf->hdr.flags,
+ pCmdBuf->hdr.length,
+ pCmdBuf->hdr.ptr.pa,
+ pCmdBuf->hdr.offset,
+ pCmdBuf->hdr.dxContext,
+ pCmdBuf->hdr.mustBeZero[0],
+ pCmdBuf->hdr.mustBeZero[1],
+ pCmdBuf->hdr.mustBeZero[2],
+ pCmdBuf->hdr.mustBeZero[3],
+ pCmdBuf->hdr.mustBeZero[4],
+ pCmdBuf->hdr.mustBeZero[5]));
+
+ /* Verify the command buffer header. */
+ if (RT_LIKELY( pCmdBuf->hdr.status == SVGA_CB_STATUS_NONE
+ && (pCmdBuf->hdr.flags & ~(SVGA_CB_FLAG_NO_IRQ | SVGA_CB_FLAG_DX_CONTEXT)) == 0 /* No unexpected flags. */
+ && pCmdBuf->hdr.length <= SVGA_CB_MAX_SIZE))
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /* Read the command buffer content. */
+ pCmdBuf->pvCommands = RTMemAlloc(pCmdBuf->hdr.length);
+ if (pCmdBuf->pvCommands)
+ {
+ RTGCPHYS const GCPhysCmd = (RTGCPHYS)pCmdBuf->hdr.ptr.pa;
+ rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhysCmd, pCmdBuf->pvCommands, pCmdBuf->hdr.length);
+ if (RT_SUCCESS(rc))
+ {
+ /* Submit the buffer. Device context buffers will be processed synchronously. */
+ if (RT_LIKELY(CBCtx < RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs)))
+ /* This usually processes the CB async and sets pCmbBuf to NULL. */
+ CBstatus = vmsvgaR3CmdBufSubmitCtx(pDevIns, pThis, pThisCC, &pCmdBuf);
+ else
+ CBstatus = vmsvgaR3CmdBufSubmitDC(pDevIns, pThisCC, &pCmdBuf, &offNextCmd);
+ }
+ else
+ {
+ ASSERT_GUEST_MSG_FAILED(("Failed to read commands at %RGp\n", GCPhysCmd));
+ CBstatus = SVGA_CB_STATUS_CB_HEADER_ERROR;
+ fIRQ = SVGA_IRQFLAG_ERROR | SVGA_IRQFLAG_COMMAND_BUFFER;
+ }
+ }
+ else
+ {
+ /* No memory for commands. */
+ CBstatus = SVGA_CB_STATUS_QUEUE_FULL;
+ }
+ }
+ else
+ {
+ ASSERT_GUEST_MSG_FAILED(("Invalid buffer header\n"));
+ CBstatus = SVGA_CB_STATUS_CB_HEADER_ERROR;
+ fIRQ = SVGA_IRQFLAG_ERROR | SVGA_IRQFLAG_COMMAND_BUFFER;
+ }
+ }
+ else
+ {
+ LogFunc(("Failed to read buffer header at %RGp\n", GCPhysCB));
+ ASSERT_GUEST_FAILED();
+ /* Do not attempt to write the status. */
+ }
+
+ /* Free the buffer if pfnCmdBufSubmit did not consume it. */
+ vmsvgaR3CmdBufFree(pCmdBuf);
+ }
+ else
+ {
+ LogFunc(("Can't allocate buffer for context id %#x\n", CBCtx));
+ AssertFailed();
+ CBstatus = SVGA_CB_STATUS_QUEUE_FULL;
+ }
+
+ if (CBstatus != SVGA_CB_STATUS_NONE)
+ {
+ LogFunc(("Write status %#x, offNextCmd %#x, fIRQ %#x\n", CBstatus, offNextCmd, fIRQ));
+ vmsvgaR3CmdBufWriteStatus(pDevIns, GCPhysCB, CBstatus, offNextCmd);
+ if (fIRQ)
+ vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, fIRQ);
+ }
+}
+
+
+/** Checks if there are some buffers to be processed.
+ *
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @return true if buffers must be processed.
+ * @thread FIFO
+ */
+static bool vmsvgaR3CmdBufHasWork(PVGASTATECC pThisCC)
+{
+ PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+ return RT_BOOL(ASMAtomicReadU32(&pSvgaR3State->fCmdBuf));
+}
+
+
+/** Processes a command buffer.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param idDXContext VGPU10 DX context of the commands or SVGA3D_INVALID_ID if they are not for a specific context.
+ * @param pvCommands Pointer to the command buffer.
+ * @param cbCommands Size of the command buffer.
+ * @param poffNextCmd Where to store the offset of the first unprocessed command.
+ * @param pu32IrqStatus Where to store SVGA_IRQFLAG_ if the IRQ is generated by the last command in the buffer.
+ * @return SVGACBStatus code.
+ * @thread FIFO
+ */
+static SVGACBStatus vmsvgaR3CmdBufProcessCommands(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t idDXContext, void const *pvCommands, uint32_t cbCommands, uint32_t *poffNextCmd, uint32_t *pu32IrqStatus)
+{
+# ifndef VBOX_WITH_VMSVGA3D
+ RT_NOREF(idDXContext);
+# endif
+ SVGACBStatus CBstatus = SVGA_CB_STATUS_COMPLETED;
+ PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+# ifdef VBOX_WITH_VMSVGA3D
+# ifdef VMSVGA3D_DX
+ /* Commands submitted for the SVGA3D_INVALID_ID context do not affect pipeline. So ignore them. */
+ if (idDXContext != SVGA3D_INVALID_ID)
+ {
+ if (pSvgaR3State->idDXContextCurrent != idDXContext)
+ {
+ LogFlow(("DXCTX: buffer %d->%d\n", pSvgaR3State->idDXContextCurrent, idDXContext));
+ vmsvga3dDXSwitchContext(pThisCC, idDXContext);
+ pSvgaR3State->idDXContextCurrent = idDXContext;
+ }
+ }
+# endif
+# endif
+
+ uint32_t RT_UNTRUSTED_VOLATILE_GUEST * const pFIFO = pThisCC->svga.pau32FIFO;
+
+ uint8_t const *pu8Cmd = (uint8_t *)pvCommands;
+ uint32_t cbRemain = cbCommands;
+ while (cbRemain)
+ {
+ /* Command identifier is a 32 bit value. */
+ if (cbRemain < sizeof(uint32_t))
+ {
+ CBstatus = SVGA_CB_STATUS_COMMAND_ERROR;
+ break;
+ }
+
+ /* Fetch the command id.
+ * 'cmdId' is actually a SVGAFifoCmdId. It is treated as uint32_t in order to avoid a compiler
+ * warning. Because we support some obsolete and deprecated commands, which are not included in
+ * the SVGAFifoCmdId enum in the VMSVGA headers anymore.
+ */
+ uint32_t const cmdId = *(uint32_t *)pu8Cmd;
+ uint32_t cbCmd = sizeof(uint32_t);
+
+ LogFunc(("[cid=%d] %s %d\n", (int32_t)idDXContext, vmsvgaR3FifoCmdToString(cmdId), cmdId));
+# ifdef LOG_ENABLED
+# ifdef VBOX_WITH_VMSVGA3D
+ if (SVGA_3D_CMD_BASE <= cmdId && cmdId < SVGA_3D_CMD_MAX)
+ {
+ SVGA3dCmdHeader const *header = (SVGA3dCmdHeader *)pu8Cmd;
+ svga_dump_command(cmdId, (uint8_t *)&header[1], header->size);
+ }
+ else if (cmdId == SVGA_CMD_FENCE)
+ {
+ Log7(("\tSVGA_CMD_FENCE\n"));
+ Log7(("\t\t0x%08x\n", ((uint32_t *)pu8Cmd)[1]));
+ }
+# endif
+# endif
+
+ /* At the end of the switch cbCmd is equal to the total length of the command including the cmdId.
+ * I.e. pu8Cmd + cbCmd must point to the next command.
+ * However if CBstatus is set to anything but SVGA_CB_STATUS_COMPLETED in the switch, then
+ * the cbCmd value is ignored (and pu8Cmd still points to the failed command).
+ */
+ /** @todo This code is very similar to the FIFO loop command processing. Think about merging. */
+ switch (cmdId)
+ {
+ case SVGA_CMD_INVALID_CMD:
+ {
+ /* Nothing to do. */
+ STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3CmdInvalidCmd);
+ break;
+ }
+
+ case SVGA_CMD_FENCE:
+ {
+ SVGAFifoCmdFence *pCmd = (SVGAFifoCmdFence *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3CmdFence);
+ Log(("SVGA_CMD_FENCE %#x\n", pCmd->fence));
+
+ uint32_t const offFifoMin = pFIFO[SVGA_FIFO_MIN];
+ if (VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_FENCE, offFifoMin))
+ {
+ pFIFO[SVGA_FIFO_FENCE] = pCmd->fence;
+
+ if (pThis->svga.u32IrqMask & SVGA_IRQFLAG_ANY_FENCE)
+ {
+ Log(("any fence irq\n"));
+ *pu32IrqStatus |= SVGA_IRQFLAG_ANY_FENCE;
+ }
+ else if ( VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_FENCE_GOAL, offFifoMin)
+ && (pThis->svga.u32IrqMask & SVGA_IRQFLAG_FENCE_GOAL)
+ && pFIFO[SVGA_FIFO_FENCE_GOAL] == pCmd->fence)
+ {
+ Log(("fence goal reached irq (fence=%#x)\n", pCmd->fence));
+ *pu32IrqStatus |= SVGA_IRQFLAG_FENCE_GOAL;
+ }
+ }
+ else
+ Log(("SVGA_CMD_FENCE is bogus when offFifoMin is %#x!\n", offFifoMin));
+ break;
+ }
+
+ case SVGA_CMD_UPDATE:
+ {
+ SVGAFifoCmdUpdate *pCmd = (SVGAFifoCmdUpdate *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdUpdate(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_UPDATE_VERBOSE:
+ {
+ SVGAFifoCmdUpdateVerbose *pCmd = (SVGAFifoCmdUpdateVerbose *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdUpdateVerbose(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DEFINE_CURSOR:
+ {
+ /* Followed by bitmap data. */
+ SVGAFifoCmdDefineCursor *pCmd = (SVGAFifoCmdDefineCursor *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+
+ /* Figure out the size of the bitmap data. */
+ ASSERT_GUEST_STMT_BREAK(pCmd->height < 2048 && pCmd->width < 2048, CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+ ASSERT_GUEST_STMT_BREAK(pCmd->andMaskDepth <= 32, CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+ ASSERT_GUEST_STMT_BREAK(pCmd->xorMaskDepth <= 32, CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ uint32_t const cbAndLine = RT_ALIGN_32(pCmd->width * (pCmd->andMaskDepth + (pCmd->andMaskDepth == 15)), 32) / 8;
+ uint32_t const cbAndMask = cbAndLine * pCmd->height;
+ uint32_t const cbXorLine = RT_ALIGN_32(pCmd->width * (pCmd->xorMaskDepth + (pCmd->xorMaskDepth == 15)), 32) / 8;
+ uint32_t const cbXorMask = cbXorLine * pCmd->height;
+
+ VMSVGA_INC_CMD_SIZE_BREAK(cbAndMask + cbXorMask);
+ vmsvgaR3CmdDefineCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DEFINE_ALPHA_CURSOR:
+ {
+ /* Followed by bitmap data. */
+ SVGAFifoCmdDefineAlphaCursor *pCmd = (SVGAFifoCmdDefineAlphaCursor *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+
+ /* Figure out the size of the bitmap data. */
+ ASSERT_GUEST_STMT_BREAK(pCmd->height < 2048 && pCmd->width < 2048, CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+
+ VMSVGA_INC_CMD_SIZE_BREAK(pCmd->width * pCmd->height * sizeof(uint32_t)); /* 32-bit BRGA format */
+ vmsvgaR3CmdDefineAlphaCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_MOVE_CURSOR:
+ {
+ /* Deprecated; there should be no driver which *requires* this command. However, if
+ * we do ecncounter this command, it might be useful to not get the FIFO completely out of
+ * alignment.
+ * May be issued by guest if SVGA_CAP_CURSOR_BYPASS is missing.
+ */
+ SVGAFifoCmdMoveCursor *pCmd = (SVGAFifoCmdMoveCursor *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdMoveCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DISPLAY_CURSOR:
+ {
+ /* Deprecated; there should be no driver which *requires* this command. However, if
+ * we do ecncounter this command, it might be useful to not get the FIFO completely out of
+ * alignment.
+ * May be issued by guest if SVGA_CAP_CURSOR_BYPASS is missing.
+ */
+ SVGAFifoCmdDisplayCursor *pCmd = (SVGAFifoCmdDisplayCursor *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdDisplayCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_RECT_FILL:
+ {
+ SVGAFifoCmdRectFill *pCmd = (SVGAFifoCmdRectFill *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdRectFill(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_RECT_COPY:
+ {
+ SVGAFifoCmdRectCopy *pCmd = (SVGAFifoCmdRectCopy *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdRectCopy(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_RECT_ROP_COPY:
+ {
+ SVGAFifoCmdRectRopCopy *pCmd = (SVGAFifoCmdRectRopCopy *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdRectRopCopy(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_ESCAPE:
+ {
+ /* Followed by 'size' bytes of data. */
+ SVGAFifoCmdEscape *pCmd = (SVGAFifoCmdEscape *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+
+ ASSERT_GUEST_STMT_BREAK(pCmd->size < pThis->svga.cbFIFO - sizeof(SVGAFifoCmdEscape), CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ VMSVGA_INC_CMD_SIZE_BREAK(pCmd->size);
+ vmsvgaR3CmdEscape(pThis, pThisCC, pCmd);
+ break;
+ }
+# ifdef VBOX_WITH_VMSVGA3D
+ case SVGA_CMD_DEFINE_GMR2:
+ {
+ SVGAFifoCmdDefineGMR2 *pCmd = (SVGAFifoCmdDefineGMR2 *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdDefineGMR2(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_REMAP_GMR2:
+ {
+ /* Followed by page descriptors or guest ptr. */
+ SVGAFifoCmdRemapGMR2 *pCmd = (SVGAFifoCmdRemapGMR2 *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+
+ /* Calculate the size of what comes after next and fetch it. */
+ uint32_t cbMore = 0;
+ if (pCmd->flags & SVGA_REMAP_GMR2_VIA_GMR)
+ cbMore = sizeof(SVGAGuestPtr);
+ else
+ {
+ uint32_t const cbPageDesc = (pCmd->flags & SVGA_REMAP_GMR2_PPN64) ? sizeof(uint64_t) : sizeof(uint32_t);
+ if (pCmd->flags & SVGA_REMAP_GMR2_SINGLE_PPN)
+ {
+ cbMore = cbPageDesc;
+ pCmd->numPages = 1;
+ }
+ else
+ {
+ ASSERT_GUEST_STMT_BREAK(pCmd->numPages <= pThis->svga.cbFIFO / cbPageDesc, CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+ cbMore = cbPageDesc * pCmd->numPages;
+ }
+ }
+ VMSVGA_INC_CMD_SIZE_BREAK(cbMore);
+ vmsvgaR3CmdRemapGMR2(pThis, pThisCC, pCmd);
+# ifdef DEBUG_GMR_ACCESS
+ VMR3ReqCallWaitU(PDMDevHlpGetUVM(pDevIns), VMCPUID_ANY, (PFNRT)vmsvgaR3RegisterGmr, 2, pDevIns, pCmd->gmrId);
+# endif
+ break;
+ }
+# endif /* VBOX_WITH_VMSVGA3D */
+ case SVGA_CMD_DEFINE_SCREEN:
+ {
+ /* The size of this command is specified by the guest and depends on capabilities. */
+ SVGAFifoCmdDefineScreen *pCmd = (SVGAFifoCmdDefineScreen *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(pCmd->screen.structSize));
+ ASSERT_GUEST_STMT_BREAK(pCmd->screen.structSize < pThis->svga.cbFIFO, CBstatus = SVGA_CB_STATUS_COMMAND_ERROR);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ VMSVGA_INC_CMD_SIZE_BREAK(RT_MAX(sizeof(pCmd->screen.structSize), pCmd->screen.structSize) - sizeof(pCmd->screen.structSize));
+ vmsvgaR3CmdDefineScreen(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DESTROY_SCREEN:
+ {
+ SVGAFifoCmdDestroyScreen *pCmd = (SVGAFifoCmdDestroyScreen *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdDestroyScreen(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DEFINE_GMRFB:
+ {
+ SVGAFifoCmdDefineGMRFB *pCmd = (SVGAFifoCmdDefineGMRFB *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdDefineGMRFB(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_BLIT_GMRFB_TO_SCREEN:
+ {
+ SVGAFifoCmdBlitGMRFBToScreen *pCmd = (SVGAFifoCmdBlitGMRFBToScreen *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdBlitGMRFBToScreen(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_BLIT_SCREEN_TO_GMRFB:
+ {
+ SVGAFifoCmdBlitScreenToGMRFB *pCmd = (SVGAFifoCmdBlitScreenToGMRFB *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdBlitScreenToGMRFB(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_ANNOTATION_FILL:
+ {
+ SVGAFifoCmdAnnotationFill *pCmd = (SVGAFifoCmdAnnotationFill *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdAnnotationFill(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_ANNOTATION_COPY:
+ {
+ SVGAFifoCmdAnnotationCopy *pCmd = (SVGAFifoCmdAnnotationCopy *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pCmd));
+ vmsvgaR3CmdAnnotationCopy(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ default:
+ {
+# ifdef VBOX_WITH_VMSVGA3D
+ if ( cmdId >= SVGA_3D_CMD_BASE
+ && cmdId < SVGA_3D_CMD_MAX)
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /* All 3d commands start with a common header, which defines the identifier and the size
+ * of the command. The identifier has been already read. Fetch the size.
+ */
+ uint32_t const *pcbMore = (uint32_t const *)&pu8Cmd[cbCmd];
+ VMSVGA_INC_CMD_SIZE_BREAK(sizeof(*pcbMore));
+ VMSVGA_INC_CMD_SIZE_BREAK(*pcbMore);
+ if (RT_LIKELY(pThis->svga.f3DEnabled))
+ { /* likely */ }
+ else
+ {
+ LogRelMax(8, ("VMSVGA: 3D disabled, command %d skipped\n", cmdId));
+ break;
+ }
+
+ /* Command data begins after the 32 bit command length. */
+ int rc = vmsvgaR3Process3dCmd(pThis, pThisCC, idDXContext, (SVGAFifo3dCmdId)cmdId, *pcbMore, pcbMore + 1);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ {
+ CBstatus = SVGA_CB_STATUS_COMMAND_ERROR;
+ break;
+ }
+ }
+ else
+# endif /* VBOX_WITH_VMSVGA3D */
+ {
+ /* Unsupported command. */
+ STAM_REL_COUNTER_INC(&pSvgaR3State->StatFifoUnkCmds);
+ ASSERT_GUEST_MSG_FAILED(("cmdId=%d\n", cmdId));
+ LogRelMax(16, ("VMSVGA: unsupported command %d\n", cmdId));
+ CBstatus = SVGA_CB_STATUS_COMMAND_ERROR;
+ break;
+ }
+ }
+ }
+
+ if (CBstatus != SVGA_CB_STATUS_COMPLETED)
+ break;
+
+ pu8Cmd += cbCmd;
+ cbRemain -= cbCmd;
+
+ /* If this is not the last command in the buffer, then generate IRQ, if required.
+ * This avoids a double call to vmsvgaR3CmdBufRaiseIRQ if FENCE is the last command
+ * in the buffer (usually the case).
+ */
+ if (RT_LIKELY(!(cbRemain && *pu32IrqStatus)))
+ { /* likely */ }
+ else
+ {
+ vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, *pu32IrqStatus);
+ *pu32IrqStatus = 0;
+ }
+ }
+
+ Assert(cbRemain <= cbCommands);
+ *poffNextCmd = cbCommands - cbRemain;
+ return CBstatus;
+}
+
+
+/** Process command buffers.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA state.
+ * @param pThisCC The VGA/VMSVGA state for the current context.
+ * @param pThread Handle of the FIFO thread.
+ * @thread FIFO
+ */
+static void vmsvgaR3CmdBufProcessBuffers(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, PPDMTHREAD pThread)
+{
+ PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+ for (;;)
+ {
+ if (pThread->enmState != PDMTHREADSTATE_RUNNING)
+ break;
+
+ /* See if there is a submitted buffer. */
+ PVMSVGACMDBUF pCmdBuf = NULL;
+
+ int rc = RTCritSectEnter(&pSvgaR3State->CritSectCmdBuf);
+ AssertRC(rc);
+
+ /* It seems that a higher queue index has a higher priority.
+ * See SVGACBContext in svga_reg.h from latest vmwgfx Linux driver.
+ */
+ for (unsigned i = RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs); i > 0; --i)
+ {
+ PVMSVGACMDBUFCTX pCmdBufCtx = pSvgaR3State->apCmdBufCtxs[i - 1];
+ if (pCmdBufCtx)
+ {
+ pCmdBuf = RTListRemoveFirst(&pCmdBufCtx->listSubmitted, VMSVGACMDBUF, nodeBuffer);
+ if (pCmdBuf)
+ {
+ Assert(pCmdBufCtx->cSubmitted > 0);
+ --pCmdBufCtx->cSubmitted;
+ break;
+ }
+ }
+ }
+
+ if (!pCmdBuf)
+ {
+ ASMAtomicWriteU32(&pSvgaR3State->fCmdBuf, 0);
+ RTCritSectLeave(&pSvgaR3State->CritSectCmdBuf);
+ break;
+ }
+
+ RTCritSectLeave(&pSvgaR3State->CritSectCmdBuf);
+
+ SVGACBStatus CBstatus = SVGA_CB_STATUS_NONE;
+ uint32_t offNextCmd = 0;
+ uint32_t u32IrqStatus = 0;
+ uint32_t const idDXContext = RT_BOOL(pCmdBuf->hdr.flags & SVGA_CB_FLAG_DX_CONTEXT)
+ ? pCmdBuf->hdr.dxContext
+ : SVGA3D_INVALID_ID;
+ /* Process one buffer. */
+ CBstatus = vmsvgaR3CmdBufProcessCommands(pDevIns, pThis, pThisCC, idDXContext, pCmdBuf->pvCommands, pCmdBuf->hdr.length, &offNextCmd, &u32IrqStatus);
+
+ if (!RT_BOOL(pCmdBuf->hdr.flags & SVGA_CB_FLAG_NO_IRQ))
+ u32IrqStatus |= SVGA_IRQFLAG_COMMAND_BUFFER;
+ if (CBstatus == SVGA_CB_STATUS_COMMAND_ERROR)
+ u32IrqStatus |= SVGA_IRQFLAG_ERROR;
+
+ vmsvgaR3CmdBufWriteStatus(pDevIns, pCmdBuf->GCPhysCB, CBstatus, offNextCmd);
+ if (u32IrqStatus)
+ vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, u32IrqStatus);
+
+ vmsvgaR3CmdBufFree(pCmdBuf);
+ }
+}
+
+
+/**
+ * Worker for vmsvgaR3FifoThread that handles an external command.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+static void vmsvgaR3FifoHandleExtCmd(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ uint8_t uExtCmd = pThis->svga.u8FIFOExtCommand;
+ switch (pThis->svga.u8FIFOExtCommand)
+ {
+ case VMSVGA_FIFO_EXTCMD_RESET:
+ Log(("vmsvgaR3FifoLoop: reset the fifo thread.\n"));
+ Assert(pThisCC->svga.pvFIFOExtCmdParam == NULL);
+
+ vmsvgaR3ResetScreens(pThis, pThisCC);
+# ifdef VBOX_WITH_VMSVGA3D
+ /* The 3d subsystem must be reset from the fifo thread. */
+ if (pThis->svga.f3DEnabled)
+ vmsvga3dReset(pThisCC);
+# endif
+ vmsvgaR3ResetSvgaState(pThis, pThisCC);
+ break;
+
+ case VMSVGA_FIFO_EXTCMD_POWEROFF:
+ Log(("vmsvgaR3FifoLoop: power off.\n"));
+ Assert(pThisCC->svga.pvFIFOExtCmdParam == NULL);
+
+ /* The screens must be reset on the FIFO thread, because they may use 3D resources. */
+ vmsvgaR3ResetScreens(pThis, pThisCC);
+ break;
+
+ case VMSVGA_FIFO_EXTCMD_TERMINATE:
+ Log(("vmsvgaR3FifoLoop: terminate the fifo thread.\n"));
+ Assert(pThisCC->svga.pvFIFOExtCmdParam == NULL);
+
+# ifdef VBOX_WITH_VMSVGA3D
+ /* The 3d subsystem must be shut down from the fifo thread. */
+ if (pThis->svga.f3DEnabled)
+ vmsvga3dTerminate(pThisCC);
+# endif
+ vmsvgaR3TerminateSvgaState(pThis, pThisCC);
+ break;
+
+ case VMSVGA_FIFO_EXTCMD_SAVESTATE:
+ {
+ Log(("vmsvgaR3FifoLoop: VMSVGA_FIFO_EXTCMD_SAVESTATE.\n"));
+ PSSMHANDLE pSSM = (PSSMHANDLE)pThisCC->svga.pvFIFOExtCmdParam;
+ AssertLogRelMsgBreak(RT_VALID_PTR(pSSM), ("pSSM=%p\n", pSSM));
+ vmsvgaR3SaveExecFifo(pDevIns->pHlpR3, pThisCC, pSSM);
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThis->svga.f3DEnabled)
+ {
+ if (vmsvga3dIsLegacyBackend(pThisCC))
+ vmsvga3dSaveExec(pDevIns, pThisCC, pSSM);
+# ifdef VMSVGA3D_DX
+ else
+ vmsvga3dDXSaveExec(pDevIns, pThisCC, pSSM);
+# endif
+ }
+# endif
+ break;
+ }
+
+ case VMSVGA_FIFO_EXTCMD_LOADSTATE:
+ {
+ Log(("vmsvgaR3FifoLoop: VMSVGA_FIFO_EXTCMD_LOADSTATE.\n"));
+ PVMSVGA_STATE_LOAD pLoadState = (PVMSVGA_STATE_LOAD)pThisCC->svga.pvFIFOExtCmdParam;
+ AssertLogRelMsgBreak(RT_VALID_PTR(pLoadState), ("pLoadState=%p\n", pLoadState));
+ vmsvgaR3LoadExecFifo(pDevIns->pHlpR3, pThis, pThisCC, pLoadState->pSSM, pLoadState->uVersion, pLoadState->uPass);
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThis->svga.f3DEnabled)
+ {
+ /* The following RT_OS_DARWIN code was in vmsvga3dLoadExec and therefore must be executed before each vmsvga3dLoadExec invocation. */
+# ifndef RT_OS_DARWIN /** @todo r=bird: this is normally done on the EMT, so for DARWIN we do that when loading saved state too now. See DevVGA-SVGA.cpp */
+ /* Must initialize now as the recreation calls below rely on an initialized 3d subsystem. */
+ vmsvgaR3PowerOnDevice(pDevIns, pThis, pThisCC, /*fLoadState=*/ true);
+# endif
+
+ if (vmsvga3dIsLegacyBackend(pThisCC))
+ vmsvga3dLoadExec(pDevIns, pThis, pThisCC, pLoadState->pSSM, pLoadState->uVersion, pLoadState->uPass);
+# ifdef VMSVGA3D_DX
+ else
+ vmsvga3dDXLoadExec(pDevIns, pThis, pThisCC, pLoadState->pSSM, pLoadState->uVersion, pLoadState->uPass);
+# endif
+ }
+# endif
+ break;
+ }
+
+ case VMSVGA_FIFO_EXTCMD_UPDATE_SURFACE_HEAP_BUFFERS:
+ {
+# ifdef VBOX_WITH_VMSVGA3D
+ uint32_t sid = (uint32_t)(uintptr_t)pThisCC->svga.pvFIFOExtCmdParam;
+ Log(("vmsvgaR3FifoLoop: VMSVGA_FIFO_EXTCMD_UPDATE_SURFACE_HEAP_BUFFERS sid=%#x\n", sid));
+ vmsvga3dUpdateHeapBuffersForSurfaces(pThisCC, sid);
+# endif
+ break;
+ }
+
+
+ default:
+ AssertLogRelMsgFailed(("uExtCmd=%#x pvFIFOExtCmdParam=%p\n", uExtCmd, pThisCC->svga.pvFIFOExtCmdParam));
+ break;
+ }
+
+ /*
+ * Signal the end of the external command.
+ */
+ pThisCC->svga.pvFIFOExtCmdParam = NULL;
+ pThis->svga.u8FIFOExtCommand = VMSVGA_FIFO_EXTCMD_NONE;
+ ASMMemoryFence(); /* paranoia^2 */
+ int rc = RTSemEventSignal(pThisCC->svga.hFIFOExtCmdSem);
+ AssertLogRelRC(rc);
+}
+
+/**
+ * Worker for vmsvgaR3Destruct, vmsvgaR3Reset, vmsvgaR3Save and vmsvgaR3Load for
+ * doing a job on the FIFO thread (even when it's officially suspended).
+ *
+ * @returns VBox status code (fully asserted).
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ * @param uExtCmd The command to execute on the FIFO thread.
+ * @param pvParam Pointer to command parameters.
+ * @param cMsWait The time to wait for the command, given in
+ * milliseconds.
+ */
+static int vmsvgaR3RunExtCmdOnFifoThread(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
+ uint8_t uExtCmd, void *pvParam, RTMSINTERVAL cMsWait)
+{
+ Assert(cMsWait >= RT_MS_1SEC * 5);
+ AssertLogRelMsg(pThis->svga.u8FIFOExtCommand == VMSVGA_FIFO_EXTCMD_NONE,
+ ("old=%d new=%d\n", pThis->svga.u8FIFOExtCommand, uExtCmd));
+
+ int rc;
+ PPDMTHREAD pThread = pThisCC->svga.pFIFOIOThread;
+ PDMTHREADSTATE enmState = pThread->enmState;
+ if (enmState == PDMTHREADSTATE_SUSPENDED)
+ {
+ /*
+ * The thread is suspended, we have to temporarily wake it up so it can
+ * perform the task.
+ * (We ASSUME not racing code here, both wrt thread state and ext commands.)
+ */
+ Log(("vmsvgaR3RunExtCmdOnFifoThread: uExtCmd=%d enmState=SUSPENDED\n", uExtCmd));
+ /* Post the request. */
+ pThis->svga.fFifoExtCommandWakeup = true;
+ pThisCC->svga.pvFIFOExtCmdParam = pvParam;
+ pThis->svga.u8FIFOExtCommand = uExtCmd;
+ ASMMemoryFence(); /* paranoia^3 */
+
+ /* Resume the thread. */
+ rc = PDMDevHlpThreadResume(pDevIns, pThread);
+ AssertLogRelRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /* Wait. Take care in case the semaphore was already posted (same as below). */
+ rc = RTSemEventWait(pThisCC->svga.hFIFOExtCmdSem, cMsWait);
+ if ( rc == VINF_SUCCESS
+ && pThis->svga.u8FIFOExtCommand == uExtCmd)
+ rc = RTSemEventWait(pThisCC->svga.hFIFOExtCmdSem, cMsWait);
+ AssertLogRelMsg(pThis->svga.u8FIFOExtCommand != uExtCmd || RT_FAILURE_NP(rc),
+ ("%#x %Rrc\n", pThis->svga.u8FIFOExtCommand, rc));
+
+ /* suspend the thread */
+ pThis->svga.fFifoExtCommandWakeup = false;
+ int rc2 = PDMDevHlpThreadSuspend(pDevIns, pThread);
+ AssertLogRelRC(rc2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ pThis->svga.fFifoExtCommandWakeup = false;
+ pThisCC->svga.pvFIFOExtCmdParam = NULL;
+ }
+ else if (enmState == PDMTHREADSTATE_RUNNING)
+ {
+ /*
+ * The thread is running, should only happen during reset and vmsvga3dsfc.
+ * We ASSUME not racing code here, both wrt thread state and ext commands.
+ */
+ Log(("vmsvgaR3RunExtCmdOnFifoThread: uExtCmd=%d enmState=RUNNING\n", uExtCmd));
+ Assert(uExtCmd == VMSVGA_FIFO_EXTCMD_RESET || uExtCmd == VMSVGA_FIFO_EXTCMD_UPDATE_SURFACE_HEAP_BUFFERS || uExtCmd == VMSVGA_FIFO_EXTCMD_POWEROFF);
+
+ /* Post the request. */
+ pThisCC->svga.pvFIFOExtCmdParam = pvParam;
+ pThis->svga.u8FIFOExtCommand = uExtCmd;
+ ASMMemoryFence(); /* paranoia^2 */
+ rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+ AssertLogRelRC(rc);
+
+ /* Wait. Take care in case the semaphore was already posted (same as above). */
+ rc = RTSemEventWait(pThisCC->svga.hFIFOExtCmdSem, cMsWait);
+ if ( rc == VINF_SUCCESS
+ && pThis->svga.u8FIFOExtCommand == uExtCmd)
+ rc = RTSemEventWait(pThisCC->svga.hFIFOExtCmdSem, cMsWait); /* it was already posted, retry the wait. */
+ AssertLogRelMsg(pThis->svga.u8FIFOExtCommand != uExtCmd || RT_FAILURE_NP(rc),
+ ("%#x %Rrc\n", pThis->svga.u8FIFOExtCommand, rc));
+
+ pThisCC->svga.pvFIFOExtCmdParam = NULL;
+ }
+ else
+ {
+ /*
+ * Something is wrong with the thread!
+ */
+ AssertLogRelMsgFailed(("uExtCmd=%d enmState=%d\n", uExtCmd, enmState));
+ rc = VERR_INVALID_STATE;
+ }
+ return rc;
+}
+
+
+/**
+ * Marks the FIFO non-busy, notifying any waiting EMTs.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ * @param pSVGAState Pointer to the ring-3 only SVGA state data.
+ * @param offFifoMin The start byte offset of the command FIFO.
+ */
+static void vmsvgaR3FifoSetNotBusy(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, PVMSVGAR3STATE pSVGAState, uint32_t offFifoMin)
+{
+ ASMAtomicAndU32(&pThis->svga.fBusy, ~(VMSVGA_BUSY_F_FIFO | VMSVGA_BUSY_F_EMT_FORCE));
+ if (VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_BUSY, offFifoMin))
+ vmsvgaHCSafeFifoBusyRegUpdate(pThis, pThisCC, pThis->svga.fBusy != 0);
+
+ /* Wake up any waiting EMTs. */
+ if (pSVGAState->cBusyDelayedEmts > 0)
+ {
+# ifdef VMSVGA_USE_EMT_HALT_CODE
+ VMCPUID idCpu = VMCpuSetFindLastPresentInternal(&pSVGAState->BusyDelayedEmts);
+ if (idCpu != NIL_VMCPUID)
+ {
+ PDMDevHlpVMNotifyCpuDeviceReady(pDevIns, idCpu);
+ while (idCpu-- > 0)
+ if (VMCPUSET_IS_PRESENT(&pSVGAState->BusyDelayedEmts, idCpu))
+ PDMDevHlpVMNotifyCpuDeviceReady(pDevIns, idCpu);
+ }
+# else
+ int rc2 = RTSemEventMultiSignal(pSVGAState->hBusyDelayedEmts);
+ AssertRC(rc2);
+# endif
+ }
+}
+
+/**
+ * Reads (more) payload into the command buffer.
+ *
+ * @returns pbBounceBuf on success
+ * @retval (void *)1 if the thread was requested to stop.
+ * @retval NULL on FIFO error.
+ *
+ * @param cbPayloadReq The number of bytes of payload requested.
+ * @param pFIFO The FIFO.
+ * @param offCurrentCmd The FIFO byte offset of the current command.
+ * @param offFifoMin The start byte offset of the command FIFO.
+ * @param offFifoMax The end byte offset of the command FIFO.
+ * @param pbBounceBuf The bounch buffer. Same size as the entire FIFO, so
+ * always sufficient size.
+ * @param pcbAlreadyRead How much payload we've already read into the bounce
+ * buffer. (We will NEVER re-read anything.)
+ * @param pThread The calling PDM thread handle.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pSVGAState Pointer to the ring-3 only SVGA state data. For
+ * statistics collection.
+ * @param pDevIns The device instance.
+ */
+static void *vmsvgaR3FifoGetCmdPayload(uint32_t cbPayloadReq, uint32_t RT_UNTRUSTED_VOLATILE_GUEST *pFIFO,
+ uint32_t offCurrentCmd, uint32_t offFifoMin, uint32_t offFifoMax,
+ uint8_t *pbBounceBuf, uint32_t *pcbAlreadyRead,
+ PPDMTHREAD pThread, PVGASTATE pThis, PVMSVGAR3STATE pSVGAState, PPDMDEVINS pDevIns)
+{
+ Assert(pbBounceBuf);
+ Assert(pcbAlreadyRead);
+ Assert(offFifoMin < offFifoMax);
+ Assert(offCurrentCmd >= offFifoMin && offCurrentCmd < offFifoMax);
+ Assert(offFifoMax <= pThis->svga.cbFIFO);
+
+ /*
+ * Check if the requested payload size has already been satisfied .
+ * .
+ * When called to read more, the caller is responsible for making sure the .
+ * new command size (cbRequsted) never is smaller than what has already .
+ * been read.
+ */
+ uint32_t cbAlreadyRead = *pcbAlreadyRead;
+ if (cbPayloadReq <= cbAlreadyRead)
+ {
+ AssertLogRelReturn(cbPayloadReq == cbAlreadyRead, NULL);
+ return pbBounceBuf;
+ }
+
+ /*
+ * Commands bigger than the fifo buffer are invalid.
+ */
+ uint32_t const cbFifoCmd = offFifoMax - offFifoMin;
+ AssertMsgReturnStmt(cbPayloadReq <= cbFifoCmd, ("cbPayloadReq=%#x cbFifoCmd=%#x\n", cbPayloadReq, cbFifoCmd),
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoErrors),
+ NULL);
+
+ /*
+ * Move offCurrentCmd past the command dword.
+ */
+ offCurrentCmd += sizeof(uint32_t);
+ if (offCurrentCmd >= offFifoMax)
+ offCurrentCmd = offFifoMin;
+
+ /*
+ * Do we have sufficient payload data available already?
+ * The host should not read beyond [SVGA_FIFO_NEXT_CMD], therefore '>=' in the condition below.
+ */
+ uint32_t cbAfter, cbBefore;
+ uint32_t offNextCmd = pFIFO[SVGA_FIFO_NEXT_CMD];
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ if (offNextCmd >= offCurrentCmd)
+ {
+ if (RT_LIKELY(offNextCmd < offFifoMax))
+ cbAfter = offNextCmd - offCurrentCmd;
+ else
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoErrors);
+ LogRelMax(16, ("vmsvgaR3FifoGetCmdPayload: Invalid offNextCmd=%#x (offFifoMin=%#x offFifoMax=%#x)\n",
+ offNextCmd, offFifoMin, offFifoMax));
+ cbAfter = offFifoMax - offCurrentCmd;
+ }
+ cbBefore = 0;
+ }
+ else
+ {
+ cbAfter = offFifoMax - offCurrentCmd;
+ if (offNextCmd >= offFifoMin)
+ cbBefore = offNextCmd - offFifoMin;
+ else
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoErrors);
+ LogRelMax(16, ("vmsvgaR3FifoGetCmdPayload: Invalid offNextCmd=%#x (offFifoMin=%#x offFifoMax=%#x)\n",
+ offNextCmd, offFifoMin, offFifoMax));
+ cbBefore = 0;
+ }
+ }
+ if (cbAfter + cbBefore < cbPayloadReq)
+ {
+ /*
+ * Insufficient, must wait for it to arrive.
+ */
+/** @todo Should clear the busy flag here to maybe encourage the guest to wake us up. */
+ STAM_REL_PROFILE_START(&pSVGAState->StatFifoStalls, Stall);
+ for (uint32_t i = 0;; i++)
+ {
+ if (pThread->enmState != PDMTHREADSTATE_RUNNING)
+ {
+ STAM_REL_PROFILE_STOP(&pSVGAState->StatFifoStalls, Stall);
+ return (void *)(uintptr_t)1;
+ }
+ Log(("Guest still copying (%x vs %x) current %x next %x stop %x loop %u; sleep a bit\n",
+ cbPayloadReq, cbAfter + cbBefore, offCurrentCmd, offNextCmd, pFIFO[SVGA_FIFO_STOP], i));
+
+ PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->svga.hFIFORequestSem, i < 16 ? 1 : 2);
+
+ offNextCmd = pFIFO[SVGA_FIFO_NEXT_CMD];
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ if (offNextCmd >= offCurrentCmd)
+ {
+ cbAfter = RT_MIN(offNextCmd, offFifoMax) - offCurrentCmd;
+ cbBefore = 0;
+ }
+ else
+ {
+ cbAfter = offFifoMax - offCurrentCmd;
+ cbBefore = RT_MAX(offNextCmd, offFifoMin) - offFifoMin;
+ }
+
+ if (cbAfter + cbBefore >= cbPayloadReq)
+ break;
+ }
+ STAM_REL_PROFILE_STOP(&pSVGAState->StatFifoStalls, Stall);
+ }
+
+ /*
+ * Copy out the memory and update what pcbAlreadyRead points to.
+ */
+ if (cbAfter >= cbPayloadReq)
+ memcpy(pbBounceBuf + cbAlreadyRead,
+ (uint8_t *)pFIFO + offCurrentCmd + cbAlreadyRead,
+ cbPayloadReq - cbAlreadyRead);
+ else
+ {
+ LogFlow(("Split data buffer at %x (%u-%u)\n", offCurrentCmd, cbAfter, cbBefore));
+ if (cbAlreadyRead < cbAfter)
+ {
+ memcpy(pbBounceBuf + cbAlreadyRead,
+ (uint8_t *)pFIFO + offCurrentCmd + cbAlreadyRead,
+ cbAfter - cbAlreadyRead);
+ cbAlreadyRead = cbAfter;
+ }
+ memcpy(pbBounceBuf + cbAlreadyRead,
+ (uint8_t *)pFIFO + offFifoMin + cbAlreadyRead - cbAfter,
+ cbPayloadReq - cbAlreadyRead);
+ }
+ *pcbAlreadyRead = cbPayloadReq;
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ return pbBounceBuf;
+}
+
+
+/**
+ * Sends cursor position and visibility information from the FIFO to the front-end.
+ * @returns SVGA_FIFO_CURSOR_COUNT value used.
+ */
+static uint32_t
+vmsvgaR3FifoUpdateCursor(PVGASTATECC pThisCC, PVMSVGAR3STATE pSVGAState, uint32_t RT_UNTRUSTED_VOLATILE_GUEST *pFIFO,
+ uint32_t offFifoMin, uint32_t uCursorUpdateCount,
+ uint32_t *pxLast, uint32_t *pyLast, uint32_t *pfLastVisible)
+{
+ /*
+ * Check if the cursor update counter has changed and try get a stable
+ * set of values if it has. This is race-prone, especially consindering
+ * the screen ID, but little we can do about that.
+ */
+ uint32_t x, y, fVisible, idScreen;
+ for (uint32_t i = 0; ; i++)
+ {
+ x = pFIFO[SVGA_FIFO_CURSOR_X];
+ y = pFIFO[SVGA_FIFO_CURSOR_Y];
+ fVisible = pFIFO[SVGA_FIFO_CURSOR_ON];
+ idScreen = VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_CURSOR_SCREEN_ID, offFifoMin)
+ ? pFIFO[SVGA_FIFO_CURSOR_SCREEN_ID] : SVGA_ID_INVALID;
+ if ( uCursorUpdateCount == pFIFO[SVGA_FIFO_CURSOR_COUNT]
+ || i > 3)
+ break;
+ if (i == 0)
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoCursorFetchAgain);
+ ASMNopPause();
+ uCursorUpdateCount = pFIFO[SVGA_FIFO_CURSOR_COUNT];
+ }
+
+ /*
+ * Check if anything has changed, as calling into pDrv is not light-weight.
+ */
+ if ( *pxLast == x
+ && *pyLast == y
+ && (idScreen != SVGA_ID_INVALID || *pfLastVisible == fVisible))
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoCursorNoChange);
+ else
+ {
+ /*
+ * Detected changes.
+ *
+ * We handle global, not per-screen visibility information by sending
+ * pfnVBVAMousePointerShape without shape data.
+ */
+ *pxLast = x;
+ *pyLast = y;
+ uint32_t fFlags = VBVA_CURSOR_VALID_DATA;
+ if (idScreen != SVGA_ID_INVALID)
+ fFlags |= VBVA_CURSOR_SCREEN_RELATIVE;
+ else if (*pfLastVisible != fVisible)
+ {
+ LogRel2(("vmsvgaR3FifoUpdateCursor: fVisible %d fLastVisible %d (%d,%d)\n", fVisible, *pfLastVisible, x, y));
+ *pfLastVisible = fVisible;
+ pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, RT_BOOL(fVisible), false, 0, 0, 0, 0, NULL);
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoCursorVisiblity);
+ }
+ pThisCC->pDrv->pfnVBVAReportCursorPosition(pThisCC->pDrv, fFlags, idScreen, x, y);
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoCursorPosition);
+ }
+
+ /*
+ * Update done. Signal this to the guest.
+ */
+ pFIFO[SVGA_FIFO_CURSOR_LAST_UPDATED] = uCursorUpdateCount;
+
+ return uCursorUpdateCount;
+}
+
+
+/**
+ * Checks if there is work to be done, either cursor updating or FIFO commands.
+ *
+ * @returns true if pending work, false if not.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ * @param uLastCursorCount The last cursor update counter value.
+ */
+DECLINLINE(bool) vmsvgaR3FifoHasWork(PVGASTATECC pThisCC, uint32_t uLastCursorCount)
+{
+ /* If FIFO does not exist than there is nothing to do. Command buffers also require the enabled FIFO. */
+ uint32_t RT_UNTRUSTED_VOLATILE_GUEST * const pFIFO = pThisCC->svga.pau32FIFO;
+ AssertReturn(pFIFO, false);
+
+ if (vmsvgaR3CmdBufHasWork(pThisCC))
+ return true;
+
+ if (pFIFO[SVGA_FIFO_NEXT_CMD] != pFIFO[SVGA_FIFO_STOP])
+ return true;
+
+ if ( uLastCursorCount != pFIFO[SVGA_FIFO_CURSOR_COUNT]
+ && VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_CURSOR_LAST_UPDATED, pFIFO[SVGA_FIFO_MIN]))
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Called by the VGA refresh timer to wake up the FIFO thread when needed.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+void vmsvgaR3FifoWatchdogTimer(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ /* Caller already checked pThis->svga.fFIFOThreadSleeping, so we only have
+ to recheck it before doing the signalling. */
+ if ( vmsvgaR3FifoHasWork(pThisCC, ASMAtomicReadU32(&pThis->svga.uLastCursorUpdateCount))
+ && pThis->svga.fFIFOThreadSleeping
+ && !ASMAtomicReadBool(&pThis->svga.fBadGuest))
+ {
+ int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+ AssertRC(rc);
+ STAM_REL_COUNTER_INC(&pThisCC->svga.pSvgaR3State->StatFifoWatchdogWakeUps);
+ }
+}
+
+
+/**
+ * Called by the FIFO thread to process pending actions.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+void vmsvgaR3FifoPendingActions(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ RT_NOREF(pDevIns);
+
+ /* Currently just mode changes. */
+ if (ASMBitTestAndClear(&pThis->svga.u32ActionFlags, VMSVGA_ACTION_CHANGEMODE_BIT))
+ {
+ vmsvgaR3ChangeMode(pThis, pThisCC);
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThisCC->svga.p3dState != NULL)
+ vmsvga3dChangeMode(pThisCC);
+# endif
+ }
+}
+
+
+/*
+ * These two macros are put outside vmsvgaR3FifoLoop because doxygen gets confused,
+ * even the latest version, and thinks we're documenting vmsvgaR3FifoLoop. Sigh.
+ */
+/** @def VMSVGAFIFO_GET_CMD_BUFFER_BREAK
+ * Macro for shortening calls to vmsvgaR3FifoGetCmdPayload.
+ *
+ * Will break out of the switch on failure.
+ * Will restart and quit the loop if the thread was requested to stop.
+ *
+ * @param a_PtrVar Request variable pointer.
+ * @param a_Type Request typedef (not pointer) for casting.
+ * @param a_cbPayloadReq How much payload to fetch.
+ * @remarks Accesses a bunch of variables in the current scope!
+ */
+# define VMSVGAFIFO_GET_CMD_BUFFER_BREAK(a_PtrVar, a_Type, a_cbPayloadReq) \
+ if (1) { \
+ (a_PtrVar) = (a_Type *)vmsvgaR3FifoGetCmdPayload((a_cbPayloadReq), pFIFO, offCurrentCmd, offFifoMin, offFifoMax, \
+ pbBounceBuf, &cbPayload, pThread, pThis, pSVGAState, pDevIns); \
+ if (RT_UNLIKELY((uintptr_t)(a_PtrVar) < 2)) { if ((uintptr_t)(a_PtrVar) == 1) continue; break; } \
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); \
+ } else do {} while (0)
+/* @def VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK
+ * Macro for shortening calls to vmsvgaR3FifoGetCmdPayload for refetching the
+ * buffer after figuring out the actual command size.
+ *
+ * Will break out of the switch on failure.
+ *
+ * @param a_PtrVar Request variable pointer.
+ * @param a_Type Request typedef (not pointer) for casting.
+ * @param a_cbPayloadReq How much payload to fetch.
+ * @remarks Accesses a bunch of variables in the current scope!
+ */
+# define VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(a_PtrVar, a_Type, a_cbPayloadReq) \
+ if (1) { \
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(a_PtrVar, a_Type, a_cbPayloadReq); \
+ } else do {} while (0)
+
+/**
+ * @callback_method_impl{PFNPDMTHREADDEV, The async FIFO handling thread.}
+ */
+static DECLCALLBACK(int) vmsvgaR3FifoLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ int rc;
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ /*
+ * Special mode where we only execute an external command and the go back
+ * to being suspended. Currently, all ext cmds ends up here, with the reset
+ * one also being eligble for runtime execution further down as well.
+ */
+ if (pThis->svga.fFifoExtCommandWakeup)
+ {
+ vmsvgaR3FifoHandleExtCmd(pDevIns, pThis, pThisCC);
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ if (pThis->svga.u8FIFOExtCommand == VMSVGA_FIFO_EXTCMD_NONE)
+ PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->svga.hFIFORequestSem, RT_MS_1MIN);
+ else
+ vmsvgaR3FifoHandleExtCmd(pDevIns, pThis, pThisCC);
+ return VINF_SUCCESS;
+ }
+
+
+ /*
+ * Signal the semaphore to make sure we don't wait for 250ms after a
+ * suspend & resume scenario (see vmsvgaR3FifoGetCmdPayload).
+ */
+ PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+
+ /*
+ * Allocate a bounce buffer for command we get from the FIFO.
+ * (All code must return via the end of the function to free this buffer.)
+ */
+ uint8_t *pbBounceBuf = (uint8_t *)RTMemAllocZ(pThis->svga.cbFIFO);
+ AssertReturn(pbBounceBuf, VERR_NO_MEMORY);
+
+ /*
+ * Polling/sleep interval config.
+ *
+ * We wait for an a short interval if the guest has recently given us work
+ * to do, but the interval increases the longer we're kept idle. Once we've
+ * reached the refresh timer interval, we'll switch to extended waits,
+ * depending on it or the guest to kick us into action when needed.
+ *
+ * Should the refresh time go fishing, we'll just continue increasing the
+ * sleep length till we reaches the 250 ms max after about 16 seconds.
+ */
+ RTMSINTERVAL const cMsMinSleep = 16;
+ RTMSINTERVAL const cMsIncSleep = 2;
+ RTMSINTERVAL const cMsMaxSleep = 250;
+ RTMSINTERVAL const cMsExtendedSleep = 15 * RT_MS_1SEC; /* Regular paranoia dictates that this cannot be indefinite. */
+ RTMSINTERVAL cMsSleep = cMsMaxSleep;
+
+ /*
+ * Cursor update state (SVGA_FIFO_CAP_CURSOR_BYPASS_3).
+ *
+ * Initialize with values that will detect an update from the guest.
+ * Make sure that if the guest never updates the cursor position, then the device does not report it.
+ * The guest has to change the value of uLastCursorUpdateCount, when the cursor position is actually updated.
+ * xLastCursor, yLastCursor and fLastCursorVisible are set to report the first update.
+ */
+ uint32_t RT_UNTRUSTED_VOLATILE_GUEST * const pFIFO = pThisCC->svga.pau32FIFO;
+ pThis->svga.uLastCursorUpdateCount = pFIFO[SVGA_FIFO_CURSOR_COUNT];
+ uint32_t xLastCursor = ~pFIFO[SVGA_FIFO_CURSOR_X];
+ uint32_t yLastCursor = ~pFIFO[SVGA_FIFO_CURSOR_Y];
+ uint32_t fLastCursorVisible = ~pFIFO[SVGA_FIFO_CURSOR_ON];
+
+ /*
+ * The FIFO loop.
+ */
+ LogFlow(("vmsvgaR3FifoLoop: started loop\n"));
+ bool fBadOrDisabledFifo = ASMAtomicReadBool(&pThis->svga.fBadGuest);
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+# if defined(RT_OS_DARWIN) && defined(VBOX_WITH_VMSVGA3D)
+ /*
+ * Should service the run loop every so often.
+ */
+ if (pThis->svga.f3DEnabled)
+ vmsvga3dCocoaServiceRunLoop();
+# endif
+
+ /* First check any pending actions. */
+ vmsvgaR3FifoPendingActions(pDevIns, pThis, pThisCC);
+
+ /*
+ * Unless there's already work pending, go to sleep for a short while.
+ * (See polling/sleep interval config above.)
+ */
+ if ( fBadOrDisabledFifo
+ || !vmsvgaR3FifoHasWork(pThisCC, pThis->svga.uLastCursorUpdateCount))
+ {
+ ASMAtomicWriteBool(&pThis->svga.fFIFOThreadSleeping, true);
+ Assert(pThis->cMilliesRefreshInterval > 0);
+ if (cMsSleep < pThis->cMilliesRefreshInterval)
+ rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->svga.hFIFORequestSem, cMsSleep);
+ else
+ {
+# ifdef VMSVGA_USE_FIFO_ACCESS_HANDLER
+ int rc2 = PDMDevHlpPGMHandlerPhysicalReset(pDevIns, pThis->svga.GCPhysFIFO);
+ AssertRC(rc2); /* No break. Racing EMTs unmapping and remapping the region. */
+# endif
+ if ( !fBadOrDisabledFifo
+ && vmsvgaR3FifoHasWork(pThisCC, pThis->svga.uLastCursorUpdateCount))
+ rc = VINF_SUCCESS;
+ else
+ {
+ STAM_REL_PROFILE_START(&pSVGAState->StatFifoExtendedSleep, Acc);
+ rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->svga.hFIFORequestSem, cMsExtendedSleep);
+ STAM_REL_PROFILE_STOP(&pSVGAState->StatFifoExtendedSleep, Acc);
+ }
+ }
+ ASMAtomicWriteBool(&pThis->svga.fFIFOThreadSleeping, false);
+ AssertBreak(RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED);
+ if (pThread->enmState != PDMTHREADSTATE_RUNNING)
+ {
+ LogFlow(("vmsvgaR3FifoLoop: thread state %x\n", pThread->enmState));
+ break;
+ }
+ }
+ else
+ rc = VINF_SUCCESS;
+ fBadOrDisabledFifo = ASMAtomicReadBool(&pThis->svga.fBadGuest);
+ if (rc == VERR_TIMEOUT)
+ {
+ if (!vmsvgaR3FifoHasWork(pThisCC, pThis->svga.uLastCursorUpdateCount))
+ {
+ cMsSleep = RT_MIN(cMsSleep + cMsIncSleep, cMsMaxSleep);
+ continue;
+ }
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoTodoTimeout);
+
+ Log(("vmsvgaR3FifoLoop: timeout\n"));
+ }
+ else if (vmsvgaR3FifoHasWork(pThisCC, pThis->svga.uLastCursorUpdateCount))
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoTodoWoken);
+ cMsSleep = cMsMinSleep;
+
+ Log(("vmsvgaR3FifoLoop: enabled=%d configured=%d busy=%d\n", pThis->svga.fEnabled, pThis->svga.fConfigured, pFIFO[SVGA_FIFO_BUSY]));
+ Log(("vmsvgaR3FifoLoop: min %x max %x\n", pFIFO[SVGA_FIFO_MIN], pFIFO[SVGA_FIFO_MAX]));
+ Log(("vmsvgaR3FifoLoop: next %x stop %x\n", pFIFO[SVGA_FIFO_NEXT_CMD], pFIFO[SVGA_FIFO_STOP]));
+
+ /*
+ * Handle external commands (currently only reset).
+ */
+ if (pThis->svga.u8FIFOExtCommand != VMSVGA_FIFO_EXTCMD_NONE)
+ {
+ vmsvgaR3FifoHandleExtCmd(pDevIns, pThis, pThisCC);
+ continue;
+ }
+
+ /*
+ * If guest misbehaves, then do nothing.
+ */
+ if (ASMAtomicReadBool(&pThis->svga.fBadGuest))
+ {
+ vmsvgaR3FifoSetNotBusy(pDevIns, pThis, pThisCC, pSVGAState, pFIFO[SVGA_FIFO_MIN]);
+ cMsSleep = cMsExtendedSleep;
+ LogRelMax(1, ("VMSVGA: FIFO processing stopped because of the guest misbehavior\n"));
+ continue;
+ }
+
+ /*
+ * The device must be enabled and configured.
+ */
+ if ( !pThis->svga.fEnabled
+ || !pThis->svga.fConfigured)
+ {
+ vmsvgaR3FifoSetNotBusy(pDevIns, pThis, pThisCC, pSVGAState, pFIFO[SVGA_FIFO_MIN]);
+ fBadOrDisabledFifo = true;
+ cMsSleep = cMsMaxSleep; /* cheat */
+ continue;
+ }
+
+ /*
+ * Get and check the min/max values. We ASSUME that they will remain
+ * unchanged while we process requests. A further ASSUMPTION is that
+ * the guest won't mess with SVGA_FIFO_NEXT_CMD while we're busy, so
+ * we don't read it back while in the loop.
+ */
+ uint32_t const offFifoMin = pFIFO[SVGA_FIFO_MIN];
+ uint32_t const offFifoMax = pFIFO[SVGA_FIFO_MAX];
+ uint32_t offCurrentCmd = pFIFO[SVGA_FIFO_STOP];
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ if (RT_UNLIKELY( !VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_STOP, offFifoMin)
+ || offFifoMax <= offFifoMin
+ || offFifoMax > pThis->svga.cbFIFO
+ || (offFifoMax & 3) != 0
+ || (offFifoMin & 3) != 0
+ || offCurrentCmd < offFifoMin
+ || offCurrentCmd > offFifoMax))
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoErrors);
+ LogRelMax(8, ("vmsvgaR3FifoLoop: Bad fifo: min=%#x stop=%#x max=%#x\n", offFifoMin, offCurrentCmd, offFifoMax));
+ vmsvgaR3FifoSetNotBusy(pDevIns, pThis, pThisCC, pSVGAState, offFifoMin);
+ fBadOrDisabledFifo = true;
+ continue;
+ }
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ if (RT_UNLIKELY(offCurrentCmd & 3))
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoErrors);
+ LogRelMax(8, ("vmsvgaR3FifoLoop: Misaligned offCurrentCmd=%#x?\n", offCurrentCmd));
+ offCurrentCmd &= ~UINT32_C(3);
+ }
+
+ /*
+ * Update the cursor position before we start on the FIFO commands.
+ */
+ /** @todo do we need to check whether the guest disabled the SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability here? */
+ if (VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_CURSOR_LAST_UPDATED, offFifoMin))
+ {
+ uint32_t const uCursorUpdateCount = pFIFO[SVGA_FIFO_CURSOR_COUNT];
+ if (uCursorUpdateCount == pThis->svga.uLastCursorUpdateCount)
+ { /* halfways likely */ }
+ else
+ {
+ uint32_t const uNewCount = vmsvgaR3FifoUpdateCursor(pThisCC, pSVGAState, pFIFO, offFifoMin, uCursorUpdateCount,
+ &xLastCursor, &yLastCursor, &fLastCursorVisible);
+ ASMAtomicWriteU32(&pThis->svga.uLastCursorUpdateCount, uNewCount);
+ }
+ }
+
+ /*
+ * Mark the FIFO as busy.
+ */
+ ASMAtomicWriteU32(&pThis->svga.fBusy, VMSVGA_BUSY_F_FIFO); // Clears VMSVGA_BUSY_F_EMT_FORCE!
+ if (VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_BUSY, offFifoMin))
+ ASMAtomicWriteU32(&pFIFO[SVGA_FIFO_BUSY], true);
+
+ /*
+ * Process all submitted command buffers.
+ */
+ vmsvgaR3CmdBufProcessBuffers(pDevIns, pThis, pThisCC, pThread);
+
+ /*
+ * Execute all queued FIFO commands.
+ * Quit if pending external command or changes in the thread state.
+ */
+ bool fDone = false;
+ while ( !(fDone = (pFIFO[SVGA_FIFO_NEXT_CMD] == offCurrentCmd))
+ && pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ uint32_t cbPayload = 0;
+ uint32_t u32IrqStatus = 0;
+
+ Assert(offCurrentCmd < offFifoMax && offCurrentCmd >= offFifoMin);
+
+ /* First check any pending actions. */
+ vmsvgaR3FifoPendingActions(pDevIns, pThis, pThisCC);
+
+ /* Check for pending external commands (reset). */
+ if (pThis->svga.u8FIFOExtCommand != VMSVGA_FIFO_EXTCMD_NONE)
+ break;
+
+ /*
+ * Process the command.
+ */
+ /* 'enmCmdId' is actually a SVGAFifoCmdId. It is treated as uint32_t in order to avoid a compiler
+ * warning. Because we implement some obsolete and deprecated commands, which are not included in
+ * the SVGAFifoCmdId enum in the VMSVGA headers anymore.
+ */
+ uint32_t const enmCmdId = pFIFO[offCurrentCmd / sizeof(uint32_t)];
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ LogFlow(("vmsvgaR3FifoLoop: FIFO command (iCmd=0x%x) %s %d\n",
+ offCurrentCmd / sizeof(uint32_t), vmsvgaR3FifoCmdToString(enmCmdId), enmCmdId));
+ switch (enmCmdId)
+ {
+ case SVGA_CMD_INVALID_CMD:
+ /* Nothing to do. */
+ STAM_REL_COUNTER_INC(&pSVGAState->StatR3CmdInvalidCmd);
+ break;
+
+ case SVGA_CMD_FENCE:
+ {
+ SVGAFifoCmdFence *pCmdFence;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmdFence, SVGAFifoCmdFence, sizeof(*pCmdFence));
+ STAM_REL_COUNTER_INC(&pSVGAState->StatR3CmdFence);
+ if (VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_FENCE, offFifoMin))
+ {
+ Log(("vmsvgaR3FifoLoop: SVGA_CMD_FENCE %#x\n", pCmdFence->fence));
+ pFIFO[SVGA_FIFO_FENCE] = pCmdFence->fence;
+
+ if (pThis->svga.u32IrqMask & SVGA_IRQFLAG_ANY_FENCE)
+ {
+ Log(("vmsvgaR3FifoLoop: any fence irq\n"));
+ u32IrqStatus |= SVGA_IRQFLAG_ANY_FENCE;
+ }
+ else
+ if ( VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_FENCE_GOAL, offFifoMin)
+ && (pThis->svga.u32IrqMask & SVGA_IRQFLAG_FENCE_GOAL)
+ && pFIFO[SVGA_FIFO_FENCE_GOAL] == pCmdFence->fence)
+ {
+ Log(("vmsvgaR3FifoLoop: fence goal reached irq (fence=%#x)\n", pCmdFence->fence));
+ u32IrqStatus |= SVGA_IRQFLAG_FENCE_GOAL;
+ }
+ }
+ else
+ Log(("SVGA_CMD_FENCE is bogus when offFifoMin is %#x!\n", offFifoMin));
+ break;
+ }
+
+ case SVGA_CMD_UPDATE:
+ {
+ SVGAFifoCmdUpdate *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdUpdate, sizeof(*pCmd));
+ vmsvgaR3CmdUpdate(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_UPDATE_VERBOSE:
+ {
+ SVGAFifoCmdUpdateVerbose *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdUpdateVerbose, sizeof(*pCmd));
+ vmsvgaR3CmdUpdateVerbose(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DEFINE_CURSOR:
+ {
+ /* Followed by bitmap data. */
+ SVGAFifoCmdDefineCursor *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineCursor, sizeof(*pCmd));
+
+ /* Figure out the size of the bitmap data. */
+ ASSERT_GUEST_BREAK(pCmd->height < 2048 && pCmd->width < 2048);
+ ASSERT_GUEST_BREAK(pCmd->andMaskDepth <= 32);
+ ASSERT_GUEST_BREAK(pCmd->xorMaskDepth <= 32);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ uint32_t const cbAndLine = RT_ALIGN_32(pCmd->width * (pCmd->andMaskDepth + (pCmd->andMaskDepth == 15)), 32) / 8;
+ uint32_t const cbAndMask = cbAndLine * pCmd->height;
+ uint32_t const cbXorLine = RT_ALIGN_32(pCmd->width * (pCmd->xorMaskDepth + (pCmd->xorMaskDepth == 15)), 32) / 8;
+ uint32_t const cbXorMask = cbXorLine * pCmd->height;
+
+ uint32_t const cbCmd = sizeof(SVGAFifoCmdDefineCursor) + cbAndMask + cbXorMask;
+ VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineCursor, cbCmd);
+ vmsvgaR3CmdDefineCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DEFINE_ALPHA_CURSOR:
+ {
+ /* Followed by bitmap data. */
+ SVGAFifoCmdDefineAlphaCursor *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineAlphaCursor, sizeof(*pCmd));
+
+ /* Figure out the size of the bitmap data. */
+ ASSERT_GUEST_BREAK(pCmd->height < 2048 && pCmd->width < 2048);
+
+ uint32_t const cbCmd = sizeof(SVGAFifoCmdDefineAlphaCursor) + pCmd->width * pCmd->height * sizeof(uint32_t) /* 32-bit BRGA format */;
+ VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineAlphaCursor, cbCmd);
+ vmsvgaR3CmdDefineAlphaCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_MOVE_CURSOR:
+ {
+ /* Deprecated; there should be no driver which *requires* this command. However, if
+ * we do ecncounter this command, it might be useful to not get the FIFO completely out of
+ * alignment.
+ * May be issued by guest if SVGA_CAP_CURSOR_BYPASS is missing.
+ */
+ SVGAFifoCmdMoveCursor *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdMoveCursor, sizeof(*pCmd));
+ vmsvgaR3CmdMoveCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DISPLAY_CURSOR:
+ {
+ /* Deprecated; there should be no driver which *requires* this command. However, if
+ * we do ecncounter this command, it might be useful to not get the FIFO completely out of
+ * alignment.
+ * May be issued by guest if SVGA_CAP_CURSOR_BYPASS is missing.
+ */
+ SVGAFifoCmdDisplayCursor *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDisplayCursor, sizeof(*pCmd));
+ vmsvgaR3CmdDisplayCursor(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_RECT_FILL:
+ {
+ SVGAFifoCmdRectFill *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdRectFill, sizeof(*pCmd));
+ vmsvgaR3CmdRectFill(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_RECT_COPY:
+ {
+ SVGAFifoCmdRectCopy *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdRectCopy, sizeof(*pCmd));
+ vmsvgaR3CmdRectCopy(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_RECT_ROP_COPY:
+ {
+ SVGAFifoCmdRectRopCopy *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdRectRopCopy, sizeof(*pCmd));
+ vmsvgaR3CmdRectRopCopy(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_ESCAPE:
+ {
+ /* Followed by 'size' bytes of data. */
+ SVGAFifoCmdEscape *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdEscape, sizeof(*pCmd));
+
+ ASSERT_GUEST_BREAK(pCmd->size < pThis->svga.cbFIFO - sizeof(SVGAFifoCmdEscape));
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ uint32_t const cbCmd = sizeof(SVGAFifoCmdEscape) + pCmd->size;
+ VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdEscape, cbCmd);
+ vmsvgaR3CmdEscape(pThis, pThisCC, pCmd);
+ break;
+ }
+# ifdef VBOX_WITH_VMSVGA3D
+ case SVGA_CMD_DEFINE_GMR2:
+ {
+ SVGAFifoCmdDefineGMR2 *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineGMR2, sizeof(*pCmd));
+ vmsvgaR3CmdDefineGMR2(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_REMAP_GMR2:
+ {
+ /* Followed by page descriptors or guest ptr. */
+ SVGAFifoCmdRemapGMR2 *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdRemapGMR2, sizeof(*pCmd));
+
+ /* Calculate the size of what comes after next and fetch it. */
+ uint32_t cbCmd = sizeof(SVGAFifoCmdRemapGMR2);
+ if (pCmd->flags & SVGA_REMAP_GMR2_VIA_GMR)
+ cbCmd += sizeof(SVGAGuestPtr);
+ else
+ {
+ uint32_t const cbPageDesc = (pCmd->flags & SVGA_REMAP_GMR2_PPN64) ? sizeof(uint64_t) : sizeof(uint32_t);
+ if (pCmd->flags & SVGA_REMAP_GMR2_SINGLE_PPN)
+ {
+ cbCmd += cbPageDesc;
+ pCmd->numPages = 1;
+ }
+ else
+ {
+ ASSERT_GUEST_BREAK(pCmd->numPages <= pThis->svga.cbFIFO / cbPageDesc);
+ cbCmd += cbPageDesc * pCmd->numPages;
+ }
+ }
+ VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdRemapGMR2, cbCmd);
+ vmsvgaR3CmdRemapGMR2(pThis, pThisCC, pCmd);
+# ifdef DEBUG_GMR_ACCESS
+ VMR3ReqCallWaitU(PDMDevHlpGetUVM(pDevIns), VMCPUID_ANY, (PFNRT)vmsvgaR3RegisterGmr, 2, pDevIns, pCmd->gmrId);
+# endif
+ break;
+ }
+# endif // VBOX_WITH_VMSVGA3D
+ case SVGA_CMD_DEFINE_SCREEN:
+ {
+ /* The size of this command is specified by the guest and depends on capabilities. */
+ Assert(pFIFO[SVGA_FIFO_CAPABILITIES] & SVGA_FIFO_CAP_SCREEN_OBJECT_2);
+
+ SVGAFifoCmdDefineScreen *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineScreen, sizeof(pCmd->screen.structSize));
+ AssertBreak(pCmd->screen.structSize < pThis->svga.cbFIFO);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ RT_BZERO(&pCmd->screen.id, sizeof(*pCmd) - RT_OFFSETOF(SVGAFifoCmdDefineScreen, screen.id));
+ VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineScreen, RT_MAX(sizeof(pCmd->screen.structSize), pCmd->screen.structSize));
+ vmsvgaR3CmdDefineScreen(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DESTROY_SCREEN:
+ {
+ SVGAFifoCmdDestroyScreen *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDestroyScreen, sizeof(*pCmd));
+ vmsvgaR3CmdDestroyScreen(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_DEFINE_GMRFB:
+ {
+ SVGAFifoCmdDefineGMRFB *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdDefineGMRFB, sizeof(*pCmd));
+ vmsvgaR3CmdDefineGMRFB(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_BLIT_GMRFB_TO_SCREEN:
+ {
+ SVGAFifoCmdBlitGMRFBToScreen *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdBlitGMRFBToScreen, sizeof(*pCmd));
+ vmsvgaR3CmdBlitGMRFBToScreen(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_BLIT_SCREEN_TO_GMRFB:
+ {
+ SVGAFifoCmdBlitScreenToGMRFB *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdBlitScreenToGMRFB, sizeof(*pCmd));
+ vmsvgaR3CmdBlitScreenToGMRFB(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_ANNOTATION_FILL:
+ {
+ SVGAFifoCmdAnnotationFill *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdAnnotationFill, sizeof(*pCmd));
+ vmsvgaR3CmdAnnotationFill(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ case SVGA_CMD_ANNOTATION_COPY:
+ {
+ SVGAFifoCmdAnnotationCopy *pCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pCmd, SVGAFifoCmdAnnotationCopy, sizeof(*pCmd));
+ vmsvgaR3CmdAnnotationCopy(pThis, pThisCC, pCmd);
+ break;
+ }
+
+ default:
+# ifdef VBOX_WITH_VMSVGA3D
+ if ( (int)enmCmdId >= SVGA_3D_CMD_BASE
+ && (int)enmCmdId < SVGA_3D_CMD_MAX)
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /* All 3d commands start with a common header, which defines the identifier and the size
+ * of the command. The identifier has been already read from FIFO. Fetch the size.
+ */
+ uint32_t *pcbCmd;
+ VMSVGAFIFO_GET_CMD_BUFFER_BREAK(pcbCmd, uint32_t, sizeof(*pcbCmd));
+ uint32_t const cbCmd = *pcbCmd;
+ AssertBreak(cbCmd < pThis->svga.cbFIFO);
+ uint32_t *pu32Cmd;
+ VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK(pu32Cmd, uint32_t, sizeof(*pcbCmd) + cbCmd);
+ pu32Cmd++; /* Skip the command size. */
+
+ if (RT_LIKELY(pThis->svga.f3DEnabled))
+ { /* likely */ }
+ else
+ {
+ LogRelMax(8, ("VMSVGA: 3D disabled, command %d skipped\n", enmCmdId));
+ break;
+ }
+
+ vmsvgaR3Process3dCmd(pThis, pThisCC, SVGA3D_INVALID_ID, (SVGAFifo3dCmdId)enmCmdId, cbCmd, pu32Cmd);
+ }
+ else
+# endif // VBOX_WITH_VMSVGA3D
+ {
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoUnkCmds);
+ AssertMsgFailed(("enmCmdId=%d\n", enmCmdId));
+ LogRelMax(16, ("VMSVGA: unsupported command %d\n", enmCmdId));
+ }
+ }
+
+ /* Go to the next slot */
+ Assert(cbPayload + sizeof(uint32_t) <= offFifoMax - offFifoMin);
+ offCurrentCmd += RT_ALIGN_32(cbPayload + sizeof(uint32_t), sizeof(uint32_t));
+ if (offCurrentCmd >= offFifoMax)
+ {
+ offCurrentCmd -= offFifoMax - offFifoMin;
+ Assert(offCurrentCmd >= offFifoMin);
+ Assert(offCurrentCmd < offFifoMax);
+ }
+ ASMAtomicWriteU32(&pFIFO[SVGA_FIFO_STOP], offCurrentCmd);
+ STAM_REL_COUNTER_INC(&pSVGAState->StatFifoCommands);
+
+ /*
+ * Raise IRQ if required. Must enter the critical section here
+ * before making final decisions here, otherwise cubebench and
+ * others may end up waiting forever.
+ */
+ if ( u32IrqStatus
+ || (pThis->svga.u32IrqMask & SVGA_IRQFLAG_FIFO_PROGRESS))
+ {
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /* FIFO progress might trigger an interrupt. */
+ if (pThis->svga.u32IrqMask & SVGA_IRQFLAG_FIFO_PROGRESS)
+ {
+ Log(("vmsvgaR3FifoLoop: fifo progress irq\n"));
+ u32IrqStatus |= SVGA_IRQFLAG_FIFO_PROGRESS;
+ }
+
+ /* Unmasked IRQ pending? */
+ if (pThis->svga.u32IrqMask & u32IrqStatus)
+ {
+ Log(("vmsvgaR3FifoLoop: Trigger interrupt with status %x\n", u32IrqStatus));
+ ASMAtomicOrU32(&pThis->svga.u32IrqStatus, u32IrqStatus);
+ PDMDevHlpPCISetIrq(pDevIns, 0, 1);
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ }
+ }
+
+ /* If really done, clear the busy flag. */
+ if (fDone)
+ {
+ Log(("vmsvgaR3FifoLoop: emptied the FIFO next=%x stop=%x\n", pFIFO[SVGA_FIFO_NEXT_CMD], offCurrentCmd));
+ vmsvgaR3FifoSetNotBusy(pDevIns, pThis, pThisCC, pSVGAState, offFifoMin);
+ }
+ }
+
+ /*
+ * Free the bounce buffer. (There are no returns above!)
+ */
+ RTMemFree(pbBounceBuf);
+
+ return VINF_SUCCESS;
+}
+
+#undef VMSVGAFIFO_GET_MORE_CMD_BUFFER_BREAK
+#undef VMSVGAFIFO_GET_CMD_BUFFER_BREAK
+
+/**
+ * @callback_method_impl{PFNPDMTHREADWAKEUPDEV,
+ * Unblock the FIFO I/O thread so it can respond to a state change.}
+ */
+static DECLCALLBACK(int) vmsvgaR3FifoLoopWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pDevIns);
+ PVGASTATE pThis = (PVGASTATE)pThread->pvUser;
+ Log(("vmsvgaR3FifoLoopWakeUp\n"));
+ return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
+}
+
+/**
+ * Enables or disables dirty page tracking for the framebuffer
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param fTraces Enable/disable traces
+ */
+static void vmsvgaR3SetTraces(PPDMDEVINS pDevIns, PVGASTATE pThis, bool fTraces)
+{
+ if ( (!pThis->svga.fConfigured || !pThis->svga.fEnabled)
+ && !fTraces)
+ {
+ //Assert(pThis->svga.fTraces);
+ Log(("vmsvgaR3SetTraces: *not* allowed to disable dirty page tracking when the device is in legacy mode.\n"));
+ return;
+ }
+
+ pThis->svga.fTraces = fTraces;
+ if (pThis->svga.fTraces)
+ {
+ unsigned cbFrameBuffer = pThis->vram_size;
+
+ Log(("vmsvgaR3SetTraces: enable dirty page handling for the frame buffer only (%x bytes)\n", 0));
+ /** @todo How does this work with screens? */
+ if (pThis->svga.uHeight != VMSVGA_VAL_UNINITIALIZED)
+ {
+# ifndef DEBUG_bird /* BB-10.3.1 triggers this as it initializes everything to zero. Better just ignore it. */
+ Assert(pThis->svga.cbScanline);
+# endif
+ /* Hardware enabled; return real framebuffer size .*/
+ cbFrameBuffer = (uint32_t)pThis->svga.uHeight * pThis->svga.cbScanline;
+ cbFrameBuffer = RT_ALIGN(cbFrameBuffer, GUEST_PAGE_SIZE);
+ }
+
+ if (!pThis->svga.fVRAMTracking)
+ {
+ Log(("vmsvgaR3SetTraces: enable frame buffer dirty page tracking. (%x bytes; vram %x)\n", cbFrameBuffer, pThis->vram_size));
+ vgaR3RegisterVRAMHandler(pDevIns, pThis, cbFrameBuffer);
+ pThis->svga.fVRAMTracking = true;
+ }
+ }
+ else
+ {
+ if (pThis->svga.fVRAMTracking)
+ {
+ Log(("vmsvgaR3SetTraces: disable frame buffer dirty page tracking\n"));
+ vgaR3UnregisterVRAMHandler(pDevIns, pThis);
+ pThis->svga.fVRAMTracking = false;
+ }
+ }
+}
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+DECLCALLBACK(int) vmsvgaR3PciIORegionFifoMapUnmap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
+ RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ int rc;
+ RT_NOREF(pPciDev);
+ Assert(pPciDev == pDevIns->apPciDevs[0]);
+
+ Log(("vmsvgaR3PciIORegionFifoMapUnmap: iRegion=%d GCPhysAddress=%RGp cb=%RGp enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
+ AssertReturn( iRegion == pThis->pciRegions.iFIFO
+ && ( enmType == PCI_ADDRESS_SPACE_MEM
+ || (enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH /* got wrong in 6.1.0RC1 */ && pThis->fStateLoaded))
+ , VERR_INTERNAL_ERROR);
+ if (GCPhysAddress != NIL_RTGCPHYS)
+ {
+ /*
+ * Mapping the FIFO RAM.
+ */
+ AssertLogRelMsg(cb == pThis->svga.cbFIFO, ("cb=%#RGp cbFIFO=%#x\n", cb, pThis->svga.cbFIFO));
+ rc = PDMDevHlpMmio2Map(pDevIns, pThis->hMmio2VmSvgaFifo, GCPhysAddress);
+ AssertRC(rc);
+
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+ if (RT_SUCCESS(rc))
+ {
+ rc = PDMDevHlpPGMHandlerPhysicalRegister(pDevIns, GCPhysAddress,
+# ifdef DEBUG_FIFO_ACCESS
+ GCPhysAddress + (pThis->svga.cbFIFO - 1),
+# else
+ GCPhysAddress + GUEST_PAGE_SIZE - 1,
+# endif
+ pThis->svga.hFifoAccessHandlerType, pThis, NIL_RTR0PTR, NIL_RTRCPTR,
+ "VMSVGA FIFO");
+ AssertRC(rc);
+ }
+# endif
+ if (RT_SUCCESS(rc))
+ {
+ pThis->svga.GCPhysFIFO = GCPhysAddress;
+ Log(("vmsvgaR3IORegionMap: GCPhysFIFO=%RGp cbFIFO=%#x\n", GCPhysAddress, pThis->svga.cbFIFO));
+ }
+ rc = VINF_PCI_MAPPING_DONE; /* caller only cares about this status, so it is okay that we overwrite errors here. */
+ }
+ else
+ {
+ Assert(pThis->svga.GCPhysFIFO);
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+ rc = PDMDevHlpPGMHandlerPhysicalDeregister(pDevIns, pThis->svga.GCPhysFIFO);
+ AssertRC(rc);
+# else
+ rc = VINF_SUCCESS;
+# endif
+ pThis->svga.GCPhysFIFO = 0;
+ }
+ return rc;
+}
+
+# ifdef VBOX_WITH_VMSVGA3D
+
+/**
+ * Used by vmsvga3dInfoSurfaceWorker to make the FIFO thread to save one or all
+ * surfaces to VMSVGA3DMIPMAPLEVEL::pSurfaceData heap buffers.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ * @param sid Either UINT32_MAX or the ID of a specific surface. If
+ * UINT32_MAX is used, all surfaces are processed.
+ */
+void vmsvgaR33dSurfaceUpdateHeapBuffersOnFifoThread(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t sid)
+{
+ vmsvgaR3RunExtCmdOnFifoThread(pDevIns, pThis, pThisCC, VMSVGA_FIFO_EXTCMD_UPDATE_SURFACE_HEAP_BUFFERS, (void *)(uintptr_t)sid,
+ sid == UINT32_MAX ? 10 * RT_MS_1SEC : RT_MS_1MIN);
+}
+
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, "vmsvga3dsfc"}
+ */
+DECLCALLBACK(void) vmsvgaR3Info3dSurface(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ /* There might be a specific surface ID at the start of the
+ arguments, if not show all surfaces. */
+ uint32_t sid = UINT32_MAX;
+ if (pszArgs)
+ pszArgs = RTStrStripL(pszArgs);
+ if (pszArgs && RT_C_IS_DIGIT(*pszArgs))
+ sid = RTStrToUInt32(pszArgs);
+
+ /* Verbose or terse display, we default to verbose. */
+ bool fVerbose = true;
+ if (RTStrIStr(pszArgs, "terse"))
+ fVerbose = false;
+
+ /* The size of the ascii art (x direction, y is 3/4 of x). */
+ uint32_t cxAscii = 80;
+ if (RTStrIStr(pszArgs, "gigantic"))
+ cxAscii = 300;
+ else if (RTStrIStr(pszArgs, "huge"))
+ cxAscii = 180;
+ else if (RTStrIStr(pszArgs, "big"))
+ cxAscii = 132;
+ else if (RTStrIStr(pszArgs, "normal"))
+ cxAscii = 80;
+ else if (RTStrIStr(pszArgs, "medium"))
+ cxAscii = 64;
+ else if (RTStrIStr(pszArgs, "small"))
+ cxAscii = 48;
+ else if (RTStrIStr(pszArgs, "tiny"))
+ cxAscii = 24;
+
+ /* Y invert the image when producing the ASCII art. */
+ bool fInvY = false;
+ if (RTStrIStr(pszArgs, "invy"))
+ fInvY = true;
+
+ vmsvga3dInfoSurfaceWorker(pDevIns, PDMDEVINS_2_DATA(pDevIns, PVGASTATE), PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC),
+ pHlp, sid, fVerbose, cxAscii, fInvY, NULL);
+}
+
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, "vmsvga3dsurf"}
+ */
+DECLCALLBACK(void) vmsvgaR3Info3dSurfaceBmp(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ /* pszArg = "sid[>dir]"
+ * Writes %dir%/info-S-sidI.bmp, where S - sequential bitmap number, I - decimal surface id.
+ */
+ char *pszBitmapPath = NULL;
+ uint32_t sid = UINT32_MAX;
+ if (pszArgs)
+ pszArgs = RTStrStripL(pszArgs);
+ if (pszArgs && RT_C_IS_DIGIT(*pszArgs))
+ RTStrToUInt32Ex(pszArgs, &pszBitmapPath, 0, &sid);
+ if ( pszBitmapPath
+ && *pszBitmapPath == '>')
+ ++pszBitmapPath;
+
+ const bool fVerbose = true;
+ const uint32_t cxAscii = 0; /* No ASCII */
+ const bool fInvY = false; /* Do not invert. */
+ vmsvga3dInfoSurfaceWorker(pDevIns, PDMDEVINS_2_DATA(pDevIns, PVGASTATE), PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC),
+ pHlp, sid, fVerbose, cxAscii, fInvY, pszBitmapPath);
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, "vmsvga3dctx"}
+ */
+DECLCALLBACK(void) vmsvgaR3Info3dContext(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ /* There might be a specific surface ID at the start of the
+ arguments, if not show all contexts. */
+ uint32_t sid = UINT32_MAX;
+ if (pszArgs)
+ pszArgs = RTStrStripL(pszArgs);
+ if (pszArgs && RT_C_IS_DIGIT(*pszArgs))
+ sid = RTStrToUInt32(pszArgs);
+
+ /* Verbose or terse display, we default to verbose. */
+ bool fVerbose = true;
+ if (RTStrIStr(pszArgs, "terse"))
+ fVerbose = false;
+
+ vmsvga3dInfoContextWorker(PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC), pHlp, sid, fVerbose);
+}
+# endif /* VBOX_WITH_VMSVGA3D */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, "vmsvga"}
+ */
+static DECLCALLBACK(void) vmsvgaR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ uint32_t RT_UNTRUSTED_VOLATILE_GUEST *pFIFO = pThisCC->svga.pau32FIFO;
+ RT_NOREF(pszArgs);
+
+ pHlp->pfnPrintf(pHlp, "Extension enabled: %RTbool\n", pThis->svga.fEnabled);
+ pHlp->pfnPrintf(pHlp, "Configured: %RTbool\n", pThis->svga.fConfigured);
+ pHlp->pfnPrintf(pHlp, "Base I/O port: %#x\n",
+ pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE
+ ? PDMDevHlpIoPortGetMappingAddress(pDevIns, pThis->hIoPortVmSvga) : UINT32_MAX);
+ pHlp->pfnPrintf(pHlp, "FIFO address: %RGp\n", pThis->svga.GCPhysFIFO);
+ pHlp->pfnPrintf(pHlp, "FIFO size: %u (%#x)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO);
+ pHlp->pfnPrintf(pHlp, "FIFO external cmd: %#x\n", pThis->svga.u8FIFOExtCommand);
+ pHlp->pfnPrintf(pHlp, "FIFO extcmd wakeup: %u\n", pThis->svga.fFifoExtCommandWakeup);
+ pHlp->pfnPrintf(pHlp, "FIFO min/max: %u/%u\n", pFIFO[SVGA_FIFO_MIN], pFIFO[SVGA_FIFO_MAX]);
+ pHlp->pfnPrintf(pHlp, "Busy: %#x\n", pThis->svga.fBusy);
+ pHlp->pfnPrintf(pHlp, "Traces: %RTbool (effective: %RTbool)\n", pThis->svga.fTraces, pThis->svga.fVRAMTracking);
+ pHlp->pfnPrintf(pHlp, "Guest ID: %#x (%d)\n", pThis->svga.u32GuestId, pThis->svga.u32GuestId);
+ pHlp->pfnPrintf(pHlp, "IRQ status: %#x\n", pThis->svga.u32IrqStatus);
+ pHlp->pfnPrintf(pHlp, "IRQ mask: %#x\n", pThis->svga.u32IrqMask);
+ pHlp->pfnPrintf(pHlp, "Pitch lock: %#x (FIFO:%#x)\n", pThis->svga.u32PitchLock, pFIFO[SVGA_FIFO_PITCHLOCK]);
+ pHlp->pfnPrintf(pHlp, "Current GMR ID: %#x\n", pThis->svga.u32CurrentGMRId);
+ pHlp->pfnPrintf(pHlp, "Device Capabilites: %#x\n", pThis->svga.u32DeviceCaps);
+ pHlp->pfnPrintf(pHlp, "Device Cap2: %#x\n", pThis->svga.u32DeviceCaps2);
+ pHlp->pfnPrintf(pHlp, "Guest driver id: %#x\n", pThis->svga.u32GuestDriverId);
+ pHlp->pfnPrintf(pHlp, "Guest driver ver1: %#x\n", pThis->svga.u32GuestDriverVer1);
+ pHlp->pfnPrintf(pHlp, "Guest driver ver2: %#x\n", pThis->svga.u32GuestDriverVer2);
+ pHlp->pfnPrintf(pHlp, "Guest driver ver3: %#x\n", pThis->svga.u32GuestDriverVer3);
+ pHlp->pfnPrintf(pHlp, "Index reg: %#x\n", pThis->svga.u32IndexReg);
+ pHlp->pfnPrintf(pHlp, "Action flags: %#x\n", pThis->svga.u32ActionFlags);
+ pHlp->pfnPrintf(pHlp, "Max display size: %ux%u\n", pThis->svga.u32MaxWidth, pThis->svga.u32MaxHeight);
+ pHlp->pfnPrintf(pHlp, "Display size: %ux%u %ubpp\n", pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
+ pHlp->pfnPrintf(pHlp, "Scanline: %u (%#x)\n", pThis->svga.cbScanline, pThis->svga.cbScanline);
+ pHlp->pfnPrintf(pHlp, "Viewport position: %ux%u\n", pThis->svga.viewport.x, pThis->svga.viewport.y);
+ pHlp->pfnPrintf(pHlp, "Viewport size: %ux%u\n", pThis->svga.viewport.cx, pThis->svga.viewport.cy);
+
+ pHlp->pfnPrintf(pHlp, "Cursor active: %RTbool\n", pSVGAState->Cursor.fActive);
+ pHlp->pfnPrintf(pHlp, "Cursor hotspot: %ux%u\n", pSVGAState->Cursor.xHotspot, pSVGAState->Cursor.yHotspot);
+ pHlp->pfnPrintf(pHlp, "Cursor size: %ux%u\n", pSVGAState->Cursor.width, pSVGAState->Cursor.height);
+ pHlp->pfnPrintf(pHlp, "Cursor byte size: %u (%#x)\n", pSVGAState->Cursor.cbData, pSVGAState->Cursor.cbData);
+
+ pHlp->pfnPrintf(pHlp, "FIFO cursor: state %u, screen %d\n", pFIFO[SVGA_FIFO_CURSOR_ON], pFIFO[SVGA_FIFO_CURSOR_SCREEN_ID]);
+ pHlp->pfnPrintf(pHlp, "FIFO cursor at: %u,%u\n", pFIFO[SVGA_FIFO_CURSOR_X], pFIFO[SVGA_FIFO_CURSOR_Y]);
+
+ pHlp->pfnPrintf(pHlp, "Legacy cursor: ID %u, state %u\n", pThis->svga.uCursorID, pThis->svga.uCursorOn);
+ pHlp->pfnPrintf(pHlp, "Legacy cursor at: %u,%u\n", pThis->svga.uCursorX, pThis->svga.uCursorY);
+
+# ifdef VBOX_WITH_VMSVGA3D
+ pHlp->pfnPrintf(pHlp, "3D enabled: %RTbool\n", pThis->svga.f3DEnabled);
+# endif
+ if (pThisCC->pDrv)
+ {
+ pHlp->pfnPrintf(pHlp, "Driver mode: %ux%u %ubpp\n", pThisCC->pDrv->cx, pThisCC->pDrv->cy, pThisCC->pDrv->cBits);
+ pHlp->pfnPrintf(pHlp, "Driver pitch: %u (%#x)\n", pThisCC->pDrv->cbScanline, pThisCC->pDrv->cbScanline);
+ }
+
+ /* Dump screen information. */
+ for (unsigned iScreen = 0; iScreen < RT_ELEMENTS(pSVGAState->aScreens); ++iScreen)
+ {
+ VMSVGASCREENOBJECT *pScreen = vmsvgaR3GetScreenObject(pThisCC, iScreen);
+ if (pScreen)
+ {
+ pHlp->pfnPrintf(pHlp, "Screen %u defined (ID %u):\n", iScreen, pScreen->idScreen);
+ pHlp->pfnPrintf(pHlp, " %u x %u x %ubpp @ %u, %u\n", pScreen->cWidth, pScreen->cHeight,
+ pScreen->cBpp, pScreen->xOrigin, pScreen->yOrigin);
+ pHlp->pfnPrintf(pHlp, " Pitch %u bytes, VRAM offset %X\n", pScreen->cbPitch, pScreen->offVRAM);
+ pHlp->pfnPrintf(pHlp, " Flags %X", pScreen->fuScreen);
+ if (pScreen->fuScreen != SVGA_SCREEN_MUST_BE_SET)
+ {
+ pHlp->pfnPrintf(pHlp, " (");
+ if (pScreen->fuScreen & SVGA_SCREEN_IS_PRIMARY)
+ pHlp->pfnPrintf(pHlp, " IS_PRIMARY");
+ if (pScreen->fuScreen & SVGA_SCREEN_FULLSCREEN_HINT)
+ pHlp->pfnPrintf(pHlp, " FULLSCREEN_HINT");
+ if (pScreen->fuScreen & SVGA_SCREEN_DEACTIVATE)
+ pHlp->pfnPrintf(pHlp, " DEACTIVATE");
+ if (pScreen->fuScreen & SVGA_SCREEN_BLANKING)
+ pHlp->pfnPrintf(pHlp, " BLANKING");
+ pHlp->pfnPrintf(pHlp, " )");
+ }
+ pHlp->pfnPrintf(pHlp, ", %smodified\n", pScreen->fModified ? "" : "not ");
+ }
+ }
+
+}
+
+static int vmsvgaR3LoadBufCtx(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, PSSMHANDLE pSSM, PVMSVGACMDBUFCTX pBufCtx, SVGACBContext CBCtx)
+{
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ PVMSVGAR3STATE pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+ uint32_t cSubmitted;
+ int rc = pHlp->pfnSSMGetU32(pSSM, &cSubmitted);
+ AssertLogRelRCReturn(rc, rc);
+
+ for (uint32_t i = 0; i < cSubmitted; ++i)
+ {
+ PVMSVGACMDBUF pCmdBuf = vmsvgaR3CmdBufAlloc(pBufCtx);
+ AssertPtrReturn(pCmdBuf, VERR_NO_MEMORY);
+
+ pHlp->pfnSSMGetGCPhys(pSSM, &pCmdBuf->GCPhysCB);
+
+ uint32_t u32;
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ AssertReturn(u32 == sizeof(SVGACBHeader), VERR_INVALID_STATE);
+ pHlp->pfnSSMGetMem(pSSM, &pCmdBuf->hdr, sizeof(SVGACBHeader));
+
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ AssertReturn(u32 == pCmdBuf->hdr.length, VERR_INVALID_STATE);
+
+ if (pCmdBuf->hdr.length)
+ {
+ pCmdBuf->pvCommands = RTMemAlloc(pCmdBuf->hdr.length);
+ AssertPtrReturn(pCmdBuf->pvCommands, VERR_NO_MEMORY);
+
+ rc = pHlp->pfnSSMGetMem(pSSM, pCmdBuf->pvCommands, pCmdBuf->hdr.length);
+ AssertRCReturn(rc, rc);
+ }
+
+ if (RT_LIKELY(CBCtx < RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs)))
+ {
+ vmsvgaR3CmdBufSubmitCtx(pDevIns, pThis, pThisCC, &pCmdBuf);
+ }
+ else
+ {
+ uint32_t offNextCmd = 0;
+ vmsvgaR3CmdBufSubmitDC(pDevIns, pThisCC, &pCmdBuf, &offNextCmd);
+ }
+
+ /* Free the buffer if CmdBufSubmit* did not consume it. */
+ vmsvgaR3CmdBufFree(pCmdBuf);
+ }
+ return rc;
+}
+
+static int vmsvgaR3LoadCommandBuffers(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, PSSMHANDLE pSSM)
+{
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ PVMSVGAR3STATE pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+ bool f;
+ uint32_t u32;
+
+ /* Device context command buffers. */
+ int rc = vmsvgaR3LoadBufCtx(pDevIns, pThis, pThisCC, pSSM, &pSvgaR3State->CmdBufCtxDC, SVGA_CB_CONTEXT_MAX);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* DX contexts command buffers. */
+ uint32_t cBufCtx;
+ rc = pHlp->pfnSSMGetU32(pSSM, &cBufCtx);
+ AssertLogRelRCReturn(rc, rc);
+ AssertReturn(cBufCtx == RT_ELEMENTS(pSvgaR3State->apCmdBufCtxs), VERR_INVALID_STATE);
+ for (uint32_t j = 0; j < cBufCtx; ++j)
+ {
+ rc = pHlp->pfnSSMGetBool(pSSM, &f);
+ AssertLogRelRCReturn(rc, rc);
+ if (f)
+ {
+ pSvgaR3State->apCmdBufCtxs[j] = (PVMSVGACMDBUFCTX)RTMemAlloc(sizeof(VMSVGACMDBUFCTX));
+ AssertPtrReturn(pSvgaR3State->apCmdBufCtxs[j], VERR_NO_MEMORY);
+ vmsvgaR3CmdBufCtxInit(pSvgaR3State->apCmdBufCtxs[j]);
+
+ rc = vmsvgaR3LoadBufCtx(pDevIns, pThis, pThisCC, pSSM, pSvgaR3State->apCmdBufCtxs[j], (SVGACBContext)j);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ pSvgaR3State->fCmdBuf = u32;
+ return rc;
+}
+
+static int vmsvgaR3LoadGbo(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, VMSVGAGBO *pGbo)
+{
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ int rc;
+ pHlp->pfnSSMGetU32(pSSM, &pGbo->fGboFlags);
+ pHlp->pfnSSMGetU32(pSSM, &pGbo->cTotalPages);
+ pHlp->pfnSSMGetU32(pSSM, &pGbo->cbTotal);
+ rc = pHlp->pfnSSMGetU32(pSSM, &pGbo->cDescriptors);
+ AssertRCReturn(rc, rc);
+
+ if (pGbo->cDescriptors)
+ {
+ pGbo->paDescriptors = (PVMSVGAGBODESCRIPTOR)RTMemAllocZ(pGbo->cDescriptors * sizeof(VMSVGAGBODESCRIPTOR));
+ AssertPtrReturn(pGbo->paDescriptors, VERR_NO_MEMORY);
+ }
+
+ for (uint32_t iDesc = 0; iDesc < pGbo->cDescriptors; ++iDesc)
+ {
+ PVMSVGAGBODESCRIPTOR pDesc = &pGbo->paDescriptors[iDesc];
+ pHlp->pfnSSMGetGCPhys(pSSM, &pDesc->GCPhys);
+ rc = pHlp->pfnSSMGetU64(pSSM, &pDesc->cPages);
+ }
+
+ if (pGbo->fGboFlags & VMSVGAGBO_F_HOST_BACKED)
+ {
+ pGbo->pvHost = RTMemAlloc(pGbo->cbTotal);
+ AssertPtrReturn(pGbo->pvHost, VERR_NO_MEMORY);
+ rc = pHlp->pfnSSMGetMem(pSSM, pGbo->pvHost, pGbo->cbTotal);
+ }
+
+ return rc;
+}
+
+/**
+ * Portion of VMSVGA state which must be loaded oin the FIFO thread.
+ */
+static int vmsvgaR3LoadExecFifo(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PVGASTATECC pThisCC,
+ PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ int rc;
+
+ if (uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_SCREENS)
+ {
+ uint32_t cScreens = 0;
+ rc = pHlp->pfnSSMGetU32(pSSM, &cScreens);
+ AssertRCReturn(rc, rc);
+ AssertLogRelMsgReturn(cScreens <= _64K, /* big enough */
+ ("cScreens=%#x\n", cScreens),
+ VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+
+ for (uint32_t i = 0; i < cScreens; ++i)
+ {
+ VMSVGASCREENOBJECT screen;
+ RT_ZERO(screen);
+
+ rc = pHlp->pfnSSMGetStructEx(pSSM, &screen, sizeof(screen), 0, g_aVMSVGASCREENOBJECTFields, NULL);
+ AssertLogRelRCReturn(rc, rc);
+
+ if (screen.idScreen < RT_ELEMENTS(pSVGAState->aScreens))
+ {
+ VMSVGASCREENOBJECT *pScreen = &pSVGAState->aScreens[screen.idScreen];
+ *pScreen = screen;
+ pScreen->fModified = true;
+
+ if (uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_DX)
+ {
+ uint32_t u32;
+ pHlp->pfnSSMGetU32(pSSM, &u32); /* Size of screen bitmap. */
+ AssertLogRelRCReturn(rc, rc);
+ if (u32)
+ {
+ pScreen->pvScreenBitmap = RTMemAlloc(u32);
+ AssertPtrReturn(pScreen->pvScreenBitmap, VERR_NO_MEMORY);
+
+ pHlp->pfnSSMGetMem(pSSM, pScreen->pvScreenBitmap, u32);
+ }
+ }
+ }
+ else
+ {
+ LogRel(("VGA: ignored screen object %d\n", screen.idScreen));
+ }
+ }
+ }
+ else
+ {
+ /* Try to setup at least the first screen. */
+ VMSVGASCREENOBJECT *pScreen = &pSVGAState->aScreens[0];
+ Assert(pScreen->idScreen == 0);
+ pScreen->fDefined = true;
+ pScreen->fModified = true;
+ pScreen->fuScreen = SVGA_SCREEN_MUST_BE_SET | SVGA_SCREEN_IS_PRIMARY;
+ pScreen->xOrigin = 0;
+ pScreen->yOrigin = 0;
+ pScreen->offVRAM = pThis->svga.uScreenOffset;
+ pScreen->cbPitch = pThis->svga.cbScanline;
+ pScreen->cWidth = pThis->svga.uWidth;
+ pScreen->cHeight = pThis->svga.uHeight;
+ pScreen->cBpp = pThis->svga.uBpp;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc FNSSMDEVLOADEXEC
+ */
+int vmsvgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ /* Load our part of the VGAState */
+ rc = pHlp->pfnSSMGetStructEx(pSSM, &pThis->svga, sizeof(pThis->svga), 0, g_aVGAStateSVGAFields, NULL);
+ AssertRCReturn(rc, rc);
+
+ /* Load the VGA framebuffer. */
+ AssertCompile(VMSVGA_VGA_FB_BACKUP_SIZE >= _32K);
+ uint32_t cbVgaFramebuffer = _32K;
+ if (uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_VGA_FB_FIX)
+ {
+ rc = pHlp->pfnSSMGetU32(pSSM, &cbVgaFramebuffer);
+ AssertRCReturn(rc, rc);
+ AssertLogRelMsgReturn(cbVgaFramebuffer <= _4M && cbVgaFramebuffer >= _32K && RT_IS_POWER_OF_TWO(cbVgaFramebuffer),
+ ("cbVgaFramebuffer=%#x - expected 32KB..4MB, power of two\n", cbVgaFramebuffer),
+ VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+ AssertCompile(VMSVGA_VGA_FB_BACKUP_SIZE <= _4M);
+ AssertCompile(RT_IS_POWER_OF_TWO(VMSVGA_VGA_FB_BACKUP_SIZE));
+ }
+ rc = pHlp->pfnSSMGetMem(pSSM, pThisCC->svga.pbVgaFrameBufferR3, RT_MIN(cbVgaFramebuffer, VMSVGA_VGA_FB_BACKUP_SIZE));
+ AssertRCReturn(rc, rc);
+ if (cbVgaFramebuffer > VMSVGA_VGA_FB_BACKUP_SIZE)
+ pHlp->pfnSSMSkip(pSSM, cbVgaFramebuffer - VMSVGA_VGA_FB_BACKUP_SIZE);
+ else if (cbVgaFramebuffer < VMSVGA_VGA_FB_BACKUP_SIZE)
+ RT_BZERO(&pThisCC->svga.pbVgaFrameBufferR3[cbVgaFramebuffer], VMSVGA_VGA_FB_BACKUP_SIZE - cbVgaFramebuffer);
+
+ /* Load the VMSVGA state. */
+ rc = pHlp->pfnSSMGetStructEx(pSSM, pSVGAState, sizeof(*pSVGAState), 0, g_aVMSVGAR3STATEFields, NULL);
+ AssertRCReturn(rc, rc);
+
+ /* Load the active cursor bitmaps. */
+ if (pSVGAState->Cursor.fActive)
+ {
+ pSVGAState->Cursor.pData = RTMemAlloc(pSVGAState->Cursor.cbData);
+ AssertReturn(pSVGAState->Cursor.pData, VERR_NO_MEMORY);
+
+ rc = pHlp->pfnSSMGetMem(pSSM, pSVGAState->Cursor.pData, pSVGAState->Cursor.cbData);
+ AssertRCReturn(rc, rc);
+ }
+
+ /* Load the GMR state. */
+ uint32_t cGMR = 256; /* Hardcoded in previous saved state versions. */
+ if (uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_GMR_COUNT)
+ {
+ rc = pHlp->pfnSSMGetU32(pSSM, &cGMR);
+ AssertRCReturn(rc, rc);
+ /* Numbers of GMRs was never less than 256. 1MB is a large arbitrary limit. */
+ AssertLogRelMsgReturn(cGMR <= _1M && cGMR >= 256,
+ ("cGMR=%#x - expected 256B..1MB\n", cGMR),
+ VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+ }
+
+ if (pThis->svga.cGMR != cGMR)
+ {
+ /* Reallocate GMR array. */
+ Assert(pSVGAState->paGMR != NULL);
+ RTMemFree(pSVGAState->paGMR);
+ pSVGAState->paGMR = (PGMR)RTMemAllocZ(cGMR * sizeof(GMR));
+ AssertReturn(pSVGAState->paGMR, VERR_NO_MEMORY);
+ pThis->svga.cGMR = cGMR;
+ }
+
+ for (uint32_t i = 0; i < cGMR; ++i)
+ {
+ PGMR pGMR = &pSVGAState->paGMR[i];
+
+ rc = pHlp->pfnSSMGetStructEx(pSSM, pGMR, sizeof(*pGMR), 0, g_aGMRFields, NULL);
+ AssertRCReturn(rc, rc);
+
+ if (pGMR->numDescriptors)
+ {
+ Assert(pGMR->cMaxPages || pGMR->cbTotal);
+ pGMR->paDesc = (PVMSVGAGMRDESCRIPTOR)RTMemAllocZ(pGMR->numDescriptors * sizeof(VMSVGAGMRDESCRIPTOR));
+ AssertReturn(pGMR->paDesc, VERR_NO_MEMORY);
+
+ for (uint32_t j = 0; j < pGMR->numDescriptors; ++j)
+ {
+ rc = pHlp->pfnSSMGetStructEx(pSSM, &pGMR->paDesc[j], sizeof(pGMR->paDesc[j]), 0, g_aVMSVGAGMRDESCRIPTORFields, NULL);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ }
+
+ if (uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_DX)
+ {
+ bool f;
+ uint32_t u32;
+
+ if (uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_DX_CMDBUF)
+ {
+ /* Command buffers are saved independently from VGPU10. */
+ rc = pHlp->pfnSSMGetBool(pSSM, &f);
+ AssertLogRelRCReturn(rc, rc);
+ if (f)
+ {
+ rc = vmsvgaR3LoadCommandBuffers(pDevIns, pThis, pThisCC, pSSM);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+
+ rc = pHlp->pfnSSMGetBool(pSSM, &f);
+ AssertLogRelRCReturn(rc, rc);
+ pThis->fVMSVGA10 = f;
+
+ if (pThis->fVMSVGA10)
+ {
+ if (uVersion < VGA_SAVEDSTATE_VERSION_VMSVGA_DX_CMDBUF)
+ {
+ rc = vmsvgaR3LoadCommandBuffers(pDevIns, pThis, pThisCC, pSSM);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ /*
+ * OTables GBOs.
+ */
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertLogRelRCReturn(rc, rc);
+ AssertReturn(u32 == SVGA_OTABLE_MAX, VERR_INVALID_STATE);
+ for (int i = 0; i < SVGA_OTABLE_MAX; ++i)
+ {
+ VMSVGAGBO *pGbo = &pSVGAState->aGboOTables[i];
+ rc = vmsvgaR3LoadGbo(pDevIns, pSSM, pGbo);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * MOBs.
+ */
+ for (;;)
+ {
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32); /* MOB id. */
+ AssertRCReturn(rc, rc);
+ if (u32 == SVGA_ID_INVALID)
+ break;
+
+ PVMSVGAMOB pMob = (PVMSVGAMOB)RTMemAllocZ(sizeof(*pMob));
+ AssertPtrReturn(pMob, VERR_NO_MEMORY);
+
+ rc = vmsvgaR3LoadGbo(pDevIns, pSSM, &pMob->Gbo);
+ AssertRCReturn(rc, rc);
+
+ pMob->Core.Key = u32;
+ if (RTAvlU32Insert(&pSVGAState->MOBTree, &pMob->Core))
+ RTListPrepend(&pSVGAState->MOBLRUList, &pMob->nodeLRU);
+ else
+ AssertFailedReturn(VERR_NO_MEMORY);
+ }
+
+# ifdef VMSVGA3D_DX
+ if (pThis->svga.f3DEnabled)
+ {
+ pHlp->pfnSSMGetU32(pSSM, &pSVGAState->idDXContextCurrent);
+ }
+# endif
+ }
+ }
+
+# ifdef RT_OS_DARWIN /** @todo r=bird: this is normally done on the EMT, so for DARWIN we do that when loading saved state too now. See DevVGA-SVGA3d-shared.h. */
+ vmsvgaR3PowerOnDevice(pDevIns, pThis, pThisCC, /*fLoadState=*/ true);
+# endif
+
+ VMSVGA_STATE_LOAD LoadState;
+ LoadState.pSSM = pSSM;
+ LoadState.uVersion = uVersion;
+ LoadState.uPass = uPass;
+ rc = vmsvgaR3RunExtCmdOnFifoThread(pDevIns, pThis, pThisCC, VMSVGA_FIFO_EXTCMD_LOADSTATE, &LoadState, RT_INDEFINITE_WAIT);
+ AssertLogRelRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Reinit the video mode after the state has been loaded.
+ */
+int vmsvgaR3LoadDone(PPDMDEVINS pDevIns)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ /* VMSVGA is working via VBVA interface, therefore it needs to be
+ * enabled on saved state restore. See @bugref{10071#c7}. */
+ if (pThis->svga.fEnabled)
+ {
+ for (uint32_t idScreen = 0; idScreen < pThis->cMonitors; ++idScreen)
+ pThisCC->pDrv->pfnVBVAEnable(pThisCC->pDrv, idScreen, NULL /*pHostFlags*/);
+ }
+
+ /* Set the active cursor. */
+ if (pSVGAState->Cursor.fActive)
+ {
+ /* We don't store the alpha flag, but we can take a guess that if
+ * the old register interface was used, the cursor was B&W.
+ */
+ bool fAlpha = pThis->svga.uCursorOn ? false : true;
+
+ int rc = pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv,
+ true /*fVisible*/,
+ fAlpha,
+ pSVGAState->Cursor.xHotspot,
+ pSVGAState->Cursor.yHotspot,
+ pSVGAState->Cursor.width,
+ pSVGAState->Cursor.height,
+ pSVGAState->Cursor.pData);
+ AssertRC(rc);
+
+ if (pThis->svga.uCursorOn)
+ pThisCC->pDrv->pfnVBVAReportCursorPosition(pThisCC->pDrv, VBVA_CURSOR_VALID_DATA, SVGA_ID_INVALID, pThis->svga.uCursorX, pThis->svga.uCursorY);
+ }
+
+ /* If the VRAM handler should not be registered, we have to explicitly
+ * unregister it here!
+ */
+ if (!pThis->svga.fVRAMTracking)
+ {
+ vgaR3UnregisterVRAMHandler(pDevIns, pThis);
+ }
+
+ /* Let the FIFO thread deal with changing the mode. */
+ ASMAtomicOrU32(&pThis->svga.u32ActionFlags, VMSVGA_ACTION_CHANGEMODE);
+
+ return VINF_SUCCESS;
+}
+
+static int vmsvgaR3SaveBufCtx(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PVMSVGACMDBUFCTX pBufCtx)
+{
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ int rc = pHlp->pfnSSMPutU32(pSSM, pBufCtx->cSubmitted);
+ AssertLogRelRCReturn(rc, rc);
+ if (pBufCtx->cSubmitted)
+ {
+ PVMSVGACMDBUF pIter;
+ RTListForEach(&pBufCtx->listSubmitted, pIter, VMSVGACMDBUF, nodeBuffer)
+ {
+ pHlp->pfnSSMPutGCPhys(pSSM, pIter->GCPhysCB);
+ pHlp->pfnSSMPutU32(pSSM, sizeof(SVGACBHeader));
+ pHlp->pfnSSMPutMem(pSSM, &pIter->hdr, sizeof(SVGACBHeader));
+ pHlp->pfnSSMPutU32(pSSM, pIter->hdr.length);
+ if (pIter->hdr.length)
+ rc = pHlp->pfnSSMPutMem(pSSM, pIter->pvCommands, pIter->hdr.length);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+ return rc;
+}
+
+static int vmsvgaR3SaveGbo(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, VMSVGAGBO *pGbo)
+{
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ int rc;
+ pHlp->pfnSSMPutU32(pSSM, pGbo->fGboFlags);
+ pHlp->pfnSSMPutU32(pSSM, pGbo->cTotalPages);
+ pHlp->pfnSSMPutU32(pSSM, pGbo->cbTotal);
+ rc = pHlp->pfnSSMPutU32(pSSM, pGbo->cDescriptors);
+ for (uint32_t iDesc = 0; iDesc < pGbo->cDescriptors; ++iDesc)
+ {
+ PVMSVGAGBODESCRIPTOR pDesc = &pGbo->paDescriptors[iDesc];
+ pHlp->pfnSSMPutGCPhys(pSSM, pDesc->GCPhys);
+ rc = pHlp->pfnSSMPutU64(pSSM, pDesc->cPages);
+ }
+ if (pGbo->fGboFlags & VMSVGAGBO_F_HOST_BACKED)
+ rc = pHlp->pfnSSMPutMem(pSSM, pGbo->pvHost, pGbo->cbTotal);
+ return rc;
+}
+
+/**
+ * Portion of SVGA state which must be saved in the FIFO thread.
+ */
+static int vmsvgaR3SaveExecFifo(PCPDMDEVHLPR3 pHlp, PVGASTATECC pThisCC, PSSMHANDLE pSSM)
+{
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ int rc;
+
+ /* Save the screen objects. */
+ /* Count defined screen object. */
+ uint32_t cScreens = 0;
+ for (uint32_t i = 0; i < RT_ELEMENTS(pSVGAState->aScreens); ++i)
+ {
+ if (pSVGAState->aScreens[i].fDefined)
+ ++cScreens;
+ }
+
+ rc = pHlp->pfnSSMPutU32(pSSM, cScreens);
+ AssertLogRelRCReturn(rc, rc);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pSVGAState->aScreens); ++i)
+ {
+ VMSVGASCREENOBJECT *pScreen = &pSVGAState->aScreens[i];
+ if (!pScreen->fDefined)
+ continue;
+
+ rc = pHlp->pfnSSMPutStructEx(pSSM, pScreen, sizeof(*pScreen), 0, g_aVMSVGASCREENOBJECTFields, NULL);
+ AssertLogRelRCReturn(rc, rc);
+
+ /*
+ * VGA_SAVEDSTATE_VERSION_VMSVGA_DX
+ */
+ if (pScreen->pvScreenBitmap)
+ {
+ uint32_t const cbScreenBitmap = pScreen->cHeight * pScreen->cbPitch;
+ pHlp->pfnSSMPutU32(pSSM, cbScreenBitmap);
+ pHlp->pfnSSMPutMem(pSSM, pScreen->pvScreenBitmap, cbScreenBitmap);
+ }
+ else
+ pHlp->pfnSSMPutU32(pSSM, 0);
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc FNSSMDEVSAVEEXEC
+ */
+int vmsvgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ /* Save our part of the VGAState */
+ rc = pHlp->pfnSSMPutStructEx(pSSM, &pThis->svga, sizeof(pThis->svga), 0, g_aVGAStateSVGAFields, NULL);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* Save the framebuffer backup. */
+ rc = pHlp->pfnSSMPutU32(pSSM, VMSVGA_VGA_FB_BACKUP_SIZE);
+ rc = pHlp->pfnSSMPutMem(pSSM, pThisCC->svga.pbVgaFrameBufferR3, VMSVGA_VGA_FB_BACKUP_SIZE);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* Save the VMSVGA state. */
+ rc = pHlp->pfnSSMPutStructEx(pSSM, pSVGAState, sizeof(*pSVGAState), 0, g_aVMSVGAR3STATEFields, NULL);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* Save the active cursor bitmaps. */
+ if (pSVGAState->Cursor.fActive)
+ {
+ rc = pHlp->pfnSSMPutMem(pSSM, pSVGAState->Cursor.pData, pSVGAState->Cursor.cbData);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ /* Save the GMR state */
+ rc = pHlp->pfnSSMPutU32(pSSM, pThis->svga.cGMR);
+ AssertLogRelRCReturn(rc, rc);
+ for (uint32_t i = 0; i < pThis->svga.cGMR; ++i)
+ {
+ PGMR pGMR = &pSVGAState->paGMR[i];
+
+ rc = pHlp->pfnSSMPutStructEx(pSSM, pGMR, sizeof(*pGMR), 0, g_aGMRFields, NULL);
+ AssertLogRelRCReturn(rc, rc);
+
+ for (uint32_t j = 0; j < pGMR->numDescriptors; ++j)
+ {
+ rc = pHlp->pfnSSMPutStructEx(pSSM, &pGMR->paDesc[j], sizeof(pGMR->paDesc[j]), 0, g_aVMSVGAGMRDESCRIPTORFields, NULL);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+
+ /*
+ * VGA_SAVEDSTATE_VERSION_VMSVGA_DX+
+ */
+ if (pThis->svga.u32DeviceCaps & SVGA_CAP_COMMAND_BUFFERS)
+ {
+ rc = pHlp->pfnSSMPutBool(pSSM, true);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* Device context command buffers. */
+ rc = vmsvgaR3SaveBufCtx(pDevIns, pSSM, &pSVGAState->CmdBufCtxDC);
+ AssertRCReturn(rc, rc);
+
+ /* DX contexts command buffers. */
+ rc = pHlp->pfnSSMPutU32(pSSM, RT_ELEMENTS(pSVGAState->apCmdBufCtxs));
+ AssertLogRelRCReturn(rc, rc);
+ for (unsigned i = 0; i < RT_ELEMENTS(pSVGAState->apCmdBufCtxs); ++i)
+ {
+ if (pSVGAState->apCmdBufCtxs[i])
+ {
+ pHlp->pfnSSMPutBool(pSSM, true);
+ rc = vmsvgaR3SaveBufCtx(pDevIns, pSSM, pSVGAState->apCmdBufCtxs[i]);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ pHlp->pfnSSMPutBool(pSSM, false);
+ }
+
+ rc = pHlp->pfnSSMPutU32(pSSM, pSVGAState->fCmdBuf);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ rc = pHlp->pfnSSMPutBool(pSSM, false);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ rc = pHlp->pfnSSMPutBool(pSSM, pThis->fVMSVGA10);
+ AssertLogRelRCReturn(rc, rc);
+
+ if (pThis->fVMSVGA10)
+ {
+ /*
+ * OTables GBOs.
+ */
+ pHlp->pfnSSMPutU32(pSSM, SVGA_OTABLE_MAX);
+ for (int i = 0; i < SVGA_OTABLE_MAX; ++i)
+ {
+ VMSVGAGBO *pGbo = &pSVGAState->aGboOTables[i];
+ rc = vmsvgaR3SaveGbo(pDevIns, pSSM, pGbo);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * MOBs.
+ */
+ PVMSVGAMOB pIter;
+ RTListForEach(&pSVGAState->MOBLRUList, pIter, VMSVGAMOB, nodeLRU)
+ {
+ pHlp->pfnSSMPutU32(pSSM, pIter->Core.Key); /* MOB id. */
+ rc = vmsvgaR3SaveGbo(pDevIns, pSSM, &pIter->Gbo);
+ AssertRCReturn(rc, rc);
+ }
+
+ pHlp->pfnSSMPutU32(pSSM, SVGA_ID_INVALID); /* End marker. */
+
+# ifdef VMSVGA3D_DX
+ if (pThis->svga.f3DEnabled)
+ {
+ pHlp->pfnSSMPutU32(pSSM, pSVGAState->idDXContextCurrent);
+ }
+# endif
+ }
+
+ /*
+ * Must save some state (3D in particular) in the FIFO thread.
+ */
+ rc = vmsvgaR3RunExtCmdOnFifoThread(pDevIns, pThis, pThisCC, VMSVGA_FIFO_EXTCMD_SAVESTATE, pSSM, RT_INDEFINITE_WAIT);
+ AssertLogRelRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destructor for PVMSVGAR3STATE structure. The structure is not deallocated.
+ *
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The device context.
+ */
+static void vmsvgaR3StateTerm(PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+# ifndef VMSVGA_USE_EMT_HALT_CODE
+ if (pSVGAState->hBusyDelayedEmts != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(pSVGAState->hBusyDelayedEmts);
+ pSVGAState->hBusyDelayedEmts = NIL_RTSEMEVENT;
+ }
+# endif
+
+ if (pSVGAState->Cursor.fActive)
+ {
+ RTMemFreeZ(pSVGAState->Cursor.pData, pSVGAState->Cursor.cbData);
+ pSVGAState->Cursor.pData = NULL;
+ pSVGAState->Cursor.fActive = false;
+ }
+
+ if (pSVGAState->paGMR)
+ {
+ for (unsigned i = 0; i < pThis->svga.cGMR; ++i)
+ if (pSVGAState->paGMR[i].paDesc)
+ RTMemFree(pSVGAState->paGMR[i].paDesc);
+
+ RTMemFree(pSVGAState->paGMR);
+ pSVGAState->paGMR = NULL;
+ }
+
+ if (RTCritSectIsInitialized(&pSVGAState->CritSectCmdBuf))
+ {
+ RTCritSectEnter(&pSVGAState->CritSectCmdBuf);
+ for (unsigned i = 0; i < RT_ELEMENTS(pSVGAState->apCmdBufCtxs); ++i)
+ {
+ vmsvgaR3CmdBufCtxTerm(pSVGAState->apCmdBufCtxs[i]);
+ RTMemFree(pSVGAState->apCmdBufCtxs[i]);
+ pSVGAState->apCmdBufCtxs[i] = NULL;
+ }
+ vmsvgaR3CmdBufCtxTerm(&pSVGAState->CmdBufCtxDC);
+ RTCritSectLeave(&pSVGAState->CritSectCmdBuf);
+ RTCritSectDelete(&pSVGAState->CritSectCmdBuf);
+ }
+}
+
+/**
+ * Constructor for PVMSVGAR3STATE structure.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The PDM device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pSVGAState Pointer to the structure. It is already allocated.
+ */
+static int vmsvgaR3StateInit(PPDMDEVINS pDevIns, PVGASTATE pThis, PVMSVGAR3STATE pSVGAState)
+{
+ int rc = VINF_SUCCESS;
+
+ pSVGAState->pDevIns = pDevIns;
+
+ pSVGAState->paGMR = (PGMR)RTMemAllocZ(pThis->svga.cGMR * sizeof(GMR));
+ AssertReturn(pSVGAState->paGMR, VERR_NO_MEMORY);
+
+# ifndef VMSVGA_USE_EMT_HALT_CODE
+ /* Create semaphore for delaying EMTs wait for the FIFO to stop being busy. */
+ rc = RTSemEventMultiCreate(&pSVGAState->hBusyDelayedEmts);
+ AssertRCReturn(rc, rc);
+# endif
+
+ rc = RTCritSectInit(&pSVGAState->CritSectCmdBuf);
+ AssertRCReturn(rc, rc);
+
+ /* Init screen ids which are constant and allow to use a pointer to aScreens element and know its index. */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pSVGAState->aScreens); ++i)
+ pSVGAState->aScreens[i].idScreen = i;
+
+ vmsvgaR3CmdBufCtxInit(&pSVGAState->CmdBufCtxDC);
+
+ RTListInit(&pSVGAState->MOBLRUList);
+# ifdef VBOX_WITH_VMSVGA3D
+# ifdef VMSVGA3D_DX
+ pSVGAState->idDXContextCurrent = SVGA3D_INVALID_ID;
+# endif
+# endif
+ return rc;
+}
+
+# ifdef VBOX_WITH_VMSVGA3D
+static void vmsvga3dR3Free3dInterfaces(PVGASTATECC pThisCC)
+{
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ RTMemFree(pSVGAState->pFuncsMap);
+ pSVGAState->pFuncsMap = NULL;
+ RTMemFree(pSVGAState->pFuncsGBO);
+ pSVGAState->pFuncsGBO = NULL;
+ RTMemFree(pSVGAState->pFuncsDX);
+ pSVGAState->pFuncsDX = NULL;
+ RTMemFree(pSVGAState->pFuncsVGPU9);
+ pSVGAState->pFuncsVGPU9 = NULL;
+ RTMemFree(pSVGAState->pFuncs3D);
+ pSVGAState->pFuncs3D = NULL;
+}
+
+/* This structure is used only by vmsvgaR3Init3dInterfaces */
+typedef struct VMSVGA3DINTERFACE
+{
+ char const *pcszName;
+ uint32_t cbFuncs;
+ void **ppvFuncs;
+} VMSVGA3DINTERFACE;
+
+extern VMSVGA3DBACKENDDESC const g_BackendLegacy;
+#if defined(VMSVGA3D_DX_BACKEND)
+extern VMSVGA3DBACKENDDESC const g_BackendDX;
+#endif
+
+/**
+ * Initializes the optional host 3D backend interfaces.
+ *
+ * @returns VBox status code.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+static int vmsvgaR3Init3dInterfaces(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+#ifndef VMSVGA3D_DX
+ RT_NOREF(pThis);
+#endif
+
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+#define ENTRY_3D_INTERFACE(a_Name, a_Field) { VMSVGA3D_BACKEND_INTERFACE_NAME_##a_Name, sizeof(VMSVGA3DBACKENDFUNCS##a_Name), (void **)&pSVGAState->a_Field }
+ VMSVGA3DINTERFACE a3dInterface[] =
+ {
+ ENTRY_3D_INTERFACE(3D, pFuncs3D),
+ ENTRY_3D_INTERFACE(VGPU9, pFuncsVGPU9),
+ ENTRY_3D_INTERFACE(DX, pFuncsDX),
+ ENTRY_3D_INTERFACE(MAP, pFuncsMap),
+ ENTRY_3D_INTERFACE(GBO, pFuncsGBO),
+ };
+#undef ENTRY_3D_INTERFACE
+
+ VMSVGA3DBACKENDDESC const *pBackend = NULL;
+#if defined(VMSVGA3D_DX_BACKEND)
+ if (pThis->fVMSVGA10)
+ pBackend = &g_BackendDX;
+ else
+#endif
+ pBackend = &g_BackendLegacy;
+
+ int rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < RT_ELEMENTS(a3dInterface); ++i)
+ {
+ VMSVGA3DINTERFACE *p = &a3dInterface[i];
+
+ int rc2 = pBackend->pfnQueryInterface(pThisCC, p->pcszName, NULL, p->cbFuncs);
+ if (RT_SUCCESS(rc2))
+ {
+ *p->ppvFuncs = RTMemAllocZ(p->cbFuncs);
+ AssertBreakStmt(*p->ppvFuncs, rc = VERR_NO_MEMORY);
+
+ pBackend->pfnQueryInterface(pThisCC, p->pcszName, *p->ppvFuncs, p->cbFuncs);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vmsvga3dInit(pDevIns, pThis, pThisCC);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+
+ vmsvga3dR3Free3dInterfaces(pThisCC);
+ return rc;
+}
+# endif /* VBOX_WITH_VMSVGA3D */
+
+/**
+ * Compute the host capabilities: device and FIFO.
+ *
+ * Depends on 3D backend initialization.
+ *
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ * @param pu32DeviceCaps Device capabilities (SVGA_CAP_*).
+ * @param pu32DeviceCaps2 Device capabilities (SVGA_CAP2_*).
+ * @param pu32FIFOCaps FIFO capabilities (SVGA_FIFO_CAPS_*).
+ */
+static void vmsvgaR3GetCaps(PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t *pu32DeviceCaps, uint32_t *pu32DeviceCaps2, uint32_t *pu32FIFOCaps)
+{
+#ifndef VBOX_WITH_VMSVGA3D
+ RT_NOREF(pThisCC);
+#endif
+
+ /* Device caps. */
+ *pu32DeviceCaps = SVGA_CAP_GMR
+ | SVGA_CAP_GMR2
+ | SVGA_CAP_CURSOR
+ | SVGA_CAP_CURSOR_BYPASS
+ | SVGA_CAP_CURSOR_BYPASS_2
+ | SVGA_CAP_EXTENDED_FIFO
+ | SVGA_CAP_IRQMASK
+ | SVGA_CAP_PITCHLOCK
+ | SVGA_CAP_RECT_COPY
+ | SVGA_CAP_TRACES
+ | SVGA_CAP_SCREEN_OBJECT_2
+ | SVGA_CAP_ALPHA_CURSOR;
+
+ *pu32DeviceCaps |= SVGA_CAP_COMMAND_BUFFERS /* Enable register based command buffer submission. */
+ ;
+
+ *pu32DeviceCaps2 = SVGA_CAP2_NONE;
+
+ /* VGPU10 capabilities. */
+ if (pThis->fVMSVGA10)
+ {
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThisCC->svga.pSvgaR3State->pFuncsGBO)
+ *pu32DeviceCaps |= SVGA_CAP_GBOBJECTS; /* Enable guest-backed objects and surfaces. */
+ if (pThisCC->svga.pSvgaR3State->pFuncsDX)
+ {
+ *pu32DeviceCaps |= SVGA_CAP_DX /* DX commands, and command buffers in a mob. */
+ | SVGA_CAP_CAP2_REGISTER /* Extended capabilities. */
+ ;
+
+ if (*pu32DeviceCaps & SVGA_CAP_CAP2_REGISTER)
+ *pu32DeviceCaps2 |= SVGA_CAP2_GROW_OTABLE /* "Allow the GrowOTable/DXGrowCOTable commands" */
+ | SVGA_CAP2_INTRA_SURFACE_COPY /* "IntraSurfaceCopy command" */
+ | SVGA_CAP2_DX2 /* Shader Model 4.1.
+ * "Allow the DefineGBSurface_v3, WholeSurfaceCopy, WriteZeroSurface, and
+ * HintZeroSurface commands, and the SVGA_REG_GUEST_DRIVER_ID register."
+ */
+ | SVGA_CAP2_GB_MEMSIZE_2 /* "Allow the SVGA_REG_GBOBJECT_MEM_SIZE_KB register" */
+ | SVGA_CAP2_OTABLE_PTDEPTH_2
+ | SVGA_CAP2_DX3 /* Shader Model 5.
+ * DefineGBSurface_v4, etc
+ */
+ ;
+ }
+# endif
+ }
+
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThisCC->svga.pSvgaR3State->pFuncs3D)
+ *pu32DeviceCaps |= SVGA_CAP_3D;
+# endif
+
+ /* FIFO capabilities. */
+ *pu32FIFOCaps = SVGA_FIFO_CAP_FENCE
+ | SVGA_FIFO_CAP_PITCHLOCK
+ | SVGA_FIFO_CAP_CURSOR_BYPASS_3
+ | SVGA_FIFO_CAP_RESERVE
+ | SVGA_FIFO_CAP_GMR2
+ | SVGA_FIFO_CAP_3D_HWVERSION_REVISED
+ | SVGA_FIFO_CAP_SCREEN_OBJECT_2;
+}
+
+/** Initialize the FIFO on power on and reset.
+ *
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+static void vmsvgaR3InitFIFO(PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ RT_BZERO(pThisCC->svga.pau32FIFO, pThis->svga.cbFIFO);
+
+ /* Valid with SVGA_FIFO_CAP_SCREEN_OBJECT_2 */
+ pThisCC->svga.pau32FIFO[SVGA_FIFO_CURSOR_SCREEN_ID] = SVGA_ID_INVALID;
+}
+
+# ifdef VBOX_WITH_VMSVGA3D
+/**
+ * Initializes the host 3D capabilities and writes them to FIFO memory.
+ *
+ * @returns VBox status code.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ */
+static void vmsvgaR3InitFifo3DCaps(PVGASTATE pThis, PVGASTATECC pThisCC)
+{
+ /* Query the capabilities and store them in the pThis->svga.au32DevCaps array. */
+ bool const fSavedBuffering = RTLogRelSetBuffering(true);
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->svga.au32DevCaps); ++i)
+ {
+ uint32_t val = 0;
+ int rc = vmsvga3dQueryCaps(pThisCC, (SVGA3dDevCapIndex)i, &val);
+ if (RT_SUCCESS(rc))
+ pThis->svga.au32DevCaps[i] = val;
+ else
+ pThis->svga.au32DevCaps[i] = 0;
+
+ /* LogRel the capability value. */
+ if (i < SVGA3D_DEVCAP_MAX)
+ {
+ char const *pszDevCapName = &vmsvgaDevCapIndexToString((SVGA3dDevCapIndex)i)[sizeof("SVGA3D_DEVCAP")];
+ if (RT_SUCCESS(rc))
+ {
+ if ( i == SVGA3D_DEVCAP_MAX_POINT_SIZE
+ || i == SVGA3D_DEVCAP_MAX_LINE_WIDTH
+ || i == SVGA3D_DEVCAP_MAX_AA_LINE_WIDTH)
+ {
+ float const fval = *(float *)&val;
+ LogRel(("VMSVGA3d: cap[%u]=" FLOAT_FMT_STR " {%s}\n", i, FLOAT_FMT_ARGS(fval), pszDevCapName));
+ }
+ else
+ LogRel(("VMSVGA3d: cap[%u]=%#010x {%s}\n", i, val, pszDevCapName));
+ }
+ else
+ LogRel(("VMSVGA3d: cap[%u]=failed rc=%Rrc {%s}\n", i, rc, pszDevCapName));
+ }
+ else
+ LogRel(("VMSVGA3d: new cap[%u]=%#010x rc=%Rrc\n", i, val, rc));
+ }
+
+ RTLogRelSetBuffering(fSavedBuffering);
+
+ /* 3d hardware version; latest and greatest */
+ pThisCC->svga.pau32FIFO[SVGA_FIFO_3D_HWVERSION_REVISED] = SVGA3D_HWVERSION_CURRENT;
+ pThisCC->svga.pau32FIFO[SVGA_FIFO_3D_HWVERSION] = SVGA3D_HWVERSION_CURRENT;
+
+ /* Fill out 3d capabilities up to SVGA3D_DEVCAP_SURFACEFMT_ATI2 in the FIFO memory.
+ * SVGA3D_DEVCAP_SURFACEFMT_ATI2 is the last capabiltiy for pre-SVGA_CAP_GBOBJECTS hardware.
+ * If the VMSVGA device supports SVGA_CAP_GBOBJECTS capability, then the guest has to use SVGA_REG_DEV_CAP
+ * register to query the devcaps. Older guests will still try to read the devcaps from FIFO.
+ */
+ SVGA3dCapsRecord *pCaps;
+ SVGA3dCapPair *pData;
+
+ pCaps = (SVGA3dCapsRecord *)&pThisCC->svga.pau32FIFO[SVGA_FIFO_3D_CAPS];
+ pCaps->header.type = SVGA3DCAPS_RECORD_DEVCAPS;
+ pData = (SVGA3dCapPair *)&pCaps->data;
+
+ AssertCompile(SVGA3D_DEVCAP_DEAD1 == SVGA3D_DEVCAP_SURFACEFMT_ATI2 + 1);
+ for (unsigned i = 0; i < SVGA3D_DEVCAP_DEAD1; ++i)
+ {
+ pData[i][0] = i;
+ pData[i][1] = pThis->svga.au32DevCaps[i];
+ }
+ pCaps->header.length = (sizeof(pCaps->header) + SVGA3D_DEVCAP_DEAD1 * sizeof(SVGA3dCapPair)) / sizeof(uint32_t);
+ pCaps = (SVGA3dCapsRecord *)((uint32_t *)pCaps + pCaps->header.length);
+
+ /* Mark end of record array (a zero word). */
+ pCaps->header.length = 0;
+}
+
+# endif
+
+/**
+ * Resets the SVGA hardware state
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ */
+int vmsvgaR3Reset(PPDMDEVINS pDevIns)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ /* Reset before init? */
+ if (!pSVGAState)
+ return VINF_SUCCESS;
+
+ Log(("vmsvgaR3Reset\n"));
+
+ /* Reset the FIFO processing as well as the 3d state (if we have one). */
+ pThisCC->svga.pau32FIFO[SVGA_FIFO_NEXT_CMD] = pThisCC->svga.pau32FIFO[SVGA_FIFO_STOP] = 0; /** @todo should probably let the FIFO thread do this ... */
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* Hack around lock order issue. FIFO thread might take the lock. */
+
+ int rc = vmsvgaR3RunExtCmdOnFifoThread(pDevIns, pThis, pThisCC, VMSVGA_FIFO_EXTCMD_RESET, NULL /*pvParam*/, 60000 /*ms*/);
+ AssertLogRelRC(rc);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /* Reset other stuff. */
+ pThis->svga.cScratchRegion = VMSVGA_SCRATCH_SIZE;
+ RT_ZERO(pThis->svga.au32ScratchRegion);
+
+ ASMAtomicWriteBool(&pThis->svga.fBadGuest, false);
+
+ vmsvgaR3StateTerm(pThis, pThisCC);
+ vmsvgaR3StateInit(pDevIns, pThis, pThisCC->svga.pSvgaR3State);
+
+ RT_BZERO(pThisCC->svga.pbVgaFrameBufferR3, VMSVGA_VGA_FB_BACKUP_SIZE);
+
+ vmsvgaR3InitFIFO(pThis, pThisCC);
+
+ /* Initialize FIFO and register capabilities. */
+ vmsvgaR3GetCaps(pThis, pThisCC, &pThis->svga.u32DeviceCaps, &pThis->svga.u32DeviceCaps2, &pThisCC->svga.pau32FIFO[SVGA_FIFO_CAPABILITIES]);
+
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThis->svga.f3DEnabled)
+ vmsvgaR3InitFifo3DCaps(pThis, pThisCC);
+# endif
+
+ /* VRAM tracking is enabled by default during bootup. */
+ pThis->svga.fVRAMTracking = true;
+ pThis->svga.fEnabled = false;
+
+ /* Invalidate current settings. */
+ pThis->svga.uWidth = VMSVGA_VAL_UNINITIALIZED;
+ pThis->svga.uHeight = VMSVGA_VAL_UNINITIALIZED;
+ pThis->svga.uBpp = pThis->svga.uHostBpp;
+ pThis->svga.cbScanline = 0;
+ pThis->svga.u32PitchLock = 0;
+
+ return rc;
+}
+
+/**
+ * Cleans up the SVGA hardware state
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ */
+int vmsvgaR3Destruct(PPDMDEVINS pDevIns)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+
+ /*
+ * Ask the FIFO thread to terminate the 3d state and then terminate it.
+ */
+ if (pThisCC->svga.pFIFOIOThread)
+ {
+ int rc = vmsvgaR3RunExtCmdOnFifoThread(pDevIns, pThis, pThisCC, VMSVGA_FIFO_EXTCMD_TERMINATE,
+ NULL /*pvParam*/, 30000 /*ms*/);
+ AssertLogRelRC(rc);
+
+ rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->svga.pFIFOIOThread, NULL);
+ AssertLogRelRC(rc);
+ pThisCC->svga.pFIFOIOThread = NULL;
+ }
+
+ /*
+ * Destroy the special SVGA state.
+ */
+ if (pThisCC->svga.pSvgaR3State)
+ {
+ vmsvgaR3StateTerm(pThis, pThisCC);
+
+# ifdef VBOX_WITH_VMSVGA3D
+ vmsvga3dR3Free3dInterfaces(pThisCC);
+# endif
+
+ RTMemFree(pThisCC->svga.pSvgaR3State);
+ pThisCC->svga.pSvgaR3State = NULL;
+ }
+
+ /*
+ * Free our resources residing in the VGA state.
+ */
+ if (pThisCC->svga.pbVgaFrameBufferR3)
+ {
+ RTMemFree(pThisCC->svga.pbVgaFrameBufferR3);
+ pThisCC->svga.pbVgaFrameBufferR3 = NULL;
+ }
+ if (pThisCC->svga.hFIFOExtCmdSem != NIL_RTSEMEVENT)
+ {
+ RTSemEventDestroy(pThisCC->svga.hFIFOExtCmdSem);
+ pThisCC->svga.hFIFOExtCmdSem = NIL_RTSEMEVENT;
+ }
+ if (pThis->svga.hFIFORequestSem != NIL_SUPSEMEVENT)
+ {
+ PDMDevHlpSUPSemEventClose(pDevIns, pThis->svga.hFIFORequestSem);
+ pThis->svga.hFIFORequestSem = NIL_SUPSEMEVENT;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(size_t) vmsvga3dFloatFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags, void *pvUser)
+{
+ RT_NOREF(pszType, cchWidth, cchPrecision, fFlags, pvUser);
+ double const v = *(double *)&pvValue;
+ return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, FLOAT_FMT_STR, FLOAT_FMT_ARGS(v));
+}
+
+/**
+ * Initialize the SVGA hardware state
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ */
+int vmsvgaR3Init(PPDMDEVINS pDevIns)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+ PVMSVGAR3STATE pSVGAState;
+ int rc;
+
+ rc = RTStrFormatTypeRegister("float", vmsvga3dFloatFormat, NULL);
+ AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc);
+
+ pThis->svga.cScratchRegion = VMSVGA_SCRATCH_SIZE;
+ memset(pThis->svga.au32ScratchRegion, 0, sizeof(pThis->svga.au32ScratchRegion));
+
+ pThis->svga.cGMR = VMSVGA_MAX_GMR_IDS;
+
+ /* Necessary for creating a backup of the text mode frame buffer when switching into svga mode. */
+ pThisCC->svga.pbVgaFrameBufferR3 = (uint8_t *)RTMemAllocZ(VMSVGA_VGA_FB_BACKUP_SIZE);
+ AssertReturn(pThisCC->svga.pbVgaFrameBufferR3, VERR_NO_MEMORY);
+
+ /* Create event semaphore. */
+ rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->svga.hFIFORequestSem);
+ AssertRCReturn(rc, rc);
+
+ /* Create event semaphore. */
+ rc = RTSemEventCreate(&pThisCC->svga.hFIFOExtCmdSem);
+ AssertRCReturn(rc, rc);
+
+ pThisCC->svga.pSvgaR3State = (PVMSVGAR3STATE)RTMemAllocZ(sizeof(VMSVGAR3STATE));
+ AssertReturn(pThisCC->svga.pSvgaR3State, VERR_NO_MEMORY);
+
+ rc = vmsvgaR3StateInit(pDevIns, pThis, pThisCC->svga.pSvgaR3State);
+ AssertMsgRCReturn(rc, ("Failed to create pSvgaR3State.\n"), rc);
+
+ pSVGAState = pThisCC->svga.pSvgaR3State;
+
+ /* VRAM tracking is enabled by default during bootup. */
+ pThis->svga.fVRAMTracking = true;
+
+ /* Set up the host bpp. This value is as a default for the programmable
+ * bpp value. On old implementations, SVGA_REG_HOST_BITS_PER_PIXEL did not
+ * exist and SVGA_REG_BITS_PER_PIXEL was read-only, returning what was later
+ * separated as SVGA_REG_HOST_BITS_PER_PIXEL.
+ *
+ * NB: The driver cBits value is currently constant for the lifetime of the
+ * VM. If that changes, the host bpp logic might need revisiting.
+ */
+ pThis->svga.uHostBpp = (pThisCC->pDrv->cBits + 7) & ~7;
+
+ /* Invalidate current settings. */
+ pThis->svga.uWidth = VMSVGA_VAL_UNINITIALIZED;
+ pThis->svga.uHeight = VMSVGA_VAL_UNINITIALIZED;
+ pThis->svga.uBpp = pThis->svga.uHostBpp;
+ pThis->svga.cbScanline = 0;
+
+ pThis->svga.u32MaxWidth = VBE_DISPI_MAX_XRES;
+ pThis->svga.u32MaxHeight = VBE_DISPI_MAX_YRES;
+ while (pThis->svga.u32MaxWidth * pThis->svga.u32MaxHeight * 4 /* 32 bpp */ > pThis->vram_size)
+ {
+ pThis->svga.u32MaxWidth -= 256;
+ pThis->svga.u32MaxHeight -= 256;
+ }
+ Log(("VMSVGA: Maximum size (%d,%d)\n", pThis->svga.u32MaxWidth, pThis->svga.u32MaxHeight));
+
+# ifdef DEBUG_GMR_ACCESS
+ /* Register the GMR access handler type. */
+ rc = PDMDevHlpPGMHandlerPhysicalTypeRegister(pDevIns, PGMPHYSHANDLERKIND_WRITE, vmsvgaR3GmrAccessHandler,
+ "VMSVGA GMR", &pThis->svga.hGmrAccessHandlerType);
+ AssertRCReturn(rc, rc);
+# endif
+
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+ /* Register the FIFO access handler type. In addition to debugging FIFO
+ access, this is also used to facilitate extended fifo thread sleeps. */
+ rc = PDMDevHlpPGMHandlerPhysicalTypeRegister(pDevIns,
+# ifdef DEBUG_FIFO_ACCESS
+ PGMPHYSHANDLERKIND_ALL,
+# else
+ PGMPHYSHANDLERKIND_WRITE,
+# endif
+ vmsvgaR3FifoAccessHandler,
+ "VMSVGA FIFO", &pThis->svga.hFifoAccessHandlerType);
+ AssertRCReturn(rc, rc);
+# endif
+
+ /* Create the async IO thread. */
+ rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->svga.pFIFOIOThread, pThis, vmsvgaR3FifoLoop, vmsvgaR3FifoLoopWakeUp, 0,
+ RTTHREADTYPE_IO, "VMSVGA FIFO");
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("%s: Async IO Thread creation for FIFO handling failed rc=%d\n", __FUNCTION__, rc));
+ return rc;
+ }
+
+ /*
+ * Statistics.
+ */
+# define REG_CNT(a_pvSample, a_pszName, a_pszDesc) \
+ PDMDevHlpSTAMRegister(pDevIns, (a_pvSample), STAMTYPE_COUNTER, a_pszName, STAMUNIT_OCCURENCES, a_pszDesc)
+# define REG_PRF(a_pvSample, a_pszName, a_pszDesc) \
+ PDMDevHlpSTAMRegister(pDevIns, (a_pvSample), STAMTYPE_PROFILE, a_pszName, STAMUNIT_TICKS_PER_CALL, a_pszDesc)
+# ifdef VBOX_WITH_STATISTICS
+ REG_PRF(&pSVGAState->StatR3Cmd3dDrawPrimitivesProf, "VMSVGA/Cmd/3dDrawPrimitivesProf", "Profiling of SVGA_3D_CMD_DRAW_PRIMITIVES.");
+ REG_PRF(&pSVGAState->StatR3Cmd3dPresentProf, "VMSVGA/Cmd/3dPresentProfBoth", "Profiling of SVGA_3D_CMD_PRESENT and SVGA_3D_CMD_PRESENT_READBACK.");
+ REG_PRF(&pSVGAState->StatR3Cmd3dSurfaceDmaProf, "VMSVGA/Cmd/3dSurfaceDmaProf", "Profiling of SVGA_3D_CMD_SURFACE_DMA.");
+# endif
+ REG_PRF(&pSVGAState->StatR3Cmd3dBlitSurfaceToScreenProf, "VMSVGA/Cmd/3dBlitSurfaceToScreenProf", "Profiling of SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN.");
+ REG_CNT(&pSVGAState->StatR3Cmd3dActivateSurface, "VMSVGA/Cmd/3dActivateSurface", "SVGA_3D_CMD_ACTIVATE_SURFACE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dBeginQuery, "VMSVGA/Cmd/3dBeginQuery", "SVGA_3D_CMD_BEGIN_QUERY");
+ REG_CNT(&pSVGAState->StatR3Cmd3dClear, "VMSVGA/Cmd/3dClear", "SVGA_3D_CMD_CLEAR");
+ REG_CNT(&pSVGAState->StatR3Cmd3dContextDefine, "VMSVGA/Cmd/3dContextDefine", "SVGA_3D_CMD_CONTEXT_DEFINE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dContextDestroy, "VMSVGA/Cmd/3dContextDestroy", "SVGA_3D_CMD_CONTEXT_DESTROY");
+ REG_CNT(&pSVGAState->StatR3Cmd3dDeactivateSurface, "VMSVGA/Cmd/3dDeactivateSurface", "SVGA_3D_CMD_DEACTIVATE_SURFACE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dDrawPrimitives, "VMSVGA/Cmd/3dDrawPrimitives", "SVGA_3D_CMD_DRAW_PRIMITIVES");
+ REG_CNT(&pSVGAState->StatR3Cmd3dEndQuery, "VMSVGA/Cmd/3dEndQuery", "SVGA_3D_CMD_END_QUERY");
+ REG_CNT(&pSVGAState->StatR3Cmd3dGenerateMipmaps, "VMSVGA/Cmd/3dGenerateMipmaps", "SVGA_3D_CMD_GENERATE_MIPMAPS");
+ REG_CNT(&pSVGAState->StatR3Cmd3dPresent, "VMSVGA/Cmd/3dPresent", "SVGA_3D_CMD_PRESENT");
+ REG_CNT(&pSVGAState->StatR3Cmd3dPresentReadBack, "VMSVGA/Cmd/3dPresentReadBack", "SVGA_3D_CMD_PRESENT_READBACK");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetClipPlane, "VMSVGA/Cmd/3dSetClipPlane", "SVGA_3D_CMD_SETCLIPPLANE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetLightData, "VMSVGA/Cmd/3dSetLightData", "SVGA_3D_CMD_SETLIGHTDATA");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetLightEnable, "VMSVGA/Cmd/3dSetLightEnable", "SVGA_3D_CMD_SETLIGHTENABLE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetMaterial, "VMSVGA/Cmd/3dSetMaterial", "SVGA_3D_CMD_SETMATERIAL");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetRenderState, "VMSVGA/Cmd/3dSetRenderState", "SVGA_3D_CMD_SETRENDERSTATE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetRenderTarget, "VMSVGA/Cmd/3dSetRenderTarget", "SVGA_3D_CMD_SETRENDERTARGET");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetScissorRect, "VMSVGA/Cmd/3dSetScissorRect", "SVGA_3D_CMD_SETSCISSORRECT");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetShader, "VMSVGA/Cmd/3dSetShader", "SVGA_3D_CMD_SET_SHADER");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetShaderConst, "VMSVGA/Cmd/3dSetShaderConst", "SVGA_3D_CMD_SET_SHADER_CONST");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetTextureState, "VMSVGA/Cmd/3dSetTextureState", "SVGA_3D_CMD_SETTEXTURESTATE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetTransform, "VMSVGA/Cmd/3dSetTransform", "SVGA_3D_CMD_SETTRANSFORM");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetViewPort, "VMSVGA/Cmd/3dSetViewPort", "SVGA_3D_CMD_SETVIEWPORT");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSetZRange, "VMSVGA/Cmd/3dSetZRange", "SVGA_3D_CMD_SETZRANGE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dShaderDefine, "VMSVGA/Cmd/3dShaderDefine", "SVGA_3D_CMD_SHADER_DEFINE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dShaderDestroy, "VMSVGA/Cmd/3dShaderDestroy", "SVGA_3D_CMD_SHADER_DESTROY");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceCopy, "VMSVGA/Cmd/3dSurfaceCopy", "SVGA_3D_CMD_SURFACE_COPY");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceDefine, "VMSVGA/Cmd/3dSurfaceDefine", "SVGA_3D_CMD_SURFACE_DEFINE");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceDefineV2, "VMSVGA/Cmd/3dSurfaceDefineV2", "SVGA_3D_CMD_SURFACE_DEFINE_V2");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceDestroy, "VMSVGA/Cmd/3dSurfaceDestroy", "SVGA_3D_CMD_SURFACE_DESTROY");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceDma, "VMSVGA/Cmd/3dSurfaceDma", "SVGA_3D_CMD_SURFACE_DMA");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceScreen, "VMSVGA/Cmd/3dSurfaceScreen", "SVGA_3D_CMD_SURFACE_SCREEN");
+ REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceStretchBlt, "VMSVGA/Cmd/3dSurfaceStretchBlt", "SVGA_3D_CMD_SURFACE_STRETCHBLT");
+ REG_CNT(&pSVGAState->StatR3Cmd3dWaitForQuery, "VMSVGA/Cmd/3dWaitForQuery", "SVGA_3D_CMD_WAIT_FOR_QUERY");
+ REG_CNT(&pSVGAState->StatR3CmdAnnotationCopy, "VMSVGA/Cmd/AnnotationCopy", "SVGA_CMD_ANNOTATION_COPY");
+ REG_CNT(&pSVGAState->StatR3CmdAnnotationFill, "VMSVGA/Cmd/AnnotationFill", "SVGA_CMD_ANNOTATION_FILL");
+ REG_CNT(&pSVGAState->StatR3CmdBlitGmrFbToScreen, "VMSVGA/Cmd/BlitGmrFbToScreen", "SVGA_CMD_BLIT_GMRFB_TO_SCREEN");
+ REG_CNT(&pSVGAState->StatR3CmdBlitScreentoGmrFb, "VMSVGA/Cmd/BlitScreentoGmrFb", "SVGA_CMD_BLIT_SCREEN_TO_GMRFB");
+ REG_CNT(&pSVGAState->StatR3CmdDefineAlphaCursor, "VMSVGA/Cmd/DefineAlphaCursor", "SVGA_CMD_DEFINE_ALPHA_CURSOR");
+ REG_CNT(&pSVGAState->StatR3CmdDefineCursor, "VMSVGA/Cmd/DefineCursor", "SVGA_CMD_DEFINE_CURSOR");
+ REG_CNT(&pSVGAState->StatR3CmdMoveCursor, "VMSVGA/Cmd/MoveCursor", "SVGA_CMD_MOVE_CURSOR");
+ REG_CNT(&pSVGAState->StatR3CmdDisplayCursor, "VMSVGA/Cmd/DisplayCursor", "SVGA_CMD_DISPLAY_CURSOR");
+ REG_CNT(&pSVGAState->StatR3CmdRectFill, "VMSVGA/Cmd/RectFill", "SVGA_CMD_RECT_FILL");
+ REG_CNT(&pSVGAState->StatR3CmdRectCopy, "VMSVGA/Cmd/RectCopy", "SVGA_CMD_RECT_COPY");
+ REG_CNT(&pSVGAState->StatR3CmdRectRopCopy, "VMSVGA/Cmd/RectRopCopy", "SVGA_CMD_RECT_ROP_COPY");
+ REG_CNT(&pSVGAState->StatR3CmdDefineGmr2, "VMSVGA/Cmd/DefineGmr2", "SVGA_CMD_DEFINE_GMR2");
+ REG_CNT(&pSVGAState->StatR3CmdDefineGmr2Free, "VMSVGA/Cmd/DefineGmr2/Free", "Number of SVGA_CMD_DEFINE_GMR2 commands that only frees.");
+ REG_CNT(&pSVGAState->StatR3CmdDefineGmr2Modify, "VMSVGA/Cmd/DefineGmr2/Modify", "Number of SVGA_CMD_DEFINE_GMR2 commands that redefines a non-free GMR.");
+ REG_CNT(&pSVGAState->StatR3CmdDefineGmrFb, "VMSVGA/Cmd/DefineGmrFb", "SVGA_CMD_DEFINE_GMRFB");
+ REG_CNT(&pSVGAState->StatR3CmdDefineScreen, "VMSVGA/Cmd/DefineScreen", "SVGA_CMD_DEFINE_SCREEN");
+ REG_CNT(&pSVGAState->StatR3CmdDestroyScreen, "VMSVGA/Cmd/DestroyScreen", "SVGA_CMD_DESTROY_SCREEN");
+ REG_CNT(&pSVGAState->StatR3CmdEscape, "VMSVGA/Cmd/Escape", "SVGA_CMD_ESCAPE");
+ REG_CNT(&pSVGAState->StatR3CmdFence, "VMSVGA/Cmd/Fence", "SVGA_CMD_FENCE");
+ REG_CNT(&pSVGAState->StatR3CmdInvalidCmd, "VMSVGA/Cmd/InvalidCmd", "SVGA_CMD_INVALID_CMD");
+ REG_CNT(&pSVGAState->StatR3CmdRemapGmr2, "VMSVGA/Cmd/RemapGmr2", "SVGA_CMD_REMAP_GMR2");
+ REG_CNT(&pSVGAState->StatR3CmdRemapGmr2Modify, "VMSVGA/Cmd/RemapGmr2/Modify", "Number of SVGA_CMD_REMAP_GMR2 commands that modifies rather than complete the definition of a GMR.");
+ REG_CNT(&pSVGAState->StatR3CmdUpdate, "VMSVGA/Cmd/Update", "SVGA_CMD_UPDATE");
+ REG_CNT(&pSVGAState->StatR3CmdUpdateVerbose, "VMSVGA/Cmd/UpdateVerbose", "SVGA_CMD_UPDATE_VERBOSE");
+
+ REG_CNT(&pSVGAState->StatR3RegConfigDoneWr, "VMSVGA/Reg/ConfigDoneWrite", "SVGA_REG_CONFIG_DONE writes");
+ REG_CNT(&pSVGAState->StatR3RegGmrDescriptorWr, "VMSVGA/Reg/GmrDescriptorWrite", "SVGA_REG_GMR_DESCRIPTOR writes");
+ REG_CNT(&pSVGAState->StatR3RegGmrDescriptorWrErrors, "VMSVGA/Reg/GmrDescriptorWrite/Errors", "Number of erroneous SVGA_REG_GMR_DESCRIPTOR commands.");
+ REG_CNT(&pSVGAState->StatR3RegGmrDescriptorWrFree, "VMSVGA/Reg/GmrDescriptorWrite/Free", "Number of SVGA_REG_GMR_DESCRIPTOR commands only freeing the GMR.");
+ REG_CNT(&pThis->svga.StatRegBitsPerPixelWr, "VMSVGA/Reg/BitsPerPixelWrite", "SVGA_REG_BITS_PER_PIXEL writes.");
+ REG_CNT(&pThis->svga.StatRegBusyWr, "VMSVGA/Reg/BusyWrite", "SVGA_REG_BUSY writes.");
+ REG_CNT(&pThis->svga.StatRegCursorXWr, "VMSVGA/Reg/CursorXWrite", "SVGA_REG_CURSOR_X writes.");
+ REG_CNT(&pThis->svga.StatRegCursorYWr, "VMSVGA/Reg/CursorYWrite", "SVGA_REG_CURSOR_Y writes.");
+ REG_CNT(&pThis->svga.StatRegCursorIdWr, "VMSVGA/Reg/CursorIdWrite", "SVGA_REG_DEAD (SVGA_REG_CURSOR_ID) writes.");
+ REG_CNT(&pThis->svga.StatRegCursorOnWr, "VMSVGA/Reg/CursorOnWrite", "SVGA_REG_CURSOR_ON writes.");
+ REG_CNT(&pThis->svga.StatRegDepthWr, "VMSVGA/Reg/DepthWrite", "SVGA_REG_DEPTH writes.");
+ REG_CNT(&pThis->svga.StatRegDisplayHeightWr, "VMSVGA/Reg/DisplayHeightWrite", "SVGA_REG_DISPLAY_HEIGHT writes.");
+ REG_CNT(&pThis->svga.StatRegDisplayIdWr, "VMSVGA/Reg/DisplayIdWrite", "SVGA_REG_DISPLAY_ID writes.");
+ REG_CNT(&pThis->svga.StatRegDisplayIsPrimaryWr, "VMSVGA/Reg/DisplayIsPrimaryWrite", "SVGA_REG_DISPLAY_IS_PRIMARY writes.");
+ REG_CNT(&pThis->svga.StatRegDisplayPositionXWr, "VMSVGA/Reg/DisplayPositionXWrite", "SVGA_REG_DISPLAY_POSITION_X writes.");
+ REG_CNT(&pThis->svga.StatRegDisplayPositionYWr, "VMSVGA/Reg/DisplayPositionYWrite", "SVGA_REG_DISPLAY_POSITION_Y writes.");
+ REG_CNT(&pThis->svga.StatRegDisplayWidthWr, "VMSVGA/Reg/DisplayWidthWrite", "SVGA_REG_DISPLAY_WIDTH writes.");
+ REG_CNT(&pThis->svga.StatRegEnableWr, "VMSVGA/Reg/EnableWrite", "SVGA_REG_ENABLE writes.");
+ REG_CNT(&pThis->svga.StatRegGmrIdWr, "VMSVGA/Reg/GmrIdWrite", "SVGA_REG_GMR_ID writes.");
+ REG_CNT(&pThis->svga.StatRegGuestIdWr, "VMSVGA/Reg/GuestIdWrite", "SVGA_REG_GUEST_ID writes.");
+ REG_CNT(&pThis->svga.StatRegHeightWr, "VMSVGA/Reg/HeightWrite", "SVGA_REG_HEIGHT writes.");
+ REG_CNT(&pThis->svga.StatRegIdWr, "VMSVGA/Reg/IdWrite", "SVGA_REG_ID writes.");
+ REG_CNT(&pThis->svga.StatRegIrqMaskWr, "VMSVGA/Reg/IrqMaskWrite", "SVGA_REG_IRQMASK writes.");
+ REG_CNT(&pThis->svga.StatRegNumDisplaysWr, "VMSVGA/Reg/NumDisplaysWrite", "SVGA_REG_NUM_DISPLAYS writes.");
+ REG_CNT(&pThis->svga.StatRegNumGuestDisplaysWr, "VMSVGA/Reg/NumGuestDisplaysWrite", "SVGA_REG_NUM_GUEST_DISPLAYS writes.");
+ REG_CNT(&pThis->svga.StatRegPaletteWr, "VMSVGA/Reg/PaletteWrite", "SVGA_PALETTE_XXXX writes.");
+ REG_CNT(&pThis->svga.StatRegPitchLockWr, "VMSVGA/Reg/PitchLockWrite", "SVGA_REG_PITCHLOCK writes.");
+ REG_CNT(&pThis->svga.StatRegPseudoColorWr, "VMSVGA/Reg/PseudoColorWrite", "SVGA_REG_PSEUDOCOLOR writes.");
+ REG_CNT(&pThis->svga.StatRegReadOnlyWr, "VMSVGA/Reg/ReadOnlyWrite", "Read-only SVGA_REG_XXXX writes.");
+ REG_CNT(&pThis->svga.StatRegScratchWr, "VMSVGA/Reg/ScratchWrite", "SVGA_REG_SCRATCH_XXXX writes.");
+ REG_CNT(&pThis->svga.StatRegSyncWr, "VMSVGA/Reg/SyncWrite", "SVGA_REG_SYNC writes.");
+ REG_CNT(&pThis->svga.StatRegTopWr, "VMSVGA/Reg/TopWrite", "SVGA_REG_TOP writes.");
+ REG_CNT(&pThis->svga.StatRegTracesWr, "VMSVGA/Reg/TracesWrite", "SVGA_REG_TRACES writes.");
+ REG_CNT(&pThis->svga.StatRegUnknownWr, "VMSVGA/Reg/UnknownWrite", "Writes to unknown register.");
+ REG_CNT(&pThis->svga.StatRegWidthWr, "VMSVGA/Reg/WidthWrite", "SVGA_REG_WIDTH writes.");
+ REG_CNT(&pThis->svga.StatRegCommandLowWr, "VMSVGA/Reg/CommandLowWrite", "SVGA_REG_COMMAND_LOW writes.");
+ REG_CNT(&pThis->svga.StatRegCommandHighWr, "VMSVGA/Reg/CommandHighWrite", "SVGA_REG_COMMAND_HIGH writes.");
+ REG_CNT(&pThis->svga.StatRegDevCapWr, "VMSVGA/Reg/DevCapWrite", "SVGA_REG_DEV_CAP writes.");
+ REG_CNT(&pThis->svga.StatRegCmdPrependLowWr, "VMSVGA/Reg/CmdPrependLowWrite", "SVGA_REG_CMD_PREPEND_LOW writes.");
+ REG_CNT(&pThis->svga.StatRegCmdPrependHighWr, "VMSVGA/Reg/CmdPrependHighWrite", "SVGA_REG_CMD_PREPEND_HIGH writes.");
+
+ REG_CNT(&pThis->svga.StatRegBitsPerPixelRd, "VMSVGA/Reg/BitsPerPixelRead", "SVGA_REG_BITS_PER_PIXEL reads.");
+ REG_CNT(&pThis->svga.StatRegBlueMaskRd, "VMSVGA/Reg/BlueMaskRead", "SVGA_REG_BLUE_MASK reads.");
+ REG_CNT(&pThis->svga.StatRegBusyRd, "VMSVGA/Reg/BusyRead", "SVGA_REG_BUSY reads.");
+ REG_CNT(&pThis->svga.StatRegBytesPerLineRd, "VMSVGA/Reg/BytesPerLineRead", "SVGA_REG_BYTES_PER_LINE reads.");
+ REG_CNT(&pThis->svga.StatRegCapabilitesRd, "VMSVGA/Reg/CapabilitesRead", "SVGA_REG_CAPABILITIES reads.");
+ REG_CNT(&pThis->svga.StatRegConfigDoneRd, "VMSVGA/Reg/ConfigDoneRead", "SVGA_REG_CONFIG_DONE reads.");
+ REG_CNT(&pThis->svga.StatRegCursorXRd, "VMSVGA/Reg/CursorXRead", "SVGA_REG_CURSOR_X reads.");
+ REG_CNT(&pThis->svga.StatRegCursorYRd, "VMSVGA/Reg/CursorYRead", "SVGA_REG_CURSOR_Y reads.");
+ REG_CNT(&pThis->svga.StatRegCursorIdRd, "VMSVGA/Reg/CursorIdRead", "SVGA_REG_DEAD (SVGA_REG_CURSOR_ID) reads.");
+ REG_CNT(&pThis->svga.StatRegCursorOnRd, "VMSVGA/Reg/CursorOnRead", "SVGA_REG_CURSOR_ON reads.");
+ REG_CNT(&pThis->svga.StatRegDepthRd, "VMSVGA/Reg/DepthRead", "SVGA_REG_DEPTH reads.");
+ REG_CNT(&pThis->svga.StatRegDisplayHeightRd, "VMSVGA/Reg/DisplayHeightRead", "SVGA_REG_DISPLAY_HEIGHT reads.");
+ REG_CNT(&pThis->svga.StatRegDisplayIdRd, "VMSVGA/Reg/DisplayIdRead", "SVGA_REG_DISPLAY_ID reads.");
+ REG_CNT(&pThis->svga.StatRegDisplayIsPrimaryRd, "VMSVGA/Reg/DisplayIsPrimaryRead", "SVGA_REG_DISPLAY_IS_PRIMARY reads.");
+ REG_CNT(&pThis->svga.StatRegDisplayPositionXRd, "VMSVGA/Reg/DisplayPositionXRead", "SVGA_REG_DISPLAY_POSITION_X reads.");
+ REG_CNT(&pThis->svga.StatRegDisplayPositionYRd, "VMSVGA/Reg/DisplayPositionYRead", "SVGA_REG_DISPLAY_POSITION_Y reads.");
+ REG_CNT(&pThis->svga.StatRegDisplayWidthRd, "VMSVGA/Reg/DisplayWidthRead", "SVGA_REG_DISPLAY_WIDTH reads.");
+ REG_CNT(&pThis->svga.StatRegEnableRd, "VMSVGA/Reg/EnableRead", "SVGA_REG_ENABLE reads.");
+ REG_CNT(&pThis->svga.StatRegFbOffsetRd, "VMSVGA/Reg/FbOffsetRead", "SVGA_REG_FB_OFFSET reads.");
+ REG_CNT(&pThis->svga.StatRegFbSizeRd, "VMSVGA/Reg/FbSizeRead", "SVGA_REG_FB_SIZE reads.");
+ REG_CNT(&pThis->svga.StatRegFbStartRd, "VMSVGA/Reg/FbStartRead", "SVGA_REG_FB_START reads.");
+ REG_CNT(&pThis->svga.StatRegGmrIdRd, "VMSVGA/Reg/GmrIdRead", "SVGA_REG_GMR_ID reads.");
+ REG_CNT(&pThis->svga.StatRegGmrMaxDescriptorLengthRd, "VMSVGA/Reg/GmrMaxDescriptorLengthRead", "SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH reads.");
+ REG_CNT(&pThis->svga.StatRegGmrMaxIdsRd, "VMSVGA/Reg/GmrMaxIdsRead", "SVGA_REG_GMR_MAX_IDS reads.");
+ REG_CNT(&pThis->svga.StatRegGmrsMaxPagesRd, "VMSVGA/Reg/GmrsMaxPagesRead", "SVGA_REG_GMRS_MAX_PAGES reads.");
+ REG_CNT(&pThis->svga.StatRegGreenMaskRd, "VMSVGA/Reg/GreenMaskRead", "SVGA_REG_GREEN_MASK reads.");
+ REG_CNT(&pThis->svga.StatRegGuestIdRd, "VMSVGA/Reg/GuestIdRead", "SVGA_REG_GUEST_ID reads.");
+ REG_CNT(&pThis->svga.StatRegHeightRd, "VMSVGA/Reg/HeightRead", "SVGA_REG_HEIGHT reads.");
+ REG_CNT(&pThis->svga.StatRegHostBitsPerPixelRd, "VMSVGA/Reg/HostBitsPerPixelRead", "SVGA_REG_HOST_BITS_PER_PIXEL reads.");
+ REG_CNT(&pThis->svga.StatRegIdRd, "VMSVGA/Reg/IdRead", "SVGA_REG_ID reads.");
+ REG_CNT(&pThis->svga.StatRegIrqMaskRd, "VMSVGA/Reg/IrqMaskRead", "SVGA_REG_IRQ_MASK reads.");
+ REG_CNT(&pThis->svga.StatRegMaxHeightRd, "VMSVGA/Reg/MaxHeightRead", "SVGA_REG_MAX_HEIGHT reads.");
+ REG_CNT(&pThis->svga.StatRegMaxWidthRd, "VMSVGA/Reg/MaxWidthRead", "SVGA_REG_MAX_WIDTH reads.");
+ REG_CNT(&pThis->svga.StatRegMemorySizeRd, "VMSVGA/Reg/MemorySizeRead", "SVGA_REG_MEMORY_SIZE reads.");
+ REG_CNT(&pThis->svga.StatRegMemRegsRd, "VMSVGA/Reg/MemRegsRead", "SVGA_REG_MEM_REGS reads.");
+ REG_CNT(&pThis->svga.StatRegMemSizeRd, "VMSVGA/Reg/MemSizeRead", "SVGA_REG_MEM_SIZE reads.");
+ REG_CNT(&pThis->svga.StatRegMemStartRd, "VMSVGA/Reg/MemStartRead", "SVGA_REG_MEM_START reads.");
+ REG_CNT(&pThis->svga.StatRegNumDisplaysRd, "VMSVGA/Reg/NumDisplaysRead", "SVGA_REG_NUM_DISPLAYS reads.");
+ REG_CNT(&pThis->svga.StatRegNumGuestDisplaysRd, "VMSVGA/Reg/NumGuestDisplaysRead", "SVGA_REG_NUM_GUEST_DISPLAYS reads.");
+ REG_CNT(&pThis->svga.StatRegPaletteRd, "VMSVGA/Reg/PaletteRead", "SVGA_REG_PLAETTE_XXXX reads.");
+ REG_CNT(&pThis->svga.StatRegPitchLockRd, "VMSVGA/Reg/PitchLockRead", "SVGA_REG_PITCHLOCK reads.");
+ REG_CNT(&pThis->svga.StatRegPsuedoColorRd, "VMSVGA/Reg/PsuedoColorRead", "SVGA_REG_PSEUDOCOLOR reads.");
+ REG_CNT(&pThis->svga.StatRegRedMaskRd, "VMSVGA/Reg/RedMaskRead", "SVGA_REG_RED_MASK reads.");
+ REG_CNT(&pThis->svga.StatRegScratchRd, "VMSVGA/Reg/ScratchRead", "SVGA_REG_SCRATCH reads.");
+ REG_CNT(&pThis->svga.StatRegScratchSizeRd, "VMSVGA/Reg/ScratchSizeRead", "SVGA_REG_SCRATCH_SIZE reads.");
+ REG_CNT(&pThis->svga.StatRegSyncRd, "VMSVGA/Reg/SyncRead", "SVGA_REG_SYNC reads.");
+ REG_CNT(&pThis->svga.StatRegTopRd, "VMSVGA/Reg/TopRead", "SVGA_REG_TOP reads.");
+ REG_CNT(&pThis->svga.StatRegTracesRd, "VMSVGA/Reg/TracesRead", "SVGA_REG_TRACES reads.");
+ REG_CNT(&pThis->svga.StatRegUnknownRd, "VMSVGA/Reg/UnknownRead", "SVGA_REG_UNKNOWN reads.");
+ REG_CNT(&pThis->svga.StatRegVramSizeRd, "VMSVGA/Reg/VramSizeRead", "SVGA_REG_VRAM_SIZE reads.");
+ REG_CNT(&pThis->svga.StatRegWidthRd, "VMSVGA/Reg/WidthRead", "SVGA_REG_WIDTH reads.");
+ REG_CNT(&pThis->svga.StatRegWriteOnlyRd, "VMSVGA/Reg/WriteOnlyRead", "Write-only SVGA_REG_XXXX reads.");
+ REG_CNT(&pThis->svga.StatRegCommandLowRd, "VMSVGA/Reg/CommandLowRead", "SVGA_REG_COMMAND_LOW reads.");
+ REG_CNT(&pThis->svga.StatRegCommandHighRd, "VMSVGA/Reg/CommandHighRead", "SVGA_REG_COMMAND_HIGH reads.");
+ REG_CNT(&pThis->svga.StatRegMaxPrimBBMemRd, "VMSVGA/Reg/MaxPrimBBMemRead", "SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM reads.");
+ REG_CNT(&pThis->svga.StatRegGBMemSizeRd, "VMSVGA/Reg/GBMemSizeRead", "SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB reads.");
+ REG_CNT(&pThis->svga.StatRegDevCapRd, "VMSVGA/Reg/DevCapRead", "SVGA_REG_DEV_CAP reads.");
+ REG_CNT(&pThis->svga.StatRegCmdPrependLowRd, "VMSVGA/Reg/CmdPrependLowRead", "SVGA_REG_CMD_PREPEND_LOW reads.");
+ REG_CNT(&pThis->svga.StatRegCmdPrependHighRd, "VMSVGA/Reg/CmdPrependHighRead", "SVGA_REG_CMD_PREPEND_HIGH reads.");
+ REG_CNT(&pThis->svga.StatRegScrnTgtMaxWidthRd, "VMSVGA/Reg/ScrnTgtMaxWidthRead", "SVGA_REG_SCREENTARGET_MAX_WIDTH reads.");
+ REG_CNT(&pThis->svga.StatRegScrnTgtMaxHeightRd, "VMSVGA/Reg/ScrnTgtMaxHeightRead", "SVGA_REG_SCREENTARGET_MAX_HEIGHT reads.");
+ REG_CNT(&pThis->svga.StatRegMobMaxSizeRd, "VMSVGA/Reg/MobMaxSizeRead", "SVGA_REG_MOB_MAX_SIZE reads.");
+
+ REG_PRF(&pSVGAState->StatBusyDelayEmts, "VMSVGA/EmtDelayOnBusyFifo", "Time we've delayed EMTs because of busy FIFO thread.");
+ REG_CNT(&pSVGAState->StatFifoCommands, "VMSVGA/FifoCommands", "FIFO command counter.");
+ REG_CNT(&pSVGAState->StatFifoErrors, "VMSVGA/FifoErrors", "FIFO error counter.");
+ REG_CNT(&pSVGAState->StatFifoUnkCmds, "VMSVGA/FifoUnknownCommands", "FIFO unknown command counter.");
+ REG_CNT(&pSVGAState->StatFifoTodoTimeout, "VMSVGA/FifoTodoTimeout", "Number of times we discovered pending work after a wait timeout.");
+ REG_CNT(&pSVGAState->StatFifoTodoWoken, "VMSVGA/FifoTodoWoken", "Number of times we discovered pending work after being woken up.");
+ REG_PRF(&pSVGAState->StatFifoStalls, "VMSVGA/FifoStalls", "Profiling of FIFO stalls (waiting for guest to finish copying data).");
+ REG_PRF(&pSVGAState->StatFifoExtendedSleep, "VMSVGA/FifoExtendedSleep", "Profiling FIFO sleeps relying on the refresh timer and/or access handler.");
+# if defined(VMSVGA_USE_FIFO_ACCESS_HANDLER) || defined(DEBUG_FIFO_ACCESS)
+ REG_CNT(&pSVGAState->StatFifoAccessHandler, "VMSVGA/FifoAccessHandler", "Number of times the FIFO access handler triggered.");
+# endif
+ REG_CNT(&pSVGAState->StatFifoCursorFetchAgain, "VMSVGA/FifoCursorFetchAgain", "Times the cursor update counter changed while reading.");
+ REG_CNT(&pSVGAState->StatFifoCursorNoChange, "VMSVGA/FifoCursorNoChange", "No cursor position change event though the update counter was modified.");
+ REG_CNT(&pSVGAState->StatFifoCursorPosition, "VMSVGA/FifoCursorPosition", "Cursor position and visibility changes.");
+ REG_CNT(&pSVGAState->StatFifoCursorVisiblity, "VMSVGA/FifoCursorVisiblity", "Cursor visibility changes.");
+ REG_CNT(&pSVGAState->StatFifoWatchdogWakeUps, "VMSVGA/FifoWatchdogWakeUps", "Number of times the FIFO refresh poller/watchdog woke up the FIFO thread.");
+
+# undef REG_CNT
+# undef REG_PRF
+
+ /*
+ * Info handlers.
+ */
+ PDMDevHlpDBGFInfoRegister(pDevIns, "vmsvga", "Basic VMSVGA device state details", vmsvgaR3Info);
+# ifdef VBOX_WITH_VMSVGA3D
+ PDMDevHlpDBGFInfoRegister(pDevIns, "vmsvga3dctx", "VMSVGA 3d context details. Accepts 'terse'.", vmsvgaR3Info3dContext);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "vmsvga3dsfc",
+ "VMSVGA 3d surface details. "
+ "Accepts 'terse', 'invy', and one of 'tiny', 'medium', 'normal', 'big', 'huge', or 'gigantic'.",
+ vmsvgaR3Info3dSurface);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "vmsvga3dsurf",
+ "VMSVGA 3d surface details and bitmap: "
+ "sid[>dir]",
+ vmsvgaR3Info3dSurfaceBmp);
+# endif
+
+ return VINF_SUCCESS;
+}
+
+/* Initialize 3D backend, set device capabilities and call pfnPowerOn callback of 3D backend.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared VGA/VMSVGA instance data.
+ * @param pThisCC The VGA/VMSVGA state for ring-3.
+ * @param fLoadState Whether saved state is being loaded.
+ */
+static void vmsvgaR3PowerOnDevice(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool fLoadState)
+{
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThis->svga.f3DEnabled)
+ {
+ /* Load a 3D backend. */
+ int rc = vmsvgaR3Init3dInterfaces(pDevIns, pThis, pThisCC);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VMSVGA3d: 3D support disabled! (vmsvga3dInit -> %Rrc)\n", rc));
+ pThis->svga.f3DEnabled = false;
+ }
+ }
+# endif
+
+# if defined(VBOX_WITH_VMSVGA3D) && defined(RT_OS_LINUX)
+ if (pThis->svga.f3DEnabled)
+ {
+ /* The FIFO thread may use X API for accelerated screen output. */
+ /* This must be done after backend initialization by vmsvgaR3Init3dInterfaces,
+ * because it dynamically resolves XInitThreads.
+ */
+ XInitThreads();
+ }
+# endif
+
+ if (!fLoadState)
+ {
+ vmsvgaR3InitFIFO(pThis, pThisCC);
+ vmsvgaR3GetCaps(pThis, pThisCC, &pThis->svga.u32DeviceCaps, &pThis->svga.u32DeviceCaps2, &pThisCC->svga.pau32FIFO[SVGA_FIFO_CAPABILITIES]);
+ }
+# ifdef DEBUG
+ else
+ {
+ /* If saved state is being loaded then FIFO and caps are already restored. */
+ uint32_t u32DeviceCaps = 0;
+ uint32_t u32DeviceCaps2 = 0;
+ uint32_t u32FIFOCaps = 0;
+ vmsvgaR3GetCaps(pThis, pThisCC, &u32DeviceCaps, &u32DeviceCaps2, &u32FIFOCaps);
+
+ /* Capabilities should not change normally.
+ * However the saved state might have a subset of currently implemented caps.
+ */
+ Assert( (pThis->svga.u32DeviceCaps & u32DeviceCaps) == pThis->svga.u32DeviceCaps
+ && (pThis->svga.u32DeviceCaps2 & u32DeviceCaps2) == pThis->svga.u32DeviceCaps2
+ && (pThisCC->svga.pau32FIFO[SVGA_FIFO_CAPABILITIES] & u32FIFOCaps) == pThisCC->svga.pau32FIFO[SVGA_FIFO_CAPABILITIES]);
+ }
+#endif
+
+# ifdef VBOX_WITH_VMSVGA3D
+ if (pThis->svga.f3DEnabled)
+ {
+ PVMSVGAR3STATE pSVGAState = pThisCC->svga.pSvgaR3State;
+ int rc = pSVGAState->pFuncs3D->pfnPowerOn(pDevIns, pThis, pThisCC);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize FIFO 3D capabilities. */
+ vmsvgaR3InitFifo3DCaps(pThis, pThisCC);
+ }
+ else
+ {
+ LogRel(("VMSVGA3d: 3D support disabled! (vmsvga3dPowerOn -> %Rrc)\n", rc));
+ pThis->svga.f3DEnabled = false;
+ }
+ }
+# else /* !VBOX_WITH_VMSVGA3D */
+ RT_NOREF(pDevIns);
+# endif /* !VBOX_WITH_VMSVGA3D */
+}
+
+
+/**
+ * Power On notification.
+ *
+ * @param pDevIns The device instance data.
+ *
+ * @remarks Caller enters the device critical section.
+ */
+DECLCALLBACK(void) vmsvgaR3PowerOn(PPDMDEVINS pDevIns)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+
+ vmsvgaR3PowerOnDevice(pDevIns, pThis, pThisCC, /*fLoadState=*/ false);
+}
+
+/**
+ * Power Off notification.
+ *
+ * @param pDevIns The device instance data.
+ *
+ * @remarks Caller enters the device critical section.
+ */
+DECLCALLBACK(void) vmsvgaR3PowerOff(PPDMDEVINS pDevIns)
+{
+ PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
+ PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
+
+ /*
+ * Notify the FIFO thread.
+ */
+ if (pThisCC->svga.pFIFOIOThread)
+ {
+ /* Hack around a deadlock:
+ * - the caller holds the device critsect;
+ * - FIFO thread may attempt to enter the critsect too (when raising an IRQ).
+ */
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+
+ int rc = vmsvgaR3RunExtCmdOnFifoThread(pDevIns, pThis, pThisCC, VMSVGA_FIFO_EXTCMD_POWEROFF,
+ NULL /*pvParam*/, 30000 /*ms*/);
+ AssertLogRelRC(rc);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+ }
+}
+
+#endif /* IN_RING3 */