diff options
Diffstat (limited to 'src/VBox/HostServices/common')
-rw-r--r-- | src/VBox/HostServices/common/client.cpp | 241 | ||||
-rw-r--r-- | src/VBox/HostServices/common/message.cpp | 301 |
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; +} + |