VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/HGCM.cpp@ 91744

Last change on this file since 91744 was 91100, checked in by vboxsync, 3 years ago

HGCM: Fixes to svcHlpDisconnectClient / DisconnectClient. Documentation updates. oem2ticketref:46 bugref:10038

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.8 KB
Line 
1/* $Id: HGCM.cpp 91100 2021-09-02 21:01:51Z vboxsync $ */
2/** @file
3 * HGCM (Host-Guest Communication Manager)
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_HGCM
19#include "LoggingNew.h"
20
21#include "HGCM.h"
22#include "HGCMThread.h"
23
24#include <VBox/err.h>
25#include <VBox/hgcmsvc.h>
26#include <VBox/vmm/ssm.h>
27#include <VBox/vmm/stam.h>
28#include <VBox/sup.h>
29#include <VBox/AssertGuest.h>
30
31#include <iprt/alloc.h>
32#include <iprt/avl.h>
33#include <iprt/critsect.h>
34#include <iprt/asm.h>
35#include <iprt/ldr.h>
36#include <iprt/param.h>
37#include <iprt/path.h>
38#include <iprt/string.h>
39#include <iprt/semaphore.h>
40#include <iprt/thread.h>
41
42#include <VBox/VMMDev.h>
43#include <new>
44
45/**
46 * A service gets one thread, which synchronously delivers messages to
47 * the service. This is good for serialization.
48 *
49 * Some services may want to process messages asynchronously, and will want
50 * a next message to be delivered, while a previous message is still being
51 * processed.
52 *
53 * The dedicated service thread delivers a next message when service
54 * returns after fetching a previous one. The service will call a message
55 * completion callback when message is actually processed. So returning
56 * from the service call means only that the service is processing message.
57 *
58 * 'Message processed' condition is indicated by service, which call the
59 * callback, even if the callback is called synchronously in the dedicated
60 * thread.
61 *
62 * This message completion callback is only valid for Call requests.
63 * Connect and Disconnect are processed synchronously by the service.
64 */
65
66
67/* The maximum allowed size of a service name in bytes. */
68#define VBOX_HGCM_SVC_NAME_MAX_BYTES 1024
69
70struct _HGCMSVCEXTHANDLEDATA
71{
72 char *pszServiceName;
73 /* The service name follows. */
74};
75
76class HGCMClient;
77
78/** Internal helper service object. HGCM code would use it to
79 * hold information about services and communicate with services.
80 * The HGCMService is an (in future) abstract class that implements
81 * common functionality. There will be derived classes for specific
82 * service types.
83 */
84
85class HGCMService
86{
87 private:
88 VBOXHGCMSVCHELPERS m_svcHelpers;
89
90 static HGCMService *sm_pSvcListHead;
91 static HGCMService *sm_pSvcListTail;
92
93 static int sm_cServices;
94
95 HGCMThread *m_pThread;
96 friend DECLCALLBACK(void) hgcmServiceThread(HGCMThread *pThread, void *pvUser);
97
98 uint32_t volatile m_u32RefCnt;
99
100 HGCMService *m_pSvcNext;
101 HGCMService *m_pSvcPrev;
102
103 char *m_pszSvcName;
104 char *m_pszSvcLibrary;
105
106 RTLDRMOD m_hLdrMod;
107 PFNVBOXHGCMSVCLOAD m_pfnLoad;
108
109 VBOXHGCMSVCFNTABLE m_fntable;
110
111 /** Set if servicing SVC_MSG_CONNECT or SVC_MSG_DISCONNECT.
112 * Used for context checking pfnDisconnectClient calls, as it can only
113 * safely be made when the main HGCM thread is waiting on the service to
114 * process those messages. */
115 bool m_fInConnectOrDisconnect;
116
117 uint32_t m_acClients[HGCM_CLIENT_CATEGORY_MAX]; /**< Clients per category. */
118 uint32_t m_cClients;
119 uint32_t m_cClientsAllocated;
120
121 uint32_t *m_paClientIds;
122
123 HGCMSVCEXTHANDLE m_hExtension;
124
125 PUVM m_pUVM;
126 PPDMIHGCMPORT m_pHgcmPort;
127
128 /** @name Statistics
129 * @{ */
130 STAMPROFILE m_StatHandleMsg;
131 STAMCOUNTER m_StatTooManyClients;
132 STAMCOUNTER m_StatTooManyCalls;
133 /** @} */
134
135 int loadServiceDLL(void);
136 void unloadServiceDLL(void);
137
138 /*
139 * Main HGCM thread methods.
140 */
141 int instanceCreate(const char *pszServiceLibrary, const char *pszServiceName, PUVM pUVM, PPDMIHGCMPORT pHgcmPort);
142 void instanceDestroy(void);
143
144 int saveClientState(uint32_t u32ClientId, PSSMHANDLE pSSM);
145 int loadClientState(uint32_t u32ClientId, PSSMHANDLE pSSM, uint32_t uVersion);
146
147 HGCMService();
148 ~HGCMService() {};
149
150 static DECLCALLBACK(int) svcHlpCallComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc);
151 static DECLCALLBACK(int) svcHlpDisconnectClient(void *pvInstance, uint32_t idClient);
152 static DECLCALLBACK(bool) svcHlpIsCallRestored(VBOXHGCMCALLHANDLE callHandle);
153 static DECLCALLBACK(bool) svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE callHandle);
154 static DECLCALLBACK(int) svcHlpStamRegisterV(void *pvInstance, void *pvSample, STAMTYPE enmType,
155 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
156 const char *pszName, va_list va);
157 static DECLCALLBACK(int) svcHlpStamDeregisterV(void *pvInstance, const char *pszPatFmt, va_list va);
158 static DECLCALLBACK(int) svcHlpInfoRegister(void *pvInstance, const char *pszName, const char *pszDesc,
159 PFNDBGFHANDLEREXT pfnHandler, void *pvUser);
160 static DECLCALLBACK(int) svcHlpInfoDeregister(void *pvInstance, const char *pszName);
161 static DECLCALLBACK(uint32_t) svcHlpGetRequestor(VBOXHGCMCALLHANDLE hCall);
162 static DECLCALLBACK(uint64_t) svcHlpGetVMMDevSessionId(void *pvInstance);
163
164 public:
165
166 /*
167 * Main HGCM thread methods.
168 */
169 static int LoadService(const char *pszServiceLibrary, const char *pszServiceName, PUVM pUVM, PPDMIHGCMPORT pHgcmPort);
170 void UnloadService(bool fUvmIsInvalid);
171
172 static void UnloadAll(bool fUvmIsInvalid);
173
174 static int ResolveService(HGCMService **ppsvc, const char *pszServiceName);
175 void ReferenceService(void);
176 void ReleaseService(void);
177
178 static void Reset(void);
179
180 static int SaveState(PSSMHANDLE pSSM);
181 static int LoadState(PSSMHANDLE pSSM, uint32_t uVersion);
182
183 int CreateAndConnectClient(uint32_t *pu32ClientIdOut, uint32_t u32ClientIdIn, uint32_t fRequestor, bool fRestoring);
184 int DisconnectClient(uint32_t u32ClientId, bool fFromService, HGCMClient *pClient);
185
186 int HostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms);
187 static void BroadcastNotify(HGCMNOTIFYEVENT enmEvent);
188 void Notify(HGCMNOTIFYEVENT enmEvent);
189
190 uint32_t SizeOfClient(void) { return m_fntable.cbClient; };
191
192 int RegisterExtension(HGCMSVCEXTHANDLE handle, PFNHGCMSVCEXT pfnExtension, void *pvExtension);
193 void UnregisterExtension(HGCMSVCEXTHANDLE handle);
194
195 /*
196 * The service thread methods.
197 */
198
199 int GuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, HGCMClient *pClient,
200 uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM aParms[], uint64_t tsArrival);
201 void GuestCancelled(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t idClient);
202};
203
204
205class HGCMClient: public HGCMObject
206{
207 public:
208 HGCMClient(uint32_t a_fRequestor, uint32_t a_idxCategory)
209 : HGCMObject(HGCMOBJ_CLIENT)
210 , pService(NULL)
211 , pvData(NULL)
212 , fRequestor(a_fRequestor)
213 , idxCategory(a_idxCategory)
214 , cPendingCalls(0)
215 {
216 Assert(idxCategory < HGCM_CLIENT_CATEGORY_MAX);
217 }
218 ~HGCMClient();
219
220 int Init(HGCMService *pSvc);
221
222 /** Service that the client is connected to. */
223 HGCMService *pService;
224
225 /** Client specific data. */
226 void *pvData;
227
228 /** The requestor flags this client was created with.
229 * @sa VMMDevRequestHeader::fRequestor */
230 uint32_t fRequestor;
231
232 /** The client category (HGCM_CLIENT_CATEGORY_XXX). */
233 uint32_t idxCategory;
234
235 /** Number of pending calls. */
236 uint32_t volatile cPendingCalls;
237
238 private: /* none of this: */
239 HGCMClient();
240 HGCMClient(HGCMClient const &);
241 HGCMClient &operator=(HGCMClient const &);
242};
243
244HGCMClient::~HGCMClient()
245{
246 if (pService->SizeOfClient() > 0)
247 {
248 RTMemFree(pvData);
249 pvData = NULL;
250 }
251}
252
253
254int HGCMClient::Init(HGCMService *pSvc)
255{
256 pService = pSvc;
257
258 if (pService->SizeOfClient() > 0)
259 {
260 pvData = RTMemAllocZ(pService->SizeOfClient());
261
262 if (!pvData)
263 {
264 return VERR_NO_MEMORY;
265 }
266 }
267
268 return VINF_SUCCESS;
269}
270
271
272#define HGCM_CLIENT_DATA(pService, pClient)(pClient->pvData)
273
274
275
276HGCMService *HGCMService::sm_pSvcListHead = NULL;
277HGCMService *HGCMService::sm_pSvcListTail = NULL;
278int HGCMService::sm_cServices = 0;
279
280HGCMService::HGCMService()
281 :
282 m_pThread (NULL),
283 m_u32RefCnt (0),
284 m_pSvcNext (NULL),
285 m_pSvcPrev (NULL),
286 m_pszSvcName (NULL),
287 m_pszSvcLibrary (NULL),
288 m_hLdrMod (NIL_RTLDRMOD),
289 m_pfnLoad (NULL),
290 m_fInConnectOrDisconnect(false),
291 m_cClients (0),
292 m_cClientsAllocated (0),
293 m_paClientIds (NULL),
294 m_hExtension (NULL),
295 m_pUVM (NULL),
296 m_pHgcmPort (NULL)
297{
298 RT_ZERO(m_acClients);
299 RT_ZERO(m_fntable);
300}
301
302
303static bool g_fResetting = false;
304static bool g_fSaveState = false;
305
306
307/** Helper function to load a local service DLL.
308 *
309 * @return VBox code
310 */
311int HGCMService::loadServiceDLL(void)
312{
313 LogFlowFunc(("m_pszSvcLibrary = %s\n", m_pszSvcLibrary));
314
315 if (m_pszSvcLibrary == NULL)
316 {
317 return VERR_INVALID_PARAMETER;
318 }
319
320 RTERRINFOSTATIC ErrInfo;
321 RTErrInfoInitStatic(&ErrInfo);
322
323 int rc;
324
325 if (RTPathHasPath(m_pszSvcLibrary))
326 rc = SUPR3HardenedLdrLoadPlugIn(m_pszSvcLibrary, &m_hLdrMod, &ErrInfo.Core);
327 else
328 rc = SUPR3HardenedLdrLoadAppPriv(m_pszSvcLibrary, &m_hLdrMod, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
329
330 if (RT_SUCCESS(rc))
331 {
332 LogFlowFunc(("successfully loaded the library.\n"));
333
334 m_pfnLoad = NULL;
335
336 rc = RTLdrGetSymbol(m_hLdrMod, VBOX_HGCM_SVCLOAD_NAME, (void**)&m_pfnLoad);
337
338 if (RT_FAILURE(rc) || !m_pfnLoad)
339 {
340 Log(("HGCMService::loadServiceDLL: Error resolving the service entry point %s, rc = %d, m_pfnLoad = %p\n",
341 VBOX_HGCM_SVCLOAD_NAME, rc, m_pfnLoad));
342
343 if (RT_SUCCESS(rc))
344 {
345 /* m_pfnLoad was NULL */
346 rc = VERR_SYMBOL_NOT_FOUND;
347 }
348 }
349
350 if (RT_SUCCESS(rc))
351 {
352 RT_ZERO(m_fntable);
353
354 m_fntable.cbSize = sizeof(m_fntable);
355 m_fntable.u32Version = VBOX_HGCM_SVC_VERSION;
356 m_fntable.pHelpers = &m_svcHelpers;
357
358 /* Total max calls: (2048 + 1024 + 1024) * 8192 = 33 554 432 */
359 m_fntable.idxLegacyClientCategory = HGCM_CLIENT_CATEGORY_KERNEL;
360 m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_KERNEL] = _2K;
361 m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_ROOT] = _1K;
362 m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_USER] = _1K;
363 m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_KERNEL] = _8K;
364 m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_ROOT] = _4K;
365 m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_USER] = _2K;
366 /** @todo provide way to configure different values via extra data. */
367
368 rc = m_pfnLoad(&m_fntable);
369
370 LogFlowFunc(("m_pfnLoad rc = %Rrc\n", rc));
371
372 if (RT_SUCCESS(rc))
373 {
374 if ( m_fntable.pfnUnload != NULL
375 && m_fntable.pfnConnect != NULL
376 && m_fntable.pfnDisconnect != NULL
377 && m_fntable.pfnCall != NULL
378 )
379 {
380 Assert(m_fntable.idxLegacyClientCategory < RT_ELEMENTS(m_fntable.acMaxClients));
381 LogRel2(("HGCMService::loadServiceDLL: acMaxClients={%u,%u,%u} acMaxCallsPerClient={%u,%u,%u} => %RU64 calls; idxLegacyClientCategory=%d; %s\n",
382 m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_KERNEL],
383 m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_ROOT],
384 m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_USER],
385 m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_KERNEL],
386 m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_ROOT],
387 m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_USER],
388 (uint64_t)m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_KERNEL]
389 * m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_KERNEL]
390 + (uint64_t)m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_ROOT]
391 * m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_ROOT]
392 + (uint64_t)m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_USER]
393 * m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_USER],
394 m_fntable.idxLegacyClientCategory, m_pszSvcName));
395 }
396 else
397 {
398 Log(("HGCMService::loadServiceDLL: at least one of function pointers is NULL\n"));
399
400 rc = VERR_INVALID_PARAMETER;
401
402 if (m_fntable.pfnUnload)
403 {
404 m_fntable.pfnUnload(m_fntable.pvService);
405 }
406 }
407 }
408 }
409 }
410 else
411 {
412 LogRel(("HGCM: Failed to load the service library: [%s], rc = %Rrc - %s. The service will be not available.\n",
413 m_pszSvcLibrary, rc, ErrInfo.Core.pszMsg));
414 m_hLdrMod = NIL_RTLDRMOD;
415 }
416
417 if (RT_FAILURE(rc))
418 {
419 unloadServiceDLL();
420 }
421
422 return rc;
423}
424
425/** Helper function to free a local service DLL.
426 *
427 * @return VBox code
428 */
429void HGCMService::unloadServiceDLL(void)
430{
431 if (m_hLdrMod)
432 {
433 RTLdrClose(m_hLdrMod);
434 }
435
436 RT_ZERO(m_fntable);
437 m_pfnLoad = NULL;
438 m_hLdrMod = NIL_RTLDRMOD;
439}
440
441/*
442 * Messages processed by service threads. These threads only call the service entry points.
443 */
444
445#define SVC_MSG_LOAD (0) /**< Load the service library and call VBOX_HGCM_SVCLOAD_NAME entry point. */
446#define SVC_MSG_UNLOAD (1) /**< call pfnUnload and unload the service library. */
447#define SVC_MSG_CONNECT (2) /**< pfnConnect */
448#define SVC_MSG_DISCONNECT (3) /**< pfnDisconnect */
449#define SVC_MSG_GUESTCALL (4) /**< pfnGuestCall */
450#define SVC_MSG_HOSTCALL (5) /**< pfnHostCall */
451#define SVC_MSG_LOADSTATE (6) /**< pfnLoadState. */
452#define SVC_MSG_SAVESTATE (7) /**< pfnSaveState. */
453#define SVC_MSG_QUIT (8) /**< Terminate the thread. */
454#define SVC_MSG_REGEXT (9) /**< pfnRegisterExtension */
455#define SVC_MSG_UNREGEXT (10) /**< pfnRegisterExtension */
456#define SVC_MSG_NOTIFY (11) /**< pfnNotify */
457#define SVC_MSG_GUESTCANCELLED (12) /**< pfnCancelled */
458
459class HGCMMsgSvcLoad: public HGCMMsgCore
460{
461 public:
462 HGCMMsgSvcLoad() : HGCMMsgCore(), pUVM() {}
463
464 /** The user mode VM handle (for statistics and such). */
465 PUVM pUVM;
466};
467
468class HGCMMsgSvcUnload: public HGCMMsgCore
469{
470};
471
472class HGCMMsgSvcConnect: public HGCMMsgCore
473{
474 public:
475 /** client identifier */
476 uint32_t u32ClientId;
477 /** Requestor flags. */
478 uint32_t fRequestor;
479 /** Set if restoring. */
480 bool fRestoring;
481};
482
483class HGCMMsgSvcDisconnect: public HGCMMsgCore
484{
485 public:
486 /** client identifier */
487 uint32_t u32ClientId;
488 /** The client instance. */
489 HGCMClient *pClient;
490};
491
492class HGCMMsgHeader: public HGCMMsgCore
493{
494 public:
495 HGCMMsgHeader() : pCmd(NULL), pHGCMPort(NULL) {};
496
497 /* Command pointer/identifier. */
498 PVBOXHGCMCMD pCmd;
499
500 /* Port to be informed on message completion. */
501 PPDMIHGCMPORT pHGCMPort;
502};
503
504class HGCMMsgCall: public HGCMMsgHeader
505{
506 public:
507 HGCMMsgCall() : pcCounter(NULL)
508 { }
509
510 HGCMMsgCall(HGCMThread *pThread)
511 : pcCounter(NULL)
512 {
513 InitializeCore(SVC_MSG_GUESTCALL, pThread);
514 Initialize();
515 }
516 ~HGCMMsgCall()
517 {
518 Log(("~HGCMMsgCall %p\n", this));
519 Assert(!pcCounter);
520 }
521
522 /** Points to HGCMClient::cPendingCalls if it needs to be decremented. */
523 uint32_t volatile *pcCounter;
524
525 /* client identifier */
526 uint32_t u32ClientId;
527
528 /* function number */
529 uint32_t u32Function;
530
531 /* number of parameters */
532 uint32_t cParms;
533
534 VBOXHGCMSVCPARM *paParms;
535
536 /** The STAM_GET_TS() value when the request arrived. */
537 uint64_t tsArrival;
538};
539
540class HGCMMsgCancelled: public HGCMMsgHeader
541{
542 public:
543 HGCMMsgCancelled() {}
544
545 HGCMMsgCancelled(HGCMThread *pThread)
546 {
547 InitializeCore(SVC_MSG_GUESTCANCELLED, pThread);
548 Initialize();
549 }
550 ~HGCMMsgCancelled() { Log(("~HGCMMsgCancelled %p\n", this)); }
551
552 /** The client identifier. */
553 uint32_t idClient;
554};
555
556class HGCMMsgLoadSaveStateClient: public HGCMMsgCore
557{
558 public:
559 PSSMHANDLE pSSM;
560 uint32_t uVersion;
561 uint32_t u32ClientId;
562};
563
564class HGCMMsgHostCallSvc: public HGCMMsgCore
565{
566 public:
567 /* function number */
568 uint32_t u32Function;
569
570 /* number of parameters */
571 uint32_t cParms;
572
573 VBOXHGCMSVCPARM *paParms;
574};
575
576class HGCMMsgSvcRegisterExtension: public HGCMMsgCore
577{
578 public:
579 /* Handle of the extension to be registered. */
580 HGCMSVCEXTHANDLE handle;
581 /* The extension entry point. */
582 PFNHGCMSVCEXT pfnExtension;
583 /* The extension pointer. */
584 void *pvExtension;
585};
586
587class HGCMMsgSvcUnregisterExtension: public HGCMMsgCore
588{
589 public:
590 /* Handle of the registered extension. */
591 HGCMSVCEXTHANDLE handle;
592};
593
594class HGCMMsgNotify: public HGCMMsgCore
595{
596 public:
597 /** The event. */
598 HGCMNOTIFYEVENT enmEvent;
599};
600
601static HGCMMsgCore *hgcmMessageAllocSvc(uint32_t u32MsgId)
602{
603 switch (u32MsgId)
604 {
605 case SVC_MSG_LOAD: return new HGCMMsgSvcLoad();
606 case SVC_MSG_UNLOAD: return new HGCMMsgSvcUnload();
607 case SVC_MSG_CONNECT: return new HGCMMsgSvcConnect();
608 case SVC_MSG_DISCONNECT: return new HGCMMsgSvcDisconnect();
609 case SVC_MSG_HOSTCALL: return new HGCMMsgHostCallSvc();
610 case SVC_MSG_GUESTCALL: return new HGCMMsgCall();
611 case SVC_MSG_LOADSTATE:
612 case SVC_MSG_SAVESTATE: return new HGCMMsgLoadSaveStateClient();
613 case SVC_MSG_REGEXT: return new HGCMMsgSvcRegisterExtension();
614 case SVC_MSG_UNREGEXT: return new HGCMMsgSvcUnregisterExtension();
615 case SVC_MSG_NOTIFY: return new HGCMMsgNotify();
616 case SVC_MSG_GUESTCANCELLED: return new HGCMMsgCancelled();
617 default:
618 AssertReleaseMsgFailed(("Msg id = %08X\n", u32MsgId));
619 }
620
621 return NULL;
622}
623
624/*
625 * The service thread. Loads the service library and calls the service entry points.
626 */
627DECLCALLBACK(void) hgcmServiceThread(HGCMThread *pThread, void *pvUser)
628{
629 HGCMService *pSvc = (HGCMService *)pvUser;
630 AssertRelease(pSvc != NULL);
631
632 bool fQuit = false;
633
634 while (!fQuit)
635 {
636 HGCMMsgCore *pMsgCore;
637 int rc = hgcmMsgGet(pThread, &pMsgCore);
638
639 if (RT_FAILURE(rc))
640 {
641 /* The error means some serious unrecoverable problem in the hgcmMsg/hgcmThread layer. */
642 AssertMsgFailed(("%Rrc\n", rc));
643 break;
644 }
645
646 STAM_REL_PROFILE_START(&pSvc->m_StatHandleMsg, a);
647
648 /* Cache required information to avoid unnecessary pMsgCore access. */
649 uint32_t u32MsgId = pMsgCore->MsgId();
650
651 switch (u32MsgId)
652 {
653 case SVC_MSG_LOAD:
654 {
655 LogFlowFunc(("SVC_MSG_LOAD\n"));
656 rc = pSvc->loadServiceDLL();
657 } break;
658
659 case SVC_MSG_UNLOAD:
660 {
661 LogFlowFunc(("SVC_MSG_UNLOAD\n"));
662 if (pSvc->m_fntable.pfnUnload)
663 {
664 pSvc->m_fntable.pfnUnload(pSvc->m_fntable.pvService);
665 }
666
667 pSvc->unloadServiceDLL();
668 fQuit = true;
669 } break;
670
671 case SVC_MSG_CONNECT:
672 {
673 HGCMMsgSvcConnect *pMsg = (HGCMMsgSvcConnect *)pMsgCore;
674
675 LogFlowFunc(("SVC_MSG_CONNECT u32ClientId = %d\n", pMsg->u32ClientId));
676
677 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT);
678
679 if (pClient)
680 {
681 pSvc->m_fInConnectOrDisconnect = true;
682 rc = pSvc->m_fntable.pfnConnect(pSvc->m_fntable.pvService, pMsg->u32ClientId,
683 HGCM_CLIENT_DATA(pSvc, pClient),
684 pMsg->fRequestor, pMsg->fRestoring);
685 pSvc->m_fInConnectOrDisconnect = false;
686
687 hgcmObjDereference(pClient);
688 }
689 else
690 {
691 rc = VERR_HGCM_INVALID_CLIENT_ID;
692 }
693 } break;
694
695 case SVC_MSG_DISCONNECT:
696 {
697 HGCMMsgSvcDisconnect *pMsg = (HGCMMsgSvcDisconnect *)pMsgCore;
698
699 LogFlowFunc(("SVC_MSG_DISCONNECT u32ClientId = %d, pClient = %p\n", pMsg->u32ClientId, pMsg->pClient));
700
701 if (pMsg->pClient)
702 {
703 pSvc->m_fInConnectOrDisconnect = true;
704 rc = pSvc->m_fntable.pfnDisconnect(pSvc->m_fntable.pvService, pMsg->u32ClientId,
705 HGCM_CLIENT_DATA(pSvc, pMsg->pClient));
706 pSvc->m_fInConnectOrDisconnect = false;
707 }
708 else
709 {
710 rc = VERR_HGCM_INVALID_CLIENT_ID;
711 }
712 } break;
713
714 case SVC_MSG_GUESTCALL:
715 {
716 HGCMMsgCall *pMsg = (HGCMMsgCall *)pMsgCore;
717
718 LogFlowFunc(("SVC_MSG_GUESTCALL u32ClientId = %d, u32Function = %d, cParms = %d, paParms = %p\n",
719 pMsg->u32ClientId, pMsg->u32Function, pMsg->cParms, pMsg->paParms));
720
721 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT);
722
723 if (pClient)
724 {
725 pSvc->m_fntable.pfnCall(pSvc->m_fntable.pvService, (VBOXHGCMCALLHANDLE)pMsg, pMsg->u32ClientId,
726 HGCM_CLIENT_DATA(pSvc, pClient), pMsg->u32Function,
727 pMsg->cParms, pMsg->paParms, pMsg->tsArrival);
728
729 hgcmObjDereference(pClient);
730 }
731 else
732 {
733 rc = VERR_HGCM_INVALID_CLIENT_ID;
734 }
735 } break;
736
737 case SVC_MSG_GUESTCANCELLED:
738 {
739 HGCMMsgCancelled *pMsg = (HGCMMsgCancelled *)pMsgCore;
740
741 LogFlowFunc(("SVC_MSG_GUESTCANCELLED idClient = %d\n", pMsg->idClient));
742
743 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->idClient, HGCMOBJ_CLIENT);
744
745 if (pClient)
746 {
747 pSvc->m_fntable.pfnCancelled(pSvc->m_fntable.pvService, pMsg->idClient, HGCM_CLIENT_DATA(pSvc, pClient));
748
749 hgcmObjDereference(pClient);
750 }
751 else
752 {
753 rc = VERR_HGCM_INVALID_CLIENT_ID;
754 }
755 } break;
756
757 case SVC_MSG_HOSTCALL:
758 {
759 HGCMMsgHostCallSvc *pMsg = (HGCMMsgHostCallSvc *)pMsgCore;
760
761 LogFlowFunc(("SVC_MSG_HOSTCALL u32Function = %d, cParms = %d, paParms = %p\n",
762 pMsg->u32Function, pMsg->cParms, pMsg->paParms));
763
764 rc = pSvc->m_fntable.pfnHostCall(pSvc->m_fntable.pvService, pMsg->u32Function, pMsg->cParms, pMsg->paParms);
765 } break;
766
767 case SVC_MSG_LOADSTATE:
768 {
769 HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pMsgCore;
770
771 LogFlowFunc(("SVC_MSG_LOADSTATE\n"));
772
773 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT);
774
775 if (pClient)
776 {
777 /* fRequestor: Restored by the message sender already. */
778 bool fHaveClientState = pSvc->m_fntable.pfnLoadState != NULL;
779 if (pMsg->uVersion > HGCM_SAVED_STATE_VERSION_V2)
780 rc = SSMR3GetBool(pMsg->pSSM, &fHaveClientState);
781 else
782 rc = VINF_SUCCESS;
783 if (RT_SUCCESS(rc) )
784 {
785 if (pSvc->m_fntable.pfnLoadState)
786 rc = pSvc->m_fntable.pfnLoadState(pSvc->m_fntable.pvService, pMsg->u32ClientId,
787 HGCM_CLIENT_DATA(pSvc, pClient), pMsg->pSSM,
788 fHaveClientState ? pMsg->uVersion : 0);
789 else
790 AssertLogRelStmt(!fHaveClientState, rc = VERR_INTERNAL_ERROR_5);
791 }
792 hgcmObjDereference(pClient);
793 }
794 else
795 {
796 rc = VERR_HGCM_INVALID_CLIENT_ID;
797 }
798 } break;
799
800 case SVC_MSG_SAVESTATE:
801 {
802 HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pMsgCore;
803
804 LogFlowFunc(("SVC_MSG_SAVESTATE\n"));
805
806 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT);
807
808 rc = VINF_SUCCESS;
809
810 if (pClient)
811 {
812 SSMR3PutU32(pMsg->pSSM, pClient->fRequestor); /* Quicker to save this here than in the message sender. */
813 rc = SSMR3PutBool(pMsg->pSSM, pSvc->m_fntable.pfnSaveState != NULL);
814 if (RT_SUCCESS(rc) && pSvc->m_fntable.pfnSaveState)
815 {
816 g_fSaveState = true;
817 rc = pSvc->m_fntable.pfnSaveState(pSvc->m_fntable.pvService, pMsg->u32ClientId,
818 HGCM_CLIENT_DATA(pSvc, pClient), pMsg->pSSM);
819 g_fSaveState = false;
820 }
821
822 hgcmObjDereference(pClient);
823 }
824 else
825 {
826 rc = VERR_HGCM_INVALID_CLIENT_ID;
827 }
828 } break;
829
830 case SVC_MSG_REGEXT:
831 {
832 HGCMMsgSvcRegisterExtension *pMsg = (HGCMMsgSvcRegisterExtension *)pMsgCore;
833
834 LogFlowFunc(("SVC_MSG_REGEXT handle = %p, pfn = %p\n", pMsg->handle, pMsg->pfnExtension));
835
836 if (pSvc->m_hExtension)
837 {
838 rc = VERR_NOT_SUPPORTED;
839 }
840 else
841 {
842 if (pSvc->m_fntable.pfnRegisterExtension)
843 {
844 rc = pSvc->m_fntable.pfnRegisterExtension(pSvc->m_fntable.pvService, pMsg->pfnExtension,
845 pMsg->pvExtension);
846 }
847 else
848 {
849 rc = VERR_NOT_SUPPORTED;
850 }
851
852 if (RT_SUCCESS(rc))
853 {
854 pSvc->m_hExtension = pMsg->handle;
855 }
856 }
857 } break;
858
859 case SVC_MSG_UNREGEXT:
860 {
861 HGCMMsgSvcUnregisterExtension *pMsg = (HGCMMsgSvcUnregisterExtension *)pMsgCore;
862
863 LogFlowFunc(("SVC_MSG_UNREGEXT handle = %p\n", pMsg->handle));
864
865 if (pSvc->m_hExtension != pMsg->handle)
866 {
867 rc = VERR_NOT_SUPPORTED;
868 }
869 else
870 {
871 if (pSvc->m_fntable.pfnRegisterExtension)
872 {
873 rc = pSvc->m_fntable.pfnRegisterExtension(pSvc->m_fntable.pvService, NULL, NULL);
874 }
875 else
876 {
877 rc = VERR_NOT_SUPPORTED;
878 }
879
880 pSvc->m_hExtension = NULL;
881 }
882 } break;
883
884 case SVC_MSG_NOTIFY:
885 {
886 HGCMMsgNotify *pMsg = (HGCMMsgNotify *)pMsgCore;
887
888 LogFlowFunc(("SVC_MSG_NOTIFY enmEvent = %d\n", pMsg->enmEvent));
889
890 pSvc->m_fntable.pfnNotify(pSvc->m_fntable.pvService, pMsg->enmEvent);
891 } break;
892
893 default:
894 {
895 AssertMsgFailed(("hgcmServiceThread::Unsupported message number %08X\n", u32MsgId));
896 rc = VERR_NOT_SUPPORTED;
897 } break;
898 }
899
900 if (u32MsgId != SVC_MSG_GUESTCALL)
901 {
902 /* For SVC_MSG_GUESTCALL the service calls the completion helper.
903 * Other messages have to be completed here.
904 */
905 hgcmMsgComplete (pMsgCore, rc);
906 }
907 STAM_REL_PROFILE_STOP(&pSvc->m_StatHandleMsg, a);
908 }
909}
910
911/**
912 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnCallComplete}
913 */
914/* static */ DECLCALLBACK(int) HGCMService::svcHlpCallComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
915{
916 HGCMMsgCore *pMsgCore = (HGCMMsgCore *)callHandle;
917
918 /* Only call the completion for these messages. The helper
919 * is called by the service, and the service does not get
920 * any other messages.
921 */
922 AssertMsgReturn(pMsgCore->MsgId() == SVC_MSG_GUESTCALL, ("%d\n", pMsgCore->MsgId()), VERR_WRONG_TYPE);
923 return hgcmMsgComplete(pMsgCore, rc);
924}
925
926/**
927 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnDisconnectClient}
928 */
929/* static */ DECLCALLBACK(int) HGCMService::svcHlpDisconnectClient(void *pvInstance, uint32_t idClient)
930{
931 HGCMService *pService = static_cast <HGCMService *> (pvInstance);
932 AssertReturn(pService, VERR_INVALID_HANDLE);
933
934 /* Only safe to call when the main HGCM thread is waiting on the service
935 to handle a SVC_MSG_CONNECT or SVC_MSG_DISCONNECT message. Otherwise
936 we'd risk racing it and corrupt data structures. */
937 AssertReturn(pService->m_fInConnectOrDisconnect, VERR_INVALID_CONTEXT);
938
939 /* Resolve the client ID and verify that it belongs to this service before
940 trying to disconnect it. */
941 int rc = VERR_NOT_FOUND;
942 HGCMClient * const pClient = (HGCMClient *)hgcmObjReference(idClient, HGCMOBJ_CLIENT);
943 if (pClient)
944 {
945 if (pClient->pService == pService)
946 rc = pService->DisconnectClient(idClient, true, pClient);
947 hgcmObjDereference(pClient);
948 }
949 return rc;
950}
951
952/**
953 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnIsCallRestored}
954 */
955/* static */ DECLCALLBACK(bool) HGCMService::svcHlpIsCallRestored(VBOXHGCMCALLHANDLE callHandle)
956{
957 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)callHandle;
958 AssertPtrReturn(pMsgHdr, false);
959
960 PVBOXHGCMCMD pCmd = pMsgHdr->pCmd;
961 AssertPtrReturn(pCmd, false);
962
963 PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;
964 AssertPtrReturn(pHgcmPort, false);
965
966 return pHgcmPort->pfnIsCmdRestored(pHgcmPort, pCmd);
967}
968
969/**
970 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnIsCallCancelled}
971 */
972/* static */ DECLCALLBACK(bool) HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE callHandle)
973{
974 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)callHandle;
975 AssertPtrReturn(pMsgHdr, false);
976
977 PVBOXHGCMCMD pCmd = pMsgHdr->pCmd;
978 AssertPtrReturn(pCmd, false);
979
980 PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;
981 AssertPtrReturn(pHgcmPort, false);
982
983 return pHgcmPort->pfnIsCmdCancelled(pHgcmPort, pCmd);
984}
985
986/**
987 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnStamRegisterV}
988 */
989/* static */ DECLCALLBACK(int)
990HGCMService::svcHlpStamRegisterV(void *pvInstance, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
991 STAMUNIT enmUnit, const char *pszDesc, const char *pszName, va_list va)
992{
993 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
994 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
995
996 return STAMR3RegisterVU(pService->m_pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, va);
997}
998
999/**
1000 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnStamDeregisterV}
1001 */
1002/* static */ DECLCALLBACK(int) HGCMService::svcHlpStamDeregisterV(void *pvInstance, const char *pszPatFmt, va_list va)
1003{
1004 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1005 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
1006
1007 if (pService->m_pUVM)
1008 return STAMR3DeregisterV(pService->m_pUVM, pszPatFmt, va);
1009 return VINF_SUCCESS;
1010}
1011
1012/**
1013 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnInfoRegister}
1014 */
1015/* static */ DECLCALLBACK(int) HGCMService::svcHlpInfoRegister(void *pvInstance, const char *pszName, const char *pszDesc,
1016 PFNDBGFHANDLEREXT pfnHandler, void *pvUser)
1017{
1018 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1019 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
1020
1021 return DBGFR3InfoRegisterExternal(pService->m_pUVM, pszName, pszDesc, pfnHandler, pvUser);
1022}
1023
1024/**
1025 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnInfoDeregister}
1026 */
1027/* static */ DECLCALLBACK(int) HGCMService::svcHlpInfoDeregister(void *pvInstance, const char *pszName)
1028{
1029 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1030 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
1031 if (pService->m_pUVM)
1032 return DBGFR3InfoDeregisterExternal(pService->m_pUVM, pszName);
1033 return VINF_SUCCESS;
1034}
1035
1036/**
1037 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnGetRequestor}
1038 */
1039/* static */ DECLCALLBACK(uint32_t) HGCMService::svcHlpGetRequestor(VBOXHGCMCALLHANDLE hCall)
1040{
1041 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)(hCall);
1042 AssertPtrReturn(pMsgHdr, VMMDEV_REQUESTOR_LOWEST);
1043
1044 PVBOXHGCMCMD pCmd = pMsgHdr->pCmd;
1045 AssertPtrReturn(pCmd, VMMDEV_REQUESTOR_LOWEST);
1046
1047 PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;
1048 AssertPtrReturn(pHgcmPort, VMMDEV_REQUESTOR_LOWEST);
1049
1050 return pHgcmPort->pfnGetRequestor(pHgcmPort, pCmd);
1051}
1052
1053/**
1054 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnGetVMMDevSessionId}
1055 */
1056/* static */ DECLCALLBACK(uint64_t) HGCMService::svcHlpGetVMMDevSessionId(void *pvInstance)
1057{
1058 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1059 AssertPtrReturn(pService, UINT64_MAX);
1060
1061 PPDMIHGCMPORT pHgcmPort = pService->m_pHgcmPort;
1062 AssertPtrReturn(pHgcmPort, UINT64_MAX);
1063
1064 return pHgcmPort->pfnGetVMMDevSessionId(pHgcmPort);
1065}
1066
1067
1068static DECLCALLBACK(int) hgcmMsgCompletionCallback(int32_t result, HGCMMsgCore *pMsgCore)
1069{
1070 /* Call the VMMDev port interface to issue IRQ notification. */
1071 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)pMsgCore;
1072
1073 LogFlow(("MAIN::hgcmMsgCompletionCallback: message %p\n", pMsgCore));
1074
1075 if (pMsgHdr->pHGCMPort)
1076 {
1077 if (!g_fResetting)
1078 return pMsgHdr->pHGCMPort->pfnCompleted(pMsgHdr->pHGCMPort,
1079 g_fSaveState ? VINF_HGCM_SAVE_STATE : result, pMsgHdr->pCmd);
1080 return VERR_ALREADY_RESET; /* best I could find. */
1081 }
1082 return VERR_NOT_AVAILABLE;
1083}
1084
1085/*
1086 * The main HGCM methods of the service.
1087 */
1088
1089int HGCMService::instanceCreate(const char *pszServiceLibrary, const char *pszServiceName, PUVM pUVM, PPDMIHGCMPORT pHgcmPort)
1090{
1091 LogFlowFunc(("name %s, lib %s\n", pszServiceName, pszServiceLibrary));
1092 /* The maximum length of the thread name, allowed by the RT is 15. */
1093 char szThreadName[16];
1094 if (!strncmp(pszServiceName, RT_STR_TUPLE("VBoxShared")))
1095 RTStrPrintf(szThreadName, sizeof(szThreadName), "Sh%s", pszServiceName + 10);
1096 else if (!strncmp(pszServiceName, RT_STR_TUPLE("VBox")))
1097 RTStrCopy(szThreadName, sizeof(szThreadName), pszServiceName + 4);
1098 else
1099 RTStrCopy(szThreadName, sizeof(szThreadName), pszServiceName);
1100
1101 int rc = hgcmThreadCreate(&m_pThread, szThreadName, hgcmServiceThread, this, pszServiceName, pUVM);
1102
1103 if (RT_SUCCESS(rc))
1104 {
1105 m_pszSvcName = RTStrDup(pszServiceName);
1106 m_pszSvcLibrary = RTStrDup(pszServiceLibrary);
1107
1108 if (!m_pszSvcName || !m_pszSvcLibrary)
1109 {
1110 RTStrFree(m_pszSvcLibrary);
1111 m_pszSvcLibrary = NULL;
1112
1113 RTStrFree(m_pszSvcName);
1114 m_pszSvcName = NULL;
1115
1116 rc = VERR_NO_MEMORY;
1117 }
1118 else
1119 {
1120 m_pUVM = pUVM;
1121 m_pHgcmPort = pHgcmPort;
1122
1123 /* Register statistics: */
1124 STAMR3RegisterFU(pUVM, &m_StatHandleMsg, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1125 "Message handling", "/HGCM/%s/Msg", pszServiceName);
1126 STAMR3RegisterFU(pUVM, &m_StatTooManyCalls, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1127 "Too many calls (per client)", "/HGCM/%s/TooManyCalls", pszServiceName);
1128 STAMR3RegisterFU(pUVM, &m_StatTooManyClients, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1129 "Too many clients", "/HGCM/%s/TooManyClients", pszServiceName);
1130 STAMR3RegisterFU(pUVM, &m_cClients, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1131 "Number of clients", "/HGCM/%s/Clients", pszServiceName);
1132 STAMR3RegisterFU(pUVM, &m_acClients[HGCM_CLIENT_CATEGORY_KERNEL], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1133 STAMUNIT_OCCURENCES, "Number of kernel clients", "/HGCM/%s/Clients/Kernel", pszServiceName);
1134 STAMR3RegisterFU(pUVM, &m_acClients[HGCM_CLIENT_CATEGORY_ROOT], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1135 STAMUNIT_OCCURENCES, "Number of root/admin clients", "/HGCM/%s/Clients/Root", pszServiceName);
1136 STAMR3RegisterFU(pUVM, &m_acClients[HGCM_CLIENT_CATEGORY_USER], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1137 STAMUNIT_OCCURENCES, "Number of regular user clients", "/HGCM/%s/Clients/User", pszServiceName);
1138 STAMR3RegisterFU(pUVM, &m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_KERNEL], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1139 STAMUNIT_OCCURENCES, "Max number of kernel clients", "/HGCM/%s/Clients/KernelMax", pszServiceName);
1140 STAMR3RegisterFU(pUVM, &m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_ROOT], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1141 STAMUNIT_OCCURENCES, "Max number of root clients", "/HGCM/%s/Clients/RootMax", pszServiceName);
1142 STAMR3RegisterFU(pUVM, &m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_USER], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1143 STAMUNIT_OCCURENCES, "Max number of user clients", "/HGCM/%s/Clients/UserMax", pszServiceName);
1144 STAMR3RegisterFU(pUVM, &m_fntable.idxLegacyClientCategory, STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1145 STAMUNIT_OCCURENCES, "Legacy client mapping", "/HGCM/%s/Clients/LegacyClientMapping", pszServiceName);
1146 STAMR3RegisterFU(pUVM, &m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_KERNEL], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1147 STAMUNIT_OCCURENCES, "Max number of call per kernel client", "/HGCM/%s/MaxCallsKernelClient", pszServiceName);
1148 STAMR3RegisterFU(pUVM, &m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_ROOT], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1149 STAMUNIT_OCCURENCES, "Max number of call per root client", "/HGCM/%s/MaxCallsRootClient", pszServiceName);
1150 STAMR3RegisterFU(pUVM, &m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_USER], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1151 STAMUNIT_OCCURENCES, "Max number of call per user client", "/HGCM/%s/MaxCallsUserClient", pszServiceName);
1152
1153 /* Initialize service helpers table. */
1154 m_svcHelpers.pfnCallComplete = svcHlpCallComplete;
1155 m_svcHelpers.pvInstance = this;
1156 m_svcHelpers.pfnDisconnectClient = svcHlpDisconnectClient;
1157 m_svcHelpers.pfnIsCallRestored = svcHlpIsCallRestored;
1158 m_svcHelpers.pfnIsCallCancelled = svcHlpIsCallCancelled;
1159 m_svcHelpers.pfnStamRegisterV = svcHlpStamRegisterV;
1160 m_svcHelpers.pfnStamDeregisterV = svcHlpStamDeregisterV;
1161 m_svcHelpers.pfnInfoRegister = svcHlpInfoRegister;
1162 m_svcHelpers.pfnInfoDeregister = svcHlpInfoDeregister;
1163 m_svcHelpers.pfnGetRequestor = svcHlpGetRequestor;
1164 m_svcHelpers.pfnGetVMMDevSessionId = svcHlpGetVMMDevSessionId;
1165
1166 /* Execute the load request on the service thread. */
1167 HGCMMsgCore *pCoreMsg;
1168 rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_LOAD, hgcmMessageAllocSvc);
1169
1170 if (RT_SUCCESS(rc))
1171 {
1172 HGCMMsgSvcLoad *pMsg = (HGCMMsgSvcLoad *)pCoreMsg;
1173
1174 pMsg->pUVM = pUVM;
1175
1176 rc = hgcmMsgSend(pMsg);
1177 }
1178 }
1179 }
1180
1181 if (RT_FAILURE(rc))
1182 {
1183 instanceDestroy();
1184 }
1185
1186 LogFlowFunc(("rc = %Rrc\n", rc));
1187 return rc;
1188}
1189
1190void HGCMService::instanceDestroy(void)
1191{
1192 LogFlowFunc(("%s\n", m_pszSvcName));
1193
1194 HGCMMsgCore *pMsg;
1195 int rc = hgcmMsgAlloc(m_pThread, &pMsg, SVC_MSG_UNLOAD, hgcmMessageAllocSvc);
1196
1197 if (RT_SUCCESS(rc))
1198 {
1199 rc = hgcmMsgSend(pMsg);
1200
1201 if (RT_SUCCESS(rc))
1202 hgcmThreadWait(m_pThread);
1203 }
1204
1205 if (m_pszSvcName && m_pUVM)
1206 STAMR3DeregisterF(m_pUVM, "/HGCM/%s/*", m_pszSvcName);
1207 m_pUVM = NULL;
1208 m_pHgcmPort = NULL;
1209
1210 RTStrFree(m_pszSvcLibrary);
1211 m_pszSvcLibrary = NULL;
1212
1213 RTStrFree(m_pszSvcName);
1214 m_pszSvcName = NULL;
1215
1216 if (m_paClientIds)
1217 {
1218 RTMemFree(m_paClientIds);
1219 m_paClientIds = NULL;
1220 }
1221}
1222
1223int HGCMService::saveClientState(uint32_t u32ClientId, PSSMHANDLE pSSM)
1224{
1225 LogFlowFunc(("%s\n", m_pszSvcName));
1226
1227 HGCMMsgCore *pCoreMsg;
1228 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_SAVESTATE, hgcmMessageAllocSvc);
1229
1230 if (RT_SUCCESS(rc))
1231 {
1232 HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pCoreMsg;
1233
1234 pMsg->u32ClientId = u32ClientId;
1235 pMsg->pSSM = pSSM;
1236
1237 rc = hgcmMsgSend(pMsg);
1238 }
1239
1240 LogFlowFunc(("rc = %Rrc\n", rc));
1241 return rc;
1242}
1243
1244int HGCMService::loadClientState(uint32_t u32ClientId, PSSMHANDLE pSSM, uint32_t uVersion)
1245{
1246 LogFlowFunc(("%s\n", m_pszSvcName));
1247
1248 HGCMMsgCore *pCoreMsg;
1249 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_LOADSTATE, hgcmMessageAllocSvc);
1250
1251 if (RT_SUCCESS(rc))
1252 {
1253 HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pCoreMsg;
1254
1255 pMsg->pSSM = pSSM;
1256 pMsg->uVersion = uVersion;
1257 pMsg->u32ClientId = u32ClientId;
1258
1259 rc = hgcmMsgSend(pMsg);
1260 }
1261
1262 LogFlowFunc(("rc = %Rrc\n", rc));
1263 return rc;
1264}
1265
1266
1267/** The method creates a service and references it.
1268 *
1269 * @param pszServiceLibrary The library to be loaded.
1270 * @param pszServiceName The name of the service.
1271 * @param pUVM The user mode VM handle (for statistics and such).
1272 * @param pHgcmPort The VMMDev HGCM port interface.
1273 *
1274 * @return VBox rc.
1275 * @thread main HGCM
1276 */
1277/* static */ int HGCMService::LoadService(const char *pszServiceLibrary, const char *pszServiceName,
1278 PUVM pUVM, PPDMIHGCMPORT pHgcmPort)
1279{
1280 LogFlowFunc(("lib %s, name = %s, pUVM = %p\n", pszServiceLibrary, pszServiceName, pUVM));
1281
1282 /* Look at already loaded services to avoid double loading. */
1283
1284 HGCMService *pSvc;
1285 int rc = HGCMService::ResolveService(&pSvc, pszServiceName);
1286
1287 if (RT_SUCCESS(rc))
1288 {
1289 /* The service is already loaded. */
1290 pSvc->ReleaseService();
1291 rc = VERR_HGCM_SERVICE_EXISTS;
1292 }
1293 else
1294 {
1295 /* Create the new service. */
1296 pSvc = new (std::nothrow) HGCMService();
1297
1298 if (!pSvc)
1299 {
1300 rc = VERR_NO_MEMORY;
1301 }
1302 else
1303 {
1304 /* Load the library and call the initialization entry point. */
1305 rc = pSvc->instanceCreate(pszServiceLibrary, pszServiceName, pUVM, pHgcmPort);
1306
1307 if (RT_SUCCESS(rc))
1308 {
1309 /* Insert the just created service to list for future references. */
1310 pSvc->m_pSvcNext = sm_pSvcListHead;
1311 pSvc->m_pSvcPrev = NULL;
1312
1313 if (sm_pSvcListHead)
1314 {
1315 sm_pSvcListHead->m_pSvcPrev = pSvc;
1316 }
1317 else
1318 {
1319 sm_pSvcListTail = pSvc;
1320 }
1321
1322 sm_pSvcListHead = pSvc;
1323
1324 sm_cServices++;
1325
1326 /* Reference the service (for first time) until it is unloaded on HGCM termination. */
1327 AssertRelease(pSvc->m_u32RefCnt == 0);
1328 pSvc->ReferenceService();
1329
1330 LogFlowFunc(("service %p\n", pSvc));
1331 }
1332 }
1333 }
1334
1335 LogFlowFunc(("rc = %Rrc\n", rc));
1336 return rc;
1337}
1338
1339/** The method unloads a service.
1340 *
1341 * @thread main HGCM
1342 */
1343void HGCMService::UnloadService(bool fUvmIsInvalid)
1344{
1345 LogFlowFunc(("name = %s\n", m_pszSvcName));
1346
1347 if (fUvmIsInvalid)
1348 {
1349 m_pUVM = NULL;
1350 m_pHgcmPort = NULL;
1351 }
1352
1353 /* Remove the service from the list. */
1354 if (m_pSvcNext)
1355 {
1356 m_pSvcNext->m_pSvcPrev = m_pSvcPrev;
1357 }
1358 else
1359 {
1360 sm_pSvcListTail = m_pSvcPrev;
1361 }
1362
1363 if (m_pSvcPrev)
1364 {
1365 m_pSvcPrev->m_pSvcNext = m_pSvcNext;
1366 }
1367 else
1368 {
1369 sm_pSvcListHead = m_pSvcNext;
1370 }
1371
1372 sm_cServices--;
1373
1374 /* The service must be unloaded only if all clients were disconnected. */
1375 LogFlowFunc(("m_u32RefCnt = %d\n", m_u32RefCnt));
1376 AssertRelease(m_u32RefCnt == 1);
1377
1378 /* Now the service can be released. */
1379 ReleaseService();
1380}
1381
1382/** The method unloads all services.
1383 *
1384 * @thread main HGCM
1385 */
1386/* static */ void HGCMService::UnloadAll(bool fUvmIsInvalid)
1387{
1388 while (sm_pSvcListHead)
1389 {
1390 sm_pSvcListHead->UnloadService(fUvmIsInvalid);
1391 }
1392}
1393
1394/** The method obtains a referenced pointer to the service with
1395 * specified name. The caller must call ReleaseService when
1396 * the pointer is no longer needed.
1397 *
1398 * @param ppSvc Where to store the pointer to the service.
1399 * @param pszServiceName The name of the service.
1400 * @return VBox rc.
1401 * @thread main HGCM
1402 */
1403/* static */ int HGCMService::ResolveService(HGCMService **ppSvc, const char *pszServiceName)
1404{
1405 LogFlowFunc(("ppSvc = %p name = %s\n",
1406 ppSvc, pszServiceName));
1407
1408 if (!ppSvc || !pszServiceName)
1409 {
1410 return VERR_INVALID_PARAMETER;
1411 }
1412
1413 HGCMService *pSvc = sm_pSvcListHead;
1414
1415 while (pSvc)
1416 {
1417 if (strcmp(pSvc->m_pszSvcName, pszServiceName) == 0)
1418 {
1419 break;
1420 }
1421
1422 pSvc = pSvc->m_pSvcNext;
1423 }
1424
1425 LogFlowFunc(("lookup in the list is %p\n", pSvc));
1426
1427 if (pSvc == NULL)
1428 {
1429 *ppSvc = NULL;
1430 return VERR_HGCM_SERVICE_NOT_FOUND;
1431 }
1432
1433 pSvc->ReferenceService();
1434
1435 *ppSvc = pSvc;
1436
1437 return VINF_SUCCESS;
1438}
1439
1440/** The method increases reference counter.
1441 *
1442 * @thread main HGCM
1443 */
1444void HGCMService::ReferenceService(void)
1445{
1446 ASMAtomicIncU32(&m_u32RefCnt);
1447 LogFlowFunc(("[%s] m_u32RefCnt = %d\n", m_pszSvcName, m_u32RefCnt));
1448}
1449
1450/** The method dereferences a service and deletes it when no more refs.
1451 *
1452 * @thread main HGCM
1453 */
1454void HGCMService::ReleaseService(void)
1455{
1456 LogFlowFunc(("m_u32RefCnt = %d\n", m_u32RefCnt));
1457 uint32_t u32RefCnt = ASMAtomicDecU32(&m_u32RefCnt);
1458 AssertRelease(u32RefCnt != ~0U);
1459
1460 LogFlowFunc(("u32RefCnt = %d, name %s\n", u32RefCnt, m_pszSvcName));
1461
1462 if (u32RefCnt == 0)
1463 {
1464 instanceDestroy();
1465 delete this;
1466 }
1467}
1468
1469/** The method is called when the VM is being reset or terminated
1470 * and disconnects all clients from all services.
1471 *
1472 * @thread main HGCM
1473 */
1474/* static */ void HGCMService::Reset(void)
1475{
1476 g_fResetting = true;
1477
1478 HGCMService *pSvc = sm_pSvcListHead;
1479
1480 while (pSvc)
1481 {
1482 while (pSvc->m_cClients && pSvc->m_paClientIds)
1483 {
1484 uint32_t const idClient = pSvc->m_paClientIds[0];
1485 HGCMClient * const pClient = (HGCMClient *)hgcmObjReference(idClient, HGCMOBJ_CLIENT);
1486 Assert(pClient);
1487 LogFlowFunc(("handle %d/%p\n", pSvc->m_paClientIds[0], pClient));
1488
1489 pSvc->DisconnectClient(pSvc->m_paClientIds[0], false, pClient);
1490
1491 hgcmObjDereference(pClient);
1492 }
1493
1494 pSvc = pSvc->m_pSvcNext;
1495 }
1496
1497 g_fResetting = false;
1498}
1499
1500/** The method saves the HGCM state.
1501 *
1502 * @param pSSM The saved state context.
1503 * @return VBox rc.
1504 * @thread main HGCM
1505 */
1506/* static */ int HGCMService::SaveState(PSSMHANDLE pSSM)
1507{
1508 /* Save the current handle count and restore afterwards to avoid client id conflicts. */
1509 int rc = SSMR3PutU32(pSSM, hgcmObjQueryHandleCount());
1510 AssertRCReturn(rc, rc);
1511
1512 LogFlowFunc(("%d services to be saved:\n", sm_cServices));
1513
1514 /* Save number of services. */
1515 rc = SSMR3PutU32(pSSM, sm_cServices);
1516 AssertRCReturn(rc, rc);
1517
1518 /* Save every service. */
1519 HGCMService *pSvc = sm_pSvcListHead;
1520
1521 while (pSvc)
1522 {
1523 LogFlowFunc(("Saving service [%s]\n", pSvc->m_pszSvcName));
1524
1525 /* Save the length of the service name. */
1526 rc = SSMR3PutU32(pSSM, (uint32_t) strlen(pSvc->m_pszSvcName) + 1);
1527 AssertRCReturn(rc, rc);
1528
1529 /* Save the name of the service. */
1530 rc = SSMR3PutStrZ(pSSM, pSvc->m_pszSvcName);
1531 AssertRCReturn(rc, rc);
1532
1533 /* Save the number of clients. */
1534 rc = SSMR3PutU32(pSSM, pSvc->m_cClients);
1535 AssertRCReturn(rc, rc);
1536
1537 /* Call the service for every client. Normally a service must not have
1538 * a global state to be saved: only per client info is relevant.
1539 * The global state of a service is configured during VM startup.
1540 */
1541 uint32_t i;
1542
1543 for (i = 0; i < pSvc->m_cClients; i++)
1544 {
1545 uint32_t u32ClientId = pSvc->m_paClientIds[i];
1546
1547 Log(("client id 0x%08X\n", u32ClientId));
1548
1549 /* Save the client id. (fRequestor is saved via SVC_MSG_SAVESTATE for convenience.) */
1550 rc = SSMR3PutU32(pSSM, u32ClientId);
1551 AssertRCReturn(rc, rc);
1552
1553 /* Call the service, so the operation is executed by the service thread. */
1554 rc = pSvc->saveClientState(u32ClientId, pSSM);
1555 AssertRCReturn(rc, rc);
1556 }
1557
1558 pSvc = pSvc->m_pSvcNext;
1559 }
1560
1561 return VINF_SUCCESS;
1562}
1563
1564/** The method loads saved HGCM state.
1565 *
1566 * @param pSSM The saved state handle.
1567 * @param uVersion The state version being loaded.
1568 * @return VBox rc.
1569 * @thread main HGCM
1570 */
1571/* static */ int HGCMService::LoadState(PSSMHANDLE pSSM, uint32_t uVersion)
1572{
1573 /* Restore handle count to avoid client id conflicts. */
1574 uint32_t u32;
1575
1576 int rc = SSMR3GetU32(pSSM, &u32);
1577 AssertRCReturn(rc, rc);
1578
1579 hgcmObjSetHandleCount(u32);
1580
1581 /* Get the number of services. */
1582 uint32_t cServices;
1583
1584 rc = SSMR3GetU32(pSSM, &cServices);
1585 AssertRCReturn(rc, rc);
1586
1587 LogFlowFunc(("%d services to be restored:\n", cServices));
1588
1589 while (cServices--)
1590 {
1591 /* Get the length of the service name. */
1592 rc = SSMR3GetU32(pSSM, &u32);
1593 AssertRCReturn(rc, rc);
1594 AssertReturn(u32 <= VBOX_HGCM_SVC_NAME_MAX_BYTES, VERR_SSM_UNEXPECTED_DATA);
1595
1596 /* Get the service name. */
1597 char szServiceName[VBOX_HGCM_SVC_NAME_MAX_BYTES];
1598 rc = SSMR3GetStrZ(pSSM, szServiceName, u32);
1599 AssertRCReturn(rc, rc);
1600
1601 LogRel(("HGCM: Restoring [%s]\n", szServiceName));
1602
1603 /* Resolve the service instance. */
1604 HGCMService *pSvc;
1605 rc = ResolveService(&pSvc, szServiceName);
1606 AssertLogRelMsgReturn(pSvc, ("rc=%Rrc, %s\n", rc, szServiceName), VERR_SSM_UNEXPECTED_DATA);
1607
1608 /* Get the number of clients. */
1609 uint32_t cClients;
1610 rc = SSMR3GetU32(pSSM, &cClients);
1611 if (RT_FAILURE(rc))
1612 {
1613 pSvc->ReleaseService();
1614 AssertFailed();
1615 return rc;
1616 }
1617
1618 while (cClients--)
1619 {
1620 /* Get the client ID and fRequest (convieniently save via SVC_MSG_SAVESTATE
1621 but restored here in time for calling CreateAndConnectClient). */
1622 uint32_t u32ClientId;
1623 rc = SSMR3GetU32(pSSM, &u32ClientId);
1624 uint32_t fRequestor = VMMDEV_REQUESTOR_LEGACY;
1625 if (RT_SUCCESS(rc) && uVersion > HGCM_SAVED_STATE_VERSION_V2)
1626 rc = SSMR3GetU32(pSSM, &fRequestor);
1627 AssertLogRelMsgRCReturnStmt(rc, ("rc=%Rrc, %s\n", rc, szServiceName), pSvc->ReleaseService(), rc);
1628
1629 /* Connect the client. */
1630 rc = pSvc->CreateAndConnectClient(NULL, u32ClientId, fRequestor, true /*fRestoring*/);
1631 AssertLogRelMsgRCReturnStmt(rc, ("rc=%Rrc, %s\n", rc, szServiceName), pSvc->ReleaseService(), rc);
1632
1633 /* Call the service, so the operation is executed by the service thread. */
1634 rc = pSvc->loadClientState(u32ClientId, pSSM, uVersion);
1635 AssertLogRelMsgRCReturnStmt(rc, ("rc=%Rrc, %s\n", rc, szServiceName), pSvc->ReleaseService(), rc);
1636 }
1637
1638 pSvc->ReleaseService();
1639 }
1640
1641 return VINF_SUCCESS;
1642}
1643
1644/* Create a new client instance and connect it to the service.
1645 *
1646 * @param pu32ClientIdOut If not NULL, then the method must generate a new handle for the client.
1647 * If NULL, use the given 'u32ClientIdIn' handle.
1648 * @param u32ClientIdIn The handle for the client, when 'pu32ClientIdOut' is NULL.
1649 * @param fRequestor The requestor flags, VMMDEV_REQUESTOR_LEGACY if not available.
1650 * @param fRestoring Set if we're restoring a saved state.
1651 * @return VBox status code.
1652 */
1653int HGCMService::CreateAndConnectClient(uint32_t *pu32ClientIdOut, uint32_t u32ClientIdIn, uint32_t fRequestor, bool fRestoring)
1654{
1655 LogFlowFunc(("pu32ClientIdOut = %p, u32ClientIdIn = %d, fRequestor = %#x, fRestoring = %d\n",
1656 pu32ClientIdOut, u32ClientIdIn, fRequestor, fRestoring));
1657
1658 /*
1659 * Categorize the client (compress VMMDEV_REQUESTOR_USR_MASK)
1660 * and check the respective client limit.
1661 */
1662 uint32_t idxClientCategory;
1663 if (fRequestor == VMMDEV_REQUESTOR_LEGACY)
1664 {
1665 idxClientCategory = m_fntable.idxLegacyClientCategory;
1666 AssertStmt(idxClientCategory < RT_ELEMENTS(m_acClients), idxClientCategory = HGCM_CLIENT_CATEGORY_KERNEL);
1667 }
1668 else
1669 switch (fRequestor & VMMDEV_REQUESTOR_USR_MASK)
1670 {
1671 case VMMDEV_REQUESTOR_USR_DRV:
1672 case VMMDEV_REQUESTOR_USR_DRV_OTHER:
1673 idxClientCategory = HGCM_CLIENT_CATEGORY_KERNEL;
1674 break;
1675 case VMMDEV_REQUESTOR_USR_ROOT:
1676 case VMMDEV_REQUESTOR_USR_SYSTEM:
1677 idxClientCategory = HGCM_CLIENT_CATEGORY_ROOT;
1678 break;
1679 default:
1680 idxClientCategory = HGCM_CLIENT_CATEGORY_USER;
1681 break;
1682 }
1683
1684 if ( m_acClients[idxClientCategory] < m_fntable.acMaxClients[idxClientCategory]
1685 || fRestoring)
1686 { }
1687 else
1688 {
1689 LogRel2(("Too many concurrenct clients for HGCM service '%s': %u, max %u; category %u\n",
1690 m_pszSvcName, m_cClients, m_fntable.acMaxClients[idxClientCategory], idxClientCategory));
1691 STAM_REL_COUNTER_INC(&m_StatTooManyClients);
1692 return VERR_HGCM_TOO_MANY_CLIENTS;
1693 }
1694
1695 /* Allocate a client information structure. */
1696 HGCMClient *pClient = new (std::nothrow) HGCMClient(fRequestor, idxClientCategory);
1697
1698 if (!pClient)
1699 {
1700 Log1WarningFunc(("Could not allocate HGCMClient!!!\n"));
1701 return VERR_NO_MEMORY;
1702 }
1703
1704 uint32_t handle;
1705
1706 if (pu32ClientIdOut != NULL)
1707 {
1708 handle = hgcmObjGenerateHandle(pClient);
1709 }
1710 else
1711 {
1712 handle = hgcmObjAssignHandle(pClient, u32ClientIdIn);
1713 }
1714
1715 LogFlowFunc(("client id = %d\n", handle));
1716
1717 AssertRelease(handle);
1718
1719 /* Initialize the HGCM part of the client. */
1720 int rc = pClient->Init(this);
1721
1722 if (RT_SUCCESS(rc))
1723 {
1724 /* Call the service. */
1725 HGCMMsgCore *pCoreMsg;
1726
1727 rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_CONNECT, hgcmMessageAllocSvc);
1728
1729 if (RT_SUCCESS(rc))
1730 {
1731 HGCMMsgSvcConnect *pMsg = (HGCMMsgSvcConnect *)pCoreMsg;
1732
1733 pMsg->u32ClientId = handle;
1734 pMsg->fRequestor = fRequestor;
1735 pMsg->fRestoring = fRestoring;
1736
1737 rc = hgcmMsgSend(pMsg);
1738
1739 if (RT_SUCCESS(rc))
1740 {
1741 /* Add the client Id to the array. */
1742 if (m_cClients == m_cClientsAllocated)
1743 {
1744 const uint32_t cDelta = 64;
1745
1746 /* Guards against integer overflow on 32bit arch and also limits size of m_paClientIds array to 4GB*/
1747 if (m_cClientsAllocated < UINT32_MAX / sizeof(m_paClientIds[0]) - cDelta)
1748 {
1749 uint32_t *paClientIdsNew;
1750
1751 paClientIdsNew = (uint32_t *)RTMemRealloc(m_paClientIds,
1752 (m_cClientsAllocated + cDelta) * sizeof(m_paClientIds[0]));
1753 Assert(paClientIdsNew);
1754
1755 if (paClientIdsNew)
1756 {
1757 m_paClientIds = paClientIdsNew;
1758 m_cClientsAllocated += cDelta;
1759 }
1760 else
1761 {
1762 rc = VERR_NO_MEMORY;
1763 }
1764 }
1765 else
1766 {
1767 rc = VERR_NO_MEMORY;
1768 }
1769 }
1770
1771 if (RT_SUCCESS(rc))
1772 {
1773 m_paClientIds[m_cClients] = handle;
1774 m_cClients++;
1775 m_acClients[idxClientCategory]++;
1776 LogFunc(("idClient=%u m_cClients=%u m_acClients[%u]=%u %s\n",
1777 handle, m_cClients, idxClientCategory, m_acClients[idxClientCategory], m_pszSvcName));
1778 }
1779 }
1780 }
1781 }
1782
1783 if (RT_SUCCESS(rc))
1784 {
1785 if (pu32ClientIdOut != NULL)
1786 {
1787 *pu32ClientIdOut = handle;
1788 }
1789
1790 ReferenceService();
1791 }
1792 else
1793 {
1794 hgcmObjDeleteHandle(handle);
1795 }
1796
1797 LogFlowFunc(("rc = %Rrc\n", rc));
1798 return rc;
1799}
1800
1801/**
1802 * Disconnect the client from the service and delete the client handle.
1803 *
1804 * @param u32ClientId The handle of the client.
1805 * @param fFromService Set if called by the service via
1806 * svcHlpDisconnectClient().
1807 * @param pClient The client disconnecting.
1808 * @return VBox status code.
1809 */
1810int HGCMService::DisconnectClient(uint32_t u32ClientId, bool fFromService, HGCMClient *pClient)
1811{
1812 AssertPtr(pClient);
1813 LogFlowFunc(("client id = %d, fFromService = %d, pClient = %p\n", u32ClientId, fFromService, pClient));
1814
1815 /*
1816 * Destroy the client handle prior to the disconnecting to avoid creating
1817 * a race with other messages from the same client. See @bugref{10038}
1818 * for further details.
1819 */
1820 Assert(pClient->idxCategory < HGCM_CLIENT_CATEGORY_MAX);
1821 Assert(m_acClients[pClient->idxCategory] > 0);
1822
1823 bool fReleaseService = false;
1824 int rc = VERR_NOT_FOUND;
1825 for (uint32_t i = 0; i < m_cClients; i++)
1826 {
1827 if (m_paClientIds[i] == u32ClientId)
1828 {
1829 if (m_acClients[pClient->idxCategory] > 0)
1830 m_acClients[pClient->idxCategory]--;
1831
1832 m_cClients--;
1833
1834 if (m_cClients > i)
1835 memmove(&m_paClientIds[i], &m_paClientIds[i + 1], sizeof(m_paClientIds[0]) * (m_cClients - i));
1836
1837 /* Delete the client handle. */
1838 hgcmObjDeleteHandle(u32ClientId);
1839 fReleaseService = true;
1840
1841 rc = VINF_SUCCESS;
1842 break;
1843 }
1844 }
1845
1846 /* Some paranoia wrt to not trusting the client ID array. */
1847 Assert(rc == VINF_SUCCESS || fFromService);
1848 if (rc == VERR_NOT_FOUND && !fFromService)
1849 {
1850 if (m_acClients[pClient->idxCategory] > 0)
1851 m_acClients[pClient->idxCategory]--;
1852
1853 hgcmObjDeleteHandle(u32ClientId);
1854 fReleaseService = true;
1855 }
1856
1857 LogFunc(("idClient=%u m_cClients=%u m_acClients[%u]=%u %s (cPendingCalls=%u) rc=%Rrc\n", u32ClientId, m_cClients,
1858 pClient->idxCategory, m_acClients[pClient->idxCategory], m_pszSvcName, pClient->cPendingCalls, rc));
1859
1860 /*
1861 * Call the service.
1862 */
1863 if (!fFromService)
1864 {
1865 /* Call the service. */
1866 HGCMMsgCore *pCoreMsg;
1867
1868 rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_DISCONNECT, hgcmMessageAllocSvc);
1869
1870 if (RT_SUCCESS(rc))
1871 {
1872 HGCMMsgSvcDisconnect *pMsg = (HGCMMsgSvcDisconnect *)pCoreMsg;
1873
1874 pMsg->u32ClientId = u32ClientId;
1875 pMsg->pClient = pClient;
1876
1877 rc = hgcmMsgSend(pMsg);
1878 }
1879 else
1880 {
1881 LogRel(("(%d, %d) [%s] hgcmMsgAlloc(%p, SVC_MSG_DISCONNECT) failed %Rrc\n",
1882 u32ClientId, fFromService, RT_VALID_PTR(m_pszSvcName)? m_pszSvcName: "", m_pThread, rc));
1883 }
1884 }
1885
1886
1887 /*
1888 * Release the pClient->pService reference.
1889 */
1890 if (fReleaseService)
1891 ReleaseService();
1892
1893 LogFlowFunc(("rc = %Rrc\n", rc));
1894 return rc;
1895}
1896
1897int HGCMService::RegisterExtension(HGCMSVCEXTHANDLE handle,
1898 PFNHGCMSVCEXT pfnExtension,
1899 void *pvExtension)
1900{
1901 LogFlowFunc(("%s\n", handle->pszServiceName));
1902
1903 /* Forward the message to the service thread. */
1904 HGCMMsgCore *pCoreMsg;
1905 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_REGEXT, hgcmMessageAllocSvc);
1906
1907 if (RT_SUCCESS(rc))
1908 {
1909 HGCMMsgSvcRegisterExtension *pMsg = (HGCMMsgSvcRegisterExtension *)pCoreMsg;
1910
1911 pMsg->handle = handle;
1912 pMsg->pfnExtension = pfnExtension;
1913 pMsg->pvExtension = pvExtension;
1914
1915 rc = hgcmMsgSend(pMsg);
1916 }
1917
1918 LogFlowFunc(("rc = %Rrc\n", rc));
1919 return rc;
1920}
1921
1922void HGCMService::UnregisterExtension(HGCMSVCEXTHANDLE handle)
1923{
1924 /* Forward the message to the service thread. */
1925 HGCMMsgCore *pCoreMsg;
1926 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_UNREGEXT, hgcmMessageAllocSvc);
1927
1928 if (RT_SUCCESS(rc))
1929 {
1930 HGCMMsgSvcUnregisterExtension *pMsg = (HGCMMsgSvcUnregisterExtension *)pCoreMsg;
1931
1932 pMsg->handle = handle;
1933
1934 rc = hgcmMsgSend(pMsg);
1935 }
1936
1937 LogFlowFunc(("rc = %Rrc\n", rc));
1938}
1939
1940/** @callback_method_impl{FNHGCMMSGCALLBACK} */
1941static DECLCALLBACK(int) hgcmMsgCallCompletionCallback(int32_t result, HGCMMsgCore *pMsgCore)
1942{
1943 /*
1944 * Do common message completion then decrement the call counter
1945 * for the client if necessary.
1946 */
1947 int rc = hgcmMsgCompletionCallback(result, pMsgCore);
1948
1949 HGCMMsgCall *pMsg = (HGCMMsgCall *)pMsgCore;
1950 if (pMsg->pcCounter)
1951 {
1952 uint32_t cCalls = ASMAtomicDecU32(pMsg->pcCounter);
1953 AssertStmt(cCalls < UINT32_MAX / 2, ASMAtomicWriteU32(pMsg->pcCounter, 0));
1954 pMsg->pcCounter = NULL;
1955 Log3Func(("pMsg=%p cPendingCalls=%u / %u (fun %u, %u parms)\n",
1956 pMsg, cCalls, pMsg->u32ClientId, pMsg->u32Function, pMsg->cParms));
1957 }
1958
1959 return rc;
1960}
1961
1962/** Perform a guest call to the service.
1963 *
1964 * @param pHGCMPort The port to be used for completion confirmation.
1965 * @param pCmd The VBox HGCM context.
1966 * @param u32ClientId The client handle to be disconnected and deleted.
1967 * @param pClient The client data.
1968 * @param u32Function The function number.
1969 * @param cParms Number of parameters.
1970 * @param paParms Pointer to array of parameters.
1971 * @param tsArrival The STAM_GET_TS() value when the request arrived.
1972 * @return VBox rc.
1973 * @retval VINF_HGCM_ASYNC_EXECUTE on success.
1974 */
1975int HGCMService::GuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, HGCMClient *pClient,
1976 uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
1977{
1978 LogFlow(("MAIN::HGCMService::GuestCall\n"));
1979
1980 int rc;
1981 HGCMMsgCall *pMsg = new(std::nothrow) HGCMMsgCall(m_pThread);
1982 if (pMsg)
1983 {
1984 pMsg->Reference(); /** @todo starts out with zero references. */
1985
1986 uint32_t cCalls = ASMAtomicIncU32(&pClient->cPendingCalls);
1987 Assert(pClient->idxCategory < RT_ELEMENTS(m_fntable.acMaxCallsPerClient));
1988 if (cCalls < m_fntable.acMaxCallsPerClient[pClient->idxCategory])
1989 {
1990 pMsg->pcCounter = &pClient->cPendingCalls;
1991 Log3(("MAIN::HGCMService::GuestCall: pMsg=%p cPendingCalls=%u / %u / %s (fun %u, %u parms)\n",
1992 pMsg, cCalls, u32ClientId, m_pszSvcName, u32Function, cParms));
1993
1994 pMsg->pCmd = pCmd;
1995 pMsg->pHGCMPort = pHGCMPort;
1996 pMsg->u32ClientId = u32ClientId;
1997 pMsg->u32Function = u32Function;
1998 pMsg->cParms = cParms;
1999 pMsg->paParms = paParms;
2000 pMsg->tsArrival = tsArrival;
2001
2002 rc = hgcmMsgPost(pMsg, hgcmMsgCallCompletionCallback);
2003
2004 if (RT_SUCCESS(rc))
2005 { /* Reference donated on success. */ }
2006 else
2007 {
2008 ASMAtomicDecU32(&pClient->cPendingCalls);
2009 pMsg->pcCounter = NULL;
2010 Log(("MAIN::HGCMService::GuestCall: hgcmMsgPost failed: %Rrc\n", rc));
2011 pMsg->Dereference();
2012 }
2013 }
2014 else
2015 {
2016 ASMAtomicDecU32(&pClient->cPendingCalls);
2017 LogRel2(("HGCM: Too many calls to '%s' from client %u: %u, max %u; category %u\n", m_pszSvcName, u32ClientId,
2018 cCalls, m_fntable.acMaxCallsPerClient[pClient->idxCategory], pClient->idxCategory));
2019 pMsg->Dereference();
2020 STAM_REL_COUNTER_INC(&m_StatTooManyCalls);
2021 rc = VERR_HGCM_TOO_MANY_CLIENT_CALLS;
2022 }
2023 }
2024 else
2025 {
2026 Log(("MAIN::HGCMService::GuestCall: Message allocation failed\n"));
2027 rc = VERR_NO_MEMORY;
2028 }
2029
2030 LogFlowFunc(("rc = %Rrc\n", rc));
2031 return rc;
2032}
2033
2034/** Guest cancelled a request (call, connection attempt, disconnect attempt).
2035 *
2036 * @param pHGCMPort The port to be used for completion confirmation
2037 * @param pCmd The VBox HGCM context.
2038 * @param idClient The client handle to be disconnected and deleted.
2039 * @return VBox rc.
2040 */
2041void HGCMService::GuestCancelled(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t idClient)
2042{
2043 LogFlow(("MAIN::HGCMService::GuestCancelled\n"));
2044
2045 if (m_fntable.pfnCancelled)
2046 {
2047 HGCMMsgCancelled *pMsg = new (std::nothrow) HGCMMsgCancelled(m_pThread);
2048 if (pMsg)
2049 {
2050 pMsg->Reference(); /** @todo starts out with zero references. */
2051
2052 pMsg->pCmd = pCmd;
2053 pMsg->pHGCMPort = pHGCMPort;
2054 pMsg->idClient = idClient;
2055
2056 hgcmMsgPost(pMsg, NULL);
2057 }
2058 else
2059 Log(("MAIN::HGCMService::GuestCancelled: Message allocation failed\n"));
2060 }
2061}
2062
2063/** Perform a host call the service.
2064 *
2065 * @param u32Function The function number.
2066 * @param cParms Number of parameters.
2067 * @param paParms Pointer to array of parameters.
2068 * @return VBox rc.
2069 */
2070int HGCMService::HostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms)
2071{
2072 LogFlowFunc(("%s u32Function = %d, cParms = %d, paParms = %p\n",
2073 m_pszSvcName, u32Function, cParms, paParms));
2074
2075 HGCMMsgCore *pCoreMsg;
2076 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_HOSTCALL, hgcmMessageAllocSvc);
2077
2078 if (RT_SUCCESS(rc))
2079 {
2080 HGCMMsgHostCallSvc *pMsg = (HGCMMsgHostCallSvc *)pCoreMsg;
2081
2082 pMsg->u32Function = u32Function;
2083 pMsg->cParms = cParms;
2084 pMsg->paParms = paParms;
2085
2086 rc = hgcmMsgSend(pMsg);
2087 }
2088
2089 LogFlowFunc(("rc = %Rrc\n", rc));
2090 return rc;
2091}
2092
2093/** Posts a broadcast notification event to all interested services.
2094 *
2095 * @param enmEvent The notification event.
2096 */
2097/*static*/ void HGCMService::BroadcastNotify(HGCMNOTIFYEVENT enmEvent)
2098{
2099 for (HGCMService *pService = sm_pSvcListHead; pService != NULL; pService = pService->m_pSvcNext)
2100 {
2101 pService->Notify(enmEvent);
2102 }
2103}
2104
2105/** Posts a broadcast notification event to the service.
2106 *
2107 * @param enmEvent The notification event.
2108 */
2109void HGCMService::Notify(HGCMNOTIFYEVENT enmEvent)
2110{
2111 LogFlowFunc(("%s enmEvent=%d pfnNotify=%p\n", m_pszSvcName, enmEvent, m_fntable.pfnNotify));
2112 if (m_fntable.pfnNotify)
2113 {
2114 HGCMMsgCore *pCoreMsg;
2115 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_NOTIFY, hgcmMessageAllocSvc);
2116 if (RT_SUCCESS(rc))
2117 {
2118 HGCMMsgNotify *pMsg = (HGCMMsgNotify *)pCoreMsg;
2119 pMsg->enmEvent = enmEvent;
2120
2121 rc = hgcmMsgPost(pMsg, NULL);
2122 AssertRC(rc);
2123 }
2124 }
2125}
2126
2127/*
2128 * Main HGCM thread that manages services.
2129 */
2130
2131/* Messages processed by the main HGCM thread. */
2132#define HGCM_MSG_CONNECT (10) /**< Connect a client to a service. */
2133#define HGCM_MSG_DISCONNECT (11) /**< Disconnect the specified client id. */
2134#define HGCM_MSG_LOAD (12) /**< Load the service. */
2135#define HGCM_MSG_HOSTCALL (13) /**< Call the service. */
2136#define HGCM_MSG_LOADSTATE (14) /**< Load saved state for the specified service. */
2137#define HGCM_MSG_SAVESTATE (15) /**< Save state for the specified service. */
2138#define HGCM_MSG_RESET (16) /**< Disconnect all clients from the specified service. */
2139#define HGCM_MSG_QUIT (17) /**< Unload all services and terminate the thread. */
2140#define HGCM_MSG_REGEXT (18) /**< Register a service extension. */
2141#define HGCM_MSG_UNREGEXT (19) /**< Unregister a service extension. */
2142#define HGCM_MSG_BRD_NOTIFY (20) /**< Broadcast notification event (VM state change). */
2143
2144class HGCMMsgMainConnect: public HGCMMsgHeader
2145{
2146 public:
2147 /* Service name. */
2148 const char *pszServiceName;
2149 /* Where to store the client handle. */
2150 uint32_t *pu32ClientId;
2151};
2152
2153class HGCMMsgMainDisconnect: public HGCMMsgHeader
2154{
2155 public:
2156 /* Handle of the client to be disconnected. */
2157 uint32_t u32ClientId;
2158};
2159
2160class HGCMMsgMainLoad: public HGCMMsgCore
2161{
2162 public:
2163 /* Name of the library to be loaded. */
2164 const char *pszServiceLibrary;
2165 /* Name to be assigned to the service. */
2166 const char *pszServiceName;
2167 /** The user mode VM handle (for statistics and such). */
2168 PUVM pUVM;
2169 /** The HGCM port on the VMMDev device (for session ID and such). */
2170 PPDMIHGCMPORT pHgcmPort;
2171};
2172
2173class HGCMMsgMainHostCall: public HGCMMsgCore
2174{
2175 public:
2176 /* Which service to call. */
2177 const char *pszServiceName;
2178 /* Function number. */
2179 uint32_t u32Function;
2180 /* Number of the function parameters. */
2181 uint32_t cParms;
2182 /* Pointer to array of the function parameters. */
2183 VBOXHGCMSVCPARM *paParms;
2184};
2185
2186class HGCMMsgMainLoadSaveState: public HGCMMsgCore
2187{
2188 public:
2189 /** Saved state handle. */
2190 PSSMHANDLE pSSM;
2191 /** The HGCM saved state version being loaded (ignore for save). */
2192 uint32_t uVersion;
2193};
2194
2195class HGCMMsgMainReset: public HGCMMsgCore
2196{
2197 public:
2198 /** Set if this is actually a shutdown and not a VM reset. */
2199 bool fForShutdown;
2200};
2201
2202class HGCMMsgMainQuit: public HGCMMsgCore
2203{
2204 public:
2205 /** Whether UVM has gone invalid already or not. */
2206 bool fUvmIsInvalid;
2207};
2208
2209class HGCMMsgMainRegisterExtension: public HGCMMsgCore
2210{
2211 public:
2212 /** Returned handle to be used in HGCMMsgMainUnregisterExtension. */
2213 HGCMSVCEXTHANDLE *pHandle;
2214 /** Name of the service. */
2215 const char *pszServiceName;
2216 /** The extension entry point. */
2217 PFNHGCMSVCEXT pfnExtension;
2218 /** The extension pointer. */
2219 void *pvExtension;
2220};
2221
2222class HGCMMsgMainUnregisterExtension: public HGCMMsgCore
2223{
2224 public:
2225 /* Handle of the registered extension. */
2226 HGCMSVCEXTHANDLE handle;
2227};
2228
2229class HGCMMsgMainBroadcastNotify: public HGCMMsgCore
2230{
2231 public:
2232 /** The notification event. */
2233 HGCMNOTIFYEVENT enmEvent;
2234};
2235
2236
2237static HGCMMsgCore *hgcmMainMessageAlloc (uint32_t u32MsgId)
2238{
2239 switch (u32MsgId)
2240 {
2241 case HGCM_MSG_CONNECT: return new HGCMMsgMainConnect();
2242 case HGCM_MSG_DISCONNECT: return new HGCMMsgMainDisconnect();
2243 case HGCM_MSG_LOAD: return new HGCMMsgMainLoad();
2244 case HGCM_MSG_HOSTCALL: return new HGCMMsgMainHostCall();
2245 case HGCM_MSG_LOADSTATE:
2246 case HGCM_MSG_SAVESTATE: return new HGCMMsgMainLoadSaveState();
2247 case HGCM_MSG_RESET: return new HGCMMsgMainReset();
2248 case HGCM_MSG_QUIT: return new HGCMMsgMainQuit();
2249 case HGCM_MSG_REGEXT: return new HGCMMsgMainRegisterExtension();
2250 case HGCM_MSG_UNREGEXT: return new HGCMMsgMainUnregisterExtension();
2251 case HGCM_MSG_BRD_NOTIFY: return new HGCMMsgMainBroadcastNotify();
2252
2253 default:
2254 AssertReleaseMsgFailed(("Msg id = %08X\n", u32MsgId));
2255 }
2256
2257 return NULL;
2258}
2259
2260
2261/* The main HGCM thread handler. */
2262static DECLCALLBACK(void) hgcmThread(HGCMThread *pThread, void *pvUser)
2263{
2264 LogFlowFunc(("pThread = %p, pvUser = %p\n", pThread, pvUser));
2265
2266 NOREF(pvUser);
2267
2268 bool fQuit = false;
2269
2270 while (!fQuit)
2271 {
2272 HGCMMsgCore *pMsgCore;
2273 int rc = hgcmMsgGet(pThread, &pMsgCore);
2274
2275 if (RT_FAILURE(rc))
2276 {
2277 /* The error means some serious unrecoverable problem in the hgcmMsg/hgcmThread layer. */
2278 AssertMsgFailed(("%Rrc\n", rc));
2279 break;
2280 }
2281
2282 uint32_t u32MsgId = pMsgCore->MsgId();
2283
2284 switch (u32MsgId)
2285 {
2286 case HGCM_MSG_CONNECT:
2287 {
2288 HGCMMsgMainConnect *pMsg = (HGCMMsgMainConnect *)pMsgCore;
2289
2290 LogFlowFunc(("HGCM_MSG_CONNECT pszServiceName %s, pu32ClientId %p\n",
2291 pMsg->pszServiceName, pMsg->pu32ClientId));
2292
2293 /* Resolve the service name to the pointer to service instance.
2294 */
2295 HGCMService *pService;
2296 rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName);
2297
2298 if (RT_SUCCESS(rc))
2299 {
2300 /* Call the service instance method. */
2301 rc = pService->CreateAndConnectClient(pMsg->pu32ClientId,
2302 0,
2303 pMsg->pHGCMPort->pfnGetRequestor(pMsg->pHGCMPort, pMsg->pCmd),
2304 pMsg->pHGCMPort->pfnIsCmdRestored(pMsg->pHGCMPort, pMsg->pCmd));
2305
2306 /* Release the service after resolve. */
2307 pService->ReleaseService();
2308 }
2309 } break;
2310
2311 case HGCM_MSG_DISCONNECT:
2312 {
2313 HGCMMsgMainDisconnect *pMsg = (HGCMMsgMainDisconnect *)pMsgCore;
2314
2315 LogFlowFunc(("HGCM_MSG_DISCONNECT u32ClientId = %d\n",
2316 pMsg->u32ClientId));
2317
2318 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT);
2319
2320 if (!pClient)
2321 {
2322 rc = VERR_HGCM_INVALID_CLIENT_ID;
2323 break;
2324 }
2325
2326 /* The service the client belongs to. */
2327 HGCMService *pService = pClient->pService;
2328
2329 /* Call the service instance to disconnect the client. */
2330 rc = pService->DisconnectClient(pMsg->u32ClientId, false, pClient);
2331
2332 hgcmObjDereference(pClient);
2333 } break;
2334
2335 case HGCM_MSG_LOAD:
2336 {
2337 HGCMMsgMainLoad *pMsg = (HGCMMsgMainLoad *)pMsgCore;
2338
2339 LogFlowFunc(("HGCM_MSG_LOAD pszServiceName = %s, pMsg->pszServiceLibrary = %s, pMsg->pUVM = %p\n",
2340 pMsg->pszServiceName, pMsg->pszServiceLibrary, pMsg->pUVM));
2341
2342 rc = HGCMService::LoadService(pMsg->pszServiceLibrary, pMsg->pszServiceName, pMsg->pUVM, pMsg->pHgcmPort);
2343 } break;
2344
2345 case HGCM_MSG_HOSTCALL:
2346 {
2347 HGCMMsgMainHostCall *pMsg = (HGCMMsgMainHostCall *)pMsgCore;
2348
2349 LogFlowFunc(("HGCM_MSG_HOSTCALL pszServiceName %s, u32Function %d, cParms %d, paParms %p\n",
2350 pMsg->pszServiceName, pMsg->u32Function, pMsg->cParms, pMsg->paParms));
2351
2352 /* Resolve the service name to the pointer to service instance. */
2353 HGCMService *pService;
2354 rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName);
2355
2356 if (RT_SUCCESS(rc))
2357 {
2358 rc = pService->HostCall(pMsg->u32Function, pMsg->cParms, pMsg->paParms);
2359
2360 pService->ReleaseService();
2361 }
2362 } break;
2363
2364 case HGCM_MSG_BRD_NOTIFY:
2365 {
2366 HGCMMsgMainBroadcastNotify *pMsg = (HGCMMsgMainBroadcastNotify *)pMsgCore;
2367
2368 LogFlowFunc(("HGCM_MSG_BRD_NOTIFY enmEvent=%d\n", pMsg->enmEvent));
2369
2370 HGCMService::BroadcastNotify(pMsg->enmEvent);
2371 } break;
2372
2373 case HGCM_MSG_RESET:
2374 {
2375 LogFlowFunc(("HGCM_MSG_RESET\n"));
2376
2377 HGCMService::Reset();
2378
2379 HGCMMsgMainReset *pMsg = (HGCMMsgMainReset *)pMsgCore;
2380 if (!pMsg->fForShutdown)
2381 HGCMService::BroadcastNotify(HGCMNOTIFYEVENT_RESET);
2382 } break;
2383
2384 case HGCM_MSG_LOADSTATE:
2385 {
2386 HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pMsgCore;
2387
2388 LogFlowFunc(("HGCM_MSG_LOADSTATE\n"));
2389
2390 rc = HGCMService::LoadState(pMsg->pSSM, pMsg->uVersion);
2391 } break;
2392
2393 case HGCM_MSG_SAVESTATE:
2394 {
2395 HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pMsgCore;
2396
2397 LogFlowFunc(("HGCM_MSG_SAVESTATE\n"));
2398
2399 rc = HGCMService::SaveState(pMsg->pSSM);
2400 } break;
2401
2402 case HGCM_MSG_QUIT:
2403 {
2404 HGCMMsgMainQuit *pMsg = (HGCMMsgMainQuit *)pMsgCore;
2405 LogFlowFunc(("HGCM_MSG_QUIT\n"));
2406
2407 HGCMService::UnloadAll(pMsg->fUvmIsInvalid);
2408
2409 fQuit = true;
2410 } break;
2411
2412 case HGCM_MSG_REGEXT:
2413 {
2414 HGCMMsgMainRegisterExtension *pMsg = (HGCMMsgMainRegisterExtension *)pMsgCore;
2415
2416 LogFlowFunc(("HGCM_MSG_REGEXT\n"));
2417
2418 /* Allocate the handle data. */
2419 HGCMSVCEXTHANDLE handle = (HGCMSVCEXTHANDLE)RTMemAllocZ(sizeof(struct _HGCMSVCEXTHANDLEDATA)
2420 + strlen(pMsg->pszServiceName)
2421 + sizeof(char));
2422
2423 if (handle == NULL)
2424 {
2425 rc = VERR_NO_MEMORY;
2426 }
2427 else
2428 {
2429 handle->pszServiceName = (char *)((uint8_t *)handle + sizeof(struct _HGCMSVCEXTHANDLEDATA));
2430 strcpy(handle->pszServiceName, pMsg->pszServiceName);
2431
2432 HGCMService *pService;
2433 rc = HGCMService::ResolveService(&pService, handle->pszServiceName);
2434
2435 if (RT_SUCCESS(rc))
2436 {
2437 pService->RegisterExtension(handle, pMsg->pfnExtension, pMsg->pvExtension);
2438
2439 pService->ReleaseService();
2440 }
2441
2442 if (RT_FAILURE(rc))
2443 {
2444 RTMemFree(handle);
2445 }
2446 else
2447 {
2448 *pMsg->pHandle = handle;
2449 }
2450 }
2451 } break;
2452
2453 case HGCM_MSG_UNREGEXT:
2454 {
2455 HGCMMsgMainUnregisterExtension *pMsg = (HGCMMsgMainUnregisterExtension *)pMsgCore;
2456
2457 LogFlowFunc(("HGCM_MSG_UNREGEXT\n"));
2458
2459 HGCMService *pService;
2460 rc = HGCMService::ResolveService(&pService, pMsg->handle->pszServiceName);
2461
2462 if (RT_SUCCESS(rc))
2463 {
2464 pService->UnregisterExtension(pMsg->handle);
2465
2466 pService->ReleaseService();
2467 }
2468
2469 RTMemFree(pMsg->handle);
2470 } break;
2471
2472 default:
2473 {
2474 AssertMsgFailed(("hgcmThread: Unsupported message number %08X!!!\n", u32MsgId));
2475 rc = VERR_NOT_SUPPORTED;
2476 } break;
2477 }
2478
2479 /* Complete the message processing. */
2480 hgcmMsgComplete(pMsgCore, rc);
2481
2482 LogFlowFunc(("message processed %Rrc\n", rc));
2483 }
2484}
2485
2486
2487/*
2488 * The HGCM API.
2489 */
2490
2491/** The main hgcm thread. */
2492static HGCMThread *g_pHgcmThread = 0;
2493
2494/*
2495 * Public HGCM functions.
2496 *
2497 * hgcmGuest* - called as a result of the guest HGCM requests.
2498 * hgcmHost* - called by the host.
2499 */
2500
2501/* Load a HGCM service from the specified library.
2502 * Assign the specified name to the service.
2503 *
2504 * @param pszServiceLibrary The library to be loaded.
2505 * @param pszServiceName The name to be assigned to the service.
2506 * @param pUVM The user mode VM handle (for statistics and such).
2507 * @param pHgcmPort The HGCM port on the VMMDev device (for session ID and such).
2508 * @return VBox rc.
2509 */
2510int HGCMHostLoad(const char *pszServiceLibrary,
2511 const char *pszServiceName,
2512 PUVM pUVM,
2513 PPDMIHGCMPORT pHgcmPort)
2514{
2515 LogFlowFunc(("lib = %s, name = %s\n", pszServiceLibrary, pszServiceName));
2516
2517 if (!pszServiceLibrary || !pszServiceName)
2518 {
2519 return VERR_INVALID_PARAMETER;
2520 }
2521
2522 /* Forward the request to the main hgcm thread. */
2523 HGCMMsgCore *pCoreMsg;
2524 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_LOAD, hgcmMainMessageAlloc);
2525
2526 if (RT_SUCCESS(rc))
2527 {
2528 /* Initialize the message. Since the message is synchronous, use the supplied pointers. */
2529 HGCMMsgMainLoad *pMsg = (HGCMMsgMainLoad *)pCoreMsg;
2530
2531 pMsg->pszServiceLibrary = pszServiceLibrary;
2532 pMsg->pszServiceName = pszServiceName;
2533 pMsg->pUVM = pUVM;
2534 pMsg->pHgcmPort = pHgcmPort;
2535
2536 rc = hgcmMsgSend(pMsg);
2537 }
2538
2539 LogFlowFunc(("rc = %Rrc\n", rc));
2540 return rc;
2541}
2542
2543/* Register a HGCM service extension.
2544 *
2545 * @param pHandle Returned handle for the registered extension.
2546 * @param pszServiceName The name of the service.
2547 * @param pfnExtension The extension entry point (callback).
2548 * @param pvExtension The extension pointer.
2549 * @return VBox rc.
2550 */
2551int HGCMHostRegisterServiceExtension(HGCMSVCEXTHANDLE *pHandle,
2552 const char *pszServiceName,
2553 PFNHGCMSVCEXT pfnExtension,
2554 void *pvExtension)
2555{
2556 LogFlowFunc(("pHandle = %p, name = %s, pfn = %p, rv = %p\n", pHandle, pszServiceName, pfnExtension, pvExtension));
2557
2558 if (!pHandle || !pszServiceName || !pfnExtension)
2559 {
2560 return VERR_INVALID_PARAMETER;
2561 }
2562
2563 /* Forward the request to the main hgcm thread. */
2564 HGCMMsgCore *pCoreMsg;
2565 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_REGEXT, hgcmMainMessageAlloc);
2566
2567 if (RT_SUCCESS(rc))
2568 {
2569 /* Initialize the message. Since the message is synchronous, use the supplied pointers. */
2570 HGCMMsgMainRegisterExtension *pMsg = (HGCMMsgMainRegisterExtension *)pCoreMsg;
2571
2572 pMsg->pHandle = pHandle;
2573 pMsg->pszServiceName = pszServiceName;
2574 pMsg->pfnExtension = pfnExtension;
2575 pMsg->pvExtension = pvExtension;
2576
2577 rc = hgcmMsgSend(pMsg);
2578 }
2579
2580 LogFlowFunc(("*pHandle = %p, rc = %Rrc\n", *pHandle, rc));
2581 return rc;
2582}
2583
2584void HGCMHostUnregisterServiceExtension(HGCMSVCEXTHANDLE handle)
2585{
2586 LogFlowFunc(("handle = %p\n", handle));
2587
2588 /* Forward the request to the main hgcm thread. */
2589 HGCMMsgCore *pCoreMsg;
2590 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_UNREGEXT, hgcmMainMessageAlloc);
2591
2592 if (RT_SUCCESS(rc))
2593 {
2594 /* Initialize the message. */
2595 HGCMMsgMainUnregisterExtension *pMsg = (HGCMMsgMainUnregisterExtension *)pCoreMsg;
2596
2597 pMsg->handle = handle;
2598
2599 rc = hgcmMsgSend(pMsg);
2600 }
2601
2602 LogFlowFunc(("rc = %Rrc\n", rc));
2603 return;
2604}
2605
2606/* Find a service and inform it about a client connection, create a client handle.
2607 *
2608 * @param pHGCMPort The port to be used for completion confirmation.
2609 * @param pCmd The VBox HGCM context.
2610 * @param pszServiceName The name of the service to be connected to.
2611 * @param pu32ClientId Where the store the created client handle.
2612 * @return VBox rc.
2613 */
2614int HGCMGuestConnect(PPDMIHGCMPORT pHGCMPort,
2615 PVBOXHGCMCMD pCmd,
2616 const char *pszServiceName,
2617 uint32_t *pu32ClientId)
2618{
2619 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, name = %s, pu32ClientId = %p\n",
2620 pHGCMPort, pCmd, pszServiceName, pu32ClientId));
2621
2622 if (pHGCMPort == NULL || pCmd == NULL || pszServiceName == NULL || pu32ClientId == NULL)
2623 {
2624 return VERR_INVALID_PARAMETER;
2625 }
2626
2627 /* Forward the request to the main hgcm thread. */
2628 HGCMMsgCore *pCoreMsg;
2629 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_CONNECT, hgcmMainMessageAlloc);
2630
2631 if (RT_SUCCESS(rc))
2632 {
2633 /* Initialize the message. Since 'pszServiceName' and 'pu32ClientId'
2634 * will not be deallocated by the caller until the message is completed,
2635 * use the supplied pointers.
2636 */
2637 HGCMMsgMainConnect *pMsg = (HGCMMsgMainConnect *)pCoreMsg;
2638
2639 pMsg->pHGCMPort = pHGCMPort;
2640 pMsg->pCmd = pCmd;
2641 pMsg->pszServiceName = pszServiceName;
2642 pMsg->pu32ClientId = pu32ClientId;
2643
2644 rc = hgcmMsgPost(pMsg, hgcmMsgCompletionCallback);
2645 }
2646
2647 LogFlowFunc(("rc = %Rrc\n", rc));
2648 return rc;
2649}
2650
2651/* Tell a service that the client is disconnecting, destroy the client handle.
2652 *
2653 * @param pHGCMPort The port to be used for completion confirmation.
2654 * @param pCmd The VBox HGCM context.
2655 * @param u32ClientId The client handle to be disconnected and deleted.
2656 * @return VBox rc.
2657 */
2658int HGCMGuestDisconnect(PPDMIHGCMPORT pHGCMPort,
2659 PVBOXHGCMCMD pCmd,
2660 uint32_t u32ClientId)
2661{
2662 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, u32ClientId = %d\n",
2663 pHGCMPort, pCmd, u32ClientId));
2664
2665 if (!pHGCMPort || !pCmd || !u32ClientId)
2666 {
2667 return VERR_INVALID_PARAMETER;
2668 }
2669
2670 /* Forward the request to the main hgcm thread. */
2671 HGCMMsgCore *pCoreMsg;
2672 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_DISCONNECT, hgcmMainMessageAlloc);
2673
2674 if (RT_SUCCESS(rc))
2675 {
2676 /* Initialize the message. */
2677 HGCMMsgMainDisconnect *pMsg = (HGCMMsgMainDisconnect *)pCoreMsg;
2678
2679 pMsg->pCmd = pCmd;
2680 pMsg->pHGCMPort = pHGCMPort;
2681 pMsg->u32ClientId = u32ClientId;
2682
2683 rc = hgcmMsgPost(pMsg, hgcmMsgCompletionCallback);
2684 }
2685
2686 LogFlowFunc(("rc = %Rrc\n", rc));
2687 return rc;
2688}
2689
2690/** Helper to send either HGCM_MSG_SAVESTATE or HGCM_MSG_LOADSTATE messages to the main HGCM thread.
2691 *
2692 * @param pSSM The SSM handle.
2693 * @param idMsg The message to be sent: HGCM_MSG_SAVESTATE or HGCM_MSG_LOADSTATE.
2694 * @param uVersion The state version being loaded.
2695 * @return VBox rc.
2696 */
2697static int hgcmHostLoadSaveState(PSSMHANDLE pSSM, uint32_t idMsg, uint32_t uVersion)
2698{
2699 LogFlowFunc(("pSSM = %p, idMsg = %d, uVersion = uVersion\n", pSSM, idMsg));
2700
2701 HGCMMsgCore *pCoreMsg;
2702 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, idMsg, hgcmMainMessageAlloc);
2703
2704 if (RT_SUCCESS(rc))
2705 {
2706 HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pCoreMsg;
2707 AssertRelease(pMsg);
2708
2709 pMsg->pSSM = pSSM;
2710 pMsg->uVersion = uVersion;
2711
2712 rc = hgcmMsgSend(pMsg);
2713 }
2714
2715 LogFlowFunc(("rc = %Rrc\n", rc));
2716 return rc;
2717}
2718
2719/** Save the state of services.
2720 *
2721 * @param pSSM The SSM handle.
2722 * @return VBox rc.
2723 */
2724int HGCMHostSaveState(PSSMHANDLE pSSM)
2725{
2726 return hgcmHostLoadSaveState(pSSM, HGCM_MSG_SAVESTATE, HGCM_SAVED_STATE_VERSION);
2727}
2728
2729/** Load the state of services.
2730 *
2731 * @param pSSM The SSM handle.
2732 * @param uVersion The state version being loaded.
2733 * @return VBox rc.
2734 */
2735int HGCMHostLoadState(PSSMHANDLE pSSM, uint32_t uVersion)
2736{
2737 return hgcmHostLoadSaveState(pSSM, HGCM_MSG_LOADSTATE, uVersion);
2738}
2739
2740/** The guest calls the service.
2741 *
2742 * @param pHGCMPort The port to be used for completion confirmation.
2743 * @param pCmd The VBox HGCM context.
2744 * @param u32ClientId The client handle.
2745 * @param u32Function The function number.
2746 * @param cParms Number of parameters.
2747 * @param paParms Pointer to array of parameters.
2748 * @param tsArrival The STAM_GET_TS() value when the request arrived.
2749 * @return VBox rc.
2750 */
2751int HGCMGuestCall(PPDMIHGCMPORT pHGCMPort,
2752 PVBOXHGCMCMD pCmd,
2753 uint32_t u32ClientId,
2754 uint32_t u32Function,
2755 uint32_t cParms,
2756 VBOXHGCMSVCPARM *paParms,
2757 uint64_t tsArrival)
2758{
2759 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, u32ClientId = %d, u32Function = %d, cParms = %d, paParms = %p\n",
2760 pHGCMPort, pCmd, u32ClientId, u32Function, cParms, paParms));
2761
2762 if (!pHGCMPort || !pCmd || u32ClientId == 0)
2763 {
2764 return VERR_INVALID_PARAMETER;
2765 }
2766
2767 int rc = VERR_HGCM_INVALID_CLIENT_ID;
2768
2769 /* Resolve the client handle to the client instance pointer. */
2770 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(u32ClientId, HGCMOBJ_CLIENT);
2771
2772 if (pClient)
2773 {
2774 AssertRelease(pClient->pService);
2775
2776 /* Forward the message to the service thread. */
2777 rc = pClient->pService->GuestCall(pHGCMPort, pCmd, u32ClientId, pClient, u32Function, cParms, paParms, tsArrival);
2778
2779 hgcmObjDereference(pClient);
2780 }
2781
2782 LogFlowFunc(("rc = %Rrc\n", rc));
2783 return rc;
2784}
2785
2786/** The guest cancelled a request (call, connect, disconnect)
2787 *
2788 * @param pHGCMPort The port to be used for completion confirmation.
2789 * @param pCmd The VBox HGCM context.
2790 * @param idClient The client handle.
2791 */
2792void HGCMGuestCancelled(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t idClient)
2793{
2794 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, idClient = %d\n", pHGCMPort, pCmd, idClient));
2795 AssertReturnVoid(pHGCMPort);
2796 AssertReturnVoid(pCmd);
2797 AssertReturnVoid(idClient != 0);
2798
2799 /* Resolve the client handle to the client instance pointer. */
2800 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(idClient, HGCMOBJ_CLIENT);
2801
2802 if (pClient)
2803 {
2804 AssertRelease(pClient->pService);
2805
2806 /* Forward the message to the service thread. */
2807 pClient->pService->GuestCancelled(pHGCMPort, pCmd, idClient);
2808
2809 hgcmObjDereference(pClient);
2810 }
2811
2812 LogFlowFunc(("returns\n"));
2813}
2814
2815/** The host calls the service.
2816 *
2817 * @param pszServiceName The service name to be called.
2818 * @param u32Function The function number.
2819 * @param cParms Number of parameters.
2820 * @param paParms Pointer to array of parameters.
2821 * @return VBox rc.
2822 */
2823int HGCMHostCall(const char *pszServiceName,
2824 uint32_t u32Function,
2825 uint32_t cParms,
2826 VBOXHGCMSVCPARM *paParms)
2827{
2828 LogFlowFunc(("name = %s, u32Function = %d, cParms = %d, paParms = %p\n",
2829 pszServiceName, u32Function, cParms, paParms));
2830
2831 if (!pszServiceName)
2832 {
2833 return VERR_INVALID_PARAMETER;
2834 }
2835
2836 /* Host calls go to main HGCM thread that resolves the service name to the
2837 * service instance pointer and then, using the service pointer, forwards
2838 * the message to the service thread.
2839 * So it is slow but host calls are intended mostly for configuration and
2840 * other non-time-critical functions.
2841 */
2842 HGCMMsgCore *pCoreMsg;
2843 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_HOSTCALL, hgcmMainMessageAlloc);
2844
2845 if (RT_SUCCESS(rc))
2846 {
2847 HGCMMsgMainHostCall *pMsg = (HGCMMsgMainHostCall *)pCoreMsg;
2848
2849 pMsg->pszServiceName = (char *)pszServiceName;
2850 pMsg->u32Function = u32Function;
2851 pMsg->cParms = cParms;
2852 pMsg->paParms = paParms;
2853
2854 rc = hgcmMsgSend(pMsg);
2855 }
2856
2857 LogFlowFunc(("rc = %Rrc\n", rc));
2858 return rc;
2859}
2860
2861/** Posts a notification event to all services.
2862 *
2863 * @param enmEvent The notification event.
2864 * @return VBox rc.
2865 */
2866int HGCMBroadcastEvent(HGCMNOTIFYEVENT enmEvent)
2867{
2868 LogFlowFunc(("enmEvent=%d\n", enmEvent));
2869
2870 HGCMMsgCore *pCoreMsg;
2871 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_BRD_NOTIFY, hgcmMainMessageAlloc);
2872
2873 if (RT_SUCCESS(rc))
2874 {
2875 HGCMMsgMainBroadcastNotify *pMsg = (HGCMMsgMainBroadcastNotify *)pCoreMsg;
2876
2877 pMsg->enmEvent = enmEvent;
2878
2879 rc = hgcmMsgPost(pMsg, NULL);
2880 }
2881
2882 LogFlowFunc(("rc = %Rrc\n", rc));
2883 return rc;
2884}
2885
2886
2887int HGCMHostReset(bool fForShutdown)
2888{
2889 LogFlowFunc(("\n"));
2890
2891 /* Disconnect all clients.
2892 */
2893
2894 HGCMMsgCore *pMsgCore;
2895 int rc = hgcmMsgAlloc(g_pHgcmThread, &pMsgCore, HGCM_MSG_RESET, hgcmMainMessageAlloc);
2896
2897 if (RT_SUCCESS(rc))
2898 {
2899 HGCMMsgMainReset *pMsg = (HGCMMsgMainReset *)pMsgCore;
2900
2901 pMsg->fForShutdown = fForShutdown;
2902
2903 rc = hgcmMsgSend(pMsg);
2904 }
2905
2906 LogFlowFunc(("rc = %Rrc\n", rc));
2907 return rc;
2908}
2909
2910int HGCMHostInit(void)
2911{
2912 LogFlowFunc(("\n"));
2913
2914 int rc = hgcmThreadInit();
2915
2916 if (RT_SUCCESS(rc))
2917 {
2918 /*
2919 * Start main HGCM thread.
2920 */
2921
2922 rc = hgcmThreadCreate(&g_pHgcmThread, "MainHGCMthread", hgcmThread, NULL /*pvUser*/, NULL /*pszStatsSubDir*/, NULL /*pUVM*/);
2923
2924 if (RT_FAILURE(rc))
2925 LogRel(("Failed to start HGCM thread. HGCM services will be unavailable!!! rc = %Rrc\n", rc));
2926 }
2927
2928 LogFlowFunc(("rc = %Rrc\n", rc));
2929 return rc;
2930}
2931
2932int HGCMHostShutdown(bool fUvmIsInvalid /*= false*/)
2933{
2934 LogFlowFunc(("\n"));
2935
2936 /*
2937 * Do HGCMReset and then unload all services.
2938 */
2939
2940 int rc = HGCMHostReset(true /*fForShutdown*/);
2941
2942 if (RT_SUCCESS(rc))
2943 {
2944 /* Send the quit message to the main hgcmThread. */
2945 HGCMMsgCore *pMsgCore;
2946 rc = hgcmMsgAlloc(g_pHgcmThread, &pMsgCore, HGCM_MSG_QUIT, hgcmMainMessageAlloc);
2947
2948 if (RT_SUCCESS(rc))
2949 {
2950 HGCMMsgMainQuit *pMsg = (HGCMMsgMainQuit *)pMsgCore;
2951 pMsg->fUvmIsInvalid = fUvmIsInvalid;
2952
2953 rc = hgcmMsgSend(pMsg);
2954
2955 if (RT_SUCCESS(rc))
2956 {
2957 /* Wait for the thread termination. */
2958 hgcmThreadWait(g_pHgcmThread);
2959 g_pHgcmThread = NULL;
2960
2961 hgcmThreadUninit();
2962 }
2963 }
2964 }
2965
2966 LogFlowFunc(("rc = %Rrc\n", rc));
2967 return rc;
2968}
2969
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette