summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp')
-rw-r--r--src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp1687
1 files changed, 1687 insertions, 0 deletions
diff --git a/src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp b/src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp
new file mode 100644
index 00000000..4a7cbe65
--- /dev/null
+++ b/src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp
@@ -0,0 +1,1687 @@
+/* $Id: HGSMIHost.cpp $ */
+/** @file
+ * VBox Host Guest Shared Memory Interface (HGSMI), host part.
+ *
+ * Host part:
+ * - virtual hardware IO handlers;
+ * - channel management;
+ * - low level interface for buffer transfer.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*
+ * Async host->guest calls. Completion by an IO write from the guest or a timer timeout.
+ *
+ * Sync guest->host calls. Initiated by an IO write from the guest.
+ *
+ * Guest->Host
+ * ___________
+ *
+ * Synchronous for the guest, an async result can be also reported later by a host->guest call:
+ *
+ * G: Alloc shared memory, fill the structure, issue an IO write (HGSMI_IO_GUEST) with the memory offset.
+ * H: Verify the shared memory and call the handler.
+ * G: Continue after the IO completion.
+ *
+ *
+ * Host->Guest
+ * __________
+ *
+ * H: Alloc shared memory, fill in the info.
+ * Register in the FIFO with a callback, issue IRQ (on EMT).
+ * Wait on a sem with timeout if necessary.
+ * G: Read FIFO from HGSMI_IO_HOST_COMMAND.
+ * H(EMT): Get the shared memory offset from FIFO to return to the guest.
+ * G: Get offset, process command, issue IO write to HGSMI_IO_HOST_COMMAND.
+ * H(EMT): Find registered shared mem, run callback, which could post the sem.
+ * H: Get results and free shared mem (could be freed automatically on EMT too).
+ *
+ *
+ * Implementation notes:
+ *
+ * Host->Guest
+ *
+ * * Shared memory allocation using a critsect.
+ * * FIFO manipulation with a critsect.
+ *
+ */
+
+#include <iprt/alloc.h>
+#include <iprt/critsect.h>
+#include <iprt/heap.h>
+#include <iprt/list.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+
+#include <VBox/AssertGuest.h>
+#include <iprt/errcore.h>
+#define LOG_GROUP LOG_GROUP_HGSMI
+#include <VBox/log.h>
+#include <VBox/vmm/ssm.h>
+
+#include "HGSMIHost.h"
+#include <HGSMIChannels.h>
+
+#include "../DevVGASavedState.h"
+
+#ifdef DEBUG_sunlover
+#define HGSMI_STRICT 1
+#endif /* !DEBUG_sunlover */
+
+#ifdef DEBUG_misha
+//# define VBOXHGSMI_STATE_DEBUG
+#endif
+
+#ifdef VBOXHGSMI_STATE_DEBUG
+# define VBOXHGSMI_STATE_START_MAGIC UINT32_C(0x12345678)
+# define VBOXHGSMI_STATE_STOP_MAGIC UINT32_C(0x87654321)
+# define VBOXHGSMI_STATE_FIFOSTART_MAGIC UINT32_C(0x9abcdef1)
+# define VBOXHGSMI_STATE_FIFOSTOP_MAGIC UINT32_C(0x1fedcba9)
+
+# define VBOXHGSMI_SAVE_START(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_START_MAGIC); AssertRC(rc2); }while(0)
+# define VBOXHGSMI_SAVE_STOP(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_STOP_MAGIC); AssertRC(rc2); }while(0)
+# define VBOXHGSMI_SAVE_FIFOSTART(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_FIFOSTART_MAGIC); AssertRC(rc2); }while(0)
+# define VBOXHGSMI_SAVE_FIFOSTOP(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_FIFOSTOP_MAGIC); AssertRC(rc2); }while(0)
+
+# define VBOXHGSMI_LOAD_CHECK(_pSSM, _v) \
+ do { \
+ uint32_t u32; \
+ int rc2 = SSMR3GetU32(_pSSM, &u32); AssertRC(rc2); \
+ Assert(u32 == (_v)); \
+ } while(0)
+
+# define VBOXHGSMI_LOAD_START(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_START_MAGIC)
+# define VBOXHGSMI_LOAD_FIFOSTART(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_FIFOSTART_MAGIC)
+# define VBOXHGSMI_LOAD_FIFOSTOP(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_FIFOSTOP_MAGIC)
+# define VBOXHGSMI_LOAD_STOP(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_STOP_MAGIC)
+#else /* !VBOXHGSMI_STATE_DEBUG */
+# define VBOXHGSMI_SAVE_START(a_pSSM) do { } while(0)
+# define VBOXHGSMI_SAVE_STOP(a_pSSM) do { } while(0)
+# define VBOXHGSMI_SAVE_FIFOSTART(a_pSSM) do { } while(0)
+# define VBOXHGSMI_SAVE_FIFOSTOP(a_pSSM) do { } while(0)
+
+# define VBOXHGSMI_LOAD_START(a_pSSM) do { } while(0)
+# define VBOXHGSMI_LOAD_FIFOSTART(a_pSSM) do { } while(0)
+# define VBOXHGSMI_LOAD_FIFOSTOP(a_pSSM) do { } while(0)
+# define VBOXHGSMI_LOAD_STOP(a_pSSM) do { } while(0)
+#endif
+
+/* Assertions for situations which could happen and normally must be processed properly
+ * but must be investigated during development: guest misbehaving, etc.
+ */
+#ifdef HGSMI_STRICT
+# define HGSMI_STRICT_ASSERT_FAILED() AssertFailed()
+# define HGSMI_STRICT_ASSERT(expr) Assert(expr)
+#else
+# define HGSMI_STRICT_ASSERT_FAILED() do {} while (0)
+# define HGSMI_STRICT_ASSERT(expr) do {} while (0)
+#endif
+
+
+/** @name Host heap types.
+ * @{ */
+#define HGSMI_HEAP_TYPE_NULL 0 /**< Heap not initialized. */
+#define HGSMI_HEAP_TYPE_POINTER 1 /**< Deprecated, used only for old saved states. RTHEAPSIMPLE. */
+#define HGSMI_HEAP_TYPE_OFFSET 2 /**< Deprecated, used only for old saved states. RTHEAPOFFSET. */
+#define HGSMI_HEAP_TYPE_MA 3 /**< Memory allocator. */
+/** @} */
+
+typedef struct HGSMIHOSTHEAP
+{
+ uint32_t u32HeapType; /**< HGSMI_HEAP_TYPE_* */
+ int32_t volatile cRefs; /**< How many blocks allocated. */
+ HGSMIAREA area; /**< Host heap location. */
+ union
+ {
+ HGSMIMADATA ma; /**< Memory allocator for the default host heap implementation. */
+ struct /**< Legacy heap implementations. For old saved states. */
+ {
+ union
+ {
+ RTHEAPSIMPLE hPtr; /**< Pointer based heap. */
+ RTHEAPOFFSET hOff; /**< Offset based heap. */
+ } u;
+ } legacy;
+ } u;
+} HGSMIHOSTHEAP;
+
+typedef struct HGSMIINSTANCE
+{
+ PVM pVM; /**< The VM. */
+
+ const char *pszName; /**< A name for the instance. Mostyl used in the log. */
+
+ RTCRITSECT instanceCritSect; /**< For updating the instance data: FIFO's, channels. */
+
+ HGSMIAREA area; /**< The shared memory description. */
+ HGSMIHOSTHEAP hostHeap; /**< Host heap instance. */
+ RTCRITSECT hostHeapCritSect; /**< Heap serialization lock. */
+
+ RTLISTANCHOR hostFIFO; /**< Pending host buffers. */
+ RTLISTANCHOR hostFIFORead; /**< Host buffers read by the guest. */
+ RTLISTANCHOR hostFIFOProcessed; /**< Processed by the guest. */
+ RTLISTANCHOR hostFIFOFree; /**< Buffers for reuse. */
+#ifdef VBOX_WITH_WDDM
+ RTLISTANCHOR guestCmdCompleted; /**< list of completed guest commands to be returned to the guest*/
+#endif
+ RTCRITSECT hostFIFOCritSect; /**< FIFO serialization lock. */
+
+ PFNHGSMINOTIFYGUEST pfnNotifyGuest; /**< Guest notification callback. */
+ void *pvNotifyGuest; /**< Guest notification callback context. */
+
+ volatile HGSMIHOSTFLAGS *pHGFlags;
+
+ HGSMICHANNELINFO channelInfo; /**< Channel handlers indexed by the channel id.
+ * The array is accessed under the instance lock.
+ */
+} HGSMIINSTANCE;
+
+
+typedef DECLCALLBACK(void) FNHGSMIHOSTFIFOCALLBACK(void *pvCallback);
+typedef FNHGSMIHOSTFIFOCALLBACK *PFNHGSMIHOSTFIFOCALLBACK;
+
+typedef struct HGSMIHOSTFIFOENTRY
+{
+ RTLISTNODE nodeEntry;
+
+ HGSMIINSTANCE *pIns; /**< Backlink to the HGSMI instance. */
+
+ volatile uint32_t fl; /**< Status flags of the entry. */
+
+ HGSMIOFFSET offBuffer; /**< Offset of the HGSMI buffer header in the HGSMI host heap:
+ * [pIns->hostHeap.area.offBase .. offLast]. */
+} HGSMIHOSTFIFOENTRY;
+
+
+#define HGSMI_F_HOST_FIFO_ALLOCATED 0x0001
+#define HGSMI_F_HOST_FIFO_QUEUED 0x0002
+#define HGSMI_F_HOST_FIFO_READ 0x0004
+#define HGSMI_F_HOST_FIFO_PROCESSED 0x0008
+#define HGSMI_F_HOST_FIFO_FREE 0x0010
+#define HGSMI_F_HOST_FIFO_CANCELED 0x0020
+
+static DECLCALLBACK(void) hgsmiHostCommandFreeCallback(void *pvCallback);
+
+#ifdef VBOX_WITH_WDDM
+
+typedef struct HGSMIGUESTCOMPLENTRY
+{
+ RTLISTNODE nodeEntry;
+ HGSMIOFFSET offBuffer; /**< Offset of the guest command buffer. */
+} HGSMIGUESTCOMPLENTRY;
+
+
+static void hgsmiGuestCompletionFIFOFree(HGSMIINSTANCE *pIns, HGSMIGUESTCOMPLENTRY *pEntry)
+{
+ NOREF (pIns);
+ RTMemFree (pEntry);
+}
+
+static int hgsmiGuestCompletionFIFOAlloc(HGSMIINSTANCE *pIns, HGSMIGUESTCOMPLENTRY **ppEntry)
+{
+ HGSMIGUESTCOMPLENTRY *pEntry = (HGSMIGUESTCOMPLENTRY *)RTMemAllocZ(sizeof(HGSMIGUESTCOMPLENTRY));
+ if (pEntry)
+ {
+ *ppEntry = pEntry;
+ return VINF_SUCCESS;
+ }
+ NOREF(pIns);
+ return VERR_NO_MEMORY;
+}
+
+#endif /* VBOX_WITH_WDDM */
+
+static int hgsmiLock(HGSMIINSTANCE *pIns)
+{
+ int rc = RTCritSectEnter(&pIns->instanceCritSect);
+ AssertRC(rc);
+ return rc;
+}
+
+static void hgsmiUnlock(HGSMIINSTANCE *pIns)
+{
+ int rc = RTCritSectLeave(&pIns->instanceCritSect);
+ AssertRC(rc);
+}
+
+static int hgsmiFIFOLock(HGSMIINSTANCE *pIns)
+{
+ int rc = RTCritSectEnter(&pIns->hostFIFOCritSect);
+ AssertRC(rc);
+ return rc;
+}
+
+static void hgsmiFIFOUnlock(HGSMIINSTANCE *pIns)
+{
+ int rc = RTCritSectLeave(&pIns->hostFIFOCritSect);
+ AssertRC(rc);
+}
+
+/*
+ * Virtual hardware IO handlers.
+ */
+
+/* The guest submits a new buffer to the host.
+ * Called from the HGSMI_IO_GUEST write handler.
+ * @thread EMT
+ */
+void HGSMIGuestWrite(PHGSMIINSTANCE pIns, HGSMIOFFSET offBuffer)
+{
+ HGSMIBufferProcess(&pIns->area, &pIns->channelInfo, offBuffer);
+}
+
+#ifdef VBOX_WITH_WDDM
+static HGSMIOFFSET hgsmiProcessGuestCmdCompletion(HGSMIINSTANCE *pIns)
+{
+ HGSMIOFFSET offCmd = HGSMIOFFSET_VOID;
+ int rc = hgsmiFIFOLock(pIns);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ HGSMIGUESTCOMPLENTRY *pEntry = RTListGetFirst(&pIns->guestCmdCompleted, HGSMIGUESTCOMPLENTRY, nodeEntry);
+ if (pEntry)
+ {
+ RTListNodeRemove(&pEntry->nodeEntry);
+ }
+
+ if (RTListIsEmpty(&pIns->guestCmdCompleted))
+ {
+ if (pIns->pHGFlags)
+ ASMAtomicAndU32(&pIns->pHGFlags->u32HostFlags, ~HGSMIHOSTFLAGS_GCOMMAND_COMPLETED);
+ }
+
+ hgsmiFIFOUnlock(pIns);
+
+ if (pEntry)
+ {
+ offCmd = pEntry->offBuffer;
+
+ LogFlowFunc(("host FIFO head %p.\n", pEntry));
+
+ hgsmiGuestCompletionFIFOFree(pIns, pEntry);
+ }
+ }
+ return offCmd;
+}
+#endif
+
+
+/* Called from HGSMI_IO_GUEST read handler. */
+HGSMIOFFSET HGSMIGuestRead(PHGSMIINSTANCE pIns)
+{
+ LogFlowFunc(("pIns %p\n", pIns));
+
+ AssertPtr(pIns);
+
+ VM_ASSERT_EMT(pIns->pVM);
+
+#ifndef VBOX_WITH_WDDM
+ /* Currently there is no functionality here. */
+ NOREF(pIns);
+
+ return HGSMIOFFSET_VOID;
+#else
+ /* use this to speedup guest cmd completion
+ * this mechanism is alternative to submitting H->G command for notification */
+ HGSMIOFFSET offCmd = hgsmiProcessGuestCmdCompletion(pIns);
+ return offCmd;
+#endif
+}
+
+static bool hgsmiProcessHostCmdCompletion(HGSMIINSTANCE *pIns, HGSMIOFFSET offBuffer, bool fCompleteFirst)
+{
+ VM_ASSERT_EMT(pIns->pVM);
+
+ int rc = hgsmiFIFOLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ /* Search the Read list for the given buffer offset. */
+ HGSMIHOSTFIFOENTRY *pEntry = NULL;
+
+ HGSMIHOSTFIFOENTRY *pIter;
+ RTListForEach(&pIns->hostFIFORead, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
+ {
+ Assert(pIter->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_READ));
+ if (fCompleteFirst || pIter->offBuffer == offBuffer)
+ {
+ pEntry = pIter;
+ break;
+ }
+ }
+
+ LogFlowFunc(("read list entry: %p.\n", pEntry));
+
+ Assert(pEntry || fCompleteFirst);
+
+ if (pEntry)
+ {
+ RTListNodeRemove(&pEntry->nodeEntry);
+
+ pEntry->fl &= ~HGSMI_F_HOST_FIFO_READ;
+ pEntry->fl |= HGSMI_F_HOST_FIFO_PROCESSED;
+
+ RTListAppend(&pIns->hostFIFOProcessed, &pEntry->nodeEntry);
+
+ hgsmiFIFOUnlock(pIns);
+
+ hgsmiHostCommandFreeCallback(pEntry);
+ return true;
+ }
+
+ hgsmiFIFOUnlock(pIns);
+ if (!fCompleteFirst)
+ LogRel(("HGSMI[%s]: ignored invalid write to the host FIFO: 0x%08X!!!\n", pIns->pszName, offBuffer));
+ }
+ return false;
+}
+
+/**
+ * The guest has finished processing of a buffer previously submitted by the
+ * host.
+ *
+ * Called from HGSMI_IO_HOST write handler.
+ * @thread EMT
+ */
+void HGSMIHostWrite(HGSMIINSTANCE *pIns, HGSMIOFFSET offBuffer)
+{
+ LogFlowFunc(("pIns %p offBuffer 0x%x\n", pIns, offBuffer));
+
+ hgsmiProcessHostCmdCompletion(pIns, offBuffer, false);
+}
+
+/**
+ * The guest reads a new host buffer to be processed.
+ *
+ * Called from the HGSMI_IO_HOST read handler.
+ *
+ * @thread EMT
+ */
+HGSMIOFFSET HGSMIHostRead(HGSMIINSTANCE *pIns)
+{
+ LogFlowFunc(("pIns %p\n", pIns));
+
+ VM_ASSERT_EMT(pIns->pVM);
+
+ AssertPtrReturn(pIns->pHGFlags, HGSMIOFFSET_VOID);
+ int rc = hgsmiFIFOLock(pIns);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /* Get the host FIFO head entry. */
+ HGSMIHOSTFIFOENTRY *pEntry = RTListGetFirst(&pIns->hostFIFO, HGSMIHOSTFIFOENTRY, nodeEntry);
+
+ LogFlowFunc(("host FIFO head %p.\n", pEntry));
+
+ if (pEntry != NULL)
+ {
+ Assert(pEntry->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_QUEUED));
+
+ /*
+ * Move the entry to the Read list.
+ */
+ RTListNodeRemove(&pEntry->nodeEntry);
+
+ if (RTListIsEmpty(&pIns->hostFIFO))
+ {
+ ASMAtomicAndU32(&pIns->pHGFlags->u32HostFlags, (~HGSMIHOSTFLAGS_COMMANDS_PENDING));
+ }
+
+ pEntry->fl &= ~HGSMI_F_HOST_FIFO_QUEUED;
+ pEntry->fl |= HGSMI_F_HOST_FIFO_READ;
+
+ RTListAppend(&pIns->hostFIFORead, &pEntry->nodeEntry);
+
+ hgsmiFIFOUnlock(pIns);
+
+ /* Return the buffer offset of the host FIFO head. */
+ return pEntry->offBuffer;
+ }
+
+ hgsmiFIFOUnlock(pIns);
+ }
+ /* Special value that means there is no host buffers to be processed. */
+ return HGSMIOFFSET_VOID;
+}
+
+
+/** Tells the guest that a new buffer to be processed is available from the host. */
+static void hgsmiNotifyGuest(HGSMIINSTANCE *pIns)
+{
+ if (pIns->pfnNotifyGuest)
+ pIns->pfnNotifyGuest(pIns->pvNotifyGuest);
+}
+
+void HGSMISetHostGuestFlags(HGSMIINSTANCE *pIns, uint32_t flags)
+{
+ AssertPtrReturnVoid(pIns->pHGFlags);
+ ASMAtomicOrU32(&pIns->pHGFlags->u32HostFlags, flags);
+}
+
+uint32_t HGSMIGetHostGuestFlags(HGSMIINSTANCE *pIns)
+{
+ return pIns->pHGFlags ? ASMAtomicReadU32(&pIns->pHGFlags->u32HostFlags) : 0;
+}
+
+void HGSMIClearHostGuestFlags(HGSMIINSTANCE *pIns, uint32_t flags)
+{
+ AssertPtrReturnVoid(pIns->pHGFlags);
+ ASMAtomicAndU32(&pIns->pHGFlags->u32HostFlags, ~flags);
+}
+
+
+/*
+ * The host heap.
+ *
+ * Uses the RTHeap implementation.
+ *
+ */
+
+static int hgsmiHostHeapLock(HGSMIINSTANCE *pIns)
+{
+ int rc = RTCritSectEnter(&pIns->hostHeapCritSect);
+ AssertRC(rc);
+ return rc;
+}
+
+static void hgsmiHostHeapUnlock(HGSMIINSTANCE *pIns)
+{
+ int rc = RTCritSectLeave(&pIns->hostHeapCritSect);
+ AssertRC(rc);
+}
+
+static HGSMIOFFSET hgsmiHostHeapOffset(HGSMIHOSTHEAP *pHeap)
+{
+ return pHeap->area.offBase;
+}
+
+static HGSMISIZE hgsmiHostHeapSize(HGSMIHOSTHEAP *pHeap)
+{
+ return pHeap->area.cbArea;
+}
+
+static void RT_UNTRUSTED_VOLATILE_GUEST *hgsmiHostHeapBufferAlloc(HGSMIHOSTHEAP *pHeap, HGSMISIZE cbBuffer)
+{
+ void RT_UNTRUSTED_VOLATILE_GUEST *pvBuf = NULL;
+
+ if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
+ pvBuf = HGSMIMAAlloc(&pHeap->u.ma, cbBuffer);
+ else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
+ pvBuf = RTHeapSimpleAlloc(pHeap->u.legacy.u.hPtr, cbBuffer, 0);
+ else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
+ pvBuf = RTHeapOffsetAlloc(pHeap->u.legacy.u.hOff, cbBuffer, 0);
+ if (pvBuf)
+ ASMAtomicIncS32(&pHeap->cRefs);
+
+ return pvBuf;
+}
+
+static void hgsmiHostHeapBufferFree(HGSMIHOSTHEAP *pHeap, void RT_UNTRUSTED_VOLATILE_GUEST *pvBuf)
+{
+ if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
+ HGSMIMAFree(&pHeap->u.ma, pvBuf);
+ else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
+ RTHeapSimpleFree(pHeap->u.legacy.u.hPtr, (void *)pvBuf);
+ else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
+ RTHeapOffsetFree(pHeap->u.legacy.u.hOff, (void *)pvBuf);
+ ASMAtomicDecS32(&pHeap->cRefs);
+}
+
+static void RT_UNTRUSTED_VOLATILE_GUEST *hgsmiHostHeapDataAlloc(HGSMIHOSTHEAP *pHeap, HGSMISIZE cbData,
+ uint8_t u8Channel, uint16_t u16ChannelInfo)
+{
+ HGSMISIZE cbAlloc = HGSMIBufferRequiredSize(cbData);
+ HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)hgsmiHostHeapBufferAlloc(pHeap, cbAlloc);
+ if (!pHeader)
+ return NULL;
+
+ HGSMIBufferInitializeSingle(&pHeap->area, pHeader, cbAlloc, u8Channel, u16ChannelInfo);
+
+ return HGSMIBufferDataFromPtr(pHeader);
+}
+
+static void hgsmiHostHeapDataFree(HGSMIHOSTHEAP *pHeap, void RT_UNTRUSTED_VOLATILE_GUEST *pvData)
+{
+ if ( pvData
+ && pHeap->u32HeapType != HGSMI_HEAP_TYPE_NULL)
+ {
+ HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_GUEST *pHeader = HGSMIBufferHeaderFromData(pvData);
+ hgsmiHostHeapBufferFree(pHeap, pHeader);
+ }
+}
+
+/* Needed for heap relocation: offset of the heap handle relative to the start of heap area. */
+static HGSMIOFFSET hgsmiHostHeapHandleLocationOffset(HGSMIHOSTHEAP *pHeap)
+{
+ HGSMIOFFSET offHeapHandle;
+ if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
+ offHeapHandle = (HGSMIOFFSET)((uintptr_t)pHeap->u.legacy.u.hPtr - (uintptr_t)pHeap->area.pu8Base);
+ else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
+ offHeapHandle = (HGSMIOFFSET)((uintptr_t)pHeap->u.legacy.u.hOff - (uintptr_t)pHeap->area.pu8Base);
+ else
+ offHeapHandle = HGSMIOFFSET_VOID;
+ return offHeapHandle;
+}
+
+static int hgsmiHostHeapRelocate(HGSMIHOSTHEAP *pHeap,
+ uint32_t u32HeapType,
+ void *pvBase,
+ uint32_t offHeapHandle,
+ uintptr_t offDelta,
+ HGSMISIZE cbArea,
+ HGSMIOFFSET offBase)
+{
+ int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
+ if (RT_SUCCESS(rc))
+ {
+ if (u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
+ pHeap->u.legacy.u.hOff = (RTHEAPOFFSET)((uint8_t *)pvBase + offHeapHandle);
+ else if (u32HeapType == HGSMI_HEAP_TYPE_POINTER)
+ {
+ pHeap->u.legacy.u.hPtr = (RTHEAPSIMPLE)((uint8_t *)pvBase + offHeapHandle);
+ rc = RTHeapSimpleRelocate(pHeap->u.legacy.u.hPtr, offDelta); AssertRC(rc);
+ }
+ else
+ {
+ /* HGSMI_HEAP_TYPE_MA does not need the relocation. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ if (RT_SUCCESS(rc))
+ pHeap->u32HeapType = u32HeapType;
+ else
+ HGSMIAreaClear(&pHeap->area);
+ }
+
+ return rc;
+}
+
+static int hgsmiHostHeapRestoreMA(HGSMIHOSTHEAP *pHeap,
+ void *pvBase,
+ HGSMISIZE cbArea,
+ HGSMIOFFSET offBase,
+ uint32_t cBlocks,
+ HGSMIOFFSET *paDescriptors,
+ HGSMISIZE cbMaxBlock,
+ HGSMIENV *pEnv)
+{
+ int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
+ if (RT_SUCCESS(rc))
+ {
+ rc = HGSMIMAInit(&pHeap->u.ma, &pHeap->area, paDescriptors, cBlocks, cbMaxBlock, pEnv);
+ if (RT_SUCCESS(rc))
+ pHeap->u32HeapType = HGSMI_HEAP_TYPE_MA;
+ else
+ HGSMIAreaClear(&pHeap->area);
+ }
+
+ return rc;
+}
+
+static void hgsmiHostHeapSetupUninitialized(HGSMIHOSTHEAP *pHeap)
+{
+ RT_ZERO(*pHeap);
+ pHeap->u32HeapType = HGSMI_HEAP_TYPE_NULL;
+}
+
+static void hgsmiHostHeapDestroy(HGSMIHOSTHEAP *pHeap)
+{
+ if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
+ HGSMIMAUninit(&pHeap->u.ma);
+ hgsmiHostHeapSetupUninitialized(pHeap);
+}
+
+static int hgsmiHostFIFOAlloc(HGSMIHOSTFIFOENTRY **ppEntry)
+{
+ HGSMIHOSTFIFOENTRY *pEntry = (HGSMIHOSTFIFOENTRY *)RTMemAllocZ(sizeof(HGSMIHOSTFIFOENTRY));
+ if (pEntry)
+ {
+ pEntry->fl = HGSMI_F_HOST_FIFO_ALLOCATED;
+ *ppEntry = pEntry;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+static void hgsmiHostFIFOFree(HGSMIHOSTFIFOENTRY *pEntry)
+{
+ RTMemFree(pEntry);
+}
+
+static int hgsmiHostCommandFreeByEntry (HGSMIHOSTFIFOENTRY *pEntry)
+{
+ LogFlowFunc(("offBuffer 0x%08X\n", pEntry->offBuffer));
+
+ HGSMIINSTANCE *pIns = pEntry->pIns;
+ int rc = hgsmiFIFOLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pEntry->nodeEntry);
+ hgsmiFIFOUnlock(pIns);
+
+ void RT_UNTRUSTED_VOLATILE_GUEST *pvData = HGSMIBufferDataFromOffset(&pIns->hostHeap.area, pEntry->offBuffer);
+
+ rc = hgsmiHostHeapLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ /* Deallocate the host heap memory. */
+ hgsmiHostHeapDataFree(&pIns->hostHeap, pvData);
+
+ hgsmiHostHeapUnlock(pIns);
+ }
+
+ hgsmiHostFIFOFree(pEntry);
+ }
+
+ LogFlowFunc(("%Rrc\n", rc));
+ return rc;
+}
+
+static int hgsmiHostCommandFree(HGSMIINSTANCE *pIns, void RT_UNTRUSTED_VOLATILE_GUEST *pvData)
+{
+ HGSMIOFFSET offBuffer = HGSMIBufferOffsetFromData(&pIns->hostHeap.area, pvData);
+ HGSMIHOSTFIFOENTRY *pEntry = NULL;
+
+ int rc = hgsmiFIFOLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ /* Search the Processed list for the given offBuffer. */
+ HGSMIHOSTFIFOENTRY *pIter;
+ RTListForEach(&pIns->hostFIFOProcessed, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
+ {
+ Assert(pIter->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_PROCESSED));
+
+ if (pIter->offBuffer == offBuffer)
+ {
+ pEntry = pIter;
+ break;
+ }
+ }
+
+ if (pEntry)
+ RTListNodeRemove(&pEntry->nodeEntry);
+ else
+ AssertLogRelMsgFailed(("HGSMI[%s]: the host frees unprocessed FIFO entry: 0x%08X\n",
+ pIns->pszName, offBuffer));
+
+ hgsmiFIFOUnlock(pIns);
+
+ rc = hgsmiHostHeapLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ /* Deallocate the host heap memory. */
+ hgsmiHostHeapDataFree(&pIns->hostHeap, pvData);
+
+ hgsmiHostHeapUnlock(pIns);
+ }
+
+ if (pEntry)
+ {
+ /* Deallocate the entry. */
+ hgsmiHostFIFOFree(pEntry);
+ }
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(void) hgsmiHostCommandFreeCallback(void *pvCallback)
+{
+ /* Guest has processed the command. */
+ HGSMIHOSTFIFOENTRY *pEntry = (HGSMIHOSTFIFOENTRY *)pvCallback;
+
+ Assert(pEntry->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_PROCESSED));
+
+ /* This is a simple callback, just signal the event. */
+ hgsmiHostCommandFreeByEntry(pEntry);
+}
+
+static int hgsmiHostCommandWrite(HGSMIINSTANCE *pIns, HGSMIOFFSET offBuffer)
+{
+ AssertPtrReturn(pIns->pHGFlags, VERR_WRONG_ORDER);
+
+ HGSMIHOSTFIFOENTRY *pEntry;
+ int rc = hgsmiHostFIFOAlloc(&pEntry);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize the new entry and add it to the FIFO. */
+ pEntry->fl |= HGSMI_F_HOST_FIFO_QUEUED;
+
+ pEntry->pIns = pIns;
+ pEntry->offBuffer = offBuffer;
+
+ rc = hgsmiFIFOLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicOrU32(&pIns->pHGFlags->u32HostFlags, HGSMIHOSTFLAGS_COMMANDS_PENDING);
+ RTListAppend(&pIns->hostFIFO, &pEntry->nodeEntry);
+
+ hgsmiFIFOUnlock(pIns);
+ }
+ else
+ hgsmiHostFIFOFree(pEntry);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Append the shared memory block to the FIFO, inform the guest.
+ *
+ * @param pIns Pointer to HGSMI instance.
+ * @param pvData The shared memory block data pointer.
+ * @param fDoIrq Whether the guest interrupt should be generated, i.e. if the command is not
+ * urgent (e.g. some guest command completion notification that does not require
+ * post-processing) the command could be submitted without raising an irq.
+ * @thread EMT
+ */
+static int hgsmiHostCommandSubmit(HGSMIINSTANCE *pIns, void RT_UNTRUSTED_VOLATILE_GUEST *pvData, bool fDoIrq)
+{
+ /* Append the command to FIFO. */
+ HGSMIOFFSET offBuffer = HGSMIBufferOffsetFromData(&pIns->hostHeap.area, pvData);
+ int rc = hgsmiHostCommandWrite(pIns, offBuffer);
+ if (RT_SUCCESS(rc))
+ {
+ if (fDoIrq)
+ {
+ /* Now guest can read the FIFO, the notification is informational. */
+ hgsmiNotifyGuest(pIns);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Allocate a shared memory buffer. The host can write command/data to the memory.
+ * The allocated buffer contains the 'header', 'data' and the 'tail', but *ppvData
+ * will point to the 'data'.
+ *
+ * @return VBox status code. Pointer to the payload data in *ppvData.
+ * @param pIns HGSMI instance,
+ * @param ppvData Where to store the allocated memory pointer to data.
+ * @param cbData How many bytes of data to allocate.
+ * @param u8Channel HGSMI channel.
+ * @param u16ChannelInfo Command parameter.
+ */
+int HGSMIHostCommandAlloc(HGSMIINSTANCE *pIns, void RT_UNTRUSTED_VOLATILE_GUEST **ppvData, HGSMISIZE cbData,
+ uint8_t u8Channel, uint16_t u16ChannelInfo)
+{
+ LogFlowFunc(("pIns = %p, cbData = %d, u8Channel %d, u16ChannelInfo 0x%04X\n",
+ pIns, cbData, u8Channel, u16ChannelInfo));
+
+ int rc = hgsmiHostHeapLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ void RT_UNTRUSTED_VOLATILE_GUEST *pvData = hgsmiHostHeapDataAlloc(&pIns->hostHeap, cbData, u8Channel, u16ChannelInfo);
+ hgsmiHostHeapUnlock(pIns);
+
+ if (pvData)
+ *ppvData = pvData;
+ else
+ {
+ LogRel(("HGSMI[%s]: host heap allocation failed %d bytes\n", pIns->pszName, cbData));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ LogFlowFunc(("%Rrc, pvData = %p\n", rc, *ppvData));
+ return rc;
+}
+
+/**
+ * Convenience function that allows posting the host command asynchronously
+ * and make it freed on completion.
+ * The caller does not get notified in any way on command completion,
+ * on successful return the pvData buffer can not be used after being passed to this function.
+ *
+ * @param pIns HGSMI instance,
+ * @param pvData The pointer returned by 'HGSMIHostCommandAlloc'.
+ * @param fDoIrq Specifies whether the guest interrupt should be generated.
+ * In case the command is not urgent (e.g. some guest command
+ * completion notification that does not require post-processing)
+ * the command could be posted without raising an irq.
+ */
+int HGSMIHostCommandSubmitAndFreeAsynch(PHGSMIINSTANCE pIns, void RT_UNTRUSTED_VOLATILE_GUEST *pvData, bool fDoIrq)
+{
+ LogFlowFunc(("pIns = %p, pvData = %p, fDoIrq = %d\n", pIns, pvData, fDoIrq));
+
+ int rc;
+ if (HGSMIAreaContainsPointer(&pIns->hostHeap.area, pvData))
+ rc = hgsmiHostCommandSubmit(pIns, pvData, fDoIrq);
+ else
+ {
+ AssertLogRelMsgFailed(("HGSMI[%s]: host submits invalid command %p/%p\n",
+ pIns->pszName, pvData, pIns->hostHeap.area.pu8Base));
+ rc = VERR_INVALID_POINTER;
+ }
+
+ LogFlowFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Free the shared memory block.
+ *
+ * @param pIns Pointer to HGSMI instance,
+ * @param pvData The pointer returned by 'HGSMIHostCommandAlloc'.
+ */
+int HGSMIHostCommandFree(HGSMIINSTANCE *pIns, void RT_UNTRUSTED_VOLATILE_GUEST *pvData)
+{
+ LogFlowFunc(("pIns = %p, pvData = %p\n", pIns, pvData));
+
+ int rc;
+ if (HGSMIAreaContainsPointer(&pIns->hostHeap.area, pvData))
+ rc = hgsmiHostCommandFree(pIns, pvData);
+ else
+ {
+ AssertLogRelMsgFailed(("HGSMI[%s]: the host frees invalid FIFO entry %p/%p\n",
+ pIns->pszName, pvData, pIns->hostHeap.area.pu8Base));
+ rc = VERR_INVALID_POINTER;
+ }
+
+ LogFlowFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb)
+{
+ NOREF(pvEnv);
+ return RTMemAlloc(cb);
+}
+
+static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv)
+{
+ NOREF(pvEnv);
+ RTMemFree(pv);
+}
+
+static HGSMIENV g_hgsmiEnv =
+{
+ NULL,
+ hgsmiEnvAlloc,
+ hgsmiEnvFree
+};
+
+int HGSMIHostHeapSetup(PHGSMIINSTANCE pIns, HGSMIOFFSET RT_UNTRUSTED_GUEST offHeap, HGSMISIZE RT_UNTRUSTED_GUEST cbHeap)
+{
+ LogFlowFunc(("pIns %p, offHeap 0x%08X, cbHeap = 0x%08X\n", pIns, offHeap, cbHeap));
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pIns, VERR_INVALID_PARAMETER);
+
+ ASSERT_GUEST_LOGREL_MSG_RETURN( offHeap < pIns->area.cbArea
+ && cbHeap <= pIns->area.cbArea
+ && offHeap <= pIns->area.cbArea - cbHeap,
+ ("Heap: %#x LB %#x; Area: %#x LB %#x\n", offHeap, cbHeap, pIns->area.offBase, pIns->area.cbArea),
+ VERR_INVALID_PARAMETER);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /*
+ * Lock the heap and do the job.
+ */
+ int rc = hgsmiHostHeapLock(pIns);
+ AssertRCReturn(rc, rc);
+
+ /* It is possible to change the heap only if there is no pending allocations. */
+ ASSERT_GUEST_LOGREL_MSG_STMT_RETURN(pIns->hostHeap.cRefs == 0,
+ ("HGSMI[%s]: host heap setup ignored. %d allocated.\n", pIns->pszName, pIns->hostHeap.cRefs),
+ hgsmiHostHeapUnlock(pIns),
+ VERR_ACCESS_DENIED);
+ rc = HGSMIAreaInitialize(&pIns->hostHeap.area, pIns->area.pu8Base + offHeap, cbHeap, offHeap);
+ if (RT_SUCCESS(rc))
+ {
+ rc = HGSMIMAInit(&pIns->hostHeap.u.ma, &pIns->hostHeap.area, NULL, 0, 0, &g_hgsmiEnv);
+ if (RT_SUCCESS(rc))
+ pIns->hostHeap.u32HeapType = HGSMI_HEAP_TYPE_MA;
+ else
+ HGSMIAreaClear(&pIns->hostHeap.area);
+ }
+
+ hgsmiHostHeapUnlock(pIns);
+
+ LogFlowFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+static int hgsmiHostSaveFifoLocked(RTLISTANCHOR *pList, PSSMHANDLE pSSM)
+{
+ VBOXHGSMI_SAVE_FIFOSTART(pSSM);
+
+ HGSMIHOSTFIFOENTRY *pIter;
+
+ uint32_t cEntries = 0;
+ RTListForEach(pList, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
+ {
+ ++cEntries;
+ }
+
+ int rc = SSMR3PutU32(pSSM, cEntries);
+ if (RT_SUCCESS(rc))
+ {
+ RTListForEach(pList, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
+ {
+ SSMR3PutU32(pSSM, pIter->fl);
+ rc = SSMR3PutU32(pSSM, pIter->offBuffer);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ VBOXHGSMI_SAVE_FIFOSTOP(pSSM);
+
+ return rc;
+}
+
+static int hgsmiHostSaveGuestCmdCompletedFifoLocked(RTLISTANCHOR *pList, PSSMHANDLE pSSM)
+{
+ VBOXHGSMI_SAVE_FIFOSTART(pSSM);
+
+ HGSMIGUESTCOMPLENTRY *pIter;
+
+ uint32_t cEntries = 0;
+ RTListForEach(pList, pIter, HGSMIGUESTCOMPLENTRY, nodeEntry)
+ {
+ ++cEntries;
+ }
+ int rc = SSMR3PutU32(pSSM, cEntries);
+ if (RT_SUCCESS(rc))
+ {
+ RTListForEach(pList, pIter, HGSMIGUESTCOMPLENTRY, nodeEntry)
+ {
+ rc = SSMR3PutU32(pSSM, pIter->offBuffer);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ VBOXHGSMI_SAVE_FIFOSTOP(pSSM);
+
+ return rc;
+}
+
+static int hgsmiHostLoadFifoEntryLocked(PHGSMIINSTANCE pIns, HGSMIHOSTFIFOENTRY **ppEntry, PSSMHANDLE pSSM)
+{
+ HGSMIHOSTFIFOENTRY *pEntry;
+ int rc = hgsmiHostFIFOAlloc(&pEntry); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32;
+ pEntry->pIns = pIns;
+ rc = SSMR3GetU32(pSSM, &u32); AssertRC(rc);
+ pEntry->fl = u32;
+ rc = SSMR3GetU32(pSSM, &pEntry->offBuffer); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ *ppEntry = pEntry;
+ else
+ hgsmiHostFIFOFree(pEntry);
+ }
+
+ return rc;
+}
+
+static int hgsmiHostLoadFifoLocked(PHGSMIINSTANCE pIns, RTLISTANCHOR *pList, PSSMHANDLE pSSM)
+{
+ VBOXHGSMI_LOAD_FIFOSTART(pSSM);
+
+ uint32_t cEntries = 0;
+ int rc = SSMR3GetU32(pSSM, &cEntries);
+ if (RT_SUCCESS(rc) && cEntries)
+ {
+ uint32_t i;
+ for (i = 0; i < cEntries; ++i)
+ {
+ HGSMIHOSTFIFOENTRY *pEntry = NULL;
+ rc = hgsmiHostLoadFifoEntryLocked(pIns, &pEntry, pSSM);
+ AssertRCBreak(rc);
+
+ RTListAppend(pList, &pEntry->nodeEntry);
+ }
+ }
+
+ VBOXHGSMI_LOAD_FIFOSTOP(pSSM);
+
+ return rc;
+}
+
+static int hgsmiHostLoadGuestCmdCompletedFifoEntryLocked (PHGSMIINSTANCE pIns, HGSMIGUESTCOMPLENTRY **ppEntry, PSSMHANDLE pSSM)
+{
+ HGSMIGUESTCOMPLENTRY *pEntry;
+ int rc = hgsmiGuestCompletionFIFOAlloc(pIns, &pEntry); AssertRC(rc);
+ if (RT_SUCCESS (rc))
+ {
+ rc = SSMR3GetU32(pSSM, &pEntry->offBuffer); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ *ppEntry = pEntry;
+ else
+ hgsmiGuestCompletionFIFOFree(pIns, pEntry);
+ }
+ return rc;
+}
+
+static int hgsmiHostLoadGuestCmdCompletedFifoLocked(PHGSMIINSTANCE pIns, RTLISTANCHOR *pList, PSSMHANDLE pSSM, uint32_t u32Version)
+{
+ VBOXHGSMI_LOAD_FIFOSTART(pSSM);
+
+ uint32_t i;
+
+ uint32_t cEntries = 0;
+ int rc = SSMR3GetU32(pSSM, &cEntries);
+ if (RT_SUCCESS(rc) && cEntries)
+ {
+ if (u32Version > VGA_SAVEDSTATE_VERSION_INV_GCMDFIFO)
+ {
+ for (i = 0; i < cEntries; ++i)
+ {
+ HGSMIGUESTCOMPLENTRY *pEntry = NULL;
+ rc = hgsmiHostLoadGuestCmdCompletedFifoEntryLocked(pIns, &pEntry, pSSM);
+ AssertRCBreak(rc);
+
+ RTListAppend(pList, &pEntry->nodeEntry);
+ }
+ }
+ else
+ {
+ LogRel(("WARNING: the current saved state version has some 3D support data missing, "
+ "which may lead to some guest applications function improperly"));
+
+ /* Just read out all invalid data and discard it. */
+ for (i = 0; i < cEntries; ++i)
+ {
+ HGSMIHOSTFIFOENTRY *pEntry = NULL;
+ rc = hgsmiHostLoadFifoEntryLocked(pIns, &pEntry, pSSM);
+ AssertRCBreak(rc);
+
+ hgsmiHostFIFOFree(pEntry);
+ }
+ }
+ }
+
+ VBOXHGSMI_LOAD_FIFOSTOP(pSSM);
+
+ return rc;
+}
+
+static int hgsmiHostSaveMA(PSSMHANDLE pSSM, HGSMIMADATA *pMA)
+{
+ int rc = SSMR3PutU32(pSSM, pMA->cBlocks);
+ if (RT_SUCCESS(rc))
+ {
+ HGSMIMABLOCK *pIter;
+ RTListForEach(&pMA->listBlocks, pIter, HGSMIMABLOCK, nodeBlock)
+ {
+ SSMR3PutU32(pSSM, pIter->descriptor);
+ }
+
+ rc = SSMR3PutU32(pSSM, pMA->cbMaxBlock);
+ }
+
+ return rc;
+}
+
+static int hgsmiHostLoadMA(PSSMHANDLE pSSM, uint32_t *pcBlocks, HGSMIOFFSET **ppaDescriptors, HGSMISIZE *pcbMaxBlock)
+{
+ int rc = SSMR3GetU32(pSSM, pcBlocks);
+ if (RT_SUCCESS(rc))
+ {
+ HGSMIOFFSET *paDescriptors = NULL;
+ if (*pcBlocks > 0)
+ {
+ paDescriptors = (HGSMIOFFSET *)RTMemAlloc(*pcBlocks * sizeof(HGSMIOFFSET));
+ if (paDescriptors)
+ {
+ uint32_t i;
+ for (i = 0; i < *pcBlocks; ++i)
+ SSMR3GetU32(pSSM, &paDescriptors[i]);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = SSMR3GetU32(pSSM, pcbMaxBlock);
+ if (RT_SUCCESS(rc))
+ *ppaDescriptors = paDescriptors;
+ else
+ RTMemFree(paDescriptors);
+ }
+
+ return rc;
+}
+
+int HGSMIHostSaveStateExec(PHGSMIINSTANCE pIns, PSSMHANDLE pSSM)
+{
+ VBOXHGSMI_SAVE_START(pSSM);
+
+ int rc;
+
+ SSMR3PutU32(pSSM, pIns->hostHeap.u32HeapType);
+
+ HGSMIOFFSET off = pIns->pHGFlags ? HGSMIPointerToOffset(&pIns->area, (const HGSMIBUFFERHEADER *)pIns->pHGFlags)
+ : HGSMIOFFSET_VOID;
+ SSMR3PutU32(pSSM, off);
+
+ off = pIns->hostHeap.u32HeapType == HGSMI_HEAP_TYPE_MA ? 0 : hgsmiHostHeapHandleLocationOffset(&pIns->hostHeap);
+ rc = SSMR3PutU32 (pSSM, off);
+ if (off != HGSMIOFFSET_VOID)
+ {
+ SSMR3PutU32(pSSM, hgsmiHostHeapOffset(&pIns->hostHeap));
+ SSMR3PutU32(pSSM, hgsmiHostHeapSize(&pIns->hostHeap));
+ /* need save mem pointer to calculate offset on restore */
+ SSMR3PutU64(pSSM, (uint64_t)(uintptr_t)pIns->area.pu8Base);
+ rc = hgsmiFIFOLock (pIns);
+ if (RT_SUCCESS(rc))
+ {
+ rc = hgsmiHostSaveFifoLocked(&pIns->hostFIFO, pSSM); AssertRC(rc);
+ rc = hgsmiHostSaveFifoLocked(&pIns->hostFIFORead, pSSM); AssertRC(rc);
+ rc = hgsmiHostSaveFifoLocked(&pIns->hostFIFOProcessed, pSSM); AssertRC(rc);
+#ifdef VBOX_WITH_WDDM
+ rc = hgsmiHostSaveGuestCmdCompletedFifoLocked(&pIns->guestCmdCompleted, pSSM); AssertRC(rc);
+#endif
+
+ hgsmiFIFOUnlock(pIns);
+ }
+
+ if (RT_SUCCESS(rc))
+ if (pIns->hostHeap.u32HeapType == HGSMI_HEAP_TYPE_MA)
+ rc = hgsmiHostSaveMA(pSSM, &pIns->hostHeap.u.ma);
+ }
+
+ VBOXHGSMI_SAVE_STOP(pSSM);
+
+ return rc;
+}
+
+int HGSMIHostLoadStateExec(PHGSMIINSTANCE pIns, PSSMHANDLE pSSM, uint32_t u32Version)
+{
+ if (u32Version < VGA_SAVEDSTATE_VERSION_HGSMI)
+ return VINF_SUCCESS;
+
+ VBOXHGSMI_LOAD_START(pSSM);
+
+ int rc;
+ uint32_t u32HeapType = HGSMI_HEAP_TYPE_NULL;
+ if (u32Version >= VGA_SAVEDSTATE_VERSION_HGSMIMA)
+ {
+ rc = SSMR3GetU32(pSSM, &u32HeapType);
+ AssertRCReturn(rc, rc);
+ }
+
+ HGSMIOFFSET off;
+ rc = SSMR3GetU32(pSSM, &off);
+ AssertLogRelRCReturn(rc, rc);
+ pIns->pHGFlags = off != HGSMIOFFSET_VOID ? (HGSMIHOSTFLAGS *)HGSMIOffsetToPointer(&pIns->area, off) : NULL;
+
+ rc = SSMR3GetU32(pSSM, &off);
+ AssertLogRelRCReturn(rc, rc);
+ if (off != HGSMIOFFSET_VOID)
+ {
+ /* There is a saved heap. */
+ if (u32HeapType == HGSMI_HEAP_TYPE_NULL)
+ u32HeapType = u32Version > VGA_SAVEDSTATE_VERSION_HOST_HEAP
+ ? HGSMI_HEAP_TYPE_OFFSET : HGSMI_HEAP_TYPE_POINTER;
+
+ HGSMIOFFSET offHeap;
+ SSMR3GetU32(pSSM, &offHeap);
+ uint32_t cbHeap;
+ SSMR3GetU32(pSSM, &cbHeap);
+ uint64_t oldMem;
+ rc = SSMR3GetU64(pSSM, &oldMem);
+ AssertLogRelRCReturn(rc, rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = hgsmiFIFOLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ rc = hgsmiHostLoadFifoLocked(pIns, &pIns->hostFIFO, pSSM);
+ if (RT_SUCCESS(rc))
+ rc = hgsmiHostLoadFifoLocked(pIns, &pIns->hostFIFORead, pSSM);
+ if (RT_SUCCESS(rc))
+ rc = hgsmiHostLoadFifoLocked(pIns, &pIns->hostFIFOProcessed, pSSM);
+#ifdef VBOX_WITH_WDDM
+ if (RT_SUCCESS(rc) && u32Version > VGA_SAVEDSTATE_VERSION_PRE_WDDM)
+ rc = hgsmiHostLoadGuestCmdCompletedFifoLocked(pIns, &pIns->guestCmdCompleted, pSSM, u32Version);
+#endif
+
+ hgsmiFIFOUnlock(pIns);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (u32HeapType == HGSMI_HEAP_TYPE_MA)
+ {
+ uint32_t cBlocks = 0;
+ HGSMISIZE cbMaxBlock = 0;
+ HGSMIOFFSET *paDescriptors = NULL;
+ rc = hgsmiHostLoadMA(pSSM, &cBlocks, &paDescriptors, &cbMaxBlock);
+ if (RT_SUCCESS(rc))
+ {
+ rc = hgsmiHostHeapRestoreMA(&pIns->hostHeap,
+ pIns->area.pu8Base+offHeap,
+ cbHeap,
+ offHeap,
+ cBlocks,
+ paDescriptors,
+ cbMaxBlock,
+ &g_hgsmiEnv);
+
+ RTMemFree(paDescriptors);
+ }
+ }
+ else if ( u32HeapType == HGSMI_HEAP_TYPE_OFFSET
+ || u32HeapType == HGSMI_HEAP_TYPE_POINTER)
+ {
+ rc = hgsmiHostHeapLock(pIns);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!pIns->hostHeap.cRefs);
+ pIns->hostHeap.cRefs = 0;
+
+ rc = hgsmiHostHeapRelocate(&pIns->hostHeap,
+ u32HeapType,
+ pIns->area.pu8Base+offHeap,
+ off,
+ uintptr_t(pIns->area.pu8Base) - uintptr_t(oldMem),
+ cbHeap,
+ offHeap);
+
+ hgsmiHostHeapUnlock(pIns);
+ }
+ }
+ }
+ }
+
+ VBOXHGSMI_LOAD_STOP(pSSM);
+
+ return rc;
+}
+
+/*
+ * Channels management.
+ */
+
+/* Register a new HGSMI channel by a predefined index.
+ */
+int HGSMIHostChannelRegister(PHGSMIINSTANCE pIns, uint8_t u8Channel,
+ PFNHGSMICHANNELHANDLER pfnChannelHandler, void *pvChannelHandler)
+{
+ LogFlowFunc(("pIns %p, u8Channel %x, pfnChannelHandler %p, pvChannelHandler %p\n",
+ pIns, u8Channel, pfnChannelHandler, pvChannelHandler));
+
+ AssertReturn(!HGSMI_IS_DYNAMIC_CHANNEL(u8Channel), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pIns, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfnChannelHandler, VERR_INVALID_PARAMETER);
+
+ int rc = hgsmiLock(pIns);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = HGSMIChannelRegister(&pIns->channelInfo, u8Channel, NULL, pfnChannelHandler, pvChannelHandler);
+
+ hgsmiUnlock(pIns);
+ }
+
+ LogFlowFunc(("leave rc = %Rrc\n", rc));
+ return rc;
+}
+
+#if 0 /* unused */
+
+static int hgsmiChannelMapCreate(PHGSMIINSTANCE pIns, const char *pszChannel, uint8_t *pu8Channel)
+{
+ RT_NOREF(pIns, pszChannel, pu8Channel);
+ /** @todo later */
+ return VERR_NOT_SUPPORTED;
+}
+
+/**
+ * Register a new HGSMI channel by name.
+ *
+ * @note currently unused.
+ */
+int HGSMIChannelRegisterName(PHGSMIINSTANCE pIns,
+ const char *pszChannel,
+ PFNHGSMICHANNELHANDLER pfnChannelHandler,
+ void *pvChannelHandler,
+ uint8_t *pu8Channel)
+{
+ LogFlowFunc(("pIns %p, pszChannel %s, pfnChannelHandler %p, pvChannelHandler %p, pu8Channel %p\n",
+ pIns, pszChannel, pfnChannelHandler, pvChannelHandler, pu8Channel));
+
+ AssertPtrReturn(pIns, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszChannel, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pu8Channel, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfnChannelHandler, VERR_INVALID_PARAMETER);
+
+ int rc;
+
+ /* The pointer to the copy will be saved in the channel description. */
+ char *pszName = RTStrDup (pszChannel);
+
+ if (pszName)
+ {
+ rc = hgsmiLock (pIns);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = hgsmiChannelMapCreate (pIns, pszName, pu8Channel);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = HGSMIChannelRegister (&pIns->channelInfo, *pu8Channel, pszName, pfnChannelHandler, pvChannelHandler);
+ }
+
+ hgsmiUnlock (pIns);
+ }
+
+ if (RT_FAILURE (rc))
+ {
+ RTStrFree (pszName);
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ LogFlowFunc(("leave rc = %Rrc\n", rc));
+
+ return rc;
+}
+#endif
+
+void RT_UNTRUSTED_VOLATILE_GUEST *HGSMIOffsetToPointerHost(PHGSMIINSTANCE pIns, HGSMIOFFSET offBuffer)
+{
+ const HGSMIAREA *pArea = &pIns->area;
+ HGSMIOFFSET const offArea = offBuffer - pArea->offBase;
+ ASSERT_GUEST_MSG_RETURN(offArea < pArea->cbArea,
+ ("offBuffer=%#x; area %#x LB %#x\n", offBuffer, pArea->offBase, pArea->cbArea),
+ NULL);
+ return &pArea->pu8Base[offArea];
+}
+
+
+HGSMIOFFSET HGSMIPointerToOffsetHost(PHGSMIINSTANCE pIns, const void RT_UNTRUSTED_VOLATILE_GUEST *pv)
+{
+ const HGSMIAREA *pArea = &pIns->area;
+ uintptr_t const offArea = (uintptr_t)pv - (uintptr_t)pArea->pu8Base;
+ ASSERT_GUEST_MSG_RETURN(offArea < pArea->cbArea,
+ ("pv=%p; area %#x LB %#x\n", pv, pArea->offBase, pArea->cbArea),
+ HGSMIOFFSET_VOID);
+ return pArea->offBase + (HGSMIOFFSET)offArea;
+}
+
+
+/**
+ * Checks if @a offBuffer is within the area of this instance.
+ *
+ * This is for use in input validations.
+ *
+ * @returns true / false.
+ * @param pIns The instance.
+ * @param offBuffer The buffer offset to check.
+ */
+bool HGSMIIsOffsetValid(PHGSMIINSTANCE pIns, HGSMIOFFSET offBuffer)
+{
+ return pIns
+ && offBuffer - pIns->area.offBase < pIns->area.cbArea;
+}
+
+
+/**
+ * Returns the area offset for use in logging and assertion messages.
+ */
+HGSMIOFFSET HGSMIGetAreaOffset(PHGSMIINSTANCE pIns)
+{
+ return pIns ? pIns->area.offBase : ~(HGSMIOFFSET)0;
+}
+
+
+/**
+ * Returns the area size for use in logging and assertion messages.
+ */
+HGSMIOFFSET HGSMIGetAreaSize(PHGSMIINSTANCE pIns)
+{
+ return pIns ? pIns->area.cbArea : 0;
+}
+
+
+void *HGSMIContext(PHGSMIINSTANCE pIns)
+{
+ uint8_t *p = (uint8_t *)pIns;
+ return p + sizeof(HGSMIINSTANCE);
+}
+
+/* The guest submitted a buffer. */
+static DECLCALLBACK(int) hgsmiChannelHandler(void *pvHandler, uint16_t u16ChannelInfo,
+ RT_UNTRUSTED_VOLATILE_GUEST void *pvBuffer, HGSMISIZE cbBuffer)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pvHandler %p, u16ChannelInfo %d, pvBuffer %p, cbBuffer %u\n",
+ pvHandler, u16ChannelInfo, pvBuffer, cbBuffer));
+
+ PHGSMIINSTANCE pIns = (PHGSMIINSTANCE)pvHandler;
+
+ switch (u16ChannelInfo)
+ {
+ case HGSMI_CC_HOST_FLAGS_LOCATION:
+ {
+ ASSERT_GUEST_RETURN(cbBuffer >= sizeof(HGSMIBUFFERLOCATION), VERR_INVALID_PARAMETER);
+ HGSMIBUFFERLOCATION RT_UNTRUSTED_VOLATILE_GUEST *pLoc = (HGSMIBUFFERLOCATION RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
+ HGSMIBUFFERLOCATION LocSafe;
+ LocSafe.cbLocation = pLoc->cbLocation;
+ LocSafe.offLocation = pLoc->offLocation;
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+
+ ASSERT_GUEST_RETURN(LocSafe.cbLocation == sizeof(HGSMIHOSTFLAGS), VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_RETURN(LocSafe.offLocation + sizeof(HGSMIHOSTFLAGS) == pIns->area.cbArea, VERR_INVALID_PARAMETER);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ pIns->pHGFlags = (HGSMIHOSTFLAGS RT_UNTRUSTED_VOLATILE_GUEST *)HGSMIOffsetToPointer(&pIns->area, LocSafe.offLocation);
+ break;
+ }
+
+ default:
+ Log(("Unsupported HGSMI guest command %d!!!\n",
+ u16ChannelInfo));
+ break;
+ }
+
+ return rc;
+}
+
+int HGSMICreate(PHGSMIINSTANCE *ppIns,
+ PVM pVM,
+ const char *pszName,
+ HGSMIOFFSET offBase,
+ uint8_t *pu8MemBase,
+ HGSMISIZE cbMem,
+ PFNHGSMINOTIFYGUEST pfnNotifyGuest,
+ void *pvNotifyGuest,
+ size_t cbContext)
+{
+ LogFlowFunc(("ppIns = %p, pVM = %p, pszName = [%s], offBase = 0x%08X, pu8MemBase = %p, cbMem = 0x%08X, "
+ "pfnNotifyGuest = %p, pvNotifyGuest = %p, cbContext = %d\n",
+ ppIns,
+ pVM,
+ pszName,
+ offBase,
+ pu8MemBase,
+ cbMem,
+ pfnNotifyGuest,
+ pvNotifyGuest,
+ cbContext
+ ));
+
+ AssertPtrReturn(ppIns, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pVM, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pu8MemBase, VERR_INVALID_PARAMETER);
+
+ int rc;
+ PHGSMIINSTANCE pIns = (PHGSMIINSTANCE)RTMemAllocZ(sizeof(HGSMIINSTANCE) + cbContext);
+ if (pIns)
+ {
+ rc = HGSMIAreaInitialize(&pIns->area, pu8MemBase, cbMem, offBase);
+ if (RT_SUCCESS (rc))
+ rc = RTCritSectInit(&pIns->instanceCritSect);
+ if (RT_SUCCESS (rc))
+ rc = RTCritSectInit(&pIns->hostHeapCritSect);
+ if (RT_SUCCESS (rc))
+ rc = RTCritSectInit(&pIns->hostFIFOCritSect);
+ if (RT_SUCCESS (rc))
+ {
+ pIns->pVM = pVM;
+ pIns->pszName = VALID_PTR(pszName) ? pszName : "";
+
+ hgsmiHostHeapSetupUninitialized(&pIns->hostHeap);
+
+ pIns->pfnNotifyGuest = pfnNotifyGuest;
+ pIns->pvNotifyGuest = pvNotifyGuest;
+
+ RTListInit(&pIns->hostFIFO);
+ RTListInit(&pIns->hostFIFORead);
+ RTListInit(&pIns->hostFIFOProcessed);
+ RTListInit(&pIns->hostFIFOFree);
+ RTListInit(&pIns->guestCmdCompleted);
+
+ rc = HGSMIHostChannelRegister(pIns, HGSMI_CH_HGSMI, hgsmiChannelHandler, pIns);
+ }
+ if (RT_SUCCESS (rc))
+ *ppIns = pIns;
+ else
+ HGSMIDestroy(pIns);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ LogFlowFunc(("leave rc = %Rrc, pIns = %p\n", rc, pIns));
+ return rc;
+}
+
+uint32_t HGSMIReset(PHGSMIINSTANCE pIns)
+{
+ uint32_t flags = 0;
+ if (pIns->pHGFlags)
+ {
+ /* treat the abandoned commands as read.. */
+ while (HGSMIHostRead(pIns) != HGSMIOFFSET_VOID)
+ {}
+ flags = pIns->pHGFlags->u32HostFlags;
+ pIns->pHGFlags->u32HostFlags = 0;
+ }
+
+ /* .. and complete them */
+ while (hgsmiProcessHostCmdCompletion(pIns, 0, true))
+ {}
+
+#ifdef VBOX_WITH_WDDM
+ while (hgsmiProcessGuestCmdCompletion(pIns) != HGSMIOFFSET_VOID)
+ {}
+#endif
+
+ hgsmiHostHeapDestroy(&pIns->hostHeap);
+
+ return flags;
+}
+
+void HGSMIDestroy(PHGSMIINSTANCE pIns)
+{
+ LogFlowFunc(("pIns = %p\n", pIns));
+
+ if (pIns)
+ {
+ hgsmiHostHeapDestroy(&pIns->hostHeap);
+ if (RTCritSectIsInitialized(&pIns->hostHeapCritSect))
+ RTCritSectDelete(&pIns->hostHeapCritSect);
+ if (RTCritSectIsInitialized(&pIns->instanceCritSect))
+ RTCritSectDelete(&pIns->instanceCritSect);
+ if (RTCritSectIsInitialized(&pIns->hostFIFOCritSect))
+ RTCritSectDelete(&pIns->hostFIFOCritSect);
+
+ memset(pIns, 0, sizeof (HGSMIINSTANCE));
+ RTMemFree(pIns);
+ }
+
+ LogFlowFunc(("leave\n"));
+}
+
+#ifdef VBOX_WITH_WDDM
+
+static int hgsmiGuestCommandComplete(HGSMIINSTANCE *pIns, HGSMIOFFSET offMem)
+{
+ HGSMIGUESTCOMPLENTRY *pEntry = NULL;
+
+ AssertPtrReturn(pIns->pHGFlags, VERR_WRONG_ORDER);
+ int rc = hgsmiGuestCompletionFIFOAlloc(pIns, &pEntry);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ pEntry->offBuffer = offMem;
+
+ rc = hgsmiFIFOLock(pIns);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pIns->guestCmdCompleted, &pEntry->nodeEntry);
+ ASMAtomicOrU32(&pIns->pHGFlags->u32HostFlags, HGSMIHOSTFLAGS_GCOMMAND_COMPLETED);
+
+ hgsmiFIFOUnlock(pIns);
+ }
+ else
+ hgsmiGuestCompletionFIFOFree(pIns, pEntry);
+ }
+
+ return rc;
+}
+
+int hgsmiCompleteGuestCommand(PHGSMIINSTANCE pIns, HGSMIOFFSET offBuffer, bool fDoIrq)
+{
+ int rc = hgsmiGuestCommandComplete(pIns, offBuffer);
+ if (RT_SUCCESS (rc))
+ {
+#ifdef DEBUG_misha
+ Assert(fDoIrq);
+#endif
+ if (fDoIrq)
+ {
+ /* Now guest can read the FIFO, the notification is informational. */
+ hgsmiNotifyGuest (pIns);
+ }
+ }
+ return rc;
+}
+
+int HGSMICompleteGuestCommand(PHGSMIINSTANCE pIns, void RT_UNTRUSTED_VOLATILE_GUEST *pvMem, bool fDoIrq)
+{
+ LogFlowFunc(("pIns = %p, pvMem = %p\n", pIns, pvMem));
+
+ HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_GUEST *pHeader = HGSMIBufferHeaderFromData(pvMem);
+ HGSMIOFFSET offBuffer = HGSMIPointerToOffset(&pIns->area, pHeader);
+ ASSERT_GUEST_RETURN(offBuffer != HGSMIOFFSET_VOID, VERR_INVALID_PARAMETER);
+
+ int rc = hgsmiCompleteGuestCommand(pIns, offBuffer, fDoIrq);
+ AssertRC(rc);
+
+ LogFlowFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+#endif /* VBOX_WITH_WDDM */
+