diff options
Diffstat (limited to 'include/VBox/GuestHost/HGCMMock.h')
-rw-r--r-- | include/VBox/GuestHost/HGCMMock.h | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/include/VBox/GuestHost/HGCMMock.h b/include/VBox/GuestHost/HGCMMock.h new file mode 100644 index 00000000..3aabed57 --- /dev/null +++ b/include/VBox/GuestHost/HGCMMock.h @@ -0,0 +1,804 @@ +/* $Id: HGCMMock.h $ */ +/** @file + * HGCMMock.h: Mocking framework for testing HGCM-based host services + + * Vbgl code on the host side. + * + * Goal is to run host service + Vbgl code as unmodified as + * possible as part of testcases to gain test coverage which + * otherwise wouldn't possible for heavily user-centric features + * like Shared Clipboard or drag'n drop (DnD). + */ + +/* + * Copyright (C) 2022 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>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef VBOX_INCLUDED_GuestHost_HGCMMock_h +#define VBOX_INCLUDED_GuestHost_HGCMMock_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/semaphore.h> +#include <iprt/test.h> +#include <iprt/time.h> +#include <iprt/thread.h> +#include <iprt/utf16.h> + +#include <VBox/err.h> +#include <VBox/VBoxGuestLib.h> +#include <VBox/hgcmsvc.h> + + +/********************************************************************************************************************************* +* Definitions. * +*********************************************************************************************************************************/ + +#if defined(IN_RING3) /* Only R3 parts implemented so far. */ + +RT_C_DECLS_BEGIN + +DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable); + +RT_C_DECLS_END + +# define VBGLR3DECL(type) DECL_HIDDEN_NOTHROW(type) VBOXCALL + +/** Simple call handle structure for the guest call completion callback. */ +typedef struct VBOXHGCMCALLHANDLE_TYPEDEF +{ + /** Where to store the result code on call completion. */ + int32_t rc; +} VBOXHGCMCALLHANDLE_TYPEDEF; + +/** + * Enumeration for a HGCM mock function type. + */ +typedef enum TSTHGCMMOCKFNTYPE +{ + TSTHGCMMOCKFNTYPE_NONE = 0, + TSTHGCMMOCKFNTYPE_CONNECT, + TSTHGCMMOCKFNTYPE_DISCONNECT, + TSTHGCMMOCKFNTYPE_CALL, + TSTHGCMMOCKFNTYPE_HOST_CALL +} TSTHGCMMOCKFNTYPE; + +/** Pointer to a mock HGCM service. */ +typedef struct TSTHGCMMOCKSVC *PTSTHGCMMOCKSVC; + +/** + * Structure for mocking a server-side HGCM client. + */ +typedef struct TSTHGCMMOCKCLIENT +{ + /** Pointer to to mock service instance this client belongs to. */ + PTSTHGCMMOCKSVC pSvc; + /** Assigned HGCM client ID. */ + uint32_t idClient; + /** Opaque pointer to service-specific client data. + * Can be NULL if not being used. */ + void *pvClient; + /** Size (in bytes) of \a pvClient. */ + size_t cbClient; + /** The client's current HGCM call handle. */ + VBOXHGCMCALLHANDLE_TYPEDEF hCall; + /** Whether the current client call has an asynchronous + * call pending or not. */ + bool fAsyncExec; + /** Event semaphore to signal call completion. */ + RTSEMEVENT hEvent; +} TSTHGCMMOCKCLIENT; +/** Pointer to a mock HGCM client. */ +typedef TSTHGCMMOCKCLIENT *PTSTHGCMMOCKCLIENT; + +/** + * Structure for keeping HGCM mock function parameters. + */ +typedef struct TSTHGCMMOCKFN +{ + /** List node for storing this struct into a queue. */ + RTLISTNODE Node; + /** Function type. */ + TSTHGCMMOCKFNTYPE enmType; + /** Pointer to associated client. */ + PTSTHGCMMOCKCLIENT pClient; + /** Union keeping function-specific parameters, + * depending on \a enmType. */ + union + { + struct + { + int32_t iFunc; + uint32_t cParms; + PVBOXHGCMSVCPARM pParms; + VBOXHGCMCALLHANDLE hCall; + } Call; + struct + { + int32_t iFunc; + uint32_t cParms; + PVBOXHGCMSVCPARM pParms; + } HostCall; + } u; +} TSTHGCMMOCKFN; +/** Pointer to a HGCM mock function parameters structure. */ +typedef TSTHGCMMOCKFN *PTSTHGCMMOCKFN; + +/** + * Structure for keeping a HGCM mock service instance. + */ +typedef struct TSTHGCMMOCKSVC +{ + /** HGCM helper table to use. */ + VBOXHGCMSVCHELPERS fnHelpers; + /** HGCM service function table to use. */ + VBOXHGCMSVCFNTABLE fnTable; + /** Next HGCM client ID to assign. + * 0 is considered as being invalid. */ + HGCMCLIENTID uNextClientId; + /** Size (in bytes) of opaque pvClient area to reserve + * for a connected client. */ + size_t cbClient; + /** Array of connected HGCM mock clients. + * Currently limited to 4 clients maximum. */ + TSTHGCMMOCKCLIENT aHgcmClient[4]; + /** Thread handle for the service's main loop. */ + RTTHREAD hThread; + /** Event semaphore for signalling a message + * queue change. */ + RTSEMEVENT hEventQueue; + /** Event semaphore for clients connecting to the server. */ + RTSEMEVENT hEventConnect; + /** Number of current host calls being served. + * Currently limited to one call at a time. */ + uint8_t cHostCallers; + /** Result code of last returned host call. */ + int rcHostCall; + /** Event semaphore for host calls. */ + RTSEMEVENT hEventHostCall; + /** List (queue) of function calls to process. */ + RTLISTANCHOR lstCall; + /** Shutdown indicator flag. */ + volatile bool fShutdown; +} TSTHGCMMOCKSVC; + +/** Static HGCM service to mock. */ +static TSTHGCMMOCKSVC s_tstHgcmSvc; + +/********************************************************************************************************************************* +* Prototypes. * +*********************************************************************************************************************************/ +PTSTHGCMMOCKSVC TstHgcmMockSvcInst(void); +PTSTHGCMMOCKCLIENT TstHgcmMockSvcWaitForConnectEx(PTSTHGCMMOCKSVC pSvc, RTMSINTERVAL msTimeout); +PTSTHGCMMOCKCLIENT TstHgcmMockSvcWaitForConnect(PTSTHGCMMOCKSVC pSvc); +int TstHgcmMockSvcCreate(PTSTHGCMMOCKSVC pSvc, size_t cbClient); +int TstHgcmMockSvcDestroy(PTSTHGCMMOCKSVC pSvc); +int TstHgcmMockSvcStart(PTSTHGCMMOCKSVC pSvc); +int TstHgcmMockSvcStop(PTSTHGCMMOCKSVC pSvc); + +int TstHgcmMockSvcHostCall(PTSTHGCMMOCKSVC pSvc, void *pvService, int32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + +VBGLR3DECL(int) VbglR3HGCMConnect(const char *pszServiceName, HGCMCLIENTID *pidClient); +VBGLR3DECL(int) VbglR3HGCMDisconnect(HGCMCLIENTID idClient); +VBGLR3DECL(int) VbglR3HGCMCall(PVBGLIOCHGCMCALL pInfo, size_t cbInfo); + + + +/********************************************************************************************************************************* +* Internal functions * +*********************************************************************************************************************************/ + +/** + * Initializes a HGCM mock client. + * + * @return VBox status code. + * @param pClient Client instance to initialize. + * @param idClient HGCM client ID to assign. + * @param cbClient Size (in bytes) of service-specific (opaque) client data to allocate. + */ +static int tstHgcmMockClientInit(PTSTHGCMMOCKCLIENT pClient, uint32_t idClient, size_t cbClient) +{ + RT_BZERO(pClient, sizeof(TSTHGCMMOCKCLIENT)); + + pClient->idClient = idClient; + if (cbClient) + { + pClient->pvClient = RTMemAllocZ(cbClient); + AssertPtrReturn(pClient->pvClient, VERR_NO_MEMORY); + pClient->cbClient = cbClient; + } + + return RTSemEventCreate(&pClient->hEvent); +} + +/** + * Destroys a HGCM mock client. + * + * @return VBox status code. + * @param pClient Client instance to destroy. + */ +static int tstHgcmMockClientDestroy(PTSTHGCMMOCKCLIENT pClient) +{ + int rc = RTSemEventDestroy(pClient->hEvent); + if (RT_SUCCESS(rc)) + { + if (pClient->pvClient) + { + Assert(pClient->cbClient); + RTMemFree(pClient->pvClient); + pClient->pvClient = NULL; + pClient->cbClient = 0; + } + + pClient->hEvent = NIL_RTSEMEVENT; + } + + return rc; +} + +/* @copydoc VBOXHGCMSVCFNTABLE::pfnConnect */ +static DECLCALLBACK(int) tstHgcmMockSvcConnect(PTSTHGCMMOCKSVC pSvc, void *pvService, uint32_t *pidClient) +{ + RT_NOREF(pvService); + + PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN)); + AssertPtrReturn(pFn, VERR_NO_MEMORY); + + PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[pSvc->uNextClientId]; + + int rc = tstHgcmMockClientInit(pClient, pSvc->uNextClientId, pSvc->cbClient); + if (RT_FAILURE(rc)) + return rc; + + pFn->enmType = TSTHGCMMOCKFNTYPE_CONNECT; + pFn->pClient = pClient; + + RTListAppend(&pSvc->lstCall, &pFn->Node); + pFn = NULL; /* Thread takes ownership now. */ + + int rc2 = RTSemEventSignal(pSvc->hEventQueue); + AssertRCReturn(rc2, rc2); + rc2 = RTSemEventWait(pClient->hEvent, RT_MS_30SEC); + AssertRCReturn(rc2, rc2); + + ASMAtomicIncU32(&pSvc->uNextClientId); + + rc2 = RTSemEventSignal(pSvc->hEventConnect); + AssertRCReturn(rc2, rc2); + + *pidClient = pClient->idClient; + + return VINF_SUCCESS; +} + +/* @copydoc VBOXHGCMSVCFNTABLE::pfnDisconnect */ +static DECLCALLBACK(int) tstHgcmMockSvcDisconnect(PTSTHGCMMOCKSVC pSvc, void *pvService, uint32_t idClient) +{ + RT_NOREF(pvService); + + PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[idClient]; + + PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN)); + AssertPtrReturn(pFn, VERR_NO_MEMORY); + + pFn->enmType = TSTHGCMMOCKFNTYPE_DISCONNECT; + pFn->pClient = pClient; + + RTListAppend(&pSvc->lstCall, &pFn->Node); + pFn = NULL; /* Thread takes ownership now. */ + + int rc2 = RTSemEventSignal(pSvc->hEventQueue); + AssertRCReturn(rc2, rc2); + + rc2 = RTSemEventWait(pClient->hEvent, RT_MS_30SEC); + AssertRCReturn(rc2, rc2); + + return tstHgcmMockClientDestroy(pClient); +} + +/* @copydoc VBOXHGCMSVCFNTABLE::pfnCall */ +static DECLCALLBACK(int) tstHgcmMockSvcCall(PTSTHGCMMOCKSVC pSvc, void *pvService, VBOXHGCMCALLHANDLE callHandle, uint32_t idClient, void *pvClient, + int32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + RT_NOREF(pvService, pvClient); + + PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[idClient]; + + PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN)); + AssertPtrReturn(pFn, VERR_NO_MEMORY); + + const size_t cbParms = cParms * sizeof(VBOXHGCMSVCPARM); + + pFn->enmType = TSTHGCMMOCKFNTYPE_CALL; + pFn->pClient = pClient; + + pFn->u.Call.hCall = callHandle; + pFn->u.Call.iFunc = function; + pFn->u.Call.pParms = (PVBOXHGCMSVCPARM)RTMemDup(paParms, cbParms); + AssertPtrReturn(pFn->u.Call.pParms, VERR_NO_MEMORY); + pFn->u.Call.cParms = cParms; + + RTListAppend(&pSvc->lstCall, &pFn->Node); + + int rc2 = RTSemEventSignal(pSvc->hEventQueue); + AssertRCReturn(rc2, rc2); + + rc2 = RTSemEventWait(pClient->hEvent, RT_INDEFINITE_WAIT); + AssertRCReturn(rc2, rc2); + + memcpy(paParms, pFn->u.Call.pParms, cbParms); + + return VINF_SUCCESS; /** @todo Return host call rc */ +} + +/* @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall */ +/** Note: Public for also being able to test host calls via testcases. */ +int TstHgcmMockSvcHostCall(PTSTHGCMMOCKSVC pSvc, void *pvService, int32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + RT_NOREF(pvService); + AssertReturn(pSvc->cHostCallers == 0, VERR_WRONG_ORDER); /* Only one host call at a time. */ + + pSvc->cHostCallers++; + + PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN)); + AssertPtrReturn(pFn, VERR_INVALID_POINTER); + + pFn->enmType = TSTHGCMMOCKFNTYPE_HOST_CALL; + pFn->u.HostCall.iFunc = function; + if (cParms) + { + pFn->u.HostCall.pParms = (PVBOXHGCMSVCPARM)RTMemDup(paParms, cParms * sizeof(VBOXHGCMSVCPARM)); + AssertPtrReturn(pFn->u.HostCall.pParms, VERR_NO_MEMORY); + pFn->u.HostCall.cParms = cParms; + } + + RTListAppend(&pSvc->lstCall, &pFn->Node); + pFn = NULL; /* Thread takes ownership now. */ + + int rc2 = RTSemEventSignal(pSvc->hEventQueue); + AssertRC(rc2); + + rc2 = RTSemEventWait(pSvc->hEventHostCall, RT_INDEFINITE_WAIT); + AssertRCReturn(rc2, rc2); + + Assert(pSvc->cHostCallers); + pSvc->cHostCallers--; + + return pSvc->rcHostCall; +} + +/** + * Call completion callback for guest calls. + * + * @return VBox status code. + * @param callHandle Call handle to complete. + * @param rc Return code to return to the caller. + */ +static DECLCALLBACK(int) tstHgcmMockSvcCallComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) +{ + PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst(); + + size_t i = 0; + for (; RT_ELEMENTS(pSvc->aHgcmClient); i++) + { + PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[i]; + if (&pClient->hCall == callHandle) /* Slow, but works for now. */ + { + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + Assert(pClient->fAsyncExec == false); + } + else /* Complete call + notify client. */ + { + callHandle->rc = rc; + + int rc2 = RTSemEventSignal(pClient->hEvent); + AssertRCReturn(rc2, rc2); + } + + return VINF_SUCCESS; + } + } + + return VERR_NOT_FOUND; +} + +/** + * Main thread of HGCM mock service. + * + * @return VBox status code. + * @param hThread Thread handle. + * @param pvUser User-supplied data of type PTSTHGCMMOCKSVC. + */ +static DECLCALLBACK(int) tstHgcmMockSvcThread(RTTHREAD hThread, void *pvUser) +{ + RT_NOREF(hThread); + PTSTHGCMMOCKSVC pSvc = (PTSTHGCMMOCKSVC)pvUser; + + pSvc->uNextClientId = 0; + + pSvc->fnTable.cbSize = sizeof(pSvc->fnTable); + pSvc->fnTable.u32Version = VBOX_HGCM_SVC_VERSION; + + RT_ZERO(pSvc->fnHelpers); + pSvc->fnHelpers.pfnCallComplete = tstHgcmMockSvcCallComplete; + pSvc->fnTable.pHelpers = &pSvc->fnHelpers; + + int rc = VBoxHGCMSvcLoad(&pSvc->fnTable); + if (RT_SUCCESS(rc)) + { + RTThreadUserSignal(hThread); + + for (;;) + { + rc = RTSemEventWait(pSvc->hEventQueue, 10 /* ms */); + if (ASMAtomicReadBool(&pSvc->fShutdown)) + { + rc = VINF_SUCCESS; + break; + } + if (rc == VERR_TIMEOUT) + continue; + + PTSTHGCMMOCKFN pFn = RTListGetFirst(&pSvc->lstCall, TSTHGCMMOCKFN, Node); + if (pFn) + { + switch (pFn->enmType) + { + case TSTHGCMMOCKFNTYPE_CONNECT: + { + rc = pSvc->fnTable.pfnConnect(pSvc->fnTable.pvService, + pFn->pClient->idClient, pFn->pClient->pvClient, + VMMDEV_REQUESTOR_USR_NOT_GIVEN /* fRequestor */, false /* fRestoring */); + + int rc2 = RTSemEventSignal(pFn->pClient->hEvent); + AssertRC(rc2); + break; + } + + case TSTHGCMMOCKFNTYPE_DISCONNECT: + { + rc = pSvc->fnTable.pfnDisconnect(pSvc->fnTable.pvService, + pFn->pClient->idClient, pFn->pClient->pvClient); + + int rc2 = RTSemEventSignal(pFn->pClient->hEvent); + AssertRC(rc2); + break; + } + + case TSTHGCMMOCKFNTYPE_CALL: + { + pSvc->fnTable.pfnCall(NULL, pFn->u.Call.hCall, pFn->pClient->idClient, pFn->pClient->pvClient, + pFn->u.Call.iFunc, pFn->u.Call.cParms, pFn->u.Call.pParms, RTTimeMilliTS()); + + /* Note: Call will be completed in the call completion callback. */ + break; + } + + case TSTHGCMMOCKFNTYPE_HOST_CALL: + { + pSvc->rcHostCall = pSvc->fnTable.pfnHostCall(NULL, pFn->u.HostCall.iFunc, pFn->u.HostCall.cParms, pFn->u.HostCall.pParms); + + int rc2 = RTSemEventSignal(pSvc->hEventHostCall); + AssertRC(rc2); + break; + } + + default: + AssertFailed(); + break; + } + RTListNodeRemove(&pFn->Node); + RTMemFree(pFn); + } + } + } + + return rc; +} + + +/********************************************************************************************************************************* +* Public functions * +*********************************************************************************************************************************/ + +/** + * Returns the pointer to the HGCM mock service instance. + * + * @return Pointer to HGCM mock service instance. + */ +PTSTHGCMMOCKSVC TstHgcmMockSvcInst(void) +{ + return &s_tstHgcmSvc; +} + +/** + * Waits for a HGCM mock client to connect, extended version. + * + * @return Pointer to connected client, or NULL if ran into timeout. + * @param pSvc HGCM mock service instance. + * @param msTimeout Timeout (in ms) to wait for connection. + */ +PTSTHGCMMOCKCLIENT TstHgcmMockSvcWaitForConnectEx(PTSTHGCMMOCKSVC pSvc, RTMSINTERVAL msTimeout) +{ + int rc = RTSemEventWait(pSvc->hEventConnect, msTimeout); + if (RT_SUCCESS(rc)) + { + Assert(pSvc->uNextClientId); + return &pSvc->aHgcmClient[pSvc->uNextClientId - 1]; + } + return NULL; +} + +/** + * Waits for a HGCM mock client to connect. + * + * @return Pointer to connected client, or NULL if waiting for connection was aborted. + * @param pSvc HGCM mock service instance. + */ +PTSTHGCMMOCKCLIENT TstHgcmMockSvcWaitForConnect(PTSTHGCMMOCKSVC pSvc) +{ + return TstHgcmMockSvcWaitForConnectEx(pSvc, RT_MS_30SEC); +} + +/** + * Creates a HGCM mock service instance. + * + * @return VBox status code. + * @param pSvc HGCM mock service instance to create. + * @param cbClient Size (in bytes) of service-specific client data to + * allocate for a HGCM mock client. + */ +int TstHgcmMockSvcCreate(PTSTHGCMMOCKSVC pSvc, size_t cbClient) +{ + AssertReturn(cbClient, VERR_INVALID_PARAMETER); + + RT_ZERO(pSvc->aHgcmClient); + pSvc->fShutdown = false; + int rc = RTSemEventCreate(&pSvc->hEventQueue); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pSvc->hEventHostCall); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pSvc->hEventConnect); + if (RT_SUCCESS(rc)) + { + RTListInit(&pSvc->lstCall); + + pSvc->cbClient = cbClient; + } + } + } + + return rc; +} + +/** + * Destroys a HGCM mock service instance. + * + * @return VBox status code. + * @param pSvc HGCM mock service instance to destroy. + */ +int TstHgcmMockSvcDestroy(PTSTHGCMMOCKSVC pSvc) +{ + int rc = RTSemEventDestroy(pSvc->hEventQueue); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventDestroy(pSvc->hEventHostCall); + if (RT_SUCCESS(rc)) + RTSemEventDestroy(pSvc->hEventConnect); + } + return rc; +} + +/** + * Starts a HGCM mock service instance. + * + * @return VBox status code. + * @param pSvc HGCM mock service instance to start. + */ +int TstHgcmMockSvcStart(PTSTHGCMMOCKSVC pSvc) +{ + int rc = RTThreadCreate(&pSvc->hThread, tstHgcmMockSvcThread, pSvc, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, + "MockSvc"); + if (RT_SUCCESS(rc)) + rc = RTThreadUserWait(pSvc->hThread, RT_MS_30SEC); + + return rc; +} + +/** + * Stops a HGCM mock service instance. + * + * @return VBox status code. + * @param pSvc HGCM mock service instance to stop. + */ +int TstHgcmMockSvcStop(PTSTHGCMMOCKSVC pSvc) +{ + ASMAtomicWriteBool(&pSvc->fShutdown, true); + + int rcThread; + int rc = RTThreadWait(pSvc->hThread, RT_MS_30SEC, &rcThread); + if (RT_SUCCESS(rc)) + rc = rcThread; + if (RT_SUCCESS(rc)) + { + pSvc->hThread = NIL_RTTHREAD; + } + + return rc; +} + + +/********************************************************************************************************************************* +* VbglR3 stubs * +*********************************************************************************************************************************/ + +/** + * Connects to an HGCM mock service. + * + * @returns VBox status code + * @param pszServiceName Name of the host service. + * @param pidClient Where to put the client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3HGCMConnect(const char *pszServiceName, HGCMCLIENTID *pidClient) +{ + RT_NOREF(pszServiceName); + + PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst(); + + return tstHgcmMockSvcConnect(pSvc, pSvc->fnTable.pvService, pidClient); +} + +/** + * Disconnect from an HGCM mock service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3HGCMConnect(). + */ +VBGLR3DECL(int) VbglR3HGCMDisconnect(HGCMCLIENTID idClient) +{ + PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst(); + + return tstHgcmMockSvcDisconnect(pSvc, pSvc->fnTable.pvService, idClient); +} + +/** + * Makes a fully prepared HGCM call to an HGCM mock service. + * + * @returns VBox status code. + * @param pInfo Fully prepared HGCM call info. + * @param cbInfo Size of the info. This may sometimes be larger than + * what the parameter count indicates because of + * parameter changes between versions and such. + */ +VBGLR3DECL(int) VbglR3HGCMCall(PVBGLIOCHGCMCALL pInfo, size_t cbInfo) +{ + RT_NOREF(cbInfo); + + AssertMsg(pInfo->Hdr.cbIn == cbInfo, ("cbIn=%#x cbInfo=%#zx\n", pInfo->Hdr.cbIn, cbInfo)); + AssertMsg(pInfo->Hdr.cbOut == cbInfo, ("cbOut=%#x cbInfo=%#zx\n", pInfo->Hdr.cbOut, cbInfo)); + Assert(sizeof(*pInfo) + pInfo->cParms * sizeof(HGCMFunctionParameter) <= cbInfo); + + HGCMFunctionParameter *offSrcParms = VBGL_HGCM_GET_CALL_PARMS(pInfo); + PVBOXHGCMSVCPARM paDstParms = (PVBOXHGCMSVCPARM)RTMemAlloc(pInfo->cParms * sizeof(VBOXHGCMSVCPARM)); + + uint16_t i = 0; + for (; i < pInfo->cParms; i++) + { + switch (offSrcParms->type) + { + case VMMDevHGCMParmType_32bit: + { + paDstParms[i].type = VBOX_HGCM_SVC_PARM_32BIT; + paDstParms[i].u.uint32 = offSrcParms->u.value32; + break; + } + + case VMMDevHGCMParmType_64bit: + { + paDstParms[i].type = VBOX_HGCM_SVC_PARM_64BIT; + paDstParms[i].u.uint64 = offSrcParms->u.value64; + break; + } + + case VMMDevHGCMParmType_LinAddr: + { + paDstParms[i].type = VBOX_HGCM_SVC_PARM_PTR; + paDstParms[i].u.pointer.addr = (void *)offSrcParms->u.LinAddr.uAddr; + paDstParms[i].u.pointer.size = offSrcParms->u.LinAddr.cb; + break; + } + + default: + AssertFailed(); + break; + } + + offSrcParms++; + } + + PTSTHGCMMOCKSVC const pSvc = TstHgcmMockSvcInst(); + + int rc2 = tstHgcmMockSvcCall(pSvc, pSvc->fnTable.pvService, &pSvc->aHgcmClient[pInfo->u32ClientID].hCall, + pInfo->u32ClientID, pSvc->aHgcmClient[pInfo->u32ClientID].pvClient, + pInfo->u32Function, pInfo->cParms, paDstParms); + if (RT_SUCCESS(rc2)) + { + offSrcParms = VBGL_HGCM_GET_CALL_PARMS(pInfo); + + for (i = 0; i < pInfo->cParms; i++) + { + paDstParms[i].type = offSrcParms->type; + switch (paDstParms[i].type) + { + case VMMDevHGCMParmType_32bit: + offSrcParms->u.value32 = paDstParms[i].u.uint32; + break; + + case VMMDevHGCMParmType_64bit: + offSrcParms->u.value64 = paDstParms[i].u.uint64; + break; + + case VMMDevHGCMParmType_LinAddr: + { + offSrcParms->u.LinAddr.cb = paDstParms[i].u.pointer.size; + break; + } + + default: + AssertFailed(); + break; + } + + offSrcParms++; + } + } + + RTMemFree(paDstParms); + + if (RT_SUCCESS(rc2)) + rc2 = pSvc->aHgcmClient[pInfo->u32ClientID].hCall.rc; + + return rc2; +} + +#endif /* IN_RING3 */ + +#endif /* !VBOX_INCLUDED_GuestHost_HGCMMock_h */ |