summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices/common')
-rw-r--r--src/VBox/HostServices/common/client.cpp241
-rw-r--r--src/VBox/HostServices/common/message.cpp301
2 files changed, 542 insertions, 0 deletions
diff --git a/src/VBox/HostServices/common/client.cpp b/src/VBox/HostServices/common/client.cpp
new file mode 100644
index 00000000..67a03b8f
--- /dev/null
+++ b/src/VBox/HostServices/common/client.cpp
@@ -0,0 +1,241 @@
+/* $Id: client.cpp $ */
+/** @file
+ * Base class for a host-guest service.
+ */
+
+/*
+ * Copyright (C) 2011-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
+ */
+
+#include <VBox/log.h>
+#include <VBox/hgcmsvc.h>
+
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/cpp/utils.h>
+
+#include <VBox/HostServices/Service.h>
+
+using namespace HGCM;
+
+Client::Client(uint32_t idClient)
+ : m_idClient(idClient)
+ , m_fDeferred(false)
+{
+ RT_ZERO(m_Deferred);
+ RT_ZERO(m_SvcCtx);
+}
+
+Client::~Client(void)
+{
+
+}
+
+/**
+ * Completes a guest call by returning the control back to the guest side,
+ * together with a status code, internal version.
+ *
+ * @returns IPRT status code.
+ * @param hHandle Call handle to complete guest call for.
+ * @param rcOp Return code to return to the guest side.
+ */
+int Client::completeInternal(VBOXHGCMCALLHANDLE hHandle, int rcOp) RT_NOEXCEPT
+{
+ LogFlowThisFunc(("idClient=%RU32\n", m_idClient));
+
+ if ( m_SvcCtx.pHelpers
+ && m_SvcCtx.pHelpers->pfnCallComplete)
+ {
+ m_SvcCtx.pHelpers->pfnCallComplete(hHandle, rcOp);
+
+ reset();
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_AVAILABLE;
+}
+
+/**
+ * Resets the client's internal state.
+ */
+void Client::reset(void) RT_NOEXCEPT
+{
+ m_fDeferred = false;
+
+ RT_ZERO(m_Deferred);
+}
+
+/**
+ * Completes a guest call by returning the control back to the guest side,
+ * together with a status code.
+ *
+ * @returns IPRT status code.
+ * @param hHandle Call handle to complete guest call for.
+ * @param rcOp Return code to return to the guest side.
+ */
+int Client::Complete(VBOXHGCMCALLHANDLE hHandle, int rcOp /* = VINF_SUCCESS */) RT_NOEXCEPT
+{
+ return completeInternal(hHandle, rcOp);
+}
+
+/**
+ * Completes a deferred guest call by returning the control back to the guest side,
+ * together with a status code.
+ *
+ * @returns IPRT status code. VERR_INVALID_STATE if the client is not in deferred mode.
+ * @param rcOp Return code to return to the guest side.
+ */
+int Client::CompleteDeferred(int rcOp) RT_NOEXCEPT
+{
+ if (m_fDeferred)
+ {
+ Assert(m_Deferred.hHandle != NULL);
+
+ int rc = completeInternal(m_Deferred.hHandle, rcOp);
+ if (RT_SUCCESS(rc))
+ m_fDeferred = false;
+
+ return rc;
+ }
+
+ AssertMsg(m_fDeferred, ("Client %RU32 is not in deferred mode\n", m_idClient));
+ return VERR_INVALID_STATE;
+}
+
+/**
+ * Returns the HGCM call handle of the client.
+ *
+ * @returns HGCM handle.
+ */
+VBOXHGCMCALLHANDLE Client::GetHandle(void) const RT_NOEXCEPT
+{
+ return m_Deferred.hHandle;
+}
+
+/**
+ * Returns the HGCM call handle of the client.
+ *
+ * @returns HGCM handle.
+ */
+uint32_t Client::GetMsgType(void) const RT_NOEXCEPT
+{
+ return m_Deferred.uType;
+}
+
+uint32_t Client::GetMsgParamCount(void) const RT_NOEXCEPT
+{
+ return m_Deferred.cParms;
+}
+
+/**
+ * Returns the client's (HGCM) ID.
+ *
+ * @returns The client's (HGCM) ID.
+ */
+uint32_t Client::GetClientID(void) const RT_NOEXCEPT
+{
+ return m_idClient;
+}
+
+/**
+ * Returns whether the client currently is in deferred mode or not.
+ *
+ * @returns \c True if in deferred mode, \c False if not.
+ */
+bool Client::IsDeferred(void) const RT_NOEXCEPT
+{
+ return m_fDeferred;
+}
+
+/**
+ * Set the client's status to deferred, meaning that it does not return to the caller
+ * until CompleteDeferred() has been called.
+ *
+ * @returns VBox status code.
+ * @param hHandle Call handle to save.
+ * @param u32Function Function number to save.
+ * @param cParms Number of HGCM parameters to save.
+ * @param paParms HGCM parameters to save.
+ */
+void Client::SetDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
+{
+ LogFlowThisFunc(("uClient=%RU32\n", m_idClient));
+
+ m_fDeferred = true;
+
+ m_Deferred.hHandle = hHandle;
+ m_Deferred.uType = u32Function;
+ m_Deferred.cParms = cParms;
+ m_Deferred.paParms = paParms;
+}
+
+/**
+ * Sets the HGCM service context.
+ *
+ * @param SvcCtx Service context to set.
+ */
+void Client::SetSvcContext(const VBOXHGCMSVCTX &SvcCtx) RT_NOEXCEPT
+{
+ m_SvcCtx = SvcCtx;
+}
+
+/**
+ * Sets the deferred parameters to a specific message type and
+ * required parameters. That way the client can re-request that message with
+ * the right amount of parameters from the service.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type (number) to set.
+ * @param cParms Number of parameters the message needs.
+ */
+int Client::SetDeferredMsgInfo(uint32_t uMsg, uint32_t cParms) RT_NOEXCEPT
+{
+ if (m_fDeferred)
+ {
+ if (m_Deferred.cParms < 2)
+ return VERR_INVALID_PARAMETER;
+
+ AssertPtrReturn(m_Deferred.paParms, VERR_BUFFER_OVERFLOW);
+
+ HGCMSvcSetU32(&m_Deferred.paParms[0], uMsg);
+ HGCMSvcSetU32(&m_Deferred.paParms[1], cParms);
+
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_INVALID_STATE;
+}
+
+/**
+ * Sets the deferred parameters to a specific message type and
+ * required parameters. That way the client can re-request that message with
+ * the right amount of parameters from the service.
+ *
+ * @returns IPRT status code.
+ * @param pMessage Message to get message type and required parameters from.
+ */
+int Client::SetDeferredMsgInfo(const Message *pMessage) RT_NOEXCEPT
+{
+ AssertPtrReturn(pMessage, VERR_INVALID_POINTER);
+ return SetDeferredMsgInfo(pMessage->GetType(), pMessage->GetParamCount());
+}
+
diff --git a/src/VBox/HostServices/common/message.cpp b/src/VBox/HostServices/common/message.cpp
new file mode 100644
index 00000000..cb295563
--- /dev/null
+++ b/src/VBox/HostServices/common/message.cpp
@@ -0,0 +1,301 @@
+/* $Id: message.cpp $ */
+/** @file
+ * Base class for wrapping HCGM messages.
+ */
+
+/*
+ * Copyright (C) 2018-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
+ */
+
+#include <VBox/HostServices/Service.h>
+#include <VBox/VMMDev.h> /* For VMMDEV_MAX_HGCM_PARMS. */
+
+using namespace HGCM;
+
+Message::Message(void)
+ : m_uMsg(0)
+ , m_cParms(0)
+ , m_paParms(NULL)
+{
+}
+
+Message::Message(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
+ : m_uMsg(0)
+ , m_cParms(0)
+ , m_paParms(NULL)
+{
+ initData(uMsg, cParms, aParms);
+}
+
+Message::~Message(void)
+{
+ reset();
+}
+
+/**
+ * Resets the message by free'ing all allocated parameters and resetting the rest.
+ */
+void Message::reset(void) RT_NOEXCEPT
+{
+ if (m_paParms)
+ {
+ for (uint32_t i = 0; i < m_cParms; ++i)
+ {
+ switch (m_paParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_PTR:
+ if (m_paParms[i].u.pointer.size)
+ RTMemFree(m_paParms[i].u.pointer.addr);
+ break;
+ }
+ }
+ RTMemFree(m_paParms);
+ m_paParms = 0;
+ }
+ m_cParms = 0;
+ m_uMsg = 0;
+}
+
+/**
+ * Returns the parameter count of this message.
+ *
+ * @returns Parameter count.
+ */
+uint32_t Message::GetParamCount(void) const RT_NOEXCEPT
+{
+ return m_cParms;
+}
+
+/**
+ * Retrieves the raw HGCM parameter data
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type to retrieve the parameter data for. Needed for sanity.
+ * @param cParms Size (in parameters) of @a aParms array.
+ * @param aParms Where to store the HGCM parameter data.
+ */
+int Message::GetData(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) const RT_NOEXCEPT
+{
+ if (m_uMsg != uMsg)
+ {
+ LogFlowFunc(("Stored message type (%RU32) does not match request (%RU32)\n", m_uMsg, uMsg));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (m_cParms == 0) /* Nothing to copy, take a shortcut. */
+ return VINF_SUCCESS;
+
+ if (m_cParms > cParms)
+ {
+ LogFlowFunc(("Stored parameter count (%RU32) exceeds request buffer (%RU32)\n", m_cParms, cParms));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ return Message::CopyParms(&aParms[0], cParms, m_paParms, m_cParms, false /* fDeepCopy */);
+}
+
+/**
+ * Retrieves a specific parameter value as uint32_t.
+ *
+ * @returns IPRT status code.
+ * @param uParm Index of parameter to retrieve.
+ * @param pu32Info Where to store the parameter value.
+ */
+int Message::GetParmU32(uint32_t uParm, uint32_t *pu32Info) const RT_NOEXCEPT
+{
+ AssertPtrReturn(pu32Info, VERR_INVALID_PARAMETER);
+ AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER);
+ AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_INVALID_PARAMETER);
+
+ *pu32Info = m_paParms[uParm].u.uint32;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Retrieves a specific parameter value as uint64_t.
+ *
+ * @returns IPRT status code.
+ * @param uParm Index of parameter to retrieve.
+ * @param pu64Info Where to store the parameter value.
+ */
+int Message::GetParmU64(uint32_t uParm, uint64_t *pu64Info) const RT_NOEXCEPT
+{
+ AssertPtrReturn(pu64Info, VERR_INVALID_PARAMETER);
+ AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER);
+ AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_INVALID_PARAMETER);
+
+ *pu64Info = m_paParms[uParm].u.uint64;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Retrieves a specific parameter value as a data address + size.
+ *
+ * @returns IPRT status code.
+ * @param uParm Index of parameter to retrieve.
+ * @param ppvAddr Where to store the data address.
+ * @param pcbSize Where to store the data size (in bytes).
+ *
+ * @remarks Does not copy (store) the actual content of the pointer (deep copy).
+ */
+int Message::GetParmPtr(uint32_t uParm, void **ppvAddr, uint32_t *pcbSize) const RT_NOEXCEPT
+{
+ AssertPtrReturn(ppvAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbSize, VERR_INVALID_PARAMETER);
+ AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER);
+ AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_INVALID_PARAMETER);
+
+ *ppvAddr = m_paParms[uParm].u.pointer.addr;
+ *pcbSize = m_paParms[uParm].u.pointer.size;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Returns the type of this message.
+ *
+ * @returns Message type.
+ */
+uint32_t Message::GetType(void) const RT_NOEXCEPT
+{
+ return m_uMsg;
+}
+
+/**
+ * Copies HGCM parameters from source to destination.
+ *
+ * @returns IPRT status code.
+ * @param paParmsDst Destination array to copy parameters to.
+ * @param cParmsDst Size (in parameters) of destination array.
+ * @param paParmsSrc Source array to copy parameters from.
+ * @param cParmsSrc Size (in parameters) of source array.
+ * @param fDeepCopy Whether to perform a deep copy of pointer parameters or not.
+ *
+ * @remark Static convenience function.
+ */
+/* static */
+int Message::CopyParms(PVBOXHGCMSVCPARM paParmsDst, uint32_t cParmsDst,
+ PVBOXHGCMSVCPARM paParmsSrc, uint32_t cParmsSrc,
+ bool fDeepCopy) RT_NOEXCEPT
+{
+ AssertPtrReturn(paParmsSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(paParmsDst, VERR_INVALID_POINTER);
+
+ if (cParmsSrc > cParmsDst)
+ return VERR_BUFFER_OVERFLOW;
+
+ for (uint32_t i = 0; i < cParmsSrc; i++)
+ {
+ paParmsDst[i].type = paParmsSrc[i].type;
+ switch (paParmsSrc[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+ {
+ paParmsDst[i].u.uint32 = paParmsSrc[i].u.uint32;
+ break;
+ }
+ case VBOX_HGCM_SVC_PARM_64BIT:
+ {
+ paParmsDst[i].u.uint64 = paParmsSrc[i].u.uint64;
+ break;
+ }
+ case VBOX_HGCM_SVC_PARM_PTR:
+ {
+ /* Do we have to perform a deep copy? */
+ if (fDeepCopy)
+ {
+ /* Yes, do so. */
+ paParmsDst[i].u.pointer.size = paParmsSrc[i].u.pointer.size;
+ if (paParmsDst[i].u.pointer.size > 0)
+ {
+ paParmsDst[i].u.pointer.addr = RTMemAlloc(paParmsDst[i].u.pointer.size);
+ if (!paParmsDst[i].u.pointer.addr)
+ return VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ /* No, but we have to check if there is enough room. */
+ if (paParmsDst[i].u.pointer.size < paParmsSrc[i].u.pointer.size)
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ if (paParmsSrc[i].u.pointer.size)
+ {
+ if ( paParmsDst[i].u.pointer.addr
+ && paParmsDst[i].u.pointer.size)
+ memcpy(paParmsDst[i].u.pointer.addr,
+ paParmsSrc[i].u.pointer.addr,
+ RT_MIN(paParmsDst[i].u.pointer.size, paParmsSrc[i].u.pointer.size));
+ else
+ return VERR_INVALID_POINTER;
+ }
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Unknown HGCM type %u\n", paParmsSrc[i].type));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initializes the message with a message type and parameters.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type to set.
+ * @param cParms Number of parameters to set.
+ * @param aParms Array of parameters to set.
+ */
+int Message::initData(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) RT_NOEXCEPT
+{
+ AssertReturn(cParms < VMMDEV_MAX_HGCM_PARMS, VERR_INVALID_PARAMETER);
+ AssertReturn(cParms == 0 || aParms != NULL, VERR_INVALID_POINTER);
+
+ /* Cleanup any eventual old stuff. */
+ reset();
+
+ m_uMsg = uMsg;
+ m_cParms = cParms;
+
+ int rc = VINF_SUCCESS;
+
+ if (cParms)
+ {
+ m_paParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * m_cParms);
+ if (m_paParms)
+ {
+ rc = Message::CopyParms(m_paParms, m_cParms, &aParms[0], cParms, true /* fDeepCopy */);
+ if (RT_FAILURE(rc))
+ reset();
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+