/* $Id: HGCM.cpp 63563 2016-08-16 14:04:28Z vboxsync $ */ /** @file * * HGCM (Host-Guest Communication Manager) */ /* * Copyright (C) 2006-2016 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. */ #define LOG_GROUP_MAIN_OVERRIDE LOG_GROUP_HGCM #include "Logging.h" #include "HGCM.h" #include "HGCMThread.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * A service gets one thread, which synchronously delivers messages to * the service. This is good for serialization. * * Some services may want to process messages asynchronously, and will want * a next message to be delivered, while a previous message is still being * processed. * * The dedicated service thread delivers a next message when service * returns after fetching a previous one. The service will call a message * completion callback when message is actually processed. So returning * from the service call means only that the service is processing message. * * 'Message processed' condition is indicated by service, which call the * callback, even if the callback is called synchronously in the dedicated * thread. * * This message completion callback is only valid for Call requests. * Connect and Disconnect are processed synchronously by the service. */ /* The maximum allowed size of a service name in bytes. */ #define VBOX_HGCM_SVC_NAME_MAX_BYTES 1024 struct _HGCMSVCEXTHANDLEDATA { char *pszServiceName; /* The service name follows. */ }; /** Internal helper service object. HGCM code would use it to * hold information about services and communicate with services. * The HGCMService is an (in future) abstract class that implements * common functionality. There will be derived classes for specific * service types. */ class HGCMService { private: VBOXHGCMSVCHELPERS m_svcHelpers; static HGCMService *sm_pSvcListHead; static HGCMService *sm_pSvcListTail; static int sm_cServices; HGCMTHREADHANDLE m_thread; friend DECLCALLBACK(void) hgcmServiceThread(HGCMTHREADHANDLE ThreadHandle, void *pvUser); uint32_t volatile m_u32RefCnt; HGCMService *m_pSvcNext; HGCMService *m_pSvcPrev; char *m_pszSvcName; char *m_pszSvcLibrary; RTLDRMOD m_hLdrMod; PFNVBOXHGCMSVCLOAD m_pfnLoad; VBOXHGCMSVCFNTABLE m_fntable; int m_cClients; int m_cClientsAllocated; uint32_t *m_paClientIds; #ifdef VBOX_WITH_CRHGSMI uint32_t m_cHandleAcquires; #endif HGCMSVCEXTHANDLE m_hExtension; int loadServiceDLL(void); void unloadServiceDLL(void); /* * Main HGCM thread methods. */ int instanceCreate(const char *pszServiceLibrary, const char *pszServiceName); void instanceDestroy(void); int saveClientState(uint32_t u32ClientId, PSSMHANDLE pSSM); int loadClientState(uint32_t u32ClientId, PSSMHANDLE pSSM); HGCMService(); ~HGCMService() {}; static DECLCALLBACK(void) svcHlpCallComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc); static DECLCALLBACK(void) svcHlpDisconnectClient(void *pvInstance, uint32_t u32ClientId); public: /* * Main HGCM thread methods. */ static int LoadService(const char *pszServiceLibrary, const char *pszServiceName); void UnloadService(void); static void UnloadAll(void); static int ResolveService(HGCMService **ppsvc, const char *pszServiceName); void ReferenceService(void); void ReleaseService(void); static void Reset(void); static int SaveState(PSSMHANDLE pSSM); static int LoadState(PSSMHANDLE pSSM); int CreateAndConnectClient(uint32_t *pu32ClientIdOut, uint32_t u32ClientIdIn); int DisconnectClient(uint32_t u32ClientId, bool fFromService); int HostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms); #ifdef VBOX_WITH_CRHGSMI int HandleAcquired(); int HandleReleased(); int HostFastCallAsync(uint32_t u32Function, VBOXHGCMSVCPARM *pParm, PHGCMHOSTFASTCALLCB pfnCompletion, void *pvCompletion); #endif uint32_t SizeOfClient(void) { return m_fntable.cbClient; }; int RegisterExtension(HGCMSVCEXTHANDLE handle, PFNHGCMSVCEXT pfnExtension, void *pvExtension); void UnregisterExtension(HGCMSVCEXTHANDLE handle); /* * The service thread methods. */ int GuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM aParms[]); }; class HGCMClient: public HGCMObject { public: HGCMClient() : HGCMObject(HGCMOBJ_CLIENT), pService(NULL), pvData(NULL) {}; ~HGCMClient(); int Init(HGCMService *pSvc); /** Service that the client is connected to. */ HGCMService *pService; /** Client specific data. */ void *pvData; }; HGCMClient::~HGCMClient() { if (pService->SizeOfClient() > 0) RTMemFree(pvData); } int HGCMClient::Init(HGCMService *pSvc) { pService = pSvc; if (pService->SizeOfClient() > 0) { pvData = RTMemAllocZ(pService->SizeOfClient()); if (!pvData) { return VERR_NO_MEMORY; } } return VINF_SUCCESS; } #define HGCM_CLIENT_DATA(pService, pClient)(pClient->pvData) HGCMService *HGCMService::sm_pSvcListHead = NULL; HGCMService *HGCMService::sm_pSvcListTail = NULL; int HGCMService::sm_cServices = 0; HGCMService::HGCMService() : m_thread (0), m_u32RefCnt (0), m_pSvcNext (NULL), m_pSvcPrev (NULL), m_pszSvcName (NULL), m_pszSvcLibrary (NULL), m_hLdrMod (NIL_RTLDRMOD), m_pfnLoad (NULL), m_cClients (0), m_cClientsAllocated (0), m_paClientIds (NULL), #ifdef VBOX_WITH_CRHGSMI m_cHandleAcquires (0), #endif m_hExtension (NULL) { RT_ZERO(m_fntable); } static bool g_fResetting = false; static bool g_fSaveState = false; /** Helper function to load a local service DLL. * * @return VBox code */ int HGCMService::loadServiceDLL(void) { LogFlowFunc(("m_pszSvcLibrary = %s\n", m_pszSvcLibrary)); if (m_pszSvcLibrary == NULL) { return VERR_INVALID_PARAMETER; } RTERRINFOSTATIC ErrInfo; RTErrInfoInitStatic(&ErrInfo); int rc; if (RTPathHasPath(m_pszSvcLibrary)) rc = SUPR3HardenedLdrLoadPlugIn(m_pszSvcLibrary, &m_hLdrMod, &ErrInfo.Core); else rc = SUPR3HardenedLdrLoadAppPriv(m_pszSvcLibrary, &m_hLdrMod, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core); if (RT_SUCCESS(rc)) { LogFlowFunc(("successfully loaded the library.\n")); m_pfnLoad = NULL; rc = RTLdrGetSymbol(m_hLdrMod, VBOX_HGCM_SVCLOAD_NAME, (void**)&m_pfnLoad); if (RT_FAILURE(rc) || !m_pfnLoad) { Log(("HGCMService::loadServiceDLL: Error resolving the service entry point %s, rc = %d, m_pfnLoad = %p\n", VBOX_HGCM_SVCLOAD_NAME, rc, m_pfnLoad)); if (RT_SUCCESS(rc)) { /* m_pfnLoad was NULL */ rc = VERR_SYMBOL_NOT_FOUND; } } if (RT_SUCCESS(rc)) { RT_ZERO(m_fntable); m_fntable.cbSize = sizeof(m_fntable); m_fntable.u32Version = VBOX_HGCM_SVC_VERSION; m_fntable.pHelpers = &m_svcHelpers; rc = m_pfnLoad(&m_fntable); LogFlowFunc(("m_pfnLoad rc = %Rrc\n", rc)); if (RT_SUCCESS(rc)) { if ( m_fntable.pfnUnload == NULL || m_fntable.pfnConnect == NULL || m_fntable.pfnDisconnect == NULL || m_fntable.pfnCall == NULL ) { Log(("HGCMService::loadServiceDLL: at least one of function pointers is NULL\n")); rc = VERR_INVALID_PARAMETER; if (m_fntable.pfnUnload) { m_fntable.pfnUnload(m_fntable.pvService); } } } } } else { LogRel(("HGCM: Failed to load the service library: [%s], rc = %Rrc - %s. The service will be not available.\n", m_pszSvcLibrary, rc, ErrInfo.Core.pszMsg)); m_hLdrMod = NIL_RTLDRMOD; } if (RT_FAILURE(rc)) { unloadServiceDLL(); } return rc; } /** Helper function to free a local service DLL. * * @return VBox code */ void HGCMService::unloadServiceDLL(void) { if (m_hLdrMod) { RTLdrClose(m_hLdrMod); } RT_ZERO(m_fntable); m_pfnLoad = NULL; m_hLdrMod = NIL_RTLDRMOD; } /* * Messages processed by service threads. These threads only call the service entry points. */ #define SVC_MSG_LOAD (0) /* Load the service library and call VBOX_HGCM_SVCLOAD_NAME entry point. */ #define SVC_MSG_UNLOAD (1) /* call pfnUnload and unload the service library. */ #define SVC_MSG_CONNECT (2) /* pfnConnect */ #define SVC_MSG_DISCONNECT (3) /* pfnDisconnect */ #define SVC_MSG_GUESTCALL (4) /* pfnGuestCall */ #define SVC_MSG_HOSTCALL (5) /* pfnHostCall */ #define SVC_MSG_LOADSTATE (6) /* pfnLoadState. */ #define SVC_MSG_SAVESTATE (7) /* pfnSaveState. */ #define SVC_MSG_QUIT (8) /* Terminate the thread. */ #define SVC_MSG_REGEXT (9) /* pfnRegisterExtension */ #define SVC_MSG_UNREGEXT (10) /* pfnRegisterExtension */ #ifdef VBOX_WITH_CRHGSMI # define SVC_MSG_HOSTFASTCALLASYNC (21) /* pfnHostCall */ #endif class HGCMMsgSvcLoad: public HGCMMsgCore { }; class HGCMMsgSvcUnload: public HGCMMsgCore { }; class HGCMMsgSvcConnect: public HGCMMsgCore { public: /* client identifier */ uint32_t u32ClientId; }; class HGCMMsgSvcDisconnect: public HGCMMsgCore { public: /* client identifier */ uint32_t u32ClientId; }; class HGCMMsgHeader: public HGCMMsgCore { public: HGCMMsgHeader() : pCmd(NULL), pHGCMPort(NULL) {}; /* Command pointer/identifier. */ PVBOXHGCMCMD pCmd; /* Port to be informed on message completion. */ PPDMIHGCMPORT pHGCMPort; }; class HGCMMsgCall: public HGCMMsgHeader { public: /* client identifier */ uint32_t u32ClientId; /* function number */ uint32_t u32Function; /* number of parameters */ uint32_t cParms; VBOXHGCMSVCPARM *paParms; }; class HGCMMsgLoadSaveStateClient: public HGCMMsgCore { public: uint32_t u32ClientId; PSSMHANDLE pSSM; }; class HGCMMsgHostCallSvc: public HGCMMsgCore { public: /* function number */ uint32_t u32Function; /* number of parameters */ uint32_t cParms; VBOXHGCMSVCPARM *paParms; }; class HGCMMsgSvcRegisterExtension: public HGCMMsgCore { public: /* Handle of the extension to be registered. */ HGCMSVCEXTHANDLE handle; /* The extension entry point. */ PFNHGCMSVCEXT pfnExtension; /* The extension pointer. */ void *pvExtension; }; class HGCMMsgSvcUnregisterExtension: public HGCMMsgCore { public: /* Handle of the registered extension. */ HGCMSVCEXTHANDLE handle; }; #ifdef VBOX_WITH_CRHGSMI class HGCMMsgHostFastCallAsyncSvc: public HGCMMsgCore { public: /* function number */ uint32_t u32Function; /* parameter */ VBOXHGCMSVCPARM Param; /* completion info */ PHGCMHOSTFASTCALLCB pfnCompletion; void *pvCompletion; }; #endif static HGCMMsgCore *hgcmMessageAllocSvc(uint32_t u32MsgId) { switch (u32MsgId) { #ifdef VBOX_WITH_CRHGSMI case SVC_MSG_HOSTFASTCALLASYNC: return new HGCMMsgHostFastCallAsyncSvc(); #endif case SVC_MSG_LOAD: return new HGCMMsgSvcLoad(); case SVC_MSG_UNLOAD: return new HGCMMsgSvcUnload(); case SVC_MSG_CONNECT: return new HGCMMsgSvcConnect(); case SVC_MSG_DISCONNECT: return new HGCMMsgSvcDisconnect(); case SVC_MSG_HOSTCALL: return new HGCMMsgHostCallSvc(); case SVC_MSG_GUESTCALL: return new HGCMMsgCall(); case SVC_MSG_LOADSTATE: case SVC_MSG_SAVESTATE: return new HGCMMsgLoadSaveStateClient(); case SVC_MSG_REGEXT: return new HGCMMsgSvcRegisterExtension(); case SVC_MSG_UNREGEXT: return new HGCMMsgSvcUnregisterExtension(); default: AssertReleaseMsgFailed(("Msg id = %08X\n", u32MsgId)); } return NULL; } /* * The service thread. Loads the service library and calls the service entry points. */ DECLCALLBACK(void) hgcmServiceThread(HGCMTHREADHANDLE ThreadHandle, void *pvUser) { HGCMService *pSvc = (HGCMService *)pvUser; AssertRelease(pSvc != NULL); bool fQuit = false; while (!fQuit) { HGCMMsgCore *pMsgCore; int rc = hgcmMsgGet(ThreadHandle, &pMsgCore); if (RT_FAILURE(rc)) { /* The error means some serious unrecoverable problem in the hgcmMsg/hgcmThread layer. */ AssertMsgFailed(("%Rrc\n", rc)); break; } /* Cache required information to avoid unnecessary pMsgCore access. */ uint32_t u32MsgId = pMsgCore->MsgId(); switch (u32MsgId) { #ifdef VBOX_WITH_CRHGSMI case SVC_MSG_HOSTFASTCALLASYNC: { HGCMMsgHostFastCallAsyncSvc *pMsg = (HGCMMsgHostFastCallAsyncSvc *)pMsgCore; LogFlowFunc(("SVC_MSG_HOSTFASTCALLASYNC u32Function = %d, pParm = %p\n", pMsg->u32Function, &pMsg->Param)); rc = pSvc->m_fntable.pfnHostCall(pSvc->m_fntable.pvService, pMsg->u32Function, 1, &pMsg->Param); } break; #endif case SVC_MSG_LOAD: { LogFlowFunc(("SVC_MSG_LOAD\n")); rc = pSvc->loadServiceDLL(); } break; case SVC_MSG_UNLOAD: { LogFlowFunc(("SVC_MSG_UNLOAD\n")); if (pSvc->m_fntable.pfnUnload) { pSvc->m_fntable.pfnUnload(pSvc->m_fntable.pvService); } pSvc->unloadServiceDLL(); fQuit = true; } break; case SVC_MSG_CONNECT: { HGCMMsgSvcConnect *pMsg = (HGCMMsgSvcConnect *)pMsgCore; LogFlowFunc(("SVC_MSG_CONNECT u32ClientId = %d\n", pMsg->u32ClientId)); HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT); if (pClient) { rc = pSvc->m_fntable.pfnConnect(pSvc->m_fntable.pvService, pMsg->u32ClientId, HGCM_CLIENT_DATA(pSvc, pClient)); hgcmObjDereference(pClient); } else { rc = VERR_HGCM_INVALID_CLIENT_ID; } } break; case SVC_MSG_DISCONNECT: { HGCMMsgSvcDisconnect *pMsg = (HGCMMsgSvcDisconnect *)pMsgCore; LogFlowFunc(("SVC_MSG_DISCONNECT u32ClientId = %d\n", pMsg->u32ClientId)); HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT); if (pClient) { rc = pSvc->m_fntable.pfnDisconnect(pSvc->m_fntable.pvService, pMsg->u32ClientId, HGCM_CLIENT_DATA(pSvc, pClient)); hgcmObjDereference(pClient); } else { rc = VERR_HGCM_INVALID_CLIENT_ID; } } break; case SVC_MSG_GUESTCALL: { HGCMMsgCall *pMsg = (HGCMMsgCall *)pMsgCore; LogFlowFunc(("SVC_MSG_GUESTCALL u32ClientId = %d, u32Function = %d, cParms = %d, paParms = %p\n", pMsg->u32ClientId, pMsg->u32Function, pMsg->cParms, pMsg->paParms)); HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT); if (pClient) { pSvc->m_fntable.pfnCall(pSvc->m_fntable.pvService, (VBOXHGCMCALLHANDLE)pMsg, pMsg->u32ClientId, HGCM_CLIENT_DATA(pSvc, pClient), pMsg->u32Function, pMsg->cParms, pMsg->paParms); hgcmObjDereference(pClient); } else { rc = VERR_HGCM_INVALID_CLIENT_ID; } } break; case SVC_MSG_HOSTCALL: { HGCMMsgHostCallSvc *pMsg = (HGCMMsgHostCallSvc *)pMsgCore; LogFlowFunc(("SVC_MSG_HOSTCALL u32Function = %d, cParms = %d, paParms = %p\n", pMsg->u32Function, pMsg->cParms, pMsg->paParms)); rc = pSvc->m_fntable.pfnHostCall(pSvc->m_fntable.pvService, pMsg->u32Function, pMsg->cParms, pMsg->paParms); } break; case SVC_MSG_LOADSTATE: { HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pMsgCore; LogFlowFunc(("SVC_MSG_LOADSTATE\n")); HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT); if (pClient) { if (pSvc->m_fntable.pfnLoadState) { rc = pSvc->m_fntable.pfnLoadState(pSvc->m_fntable.pvService, pMsg->u32ClientId, HGCM_CLIENT_DATA(pSvc, pClient), pMsg->pSSM); } hgcmObjDereference(pClient); } else { rc = VERR_HGCM_INVALID_CLIENT_ID; } } break; case SVC_MSG_SAVESTATE: { HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pMsgCore; LogFlowFunc(("SVC_MSG_SAVESTATE\n")); HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT); rc = VINF_SUCCESS; if (pClient) { if (pSvc->m_fntable.pfnSaveState) { g_fSaveState = true; rc = pSvc->m_fntable.pfnSaveState(pSvc->m_fntable.pvService, pMsg->u32ClientId, HGCM_CLIENT_DATA(pSvc, pClient), pMsg->pSSM); g_fSaveState = false; } hgcmObjDereference(pClient); } else { rc = VERR_HGCM_INVALID_CLIENT_ID; } } break; case SVC_MSG_REGEXT: { HGCMMsgSvcRegisterExtension *pMsg = (HGCMMsgSvcRegisterExtension *)pMsgCore; LogFlowFunc(("SVC_MSG_REGEXT handle = %p, pfn = %p\n", pMsg->handle, pMsg->pfnExtension)); if (pSvc->m_hExtension) { rc = VERR_NOT_SUPPORTED; } else { if (pSvc->m_fntable.pfnRegisterExtension) { rc = pSvc->m_fntable.pfnRegisterExtension(pSvc->m_fntable.pvService, pMsg->pfnExtension, pMsg->pvExtension); } else { rc = VERR_NOT_SUPPORTED; } if (RT_SUCCESS(rc)) { pSvc->m_hExtension = pMsg->handle; } } } break; case SVC_MSG_UNREGEXT: { HGCMMsgSvcUnregisterExtension *pMsg = (HGCMMsgSvcUnregisterExtension *)pMsgCore; LogFlowFunc(("SVC_MSG_UNREGEXT handle = %p\n", pMsg->handle)); if (pSvc->m_hExtension != pMsg->handle) { rc = VERR_NOT_SUPPORTED; } else { if (pSvc->m_fntable.pfnRegisterExtension) { rc = pSvc->m_fntable.pfnRegisterExtension(pSvc->m_fntable.pvService, NULL, NULL); } else { rc = VERR_NOT_SUPPORTED; } pSvc->m_hExtension = NULL; } } break; default: { AssertMsgFailed(("hgcmServiceThread::Unsupported message number %08X\n", u32MsgId)); rc = VERR_NOT_SUPPORTED; } break; } if (u32MsgId != SVC_MSG_GUESTCALL) { /* For SVC_MSG_GUESTCALL the service calls the completion helper. * Other messages have to be completed here. */ hgcmMsgComplete (pMsgCore, rc); } } } /* static */ DECLCALLBACK(void) HGCMService::svcHlpCallComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) { HGCMMsgCore *pMsgCore = (HGCMMsgCore *)callHandle; if (pMsgCore->MsgId () == SVC_MSG_GUESTCALL) { /* Only call the completion for these messages. The helper * is called by the service, and the service does not get * any other messages. */ hgcmMsgComplete(pMsgCore, rc); } else { AssertFailed(); } } /* static */ DECLCALLBACK(void) HGCMService::svcHlpDisconnectClient(void *pvInstance, uint32_t u32ClientId) { HGCMService *pService = static_cast (pvInstance); if (pService) { pService->DisconnectClient(u32ClientId, true); } } static DECLCALLBACK(void) hgcmMsgCompletionCallback(int32_t result, HGCMMsgCore *pMsgCore) { /* Call the VMMDev port interface to issue IRQ notification. */ HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)pMsgCore; LogFlow(("MAIN::hgcmMsgCompletionCallback: message %p\n", pMsgCore)); if (pMsgHdr->pHGCMPort && !g_fResetting) { pMsgHdr->pHGCMPort->pfnCompleted(pMsgHdr->pHGCMPort, g_fSaveState? VINF_HGCM_SAVE_STATE: result, pMsgHdr->pCmd); } } /* * The main HGCM methods of the service. */ int HGCMService::instanceCreate(const char *pszServiceLibrary, const char *pszServiceName) { LogFlowFunc(("name %s, lib %s\n", pszServiceName, pszServiceLibrary)); /* The maximum length of the thread name, allowed by the RT is 15. */ char szThreadName[16]; if (!strncmp(pszServiceName, RT_STR_TUPLE("VBoxShared"))) RTStrPrintf(szThreadName, sizeof(szThreadName), "Sh%s", pszServiceName + 10); else if (!strncmp(pszServiceName, RT_STR_TUPLE("VBox"))) RTStrCopy(szThreadName, sizeof(szThreadName), pszServiceName + 4); else RTStrCopy(szThreadName, sizeof(szThreadName), pszServiceName); int rc = hgcmThreadCreate(&m_thread, szThreadName, hgcmServiceThread, this); if (RT_SUCCESS(rc)) { m_pszSvcName = RTStrDup(pszServiceName); m_pszSvcLibrary = RTStrDup(pszServiceLibrary); if (!m_pszSvcName || !m_pszSvcLibrary) { RTStrFree(m_pszSvcLibrary); m_pszSvcLibrary = NULL; RTStrFree(m_pszSvcName); m_pszSvcName = NULL; rc = VERR_NO_MEMORY; } else { /* Initialize service helpers table. */ m_svcHelpers.pfnCallComplete = svcHlpCallComplete; m_svcHelpers.pvInstance = this; m_svcHelpers.pfnDisconnectClient = svcHlpDisconnectClient; /* Execute the load request on the service thread. */ HGCMMSGHANDLE hMsg; rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_LOAD, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { rc = hgcmMsgSend(hMsg); } } } if (RT_FAILURE(rc)) { instanceDestroy(); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } void HGCMService::instanceDestroy(void) { LogFlowFunc(("%s\n", m_pszSvcName)); HGCMMSGHANDLE hMsg; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_UNLOAD, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { rc = hgcmMsgSend(hMsg); if (RT_SUCCESS(rc)) { hgcmThreadWait(m_thread); } } RTStrFree(m_pszSvcLibrary); m_pszSvcLibrary = NULL; RTStrFree(m_pszSvcName); m_pszSvcName = NULL; } int HGCMService::saveClientState(uint32_t u32ClientId, PSSMHANDLE pSSM) { LogFlowFunc(("%s\n", m_pszSvcName)); HGCMMSGHANDLE hMsg; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_SAVESTATE, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->u32ClientId = u32ClientId; pMsg->pSSM = pSSM; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } int HGCMService::loadClientState(uint32_t u32ClientId, PSSMHANDLE pSSM) { LogFlowFunc(("%s\n", m_pszSvcName)); HGCMMSGHANDLE hMsg; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_LOADSTATE, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->u32ClientId = u32ClientId; pMsg->pSSM = pSSM; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /** The method creates a service and references it. * * @param pszServiceLibrary The library to be loaded. * @param pszServiceName The name of the service. * @return VBox rc. * @thread main HGCM */ /* static */ int HGCMService::LoadService(const char *pszServiceLibrary, const char *pszServiceName) { LogFlowFunc(("lib %s, name = %s\n", pszServiceLibrary, pszServiceName)); /* Look at already loaded services to avoid double loading. */ HGCMService *pSvc; int rc = HGCMService::ResolveService(&pSvc, pszServiceName); if (RT_SUCCESS(rc)) { /* The service is already loaded. */ pSvc->ReleaseService(); rc = VERR_HGCM_SERVICE_EXISTS; } else { /* Create the new service. */ pSvc = new HGCMService(); if (!pSvc) { rc = VERR_NO_MEMORY; } else { /* Load the library and call the initialization entry point. */ rc = pSvc->instanceCreate(pszServiceLibrary, pszServiceName); if (RT_SUCCESS(rc)) { /* Insert the just created service to list for future references. */ pSvc->m_pSvcNext = sm_pSvcListHead; pSvc->m_pSvcPrev = NULL; if (sm_pSvcListHead) { sm_pSvcListHead->m_pSvcPrev = pSvc; } else { sm_pSvcListTail = pSvc; } sm_pSvcListHead = pSvc; sm_cServices++; /* Reference the service (for first time) until it is unloaded on HGCM termination. */ AssertRelease(pSvc->m_u32RefCnt == 0); pSvc->ReferenceService(); LogFlowFunc(("service %p\n", pSvc)); } } } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /** The method unloads a service. * * @thread main HGCM */ void HGCMService::UnloadService(void) { LogFlowFunc(("name = %s\n", m_pszSvcName)); /* Remove the service from the list. */ if (m_pSvcNext) { m_pSvcNext->m_pSvcPrev = m_pSvcPrev; } else { sm_pSvcListTail = m_pSvcPrev; } if (m_pSvcPrev) { m_pSvcPrev->m_pSvcNext = m_pSvcNext; } else { sm_pSvcListHead = m_pSvcNext; } sm_cServices--; /* The service must be unloaded only if all clients were disconnected. */ LogFlowFunc(("m_u32RefCnt = %d\n", m_u32RefCnt)); AssertRelease(m_u32RefCnt == 1); /* Now the service can be released. */ ReleaseService(); } /** The method unloads all services. * * @thread main HGCM */ /* static */ void HGCMService::UnloadAll(void) { while (sm_pSvcListHead) { sm_pSvcListHead->UnloadService(); } } /** The method obtains a referenced pointer to the service with * specified name. The caller must call ReleaseService when * the pointer is no longer needed. * * @param ppSvc Where to store the pointer to the service. * @param pszServiceName The name of the service. * @return VBox rc. * @thread main HGCM */ /* static */ int HGCMService::ResolveService(HGCMService **ppSvc, const char *pszServiceName) { LogFlowFunc(("ppSvc = %p name = %s\n", ppSvc, pszServiceName)); if (!ppSvc || !pszServiceName) { return VERR_INVALID_PARAMETER; } HGCMService *pSvc = sm_pSvcListHead; while (pSvc) { if (strcmp(pSvc->m_pszSvcName, pszServiceName) == 0) { break; } pSvc = pSvc->m_pSvcNext; } LogFlowFunc(("lookup in the list is %p\n", pSvc)); if (pSvc == NULL) { *ppSvc = NULL; return VERR_HGCM_SERVICE_NOT_FOUND; } pSvc->ReferenceService(); *ppSvc = pSvc; return VINF_SUCCESS; } /** The method increases reference counter. * * @thread main HGCM */ void HGCMService::ReferenceService(void) { ASMAtomicIncU32(&m_u32RefCnt); LogFlowFunc(("[%s] m_u32RefCnt = %d\n", m_pszSvcName, m_u32RefCnt)); } /** The method dereferences a service and deletes it when no more refs. * * @thread main HGCM */ void HGCMService::ReleaseService(void) { LogFlowFunc(("m_u32RefCnt = %d\n", m_u32RefCnt)); uint32_t u32RefCnt = ASMAtomicDecU32(&m_u32RefCnt); AssertRelease(u32RefCnt != ~0U); LogFlowFunc(("u32RefCnt = %d, name %s\n", u32RefCnt, m_pszSvcName)); if (u32RefCnt == 0) { instanceDestroy(); delete this; } } /** The method is called when the VM is being reset or terminated * and disconnects all clients from all services. * * @thread main HGCM */ /* static */ void HGCMService::Reset(void) { g_fResetting = true; HGCMService *pSvc = sm_pSvcListHead; while (pSvc) { while (pSvc->m_cClients && pSvc->m_paClientIds) { LogFlowFunc(("handle %d\n", pSvc->m_paClientIds[0])); pSvc->DisconnectClient(pSvc->m_paClientIds[0], false); } #ifdef VBOX_WITH_CRHGSMI /** @todo could this actually happen that the service is destroyed on ReleaseService? */ HGCMService *pNextSvc = pSvc->m_pSvcNext; while (pSvc->m_cHandleAcquires) { pSvc->HandleReleased(); pSvc->ReleaseService(); } pSvc = pNextSvc; #else pSvc = pSvc->m_pSvcNext; #endif } g_fResetting = false; } /** The method saves the HGCM state. * * @param pSSM The saved state context. * @return VBox rc. * @thread main HGCM */ /* static */ int HGCMService::SaveState(PSSMHANDLE pSSM) { /* Save the current handle count and restore afterwards to avoid client id conflicts. */ int rc = SSMR3PutU32(pSSM, hgcmObjQueryHandleCount()); AssertRCReturn(rc, rc); LogFlowFunc(("%d services to be saved:\n", sm_cServices)); /* Save number of services. */ rc = SSMR3PutU32(pSSM, sm_cServices); AssertRCReturn(rc, rc); /* Save every service. */ HGCMService *pSvc = sm_pSvcListHead; while (pSvc) { LogFlowFunc(("Saving service [%s]\n", pSvc->m_pszSvcName)); /* Save the length of the service name. */ rc = SSMR3PutU32(pSSM, (uint32_t) strlen(pSvc->m_pszSvcName) + 1); AssertRCReturn(rc, rc); /* Save the name of the service. */ rc = SSMR3PutStrZ(pSSM, pSvc->m_pszSvcName); AssertRCReturn(rc, rc); /* Save the number of clients. */ rc = SSMR3PutU32(pSSM, pSvc->m_cClients); AssertRCReturn(rc, rc); /* Call the service for every client. Normally a service must not have * a global state to be saved: only per client info is relevant. * The global state of a service is configured during VM startup. */ int i; for (i = 0; i < pSvc->m_cClients; i++) { uint32_t u32ClientId = pSvc->m_paClientIds[i]; Log(("client id 0x%08X\n", u32ClientId)); /* Save the client id. */ rc = SSMR3PutU32(pSSM, u32ClientId); AssertRCReturn(rc, rc); /* Call the service, so the operation is executed by the service thread. */ rc = pSvc->saveClientState(u32ClientId, pSSM); AssertRCReturn(rc, rc); } pSvc = pSvc->m_pSvcNext; } return VINF_SUCCESS; } /** The method loads saved HGCM state. * * @param pSSM The saved state context. * @return VBox rc. * @thread main HGCM */ /* static */ int HGCMService::LoadState(PSSMHANDLE pSSM) { /* Restore handle count to avoid client id conflicts. */ uint32_t u32; int rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); hgcmObjSetHandleCount(u32); /* Get the number of services. */ uint32_t cServices; rc = SSMR3GetU32(pSSM, &cServices); AssertRCReturn(rc, rc); LogFlowFunc(("%d services to be restored:\n", cServices)); while (cServices--) { /* Get the length of the service name. */ rc = SSMR3GetU32(pSSM, &u32); AssertRCReturn(rc, rc); AssertReturn(u32 <= VBOX_HGCM_SVC_NAME_MAX_BYTES, VERR_SSM_UNEXPECTED_DATA); char *pszServiceName = (char *)alloca(u32); /* Get the service name. */ rc = SSMR3GetStrZ(pSSM, pszServiceName, u32); AssertRCReturn(rc, rc); LogRel(("HGCM: restoring [%s]\n", pszServiceName)); /* Resolve the service instance. */ HGCMService *pSvc; rc = ResolveService(&pSvc, pszServiceName); AssertLogRelMsgReturn(pSvc, ("rc=%Rrc, %s\n", rc, pszServiceName), VERR_SSM_UNEXPECTED_DATA); /* Get the number of clients. */ uint32_t cClients; rc = SSMR3GetU32(pSSM, &cClients); if (RT_FAILURE(rc)) { pSvc->ReleaseService(); AssertFailed(); return rc; } while (cClients--) { /* Get the client id. */ uint32_t u32ClientId; rc = SSMR3GetU32(pSSM, &u32ClientId); if (RT_FAILURE(rc)) { pSvc->ReleaseService(); AssertFailed(); return rc; } /* Connect the client. */ rc = pSvc->CreateAndConnectClient(NULL, u32ClientId); if (RT_FAILURE(rc)) { pSvc->ReleaseService(); AssertLogRelMsgFailed(("rc=%Rrc %s\n", rc, pszServiceName)); return rc; } /* Call the service, so the operation is executed by the service thread. */ rc = pSvc->loadClientState(u32ClientId, pSSM); if (RT_FAILURE(rc)) { pSvc->ReleaseService(); AssertLogRelMsgFailed(("rc=%Rrc %s\n", rc, pszServiceName)); return rc; } } pSvc->ReleaseService(); } return VINF_SUCCESS; } /* Create a new client instance and connect it to the service. * * @param pu32ClientIdOut If not NULL, then the method must generate a new handle for the client. * If NULL, use the given 'u32ClientIdIn' handle. * @param u32ClientIdIn The handle for the client, when 'pu32ClientIdOut' is NULL. * @return VBox rc. */ int HGCMService::CreateAndConnectClient(uint32_t *pu32ClientIdOut, uint32_t u32ClientIdIn) { LogFlowFunc(("pu32ClientIdOut = %p, u32ClientIdIn = %d\n", pu32ClientIdOut, u32ClientIdIn)); /* Allocate a client information structure. */ HGCMClient *pClient = new HGCMClient(); if (!pClient) { Log1WarningFunc(("Could not allocate HGCMClient!!!\n")); return VERR_NO_MEMORY; } uint32_t handle; if (pu32ClientIdOut != NULL) { handle = hgcmObjGenerateHandle(pClient); } else { handle = hgcmObjAssignHandle(pClient, u32ClientIdIn); } LogFlowFunc(("client id = %d\n", handle)); AssertRelease(handle); /* Initialize the HGCM part of the client. */ int rc = pClient->Init(this); if (RT_SUCCESS(rc)) { /* Call the service. */ HGCMMSGHANDLE hMsg; rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_CONNECT, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgSvcConnect *pMsg = (HGCMMsgSvcConnect *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->u32ClientId = handle; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); if (RT_SUCCESS(rc)) { /* Add the client Id to the array. */ if (m_cClients == m_cClientsAllocated) { m_paClientIds = (uint32_t *)RTMemRealloc(m_paClientIds, (m_cClientsAllocated + 64) * sizeof(m_paClientIds[0])); Assert(m_paClientIds); m_cClientsAllocated += 64; } m_paClientIds[m_cClients] = handle; m_cClients++; } } } if (RT_FAILURE(rc)) { hgcmObjDeleteHandle(handle); } else { if (pu32ClientIdOut != NULL) { *pu32ClientIdOut = handle; } ReferenceService(); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* Disconnect the client from the service and delete the client handle. * * @param u32ClientId The handle of the client. * @return VBox rc. */ int HGCMService::DisconnectClient(uint32_t u32ClientId, bool fFromService) { int rc = VINF_SUCCESS; LogFlowFunc(("client id = %d, fFromService = %d\n", u32ClientId, fFromService)); if (!fFromService) { /* Call the service. */ HGCMMSGHANDLE hMsg; rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_DISCONNECT, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgSvcDisconnect *pMsg = (HGCMMsgSvcDisconnect *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->u32ClientId = u32ClientId; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } else { LogRel(("(%d, %d) [%s] hgcmMsgAlloc(%p, SVC_MSG_DISCONNECT) failed %Rrc\n", u32ClientId, fFromService, RT_VALID_PTR(m_pszSvcName)? m_pszSvcName: "", m_thread, rc)); } } /* Remove the client id from the array in any case, rc does not matter. */ int i; for (i = 0; i < m_cClients; i++) { if (m_paClientIds[i] == u32ClientId) { m_cClients--; if (m_cClients > i) { memmove (&m_paClientIds[i], &m_paClientIds[i + 1], sizeof(m_paClientIds[0]) * (m_cClients - i)); } /* Delete the client handle. */ hgcmObjDeleteHandle(u32ClientId); /* The service must be released. */ ReleaseService(); break; } } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } int HGCMService::RegisterExtension(HGCMSVCEXTHANDLE handle, PFNHGCMSVCEXT pfnExtension, void *pvExtension) { LogFlowFunc(("%s\n", handle->pszServiceName)); /* Forward the message to the service thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_REGEXT, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgSvcRegisterExtension *pMsg = (HGCMMsgSvcRegisterExtension *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->handle = handle; pMsg->pfnExtension = pfnExtension; pMsg->pvExtension = pvExtension; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } void HGCMService::UnregisterExtension(HGCMSVCEXTHANDLE handle) { /* Forward the message to the service thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_UNREGEXT, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgSvcUnregisterExtension *pMsg = (HGCMMsgSvcUnregisterExtension *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->handle = handle; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); } /* Perform a guest call to the service. * * @param pHGCMPort The port to be used for completion confirmation. * @param pCmd The VBox HGCM context. * @param u32ClientId The client handle to be disconnected and deleted. * @param u32Function The function number. * @param cParms Number of parameters. * @param paParms Pointer to array of parameters. * @return VBox rc. */ int HGCMService::GuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { HGCMMSGHANDLE hMsg = 0; LogFlow(("MAIN::HGCMService::Call\n")); int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_GUESTCALL, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgCall *pMsg = (HGCMMsgCall *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pCmd = pCmd; pMsg->pHGCMPort = pHGCMPort; pMsg->u32ClientId = u32ClientId; pMsg->u32Function = u32Function; pMsg->cParms = cParms; pMsg->paParms = paParms; hgcmObjDereference(pMsg); rc = hgcmMsgPost(hMsg, hgcmMsgCompletionCallback); } else { Log(("MAIN::HGCMService::Call: Message allocation failed: %Rrc\n", rc)); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* Perform a host call the service. * * @param u32Function The function number. * @param cParms Number of parameters. * @param paParms Pointer to array of parameters. * @return VBox rc. */ int HGCMService::HostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms) { LogFlowFunc(("%s u32Function = %d, cParms = %d, paParms = %p\n", m_pszSvcName, u32Function, cParms, paParms)); HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_HOSTCALL, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgHostCallSvc *pMsg = (HGCMMsgHostCallSvc *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->u32Function = u32Function; pMsg->cParms = cParms; pMsg->paParms = paParms; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } #ifdef VBOX_WITH_CRHGSMI static DECLCALLBACK(void) hgcmMsgFastCallCompletionCallback(int32_t result, HGCMMsgCore *pMsgCore) { /* Call the VMMDev port interface to issue IRQ notification. */ LogFlow(("MAIN::hgcmMsgFastCallCompletionCallback: message %p\n", pMsgCore)); HGCMMsgHostFastCallAsyncSvc *pMsg = (HGCMMsgHostFastCallAsyncSvc *)pMsgCore; if (pMsg->pfnCompletion) { pMsg->pfnCompletion(result, pMsg->u32Function, &pMsg->Param, pMsg->pvCompletion); } } int HGCMService::HandleAcquired() { ++m_cHandleAcquires; return VINF_SUCCESS; } int HGCMService::HandleReleased() { Assert(m_cHandleAcquires); if (m_cHandleAcquires) { --m_cHandleAcquires; return VINF_SUCCESS; } return VERR_INVALID_STATE; } int HGCMService::HostFastCallAsync(uint32_t u32Function, VBOXHGCMSVCPARM *pParm, PHGCMHOSTFASTCALLCB pfnCompletion, void *pvCompletion) { LogFlowFunc(("%s u32Function = %d, pParm = %p\n", m_pszSvcName, u32Function, pParm)); HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(m_thread, &hMsg, SVC_MSG_HOSTFASTCALLASYNC, hgcmMessageAllocSvc); if (RT_SUCCESS(rc)) { HGCMMsgHostFastCallAsyncSvc *pMsg = (HGCMMsgHostFastCallAsyncSvc *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->u32Function = u32Function; pMsg->Param = *pParm; pMsg->pfnCompletion = pfnCompletion; pMsg->pvCompletion = pvCompletion; hgcmObjDereference(pMsg); rc = hgcmMsgPost(hMsg, hgcmMsgFastCallCompletionCallback); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } #endif /* * Main HGCM thread that manages services. */ /* Messages processed by the main HGCM thread. */ #define HGCM_MSG_CONNECT (10) /* Connect a client to a service. */ #define HGCM_MSG_DISCONNECT (11) /* Disconnect the specified client id. */ #define HGCM_MSG_LOAD (12) /* Load the service. */ #define HGCM_MSG_HOSTCALL (13) /* Call the service. */ #define HGCM_MSG_LOADSTATE (14) /* Load saved state for the specified service. */ #define HGCM_MSG_SAVESTATE (15) /* Save state for the specified service. */ #define HGCM_MSG_RESET (16) /* Disconnect all clients from the specified service. */ #define HGCM_MSG_QUIT (17) /* Unload all services and terminate the thread. */ #define HGCM_MSG_REGEXT (18) /* Register a service extension. */ #define HGCM_MSG_UNREGEXT (19) /* Unregister a service extension. */ #ifdef VBOX_WITH_CRHGSMI # define HGCM_MSG_SVCAQUIRE (30) /* Acquire a service handle (for fast host calls) */ # define HGCM_MSG_SVCRELEASE (31) /* Release a service */ #endif class HGCMMsgMainConnect: public HGCMMsgHeader { public: /* Service name. */ const char *pszServiceName; /* Where to store the client handle. */ uint32_t *pu32ClientId; }; class HGCMMsgMainDisconnect: public HGCMMsgHeader { public: /* Handle of the client to be disconnected. */ uint32_t u32ClientId; }; class HGCMMsgMainLoad: public HGCMMsgCore { public: /* Name of the library to be loaded. */ const char *pszServiceLibrary; /* Name to be assigned to the service. */ const char *pszServiceName; }; class HGCMMsgMainHostCall: public HGCMMsgCore { public: /* Which service to call. */ const char *pszServiceName; /* Function number. */ uint32_t u32Function; /* Number of the function parameters. */ uint32_t cParms; /* Pointer to array of the function parameters. */ VBOXHGCMSVCPARM *paParms; }; class HGCMMsgMainLoadSaveState: public HGCMMsgCore { public: /* SSM context. */ PSSMHANDLE pSSM; }; class HGCMMsgMainReset: public HGCMMsgCore { }; class HGCMMsgMainQuit: public HGCMMsgCore { }; class HGCMMsgMainRegisterExtension: public HGCMMsgCore { public: /* Returned handle to be used in HGCMMsgMainUnregisterExtension. */ HGCMSVCEXTHANDLE *pHandle; /* Name of the service. */ const char *pszServiceName; /* The extension entry point. */ PFNHGCMSVCEXT pfnExtension; /* The extension pointer. */ void *pvExtension; }; class HGCMMsgMainUnregisterExtension: public HGCMMsgCore { public: /* Handle of the registered extension. */ HGCMSVCEXTHANDLE handle; }; #ifdef VBOX_WITH_CRHGSMI class HGCMMsgMainSvcAcquire: public HGCMMsgCore { public: /* Which service to call. */ const char *pszServiceName; /* Returned service. */ HGCMService *pService; }; class HGCMMsgMainSvcRelease: public HGCMMsgCore { public: /* Svc . */ HGCMService *pService; }; #endif static HGCMMsgCore *hgcmMainMessageAlloc (uint32_t u32MsgId) { switch (u32MsgId) { case HGCM_MSG_CONNECT: return new HGCMMsgMainConnect(); case HGCM_MSG_DISCONNECT: return new HGCMMsgMainDisconnect(); case HGCM_MSG_LOAD: return new HGCMMsgMainLoad(); case HGCM_MSG_HOSTCALL: return new HGCMMsgMainHostCall(); case HGCM_MSG_LOADSTATE: case HGCM_MSG_SAVESTATE: return new HGCMMsgMainLoadSaveState(); case HGCM_MSG_RESET: return new HGCMMsgMainReset(); case HGCM_MSG_QUIT: return new HGCMMsgMainQuit(); case HGCM_MSG_REGEXT: return new HGCMMsgMainRegisterExtension(); case HGCM_MSG_UNREGEXT: return new HGCMMsgMainUnregisterExtension(); #ifdef VBOX_WITH_CRHGSMI case HGCM_MSG_SVCAQUIRE: return new HGCMMsgMainSvcAcquire(); case HGCM_MSG_SVCRELEASE: return new HGCMMsgMainSvcRelease(); #endif default: AssertReleaseMsgFailed(("Msg id = %08X\n", u32MsgId)); } return NULL; } /* The main HGCM thread handler. */ static DECLCALLBACK(void) hgcmThread(HGCMTHREADHANDLE ThreadHandle, void *pvUser) { LogFlowFunc(("ThreadHandle = %p, pvUser = %p\n", ThreadHandle, pvUser)); NOREF(pvUser); bool fQuit = false; while (!fQuit) { HGCMMsgCore *pMsgCore; int rc = hgcmMsgGet(ThreadHandle, &pMsgCore); if (RT_FAILURE(rc)) { /* The error means some serious unrecoverable problem in the hgcmMsg/hgcmThread layer. */ AssertMsgFailed(("%Rrc\n", rc)); break; } uint32_t u32MsgId = pMsgCore->MsgId(); switch (u32MsgId) { case HGCM_MSG_CONNECT: { HGCMMsgMainConnect *pMsg = (HGCMMsgMainConnect *)pMsgCore; LogFlowFunc(("HGCM_MSG_CONNECT pszServiceName %s, pu32ClientId %p\n", pMsg->pszServiceName, pMsg->pu32ClientId)); /* Resolve the service name to the pointer to service instance. */ HGCMService *pService; rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName); if (RT_SUCCESS(rc)) { /* Call the service instance method. */ rc = pService->CreateAndConnectClient(pMsg->pu32ClientId, 0); /* Release the service after resolve. */ pService->ReleaseService(); } } break; case HGCM_MSG_DISCONNECT: { HGCMMsgMainDisconnect *pMsg = (HGCMMsgMainDisconnect *)pMsgCore; LogFlowFunc(("HGCM_MSG_DISCONNECT u32ClientId = %d\n", pMsg->u32ClientId)); HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT); if (!pClient) { rc = VERR_HGCM_INVALID_CLIENT_ID; break; } /* The service the client belongs to. */ HGCMService *pService = pClient->pService; /* Call the service instance to disconnect the client. */ rc = pService->DisconnectClient(pMsg->u32ClientId, false); hgcmObjDereference(pClient); } break; case HGCM_MSG_LOAD: { HGCMMsgMainLoad *pMsg = (HGCMMsgMainLoad *)pMsgCore; LogFlowFunc(("HGCM_MSG_LOAD pszServiceName = %s, pMsg->pszServiceLibrary = %s\n", pMsg->pszServiceName, pMsg->pszServiceLibrary)); rc = HGCMService::LoadService(pMsg->pszServiceLibrary, pMsg->pszServiceName); } break; case HGCM_MSG_HOSTCALL: { HGCMMsgMainHostCall *pMsg = (HGCMMsgMainHostCall *)pMsgCore; LogFlowFunc(("HGCM_MSG_HOSTCALL pszServiceName %s, u32Function %d, cParms %d, paParms %p\n", pMsg->pszServiceName, pMsg->u32Function, pMsg->cParms, pMsg->paParms)); /* Resolve the service name to the pointer to service instance. */ HGCMService *pService; rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName); if (RT_SUCCESS(rc)) { rc = pService->HostCall(pMsg->u32Function, pMsg->cParms, pMsg->paParms); pService->ReleaseService(); } } break; #ifdef VBOX_WITH_CRHGSMI case HGCM_MSG_SVCAQUIRE: { HGCMMsgMainSvcAcquire *pMsg = (HGCMMsgMainSvcAcquire *)pMsgCore; LogFlowFunc(("HGCM_MSG_SVCAQUIRE pszServiceName %s\n", pMsg->pszServiceName)); /* Resolve the service name to the pointer to service instance. */ HGCMService *pService; rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName); if (RT_SUCCESS(rc)) { rc = pService->HandleAcquired(); if (RT_SUCCESS(rc)) { pMsg->pService = pService; } else { pService->ReleaseService(); } } } break; case HGCM_MSG_SVCRELEASE: { HGCMMsgMainSvcRelease *pMsg = (HGCMMsgMainSvcRelease *)pMsgCore; LogFlowFunc(("HGCM_MSG_SVCARELEASE pService %p\n", pMsg->pService)); /* Resolve the service name to the pointer to service instance. */ rc = pMsg->pService->HandleReleased(); if (RT_SUCCESS(rc)) { pMsg->pService->ReleaseService(); } } break; #endif case HGCM_MSG_RESET: { LogFlowFunc(("HGCM_MSG_RESET\n")); HGCMService::Reset(); } break; case HGCM_MSG_LOADSTATE: { HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pMsgCore; LogFlowFunc(("HGCM_MSG_LOADSTATE\n")); rc = HGCMService::LoadState(pMsg->pSSM); } break; case HGCM_MSG_SAVESTATE: { HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pMsgCore; LogFlowFunc(("HGCM_MSG_SAVESTATE\n")); rc = HGCMService::SaveState(pMsg->pSSM); } break; case HGCM_MSG_QUIT: { LogFlowFunc(("HGCM_MSG_QUIT\n")); HGCMService::UnloadAll(); fQuit = true; } break; case HGCM_MSG_REGEXT: { HGCMMsgMainRegisterExtension *pMsg = (HGCMMsgMainRegisterExtension *)pMsgCore; LogFlowFunc(("HGCM_MSG_REGEXT\n")); /* Allocate the handle data. */ HGCMSVCEXTHANDLE handle = (HGCMSVCEXTHANDLE)RTMemAllocZ(sizeof(struct _HGCMSVCEXTHANDLEDATA) + strlen(pMsg->pszServiceName) + sizeof(char)); if (handle == NULL) { rc = VERR_NO_MEMORY; } else { handle->pszServiceName = (char *)((uint8_t *)handle + sizeof(struct _HGCMSVCEXTHANDLEDATA)); strcpy(handle->pszServiceName, pMsg->pszServiceName); HGCMService *pService; rc = HGCMService::ResolveService(&pService, handle->pszServiceName); if (RT_SUCCESS(rc)) { pService->RegisterExtension(handle, pMsg->pfnExtension, pMsg->pvExtension); pService->ReleaseService(); } if (RT_FAILURE(rc)) { RTMemFree(handle); } else { *pMsg->pHandle = handle; } } } break; case HGCM_MSG_UNREGEXT: { HGCMMsgMainUnregisterExtension *pMsg = (HGCMMsgMainUnregisterExtension *)pMsgCore; LogFlowFunc(("HGCM_MSG_UNREGEXT\n")); HGCMService *pService; rc = HGCMService::ResolveService(&pService, pMsg->handle->pszServiceName); if (RT_SUCCESS(rc)) { pService->UnregisterExtension(pMsg->handle); pService->ReleaseService(); } RTMemFree(pMsg->handle); } break; default: { AssertMsgFailed(("hgcmThread: Unsupported message number %08X!!!\n", u32MsgId)); rc = VERR_NOT_SUPPORTED; } break; } /* Complete the message processing. */ hgcmMsgComplete(pMsgCore, rc); LogFlowFunc(("message processed %Rrc\n", rc)); } } /* * The HGCM API. */ /* The main hgcm thread. */ static HGCMTHREADHANDLE g_hgcmThread = 0; /* * Public HGCM functions. * * hgcmGuest* - called as a result of the guest HGCM requests. * hgcmHost* - called by the host. */ /* Load a HGCM service from the specified library. * Assign the specified name to the service. * * @param pszServiceLibrary The library to be loaded. * @param pszServiceName The name to be assigned to the service. * @return VBox rc. */ int HGCMHostLoad(const char *pszServiceLibrary, const char *pszServiceName) { LogFlowFunc(("lib = %s, name = %s\n", pszServiceLibrary, pszServiceName)); if (!pszServiceLibrary || !pszServiceName) { return VERR_INVALID_PARAMETER; } /* Forward the request to the main hgcm thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_LOAD, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { /* Initialize the message. Since the message is synchronous, use the supplied pointers. */ HGCMMsgMainLoad *pMsg = (HGCMMsgMainLoad *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pszServiceLibrary = pszServiceLibrary; pMsg->pszServiceName = pszServiceName; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* Register a HGCM service extension. * * @param pHandle Returned handle for the registered extension. * @param pszServiceName The name of the service. * @param pfnExtension The extension entry point (callback). * @param pvExtension The extension pointer. * @return VBox rc. */ int HGCMHostRegisterServiceExtension(HGCMSVCEXTHANDLE *pHandle, const char *pszServiceName, PFNHGCMSVCEXT pfnExtension, void *pvExtension) { LogFlowFunc(("pHandle = %p, name = %s, pfn = %p, rv = %p\n", pHandle, pszServiceName, pfnExtension, pvExtension)); if (!pHandle || !pszServiceName || !pfnExtension) { return VERR_INVALID_PARAMETER; } /* Forward the request to the main hgcm thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_REGEXT, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { /* Initialize the message. Since the message is synchronous, use the supplied pointers. */ HGCMMsgMainRegisterExtension *pMsg = (HGCMMsgMainRegisterExtension *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pHandle = pHandle; pMsg->pszServiceName = pszServiceName; pMsg->pfnExtension = pfnExtension; pMsg->pvExtension = pvExtension; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("*pHandle = %p, rc = %Rrc\n", *pHandle, rc)); return rc; } void HGCMHostUnregisterServiceExtension(HGCMSVCEXTHANDLE handle) { LogFlowFunc(("handle = %p\n", handle)); /* Forward the request to the main hgcm thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_UNREGEXT, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { /* Initialize the message. */ HGCMMsgMainUnregisterExtension *pMsg = (HGCMMsgMainUnregisterExtension *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->handle = handle; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return; } /* Find a service and inform it about a client connection, create a client handle. * * @param pHGCMPort The port to be used for completion confirmation. * @param pCmd The VBox HGCM context. * @param pszServiceName The name of the service to be connected to. * @param pu32ClientId Where the store the created client handle. * @return VBox rc. */ int HGCMGuestConnect(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, const char *pszServiceName, uint32_t *pu32ClientId) { LogFlowFunc(("pHGCMPort = %p, pCmd = %p, name = %s, pu32ClientId = %p\n", pHGCMPort, pCmd, pszServiceName, pu32ClientId)); if (pHGCMPort == NULL || pCmd == NULL || pszServiceName == NULL || pu32ClientId == NULL) { return VERR_INVALID_PARAMETER; } /* Forward the request to the main hgcm thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_CONNECT, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { /* Initialize the message. Since 'pszServiceName' and 'pu32ClientId' * will not be deallocated by the caller until the message is completed, * use the supplied pointers. */ HGCMMsgMainConnect *pMsg = (HGCMMsgMainConnect *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pHGCMPort = pHGCMPort; pMsg->pCmd = pCmd; pMsg->pszServiceName = pszServiceName; pMsg->pu32ClientId = pu32ClientId; hgcmObjDereference(pMsg); rc = hgcmMsgPost(hMsg, hgcmMsgCompletionCallback); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* Tell a service that the client is disconnecting, destroy the client handle. * * @param pHGCMPort The port to be used for completion confirmation. * @param pCmd The VBox HGCM context. * @param u32ClientId The client handle to be disconnected and deleted. * @return VBox rc. */ int HGCMGuestDisconnect(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId) { LogFlowFunc(("pHGCMPort = %p, pCmd = %p, u32ClientId = %d\n", pHGCMPort, pCmd, u32ClientId)); if (!pHGCMPort || !pCmd || !u32ClientId) { return VERR_INVALID_PARAMETER; } /* Forward the request to the main hgcm thread. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_DISCONNECT, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { /* Initialize the message. */ HGCMMsgMainDisconnect *pMsg = (HGCMMsgMainDisconnect *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pCmd = pCmd; pMsg->pHGCMPort = pHGCMPort; pMsg->u32ClientId = u32ClientId; hgcmObjDereference(pMsg); rc = hgcmMsgPost(hMsg, hgcmMsgCompletionCallback); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* Helper to send either HGCM_MSG_SAVESTATE or HGCM_MSG_LOADSTATE messages to the main HGCM thread. * * @param pSSM The SSM handle. * @param u32MsgId The message to be sent: HGCM_MSG_SAVESTATE or HGCM_MSG_LOADSTATE. * @return VBox rc. */ static int hgcmHostLoadSaveState(PSSMHANDLE pSSM, uint32_t u32MsgId) { LogFlowFunc(("pSSM = %p, u32MsgId = %d\n", pSSM, u32MsgId)); HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, u32MsgId, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pSSM = pSSM; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* Save the state of services. * * @param pSSM The SSM handle. * @return VBox rc. */ int HGCMHostSaveState(PSSMHANDLE pSSM) { return hgcmHostLoadSaveState(pSSM, HGCM_MSG_SAVESTATE); } /* Load the state of services. * * @param pSSM The SSM handle. * @return VBox rc. */ int HGCMHostLoadState(PSSMHANDLE pSSM) { return hgcmHostLoadSaveState(pSSM, HGCM_MSG_LOADSTATE); } /* The guest calls the service. * * @param pHGCMPort The port to be used for completion confirmation. * @param pCmd The VBox HGCM context. * @param u32ClientId The client handle to be disconnected and deleted. * @param u32Function The function number. * @param cParms Number of parameters. * @param paParms Pointer to array of parameters. * @return VBox rc. */ int HGCMGuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms) { LogFlowFunc(("pHGCMPort = %p, pCmd = %p, u32ClientId = %d, u32Function = %d, cParms = %d, paParms = %p\n", pHGCMPort, pCmd, u32ClientId, u32Function, cParms, paParms)); if (!pHGCMPort || !pCmd || u32ClientId == 0) { return VERR_INVALID_PARAMETER; } int rc = VERR_HGCM_INVALID_CLIENT_ID; /* Resolve the client handle to the client instance pointer. */ HGCMClient *pClient = (HGCMClient *)hgcmObjReference(u32ClientId, HGCMOBJ_CLIENT); if (pClient) { AssertRelease(pClient->pService); /* Forward the message to the service thread. */ rc = pClient->pService->GuestCall(pHGCMPort, pCmd, u32ClientId, u32Function, cParms, paParms); hgcmObjDereference(pClient); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } /* The host calls the service. * * @param pszServiceName The service name to be called. * @param u32Function The function number. * @param cParms Number of parameters. * @param paParms Pointer to array of parameters. * @return VBox rc. */ int HGCMHostCall(const char *pszServiceName, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms) { LogFlowFunc(("name = %s, u32Function = %d, cParms = %d, paParms = %p\n", pszServiceName, u32Function, cParms, paParms)); if (!pszServiceName) { return VERR_INVALID_PARAMETER; } HGCMMSGHANDLE hMsg = 0; /* Host calls go to main HGCM thread that resolves the service name to the * service instance pointer and then, using the service pointer, forwards * the message to the service thread. * So it is slow but host calls are intended mostly for configuration and * other non-time-critical functions. */ int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_HOSTCALL, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { HGCMMsgMainHostCall *pMsg = (HGCMMsgMainHostCall *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pszServiceName = (char *)pszServiceName; pMsg->u32Function = u32Function; pMsg->cParms = cParms; pMsg->paParms = paParms; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } #ifdef VBOX_WITH_CRHGSMI int HGCMHostSvcHandleCreate(const char *pszServiceName, HGCMCVSHANDLE * phSvc) { LogFlowFunc(("name = %s\n", pszServiceName)); if (!pszServiceName) { return VERR_INVALID_PARAMETER; } if (!phSvc) { return VERR_INVALID_PARAMETER; } HGCMMSGHANDLE hMsg = 0; /* Host calls go to main HGCM thread that resolves the service name to the * service instance pointer and then, using the service pointer, forwards * the message to the service thread. * So it is slow but host calls are intended mostly for configuration and * other non-time-critical functions. */ int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_SVCAQUIRE, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { HGCMMsgMainSvcAcquire *pMsg = (HGCMMsgMainSvcAcquire *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pszServiceName = (char *)pszServiceName; pMsg->pService = NULL; rc = hgcmMsgSend(hMsg); if (RT_SUCCESS(rc)) { /* for simplicity just use a svc ptr as handle for now */ *phSvc = (HGCMCVSHANDLE)pMsg->pService; } hgcmObjDereference(pMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } int HGCMHostSvcHandleDestroy(HGCMCVSHANDLE hSvc) { LogFlowFunc(("hSvc = %p\n", hSvc)); if (!hSvc) { return VERR_INVALID_PARAMETER; } HGCMMSGHANDLE hMsg = 0; /* Host calls go to main HGCM thread that resolves the service name to the * service instance pointer and then, using the service pointer, forwards * the message to the service thread. * So it is slow but host calls are intended mostly for configuration and * other non-time-critical functions. */ int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_SVCRELEASE, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { HGCMMsgMainSvcRelease *pMsg = (HGCMMsgMainSvcRelease *)hgcmObjReference(hMsg, HGCMOBJ_MSG); AssertRelease(pMsg); pMsg->pService = (HGCMService *)hSvc; hgcmObjDereference(pMsg); rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } int HGCMHostFastCallAsync(HGCMCVSHANDLE hSvc, uint32_t function, VBOXHGCMSVCPARM *pParm, PHGCMHOSTFASTCALLCB pfnCompletion, void *pvCompletion) { LogFlowFunc(("hSvc = %p, u32Function = %d, pParm = %p\n", hSvc, function, pParm)); if (!hSvc) { return VERR_INVALID_PARAMETER; } HGCMService *pService = (HGCMService *)hSvc; int rc = pService->HostFastCallAsync(function, pParm, pfnCompletion, pvCompletion); LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } #endif int HGCMHostReset(void) { LogFlowFunc(("\n")); /* Disconnect all clients. */ HGCMMSGHANDLE hMsg = 0; int rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_RESET, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { rc = hgcmMsgSend(hMsg); } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } int HGCMHostInit(void) { LogFlowFunc(("\n")); int rc = hgcmThreadInit(); if (RT_SUCCESS(rc)) { /* * Start main HGCM thread. */ rc = hgcmThreadCreate(&g_hgcmThread, "MainHGCMthread", hgcmThread, NULL); if (RT_FAILURE(rc)) { LogRel(("Failed to start HGCM thread. HGCM services will be unavailable!!! rc = %Rrc\n", rc)); } } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } int HGCMHostShutdown(void) { LogFlowFunc(("\n")); /* * Do HGCMReset and then unload all services. */ int rc = HGCMHostReset(); if (RT_SUCCESS(rc)) { /* Send the quit message to the main hgcmThread. */ HGCMMSGHANDLE hMsg = 0; rc = hgcmMsgAlloc(g_hgcmThread, &hMsg, HGCM_MSG_QUIT, hgcmMainMessageAlloc); if (RT_SUCCESS(rc)) { rc = hgcmMsgSend(hMsg); if (RT_SUCCESS(rc)) { /* Wait for the thread termination. */ hgcmThreadWait(g_hgcmThread); g_hgcmThread = 0; hgcmThreadUninit(); } } } LogFlowFunc(("rc = %Rrc\n", rc)); return rc; }