VirtualBox

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

Last change on this file since 91097 was 91097, checked in by vboxsync, 4 years ago

HGCM: Put back pfnDisconnectClient. oem2ticketref:46

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.6 KB
Line 
1/* $Id: HGCM.cpp 91097 2021-09-02 14:42:26Z 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 u32ClientId);
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 u32ClientId)
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 return pService->DisconnectClient(u32ClientId, true, NULL);
940}
941
942/**
943 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnIsCallRestored}
944 */
945/* static */ DECLCALLBACK(bool) HGCMService::svcHlpIsCallRestored(VBOXHGCMCALLHANDLE callHandle)
946{
947 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)callHandle;
948 AssertPtrReturn(pMsgHdr, false);
949
950 PVBOXHGCMCMD pCmd = pMsgHdr->pCmd;
951 AssertPtrReturn(pCmd, false);
952
953 PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;
954 AssertPtrReturn(pHgcmPort, false);
955
956 return pHgcmPort->pfnIsCmdRestored(pHgcmPort, pCmd);
957}
958
959/**
960 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnIsCallCancelled}
961 */
962/* static */ DECLCALLBACK(bool) HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE callHandle)
963{
964 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)callHandle;
965 AssertPtrReturn(pMsgHdr, false);
966
967 PVBOXHGCMCMD pCmd = pMsgHdr->pCmd;
968 AssertPtrReturn(pCmd, false);
969
970 PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;
971 AssertPtrReturn(pHgcmPort, false);
972
973 return pHgcmPort->pfnIsCmdCancelled(pHgcmPort, pCmd);
974}
975
976/**
977 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnStamRegisterV}
978 */
979/* static */ DECLCALLBACK(int)
980HGCMService::svcHlpStamRegisterV(void *pvInstance, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
981 STAMUNIT enmUnit, const char *pszDesc, const char *pszName, va_list va)
982{
983 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
984 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
985
986 return STAMR3RegisterVU(pService->m_pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, va);
987}
988
989/**
990 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnStamDeregisterV}
991 */
992/* static */ DECLCALLBACK(int) HGCMService::svcHlpStamDeregisterV(void *pvInstance, const char *pszPatFmt, va_list va)
993{
994 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
995 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
996
997 if (pService->m_pUVM)
998 return STAMR3DeregisterV(pService->m_pUVM, pszPatFmt, va);
999 return VINF_SUCCESS;
1000}
1001
1002/**
1003 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnInfoRegister}
1004 */
1005/* static */ DECLCALLBACK(int) HGCMService::svcHlpInfoRegister(void *pvInstance, const char *pszName, const char *pszDesc,
1006 PFNDBGFHANDLEREXT pfnHandler, void *pvUser)
1007{
1008 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1009 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
1010
1011 return DBGFR3InfoRegisterExternal(pService->m_pUVM, pszName, pszDesc, pfnHandler, pvUser);
1012}
1013
1014/**
1015 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnInfoDeregister}
1016 */
1017/* static */ DECLCALLBACK(int) HGCMService::svcHlpInfoDeregister(void *pvInstance, const char *pszName)
1018{
1019 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1020 AssertPtrReturn(pService, VERR_INVALID_PARAMETER);
1021 if (pService->m_pUVM)
1022 return DBGFR3InfoDeregisterExternal(pService->m_pUVM, pszName);
1023 return VINF_SUCCESS;
1024}
1025
1026/**
1027 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnGetRequestor}
1028 */
1029/* static */ DECLCALLBACK(uint32_t) HGCMService::svcHlpGetRequestor(VBOXHGCMCALLHANDLE hCall)
1030{
1031 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)(hCall);
1032 AssertPtrReturn(pMsgHdr, VMMDEV_REQUESTOR_LOWEST);
1033
1034 PVBOXHGCMCMD pCmd = pMsgHdr->pCmd;
1035 AssertPtrReturn(pCmd, VMMDEV_REQUESTOR_LOWEST);
1036
1037 PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;
1038 AssertPtrReturn(pHgcmPort, VMMDEV_REQUESTOR_LOWEST);
1039
1040 return pHgcmPort->pfnGetRequestor(pHgcmPort, pCmd);
1041}
1042
1043/**
1044 * @interface_method_impl{VBOXHGCMSVCHELPERS,pfnGetVMMDevSessionId}
1045 */
1046/* static */ DECLCALLBACK(uint64_t) HGCMService::svcHlpGetVMMDevSessionId(void *pvInstance)
1047{
1048 HGCMService *pService = static_cast <HGCMService *>(pvInstance);
1049 AssertPtrReturn(pService, UINT64_MAX);
1050
1051 PPDMIHGCMPORT pHgcmPort = pService->m_pHgcmPort;
1052 AssertPtrReturn(pHgcmPort, UINT64_MAX);
1053
1054 return pHgcmPort->pfnGetVMMDevSessionId(pHgcmPort);
1055}
1056
1057
1058static DECLCALLBACK(int) hgcmMsgCompletionCallback(int32_t result, HGCMMsgCore *pMsgCore)
1059{
1060 /* Call the VMMDev port interface to issue IRQ notification. */
1061 HGCMMsgHeader *pMsgHdr = (HGCMMsgHeader *)pMsgCore;
1062
1063 LogFlow(("MAIN::hgcmMsgCompletionCallback: message %p\n", pMsgCore));
1064
1065 if (pMsgHdr->pHGCMPort)
1066 {
1067 if (!g_fResetting)
1068 return pMsgHdr->pHGCMPort->pfnCompleted(pMsgHdr->pHGCMPort,
1069 g_fSaveState ? VINF_HGCM_SAVE_STATE : result, pMsgHdr->pCmd);
1070 return VERR_ALREADY_RESET; /* best I could find. */
1071 }
1072 return VERR_NOT_AVAILABLE;
1073}
1074
1075/*
1076 * The main HGCM methods of the service.
1077 */
1078
1079int HGCMService::instanceCreate(const char *pszServiceLibrary, const char *pszServiceName, PUVM pUVM, PPDMIHGCMPORT pHgcmPort)
1080{
1081 LogFlowFunc(("name %s, lib %s\n", pszServiceName, pszServiceLibrary));
1082 /* The maximum length of the thread name, allowed by the RT is 15. */
1083 char szThreadName[16];
1084 if (!strncmp(pszServiceName, RT_STR_TUPLE("VBoxShared")))
1085 RTStrPrintf(szThreadName, sizeof(szThreadName), "Sh%s", pszServiceName + 10);
1086 else if (!strncmp(pszServiceName, RT_STR_TUPLE("VBox")))
1087 RTStrCopy(szThreadName, sizeof(szThreadName), pszServiceName + 4);
1088 else
1089 RTStrCopy(szThreadName, sizeof(szThreadName), pszServiceName);
1090
1091 int rc = hgcmThreadCreate(&m_pThread, szThreadName, hgcmServiceThread, this, pszServiceName, pUVM);
1092
1093 if (RT_SUCCESS(rc))
1094 {
1095 m_pszSvcName = RTStrDup(pszServiceName);
1096 m_pszSvcLibrary = RTStrDup(pszServiceLibrary);
1097
1098 if (!m_pszSvcName || !m_pszSvcLibrary)
1099 {
1100 RTStrFree(m_pszSvcLibrary);
1101 m_pszSvcLibrary = NULL;
1102
1103 RTStrFree(m_pszSvcName);
1104 m_pszSvcName = NULL;
1105
1106 rc = VERR_NO_MEMORY;
1107 }
1108 else
1109 {
1110 m_pUVM = pUVM;
1111 m_pHgcmPort = pHgcmPort;
1112
1113 /* Register statistics: */
1114 STAMR3RegisterFU(pUVM, &m_StatHandleMsg, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1115 "Message handling", "/HGCM/%s/Msg", pszServiceName);
1116 STAMR3RegisterFU(pUVM, &m_StatTooManyCalls, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1117 "Too many calls (per client)", "/HGCM/%s/TooManyCalls", pszServiceName);
1118 STAMR3RegisterFU(pUVM, &m_StatTooManyClients, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1119 "Too many clients", "/HGCM/%s/TooManyClients", pszServiceName);
1120 STAMR3RegisterFU(pUVM, &m_cClients, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
1121 "Number of clients", "/HGCM/%s/Clients", pszServiceName);
1122 STAMR3RegisterFU(pUVM, &m_acClients[HGCM_CLIENT_CATEGORY_KERNEL], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1123 STAMUNIT_OCCURENCES, "Number of kernel clients", "/HGCM/%s/Clients/Kernel", pszServiceName);
1124 STAMR3RegisterFU(pUVM, &m_acClients[HGCM_CLIENT_CATEGORY_ROOT], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1125 STAMUNIT_OCCURENCES, "Number of root/admin clients", "/HGCM/%s/Clients/Root", pszServiceName);
1126 STAMR3RegisterFU(pUVM, &m_acClients[HGCM_CLIENT_CATEGORY_USER], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1127 STAMUNIT_OCCURENCES, "Number of regular user clients", "/HGCM/%s/Clients/User", pszServiceName);
1128 STAMR3RegisterFU(pUVM, &m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_KERNEL], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1129 STAMUNIT_OCCURENCES, "Max number of kernel clients", "/HGCM/%s/Clients/KernelMax", pszServiceName);
1130 STAMR3RegisterFU(pUVM, &m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_ROOT], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1131 STAMUNIT_OCCURENCES, "Max number of root clients", "/HGCM/%s/Clients/RootMax", pszServiceName);
1132 STAMR3RegisterFU(pUVM, &m_fntable.acMaxClients[HGCM_CLIENT_CATEGORY_USER], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1133 STAMUNIT_OCCURENCES, "Max number of user clients", "/HGCM/%s/Clients/UserMax", pszServiceName);
1134 STAMR3RegisterFU(pUVM, &m_fntable.idxLegacyClientCategory, STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1135 STAMUNIT_OCCURENCES, "Legacy client mapping", "/HGCM/%s/Clients/LegacyClientMapping", pszServiceName);
1136 STAMR3RegisterFU(pUVM, &m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_KERNEL], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1137 STAMUNIT_OCCURENCES, "Max number of call per kernel client", "/HGCM/%s/MaxCallsKernelClient", pszServiceName);
1138 STAMR3RegisterFU(pUVM, &m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_ROOT], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1139 STAMUNIT_OCCURENCES, "Max number of call per root client", "/HGCM/%s/MaxCallsRootClient", pszServiceName);
1140 STAMR3RegisterFU(pUVM, &m_fntable.acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_USER], STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
1141 STAMUNIT_OCCURENCES, "Max number of call per user client", "/HGCM/%s/MaxCallsUserClient", pszServiceName);
1142
1143 /* Initialize service helpers table. */
1144 m_svcHelpers.pfnCallComplete = svcHlpCallComplete;
1145 m_svcHelpers.pvInstance = this;
1146 m_svcHelpers.pfnDisconnectClient = svcHlpDisconnectClient;
1147 m_svcHelpers.pfnIsCallRestored = svcHlpIsCallRestored;
1148 m_svcHelpers.pfnIsCallCancelled = svcHlpIsCallCancelled;
1149 m_svcHelpers.pfnStamRegisterV = svcHlpStamRegisterV;
1150 m_svcHelpers.pfnStamDeregisterV = svcHlpStamDeregisterV;
1151 m_svcHelpers.pfnInfoRegister = svcHlpInfoRegister;
1152 m_svcHelpers.pfnInfoDeregister = svcHlpInfoDeregister;
1153 m_svcHelpers.pfnGetRequestor = svcHlpGetRequestor;
1154 m_svcHelpers.pfnGetVMMDevSessionId = svcHlpGetVMMDevSessionId;
1155
1156 /* Execute the load request on the service thread. */
1157 HGCMMsgCore *pCoreMsg;
1158 rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_LOAD, hgcmMessageAllocSvc);
1159
1160 if (RT_SUCCESS(rc))
1161 {
1162 HGCMMsgSvcLoad *pMsg = (HGCMMsgSvcLoad *)pCoreMsg;
1163
1164 pMsg->pUVM = pUVM;
1165
1166 rc = hgcmMsgSend(pMsg);
1167 }
1168 }
1169 }
1170
1171 if (RT_FAILURE(rc))
1172 {
1173 instanceDestroy();
1174 }
1175
1176 LogFlowFunc(("rc = %Rrc\n", rc));
1177 return rc;
1178}
1179
1180void HGCMService::instanceDestroy(void)
1181{
1182 LogFlowFunc(("%s\n", m_pszSvcName));
1183
1184 HGCMMsgCore *pMsg;
1185 int rc = hgcmMsgAlloc(m_pThread, &pMsg, SVC_MSG_UNLOAD, hgcmMessageAllocSvc);
1186
1187 if (RT_SUCCESS(rc))
1188 {
1189 rc = hgcmMsgSend(pMsg);
1190
1191 if (RT_SUCCESS(rc))
1192 hgcmThreadWait(m_pThread);
1193 }
1194
1195 if (m_pszSvcName && m_pUVM)
1196 STAMR3DeregisterF(m_pUVM, "/HGCM/%s/*", m_pszSvcName);
1197 m_pUVM = NULL;
1198 m_pHgcmPort = NULL;
1199
1200 RTStrFree(m_pszSvcLibrary);
1201 m_pszSvcLibrary = NULL;
1202
1203 RTStrFree(m_pszSvcName);
1204 m_pszSvcName = NULL;
1205
1206 if (m_paClientIds)
1207 {
1208 RTMemFree(m_paClientIds);
1209 m_paClientIds = NULL;
1210 }
1211}
1212
1213int HGCMService::saveClientState(uint32_t u32ClientId, PSSMHANDLE pSSM)
1214{
1215 LogFlowFunc(("%s\n", m_pszSvcName));
1216
1217 HGCMMsgCore *pCoreMsg;
1218 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_SAVESTATE, hgcmMessageAllocSvc);
1219
1220 if (RT_SUCCESS(rc))
1221 {
1222 HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pCoreMsg;
1223
1224 pMsg->u32ClientId = u32ClientId;
1225 pMsg->pSSM = pSSM;
1226
1227 rc = hgcmMsgSend(pMsg);
1228 }
1229
1230 LogFlowFunc(("rc = %Rrc\n", rc));
1231 return rc;
1232}
1233
1234int HGCMService::loadClientState(uint32_t u32ClientId, PSSMHANDLE pSSM, uint32_t uVersion)
1235{
1236 LogFlowFunc(("%s\n", m_pszSvcName));
1237
1238 HGCMMsgCore *pCoreMsg;
1239 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_LOADSTATE, hgcmMessageAllocSvc);
1240
1241 if (RT_SUCCESS(rc))
1242 {
1243 HGCMMsgLoadSaveStateClient *pMsg = (HGCMMsgLoadSaveStateClient *)pCoreMsg;
1244
1245 pMsg->pSSM = pSSM;
1246 pMsg->uVersion = uVersion;
1247 pMsg->u32ClientId = u32ClientId;
1248
1249 rc = hgcmMsgSend(pMsg);
1250 }
1251
1252 LogFlowFunc(("rc = %Rrc\n", rc));
1253 return rc;
1254}
1255
1256
1257/** The method creates a service and references it.
1258 *
1259 * @param pszServiceLibrary The library to be loaded.
1260 * @param pszServiceName The name of the service.
1261 * @param pUVM The user mode VM handle (for statistics and such).
1262 * @param pHgcmPort The VMMDev HGCM port interface.
1263 *
1264 * @return VBox rc.
1265 * @thread main HGCM
1266 */
1267/* static */ int HGCMService::LoadService(const char *pszServiceLibrary, const char *pszServiceName,
1268 PUVM pUVM, PPDMIHGCMPORT pHgcmPort)
1269{
1270 LogFlowFunc(("lib %s, name = %s, pUVM = %p\n", pszServiceLibrary, pszServiceName, pUVM));
1271
1272 /* Look at already loaded services to avoid double loading. */
1273
1274 HGCMService *pSvc;
1275 int rc = HGCMService::ResolveService(&pSvc, pszServiceName);
1276
1277 if (RT_SUCCESS(rc))
1278 {
1279 /* The service is already loaded. */
1280 pSvc->ReleaseService();
1281 rc = VERR_HGCM_SERVICE_EXISTS;
1282 }
1283 else
1284 {
1285 /* Create the new service. */
1286 pSvc = new (std::nothrow) HGCMService();
1287
1288 if (!pSvc)
1289 {
1290 rc = VERR_NO_MEMORY;
1291 }
1292 else
1293 {
1294 /* Load the library and call the initialization entry point. */
1295 rc = pSvc->instanceCreate(pszServiceLibrary, pszServiceName, pUVM, pHgcmPort);
1296
1297 if (RT_SUCCESS(rc))
1298 {
1299 /* Insert the just created service to list for future references. */
1300 pSvc->m_pSvcNext = sm_pSvcListHead;
1301 pSvc->m_pSvcPrev = NULL;
1302
1303 if (sm_pSvcListHead)
1304 {
1305 sm_pSvcListHead->m_pSvcPrev = pSvc;
1306 }
1307 else
1308 {
1309 sm_pSvcListTail = pSvc;
1310 }
1311
1312 sm_pSvcListHead = pSvc;
1313
1314 sm_cServices++;
1315
1316 /* Reference the service (for first time) until it is unloaded on HGCM termination. */
1317 AssertRelease(pSvc->m_u32RefCnt == 0);
1318 pSvc->ReferenceService();
1319
1320 LogFlowFunc(("service %p\n", pSvc));
1321 }
1322 }
1323 }
1324
1325 LogFlowFunc(("rc = %Rrc\n", rc));
1326 return rc;
1327}
1328
1329/** The method unloads a service.
1330 *
1331 * @thread main HGCM
1332 */
1333void HGCMService::UnloadService(bool fUvmIsInvalid)
1334{
1335 LogFlowFunc(("name = %s\n", m_pszSvcName));
1336
1337 if (fUvmIsInvalid)
1338 {
1339 m_pUVM = NULL;
1340 m_pHgcmPort = NULL;
1341 }
1342
1343 /* Remove the service from the list. */
1344 if (m_pSvcNext)
1345 {
1346 m_pSvcNext->m_pSvcPrev = m_pSvcPrev;
1347 }
1348 else
1349 {
1350 sm_pSvcListTail = m_pSvcPrev;
1351 }
1352
1353 if (m_pSvcPrev)
1354 {
1355 m_pSvcPrev->m_pSvcNext = m_pSvcNext;
1356 }
1357 else
1358 {
1359 sm_pSvcListHead = m_pSvcNext;
1360 }
1361
1362 sm_cServices--;
1363
1364 /* The service must be unloaded only if all clients were disconnected. */
1365 LogFlowFunc(("m_u32RefCnt = %d\n", m_u32RefCnt));
1366 AssertRelease(m_u32RefCnt == 1);
1367
1368 /* Now the service can be released. */
1369 ReleaseService();
1370}
1371
1372/** The method unloads all services.
1373 *
1374 * @thread main HGCM
1375 */
1376/* static */ void HGCMService::UnloadAll(bool fUvmIsInvalid)
1377{
1378 while (sm_pSvcListHead)
1379 {
1380 sm_pSvcListHead->UnloadService(fUvmIsInvalid);
1381 }
1382}
1383
1384/** The method obtains a referenced pointer to the service with
1385 * specified name. The caller must call ReleaseService when
1386 * the pointer is no longer needed.
1387 *
1388 * @param ppSvc Where to store the pointer to the service.
1389 * @param pszServiceName The name of the service.
1390 * @return VBox rc.
1391 * @thread main HGCM
1392 */
1393/* static */ int HGCMService::ResolveService(HGCMService **ppSvc, const char *pszServiceName)
1394{
1395 LogFlowFunc(("ppSvc = %p name = %s\n",
1396 ppSvc, pszServiceName));
1397
1398 if (!ppSvc || !pszServiceName)
1399 {
1400 return VERR_INVALID_PARAMETER;
1401 }
1402
1403 HGCMService *pSvc = sm_pSvcListHead;
1404
1405 while (pSvc)
1406 {
1407 if (strcmp(pSvc->m_pszSvcName, pszServiceName) == 0)
1408 {
1409 break;
1410 }
1411
1412 pSvc = pSvc->m_pSvcNext;
1413 }
1414
1415 LogFlowFunc(("lookup in the list is %p\n", pSvc));
1416
1417 if (pSvc == NULL)
1418 {
1419 *ppSvc = NULL;
1420 return VERR_HGCM_SERVICE_NOT_FOUND;
1421 }
1422
1423 pSvc->ReferenceService();
1424
1425 *ppSvc = pSvc;
1426
1427 return VINF_SUCCESS;
1428}
1429
1430/** The method increases reference counter.
1431 *
1432 * @thread main HGCM
1433 */
1434void HGCMService::ReferenceService(void)
1435{
1436 ASMAtomicIncU32(&m_u32RefCnt);
1437 LogFlowFunc(("[%s] m_u32RefCnt = %d\n", m_pszSvcName, m_u32RefCnt));
1438}
1439
1440/** The method dereferences a service and deletes it when no more refs.
1441 *
1442 * @thread main HGCM
1443 */
1444void HGCMService::ReleaseService(void)
1445{
1446 LogFlowFunc(("m_u32RefCnt = %d\n", m_u32RefCnt));
1447 uint32_t u32RefCnt = ASMAtomicDecU32(&m_u32RefCnt);
1448 AssertRelease(u32RefCnt != ~0U);
1449
1450 LogFlowFunc(("u32RefCnt = %d, name %s\n", u32RefCnt, m_pszSvcName));
1451
1452 if (u32RefCnt == 0)
1453 {
1454 instanceDestroy();
1455 delete this;
1456 }
1457}
1458
1459/** The method is called when the VM is being reset or terminated
1460 * and disconnects all clients from all services.
1461 *
1462 * @thread main HGCM
1463 */
1464/* static */ void HGCMService::Reset(void)
1465{
1466 g_fResetting = true;
1467
1468 HGCMService *pSvc = sm_pSvcListHead;
1469
1470 while (pSvc)
1471 {
1472 while (pSvc->m_cClients && pSvc->m_paClientIds)
1473 {
1474 uint32_t const idClient = pSvc->m_paClientIds[0];
1475 HGCMClient * const pClient = (HGCMClient *)hgcmObjReference(idClient, HGCMOBJ_CLIENT);
1476 Assert(pClient);
1477 LogFlowFunc(("handle %d/%p\n", pSvc->m_paClientIds[0], pClient));
1478
1479 pSvc->DisconnectClient(pSvc->m_paClientIds[0], false, pClient);
1480
1481 hgcmObjDereference(pClient);
1482 }
1483
1484 pSvc = pSvc->m_pSvcNext;
1485 }
1486
1487 g_fResetting = false;
1488}
1489
1490/** The method saves the HGCM state.
1491 *
1492 * @param pSSM The saved state context.
1493 * @return VBox rc.
1494 * @thread main HGCM
1495 */
1496/* static */ int HGCMService::SaveState(PSSMHANDLE pSSM)
1497{
1498 /* Save the current handle count and restore afterwards to avoid client id conflicts. */
1499 int rc = SSMR3PutU32(pSSM, hgcmObjQueryHandleCount());
1500 AssertRCReturn(rc, rc);
1501
1502 LogFlowFunc(("%d services to be saved:\n", sm_cServices));
1503
1504 /* Save number of services. */
1505 rc = SSMR3PutU32(pSSM, sm_cServices);
1506 AssertRCReturn(rc, rc);
1507
1508 /* Save every service. */
1509 HGCMService *pSvc = sm_pSvcListHead;
1510
1511 while (pSvc)
1512 {
1513 LogFlowFunc(("Saving service [%s]\n", pSvc->m_pszSvcName));
1514
1515 /* Save the length of the service name. */
1516 rc = SSMR3PutU32(pSSM, (uint32_t) strlen(pSvc->m_pszSvcName) + 1);
1517 AssertRCReturn(rc, rc);
1518
1519 /* Save the name of the service. */
1520 rc = SSMR3PutStrZ(pSSM, pSvc->m_pszSvcName);
1521 AssertRCReturn(rc, rc);
1522
1523 /* Save the number of clients. */
1524 rc = SSMR3PutU32(pSSM, pSvc->m_cClients);
1525 AssertRCReturn(rc, rc);
1526
1527 /* Call the service for every client. Normally a service must not have
1528 * a global state to be saved: only per client info is relevant.
1529 * The global state of a service is configured during VM startup.
1530 */
1531 uint32_t i;
1532
1533 for (i = 0; i < pSvc->m_cClients; i++)
1534 {
1535 uint32_t u32ClientId = pSvc->m_paClientIds[i];
1536
1537 Log(("client id 0x%08X\n", u32ClientId));
1538
1539 /* Save the client id. (fRequestor is saved via SVC_MSG_SAVESTATE for convenience.) */
1540 rc = SSMR3PutU32(pSSM, u32ClientId);
1541 AssertRCReturn(rc, rc);
1542
1543 /* Call the service, so the operation is executed by the service thread. */
1544 rc = pSvc->saveClientState(u32ClientId, pSSM);
1545 AssertRCReturn(rc, rc);
1546 }
1547
1548 pSvc = pSvc->m_pSvcNext;
1549 }
1550
1551 return VINF_SUCCESS;
1552}
1553
1554/** The method loads saved HGCM state.
1555 *
1556 * @param pSSM The saved state handle.
1557 * @param uVersion The state version being loaded.
1558 * @return VBox rc.
1559 * @thread main HGCM
1560 */
1561/* static */ int HGCMService::LoadState(PSSMHANDLE pSSM, uint32_t uVersion)
1562{
1563 /* Restore handle count to avoid client id conflicts. */
1564 uint32_t u32;
1565
1566 int rc = SSMR3GetU32(pSSM, &u32);
1567 AssertRCReturn(rc, rc);
1568
1569 hgcmObjSetHandleCount(u32);
1570
1571 /* Get the number of services. */
1572 uint32_t cServices;
1573
1574 rc = SSMR3GetU32(pSSM, &cServices);
1575 AssertRCReturn(rc, rc);
1576
1577 LogFlowFunc(("%d services to be restored:\n", cServices));
1578
1579 while (cServices--)
1580 {
1581 /* Get the length of the service name. */
1582 rc = SSMR3GetU32(pSSM, &u32);
1583 AssertRCReturn(rc, rc);
1584 AssertReturn(u32 <= VBOX_HGCM_SVC_NAME_MAX_BYTES, VERR_SSM_UNEXPECTED_DATA);
1585
1586 /* Get the service name. */
1587 char szServiceName[VBOX_HGCM_SVC_NAME_MAX_BYTES];
1588 rc = SSMR3GetStrZ(pSSM, szServiceName, u32);
1589 AssertRCReturn(rc, rc);
1590
1591 LogRel(("HGCM: Restoring [%s]\n", szServiceName));
1592
1593 /* Resolve the service instance. */
1594 HGCMService *pSvc;
1595 rc = ResolveService(&pSvc, szServiceName);
1596 AssertLogRelMsgReturn(pSvc, ("rc=%Rrc, %s\n", rc, szServiceName), VERR_SSM_UNEXPECTED_DATA);
1597
1598 /* Get the number of clients. */
1599 uint32_t cClients;
1600 rc = SSMR3GetU32(pSSM, &cClients);
1601 if (RT_FAILURE(rc))
1602 {
1603 pSvc->ReleaseService();
1604 AssertFailed();
1605 return rc;
1606 }
1607
1608 while (cClients--)
1609 {
1610 /* Get the client ID and fRequest (convieniently save via SVC_MSG_SAVESTATE
1611 but restored here in time for calling CreateAndConnectClient). */
1612 uint32_t u32ClientId;
1613 rc = SSMR3GetU32(pSSM, &u32ClientId);
1614 uint32_t fRequestor = VMMDEV_REQUESTOR_LEGACY;
1615 if (RT_SUCCESS(rc) && uVersion > HGCM_SAVED_STATE_VERSION_V2)
1616 rc = SSMR3GetU32(pSSM, &fRequestor);
1617 AssertLogRelMsgRCReturnStmt(rc, ("rc=%Rrc, %s\n", rc, szServiceName), pSvc->ReleaseService(), rc);
1618
1619 /* Connect the client. */
1620 rc = pSvc->CreateAndConnectClient(NULL, u32ClientId, fRequestor, true /*fRestoring*/);
1621 AssertLogRelMsgRCReturnStmt(rc, ("rc=%Rrc, %s\n", rc, szServiceName), pSvc->ReleaseService(), rc);
1622
1623 /* Call the service, so the operation is executed by the service thread. */
1624 rc = pSvc->loadClientState(u32ClientId, pSSM, uVersion);
1625 AssertLogRelMsgRCReturnStmt(rc, ("rc=%Rrc, %s\n", rc, szServiceName), pSvc->ReleaseService(), rc);
1626 }
1627
1628 pSvc->ReleaseService();
1629 }
1630
1631 return VINF_SUCCESS;
1632}
1633
1634/* Create a new client instance and connect it to the service.
1635 *
1636 * @param pu32ClientIdOut If not NULL, then the method must generate a new handle for the client.
1637 * If NULL, use the given 'u32ClientIdIn' handle.
1638 * @param u32ClientIdIn The handle for the client, when 'pu32ClientIdOut' is NULL.
1639 * @param fRequestor The requestor flags, VMMDEV_REQUESTOR_LEGACY if not available.
1640 * @param fRestoring Set if we're restoring a saved state.
1641 * @return VBox status code.
1642 */
1643int HGCMService::CreateAndConnectClient(uint32_t *pu32ClientIdOut, uint32_t u32ClientIdIn, uint32_t fRequestor, bool fRestoring)
1644{
1645 LogFlowFunc(("pu32ClientIdOut = %p, u32ClientIdIn = %d, fRequestor = %#x, fRestoring = %d\n",
1646 pu32ClientIdOut, u32ClientIdIn, fRequestor, fRestoring));
1647
1648 /*
1649 * Categorize the client (compress VMMDEV_REQUESTOR_USR_MASK)
1650 * and check the respective client limit.
1651 */
1652 uint32_t idxClientCategory;
1653 if (fRequestor == VMMDEV_REQUESTOR_LEGACY)
1654 {
1655 idxClientCategory = m_fntable.idxLegacyClientCategory;
1656 AssertStmt(idxClientCategory < RT_ELEMENTS(m_acClients), idxClientCategory = HGCM_CLIENT_CATEGORY_KERNEL);
1657 }
1658 else
1659 switch (fRequestor & VMMDEV_REQUESTOR_USR_MASK)
1660 {
1661 case VMMDEV_REQUESTOR_USR_DRV:
1662 case VMMDEV_REQUESTOR_USR_DRV_OTHER:
1663 idxClientCategory = HGCM_CLIENT_CATEGORY_KERNEL;
1664 break;
1665 case VMMDEV_REQUESTOR_USR_ROOT:
1666 case VMMDEV_REQUESTOR_USR_SYSTEM:
1667 idxClientCategory = HGCM_CLIENT_CATEGORY_ROOT;
1668 break;
1669 default:
1670 idxClientCategory = HGCM_CLIENT_CATEGORY_USER;
1671 break;
1672 }
1673
1674 if ( m_acClients[idxClientCategory] < m_fntable.acMaxClients[idxClientCategory]
1675 || fRestoring)
1676 { }
1677 else
1678 {
1679 LogRel2(("Too many concurrenct clients for HGCM service '%s': %u, max %u; category %u\n",
1680 m_pszSvcName, m_cClients, m_fntable.acMaxClients[idxClientCategory], idxClientCategory));
1681 STAM_REL_COUNTER_INC(&m_StatTooManyClients);
1682 return VERR_HGCM_TOO_MANY_CLIENTS;
1683 }
1684
1685 /* Allocate a client information structure. */
1686 HGCMClient *pClient = new (std::nothrow) HGCMClient(fRequestor, idxClientCategory);
1687
1688 if (!pClient)
1689 {
1690 Log1WarningFunc(("Could not allocate HGCMClient!!!\n"));
1691 return VERR_NO_MEMORY;
1692 }
1693
1694 uint32_t handle;
1695
1696 if (pu32ClientIdOut != NULL)
1697 {
1698 handle = hgcmObjGenerateHandle(pClient);
1699 }
1700 else
1701 {
1702 handle = hgcmObjAssignHandle(pClient, u32ClientIdIn);
1703 }
1704
1705 LogFlowFunc(("client id = %d\n", handle));
1706
1707 AssertRelease(handle);
1708
1709 /* Initialize the HGCM part of the client. */
1710 int rc = pClient->Init(this);
1711
1712 if (RT_SUCCESS(rc))
1713 {
1714 /* Call the service. */
1715 HGCMMsgCore *pCoreMsg;
1716
1717 rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_CONNECT, hgcmMessageAllocSvc);
1718
1719 if (RT_SUCCESS(rc))
1720 {
1721 HGCMMsgSvcConnect *pMsg = (HGCMMsgSvcConnect *)pCoreMsg;
1722
1723 pMsg->u32ClientId = handle;
1724 pMsg->fRequestor = fRequestor;
1725 pMsg->fRestoring = fRestoring;
1726
1727 rc = hgcmMsgSend(pMsg);
1728
1729 if (RT_SUCCESS(rc))
1730 {
1731 /* Add the client Id to the array. */
1732 if (m_cClients == m_cClientsAllocated)
1733 {
1734 const uint32_t cDelta = 64;
1735
1736 /* Guards against integer overflow on 32bit arch and also limits size of m_paClientIds array to 4GB*/
1737 if (m_cClientsAllocated < UINT32_MAX / sizeof(m_paClientIds[0]) - cDelta)
1738 {
1739 uint32_t *paClientIdsNew;
1740
1741 paClientIdsNew = (uint32_t *)RTMemRealloc(m_paClientIds,
1742 (m_cClientsAllocated + cDelta) * sizeof(m_paClientIds[0]));
1743 Assert(paClientIdsNew);
1744
1745 if (paClientIdsNew)
1746 {
1747 m_paClientIds = paClientIdsNew;
1748 m_cClientsAllocated += cDelta;
1749 }
1750 else
1751 {
1752 rc = VERR_NO_MEMORY;
1753 }
1754 }
1755 else
1756 {
1757 rc = VERR_NO_MEMORY;
1758 }
1759 }
1760
1761 if (RT_SUCCESS(rc))
1762 {
1763 m_paClientIds[m_cClients] = handle;
1764 m_cClients++;
1765 m_acClients[idxClientCategory]++;
1766 LogFunc(("idClient=%u m_cClients=%u m_acClients[%u]=%u %s\n",
1767 handle, m_cClients, idxClientCategory, m_acClients[idxClientCategory], m_pszSvcName));
1768 }
1769 }
1770 }
1771 }
1772
1773 if (RT_SUCCESS(rc))
1774 {
1775 if (pu32ClientIdOut != NULL)
1776 {
1777 *pu32ClientIdOut = handle;
1778 }
1779
1780 ReferenceService();
1781 }
1782 else
1783 {
1784 hgcmObjDeleteHandle(handle);
1785 }
1786
1787 LogFlowFunc(("rc = %Rrc\n", rc));
1788 return rc;
1789}
1790
1791/**
1792 * Disconnect the client from the service and delete the client handle.
1793 *
1794 * @param u32ClientId The handle of the client.
1795 * @param fFromService Set if called by the service via
1796 * svcHlpDisconnectClient(). pClient can be NULL when
1797 * this is @c true.
1798 * @param pClient The client disconnecting. NULL if from service.
1799 * @return VBox status code.
1800 */
1801int HGCMService::DisconnectClient(uint32_t u32ClientId, bool fFromService, HGCMClient *pClient)
1802{
1803 Assert(pClient || !fFromService);
1804
1805 LogFlowFunc(("client id = %d, fFromService = %d, pClient = %p\n", u32ClientId, fFromService, pClient));
1806
1807 /*
1808 * Destroy the client handle prior to the disconnecting to avoid creating
1809 * a race with other messages from the same client. See @bugref{10038}
1810 * for further details.
1811 */
1812 Assert(pClient->idxCategory < HGCM_CLIENT_CATEGORY_MAX);
1813 Assert(m_acClients[pClient->idxCategory] > 0);
1814
1815 bool fReleaseService = false;
1816 int rc = VERR_NOT_FOUND;
1817 for (uint32_t i = 0; i < m_cClients; i++)
1818 {
1819 if (m_paClientIds[i] == u32ClientId)
1820 {
1821 if (m_acClients[pClient->idxCategory] > 0)
1822 m_acClients[pClient->idxCategory]--;
1823
1824 m_cClients--;
1825
1826 if (m_cClients > i)
1827 memmove(&m_paClientIds[i], &m_paClientIds[i + 1], sizeof(m_paClientIds[0]) * (m_cClients - i));
1828
1829 /* Delete the client handle. */
1830 hgcmObjDeleteHandle(u32ClientId);
1831 fReleaseService = true;
1832
1833 rc = VINF_SUCCESS;
1834 break;
1835 }
1836 }
1837
1838 /* Some paranoia wrt to not trusting the client ID array. */
1839 Assert(rc == VINF_SUCCESS || fFromService);
1840 if (rc == VERR_NOT_FOUND && !fFromService)
1841 {
1842 if (m_acClients[pClient->idxCategory] > 0)
1843 m_acClients[pClient->idxCategory]--;
1844
1845 hgcmObjDeleteHandle(u32ClientId);
1846 fReleaseService = true;
1847 }
1848
1849 if (pClient)
1850 LogFunc(("idClient=%u m_cClients=%u m_acClients[%u]=%u %s (cPendingCalls=%u) rc=%Rrc\n", u32ClientId, m_cClients,
1851 pClient->idxCategory, m_acClients[pClient->idxCategory], m_pszSvcName, pClient->cPendingCalls, rc));
1852
1853 /*
1854 * Call the service.
1855 */
1856 if (!fFromService)
1857 {
1858 /* Call the service. */
1859 HGCMMsgCore *pCoreMsg;
1860
1861 rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_DISCONNECT, hgcmMessageAllocSvc);
1862
1863 if (RT_SUCCESS(rc))
1864 {
1865 HGCMMsgSvcDisconnect *pMsg = (HGCMMsgSvcDisconnect *)pCoreMsg;
1866
1867 pMsg->u32ClientId = u32ClientId;
1868 pMsg->pClient = pClient;
1869
1870 rc = hgcmMsgSend(pMsg);
1871 }
1872 else
1873 {
1874 LogRel(("(%d, %d) [%s] hgcmMsgAlloc(%p, SVC_MSG_DISCONNECT) failed %Rrc\n",
1875 u32ClientId, fFromService, RT_VALID_PTR(m_pszSvcName)? m_pszSvcName: "", m_pThread, rc));
1876 }
1877 }
1878
1879
1880 /*
1881 * Release the pClient->pService reference.
1882 */
1883 if (fReleaseService)
1884 ReleaseService();
1885
1886 LogFlowFunc(("rc = %Rrc\n", rc));
1887 return rc;
1888}
1889
1890int HGCMService::RegisterExtension(HGCMSVCEXTHANDLE handle,
1891 PFNHGCMSVCEXT pfnExtension,
1892 void *pvExtension)
1893{
1894 LogFlowFunc(("%s\n", handle->pszServiceName));
1895
1896 /* Forward the message to the service thread. */
1897 HGCMMsgCore *pCoreMsg;
1898 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_REGEXT, hgcmMessageAllocSvc);
1899
1900 if (RT_SUCCESS(rc))
1901 {
1902 HGCMMsgSvcRegisterExtension *pMsg = (HGCMMsgSvcRegisterExtension *)pCoreMsg;
1903
1904 pMsg->handle = handle;
1905 pMsg->pfnExtension = pfnExtension;
1906 pMsg->pvExtension = pvExtension;
1907
1908 rc = hgcmMsgSend(pMsg);
1909 }
1910
1911 LogFlowFunc(("rc = %Rrc\n", rc));
1912 return rc;
1913}
1914
1915void HGCMService::UnregisterExtension(HGCMSVCEXTHANDLE handle)
1916{
1917 /* Forward the message to the service thread. */
1918 HGCMMsgCore *pCoreMsg;
1919 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_UNREGEXT, hgcmMessageAllocSvc);
1920
1921 if (RT_SUCCESS(rc))
1922 {
1923 HGCMMsgSvcUnregisterExtension *pMsg = (HGCMMsgSvcUnregisterExtension *)pCoreMsg;
1924
1925 pMsg->handle = handle;
1926
1927 rc = hgcmMsgSend(pMsg);
1928 }
1929
1930 LogFlowFunc(("rc = %Rrc\n", rc));
1931}
1932
1933/** @callback_method_impl{FNHGCMMSGCALLBACK} */
1934static DECLCALLBACK(int) hgcmMsgCallCompletionCallback(int32_t result, HGCMMsgCore *pMsgCore)
1935{
1936 /*
1937 * Do common message completion then decrement the call counter
1938 * for the client if necessary.
1939 */
1940 int rc = hgcmMsgCompletionCallback(result, pMsgCore);
1941
1942 HGCMMsgCall *pMsg = (HGCMMsgCall *)pMsgCore;
1943 if (pMsg->pcCounter)
1944 {
1945 uint32_t cCalls = ASMAtomicDecU32(pMsg->pcCounter);
1946 AssertStmt(cCalls < UINT32_MAX / 2, ASMAtomicWriteU32(pMsg->pcCounter, 0));
1947 pMsg->pcCounter = NULL;
1948 Log3Func(("pMsg=%p cPendingCalls=%u / %u (fun %u, %u parms)\n",
1949 pMsg, cCalls, pMsg->u32ClientId, pMsg->u32Function, pMsg->cParms));
1950 }
1951
1952 return rc;
1953}
1954
1955/** Perform a guest call to the service.
1956 *
1957 * @param pHGCMPort The port to be used for completion confirmation.
1958 * @param pCmd The VBox HGCM context.
1959 * @param u32ClientId The client handle to be disconnected and deleted.
1960 * @param pClient The client data.
1961 * @param u32Function The function number.
1962 * @param cParms Number of parameters.
1963 * @param paParms Pointer to array of parameters.
1964 * @param tsArrival The STAM_GET_TS() value when the request arrived.
1965 * @return VBox rc.
1966 * @retval VINF_HGCM_ASYNC_EXECUTE on success.
1967 */
1968int HGCMService::GuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, HGCMClient *pClient,
1969 uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
1970{
1971 LogFlow(("MAIN::HGCMService::GuestCall\n"));
1972
1973 int rc;
1974 HGCMMsgCall *pMsg = new(std::nothrow) HGCMMsgCall(m_pThread);
1975 if (pMsg)
1976 {
1977 pMsg->Reference(); /** @todo starts out with zero references. */
1978
1979 uint32_t cCalls = ASMAtomicIncU32(&pClient->cPendingCalls);
1980 Assert(pClient->idxCategory < RT_ELEMENTS(m_fntable.acMaxCallsPerClient));
1981 if (cCalls < m_fntable.acMaxCallsPerClient[pClient->idxCategory])
1982 {
1983 pMsg->pcCounter = &pClient->cPendingCalls;
1984 Log3(("MAIN::HGCMService::GuestCall: pMsg=%p cPendingCalls=%u / %u / %s (fun %u, %u parms)\n",
1985 pMsg, cCalls, u32ClientId, m_pszSvcName, u32Function, cParms));
1986
1987 pMsg->pCmd = pCmd;
1988 pMsg->pHGCMPort = pHGCMPort;
1989 pMsg->u32ClientId = u32ClientId;
1990 pMsg->u32Function = u32Function;
1991 pMsg->cParms = cParms;
1992 pMsg->paParms = paParms;
1993 pMsg->tsArrival = tsArrival;
1994
1995 rc = hgcmMsgPost(pMsg, hgcmMsgCallCompletionCallback);
1996
1997 if (RT_SUCCESS(rc))
1998 { /* Reference donated on success. */ }
1999 else
2000 {
2001 ASMAtomicDecU32(&pClient->cPendingCalls);
2002 pMsg->pcCounter = NULL;
2003 Log(("MAIN::HGCMService::GuestCall: hgcmMsgPost failed: %Rrc\n", rc));
2004 pMsg->Dereference();
2005 }
2006 }
2007 else
2008 {
2009 ASMAtomicDecU32(&pClient->cPendingCalls);
2010 LogRel2(("HGCM: Too many calls to '%s' from client %u: %u, max %u; category %u\n", m_pszSvcName, u32ClientId,
2011 cCalls, m_fntable.acMaxCallsPerClient[pClient->idxCategory], pClient->idxCategory));
2012 pMsg->Dereference();
2013 STAM_REL_COUNTER_INC(&m_StatTooManyCalls);
2014 rc = VERR_HGCM_TOO_MANY_CLIENT_CALLS;
2015 }
2016 }
2017 else
2018 {
2019 Log(("MAIN::HGCMService::GuestCall: Message allocation failed\n"));
2020 rc = VERR_NO_MEMORY;
2021 }
2022
2023 LogFlowFunc(("rc = %Rrc\n", rc));
2024 return rc;
2025}
2026
2027/** Guest cancelled a request (call, connection attempt, disconnect attempt).
2028 *
2029 * @param pHGCMPort The port to be used for completion confirmation
2030 * @param pCmd The VBox HGCM context.
2031 * @param idClient The client handle to be disconnected and deleted.
2032 * @return VBox rc.
2033 */
2034void HGCMService::GuestCancelled(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t idClient)
2035{
2036 LogFlow(("MAIN::HGCMService::GuestCancelled\n"));
2037
2038 if (m_fntable.pfnCancelled)
2039 {
2040 HGCMMsgCancelled *pMsg = new (std::nothrow) HGCMMsgCancelled(m_pThread);
2041 if (pMsg)
2042 {
2043 pMsg->Reference(); /** @todo starts out with zero references. */
2044
2045 pMsg->pCmd = pCmd;
2046 pMsg->pHGCMPort = pHGCMPort;
2047 pMsg->idClient = idClient;
2048
2049 hgcmMsgPost(pMsg, NULL);
2050 }
2051 else
2052 Log(("MAIN::HGCMService::GuestCancelled: Message allocation failed\n"));
2053 }
2054}
2055
2056/** Perform a host call the service.
2057 *
2058 * @param u32Function The function number.
2059 * @param cParms Number of parameters.
2060 * @param paParms Pointer to array of parameters.
2061 * @return VBox rc.
2062 */
2063int HGCMService::HostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM *paParms)
2064{
2065 LogFlowFunc(("%s u32Function = %d, cParms = %d, paParms = %p\n",
2066 m_pszSvcName, u32Function, cParms, paParms));
2067
2068 HGCMMsgCore *pCoreMsg;
2069 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_HOSTCALL, hgcmMessageAllocSvc);
2070
2071 if (RT_SUCCESS(rc))
2072 {
2073 HGCMMsgHostCallSvc *pMsg = (HGCMMsgHostCallSvc *)pCoreMsg;
2074
2075 pMsg->u32Function = u32Function;
2076 pMsg->cParms = cParms;
2077 pMsg->paParms = paParms;
2078
2079 rc = hgcmMsgSend(pMsg);
2080 }
2081
2082 LogFlowFunc(("rc = %Rrc\n", rc));
2083 return rc;
2084}
2085
2086/** Posts a broadcast notification event to all interested services.
2087 *
2088 * @param enmEvent The notification event.
2089 */
2090/*static*/ void HGCMService::BroadcastNotify(HGCMNOTIFYEVENT enmEvent)
2091{
2092 for (HGCMService *pService = sm_pSvcListHead; pService != NULL; pService = pService->m_pSvcNext)
2093 {
2094 pService->Notify(enmEvent);
2095 }
2096}
2097
2098/** Posts a broadcast notification event to the service.
2099 *
2100 * @param enmEvent The notification event.
2101 */
2102void HGCMService::Notify(HGCMNOTIFYEVENT enmEvent)
2103{
2104 LogFlowFunc(("%s enmEvent=%d pfnNotify=%p\n", m_pszSvcName, enmEvent, m_fntable.pfnNotify));
2105 if (m_fntable.pfnNotify)
2106 {
2107 HGCMMsgCore *pCoreMsg;
2108 int rc = hgcmMsgAlloc(m_pThread, &pCoreMsg, SVC_MSG_NOTIFY, hgcmMessageAllocSvc);
2109 if (RT_SUCCESS(rc))
2110 {
2111 HGCMMsgNotify *pMsg = (HGCMMsgNotify *)pCoreMsg;
2112 pMsg->enmEvent = enmEvent;
2113
2114 rc = hgcmMsgPost(pMsg, NULL);
2115 AssertRC(rc);
2116 }
2117 }
2118}
2119
2120/*
2121 * Main HGCM thread that manages services.
2122 */
2123
2124/* Messages processed by the main HGCM thread. */
2125#define HGCM_MSG_CONNECT (10) /**< Connect a client to a service. */
2126#define HGCM_MSG_DISCONNECT (11) /**< Disconnect the specified client id. */
2127#define HGCM_MSG_LOAD (12) /**< Load the service. */
2128#define HGCM_MSG_HOSTCALL (13) /**< Call the service. */
2129#define HGCM_MSG_LOADSTATE (14) /**< Load saved state for the specified service. */
2130#define HGCM_MSG_SAVESTATE (15) /**< Save state for the specified service. */
2131#define HGCM_MSG_RESET (16) /**< Disconnect all clients from the specified service. */
2132#define HGCM_MSG_QUIT (17) /**< Unload all services and terminate the thread. */
2133#define HGCM_MSG_REGEXT (18) /**< Register a service extension. */
2134#define HGCM_MSG_UNREGEXT (19) /**< Unregister a service extension. */
2135#define HGCM_MSG_BRD_NOTIFY (20) /**< Broadcast notification event (VM state change). */
2136
2137class HGCMMsgMainConnect: public HGCMMsgHeader
2138{
2139 public:
2140 /* Service name. */
2141 const char *pszServiceName;
2142 /* Where to store the client handle. */
2143 uint32_t *pu32ClientId;
2144};
2145
2146class HGCMMsgMainDisconnect: public HGCMMsgHeader
2147{
2148 public:
2149 /* Handle of the client to be disconnected. */
2150 uint32_t u32ClientId;
2151};
2152
2153class HGCMMsgMainLoad: public HGCMMsgCore
2154{
2155 public:
2156 /* Name of the library to be loaded. */
2157 const char *pszServiceLibrary;
2158 /* Name to be assigned to the service. */
2159 const char *pszServiceName;
2160 /** The user mode VM handle (for statistics and such). */
2161 PUVM pUVM;
2162 /** The HGCM port on the VMMDev device (for session ID and such). */
2163 PPDMIHGCMPORT pHgcmPort;
2164};
2165
2166class HGCMMsgMainHostCall: public HGCMMsgCore
2167{
2168 public:
2169 /* Which service to call. */
2170 const char *pszServiceName;
2171 /* Function number. */
2172 uint32_t u32Function;
2173 /* Number of the function parameters. */
2174 uint32_t cParms;
2175 /* Pointer to array of the function parameters. */
2176 VBOXHGCMSVCPARM *paParms;
2177};
2178
2179class HGCMMsgMainLoadSaveState: public HGCMMsgCore
2180{
2181 public:
2182 /** Saved state handle. */
2183 PSSMHANDLE pSSM;
2184 /** The HGCM saved state version being loaded (ignore for save). */
2185 uint32_t uVersion;
2186};
2187
2188class HGCMMsgMainReset: public HGCMMsgCore
2189{
2190 public:
2191 /** Set if this is actually a shutdown and not a VM reset. */
2192 bool fForShutdown;
2193};
2194
2195class HGCMMsgMainQuit: public HGCMMsgCore
2196{
2197 public:
2198 /** Whether UVM has gone invalid already or not. */
2199 bool fUvmIsInvalid;
2200};
2201
2202class HGCMMsgMainRegisterExtension: public HGCMMsgCore
2203{
2204 public:
2205 /** Returned handle to be used in HGCMMsgMainUnregisterExtension. */
2206 HGCMSVCEXTHANDLE *pHandle;
2207 /** Name of the service. */
2208 const char *pszServiceName;
2209 /** The extension entry point. */
2210 PFNHGCMSVCEXT pfnExtension;
2211 /** The extension pointer. */
2212 void *pvExtension;
2213};
2214
2215class HGCMMsgMainUnregisterExtension: public HGCMMsgCore
2216{
2217 public:
2218 /* Handle of the registered extension. */
2219 HGCMSVCEXTHANDLE handle;
2220};
2221
2222class HGCMMsgMainBroadcastNotify: public HGCMMsgCore
2223{
2224 public:
2225 /** The notification event. */
2226 HGCMNOTIFYEVENT enmEvent;
2227};
2228
2229
2230static HGCMMsgCore *hgcmMainMessageAlloc (uint32_t u32MsgId)
2231{
2232 switch (u32MsgId)
2233 {
2234 case HGCM_MSG_CONNECT: return new HGCMMsgMainConnect();
2235 case HGCM_MSG_DISCONNECT: return new HGCMMsgMainDisconnect();
2236 case HGCM_MSG_LOAD: return new HGCMMsgMainLoad();
2237 case HGCM_MSG_HOSTCALL: return new HGCMMsgMainHostCall();
2238 case HGCM_MSG_LOADSTATE:
2239 case HGCM_MSG_SAVESTATE: return new HGCMMsgMainLoadSaveState();
2240 case HGCM_MSG_RESET: return new HGCMMsgMainReset();
2241 case HGCM_MSG_QUIT: return new HGCMMsgMainQuit();
2242 case HGCM_MSG_REGEXT: return new HGCMMsgMainRegisterExtension();
2243 case HGCM_MSG_UNREGEXT: return new HGCMMsgMainUnregisterExtension();
2244 case HGCM_MSG_BRD_NOTIFY: return new HGCMMsgMainBroadcastNotify();
2245
2246 default:
2247 AssertReleaseMsgFailed(("Msg id = %08X\n", u32MsgId));
2248 }
2249
2250 return NULL;
2251}
2252
2253
2254/* The main HGCM thread handler. */
2255static DECLCALLBACK(void) hgcmThread(HGCMThread *pThread, void *pvUser)
2256{
2257 LogFlowFunc(("pThread = %p, pvUser = %p\n", pThread, pvUser));
2258
2259 NOREF(pvUser);
2260
2261 bool fQuit = false;
2262
2263 while (!fQuit)
2264 {
2265 HGCMMsgCore *pMsgCore;
2266 int rc = hgcmMsgGet(pThread, &pMsgCore);
2267
2268 if (RT_FAILURE(rc))
2269 {
2270 /* The error means some serious unrecoverable problem in the hgcmMsg/hgcmThread layer. */
2271 AssertMsgFailed(("%Rrc\n", rc));
2272 break;
2273 }
2274
2275 uint32_t u32MsgId = pMsgCore->MsgId();
2276
2277 switch (u32MsgId)
2278 {
2279 case HGCM_MSG_CONNECT:
2280 {
2281 HGCMMsgMainConnect *pMsg = (HGCMMsgMainConnect *)pMsgCore;
2282
2283 LogFlowFunc(("HGCM_MSG_CONNECT pszServiceName %s, pu32ClientId %p\n",
2284 pMsg->pszServiceName, pMsg->pu32ClientId));
2285
2286 /* Resolve the service name to the pointer to service instance.
2287 */
2288 HGCMService *pService;
2289 rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName);
2290
2291 if (RT_SUCCESS(rc))
2292 {
2293 /* Call the service instance method. */
2294 rc = pService->CreateAndConnectClient(pMsg->pu32ClientId,
2295 0,
2296 pMsg->pHGCMPort->pfnGetRequestor(pMsg->pHGCMPort, pMsg->pCmd),
2297 pMsg->pHGCMPort->pfnIsCmdRestored(pMsg->pHGCMPort, pMsg->pCmd));
2298
2299 /* Release the service after resolve. */
2300 pService->ReleaseService();
2301 }
2302 } break;
2303
2304 case HGCM_MSG_DISCONNECT:
2305 {
2306 HGCMMsgMainDisconnect *pMsg = (HGCMMsgMainDisconnect *)pMsgCore;
2307
2308 LogFlowFunc(("HGCM_MSG_DISCONNECT u32ClientId = %d\n",
2309 pMsg->u32ClientId));
2310
2311 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(pMsg->u32ClientId, HGCMOBJ_CLIENT);
2312
2313 if (!pClient)
2314 {
2315 rc = VERR_HGCM_INVALID_CLIENT_ID;
2316 break;
2317 }
2318
2319 /* The service the client belongs to. */
2320 HGCMService *pService = pClient->pService;
2321
2322 /* Call the service instance to disconnect the client. */
2323 rc = pService->DisconnectClient(pMsg->u32ClientId, false, pClient);
2324
2325 hgcmObjDereference(pClient);
2326 } break;
2327
2328 case HGCM_MSG_LOAD:
2329 {
2330 HGCMMsgMainLoad *pMsg = (HGCMMsgMainLoad *)pMsgCore;
2331
2332 LogFlowFunc(("HGCM_MSG_LOAD pszServiceName = %s, pMsg->pszServiceLibrary = %s, pMsg->pUVM = %p\n",
2333 pMsg->pszServiceName, pMsg->pszServiceLibrary, pMsg->pUVM));
2334
2335 rc = HGCMService::LoadService(pMsg->pszServiceLibrary, pMsg->pszServiceName, pMsg->pUVM, pMsg->pHgcmPort);
2336 } break;
2337
2338 case HGCM_MSG_HOSTCALL:
2339 {
2340 HGCMMsgMainHostCall *pMsg = (HGCMMsgMainHostCall *)pMsgCore;
2341
2342 LogFlowFunc(("HGCM_MSG_HOSTCALL pszServiceName %s, u32Function %d, cParms %d, paParms %p\n",
2343 pMsg->pszServiceName, pMsg->u32Function, pMsg->cParms, pMsg->paParms));
2344
2345 /* Resolve the service name to the pointer to service instance. */
2346 HGCMService *pService;
2347 rc = HGCMService::ResolveService(&pService, pMsg->pszServiceName);
2348
2349 if (RT_SUCCESS(rc))
2350 {
2351 rc = pService->HostCall(pMsg->u32Function, pMsg->cParms, pMsg->paParms);
2352
2353 pService->ReleaseService();
2354 }
2355 } break;
2356
2357 case HGCM_MSG_BRD_NOTIFY:
2358 {
2359 HGCMMsgMainBroadcastNotify *pMsg = (HGCMMsgMainBroadcastNotify *)pMsgCore;
2360
2361 LogFlowFunc(("HGCM_MSG_BRD_NOTIFY enmEvent=%d\n", pMsg->enmEvent));
2362
2363 HGCMService::BroadcastNotify(pMsg->enmEvent);
2364 } break;
2365
2366 case HGCM_MSG_RESET:
2367 {
2368 LogFlowFunc(("HGCM_MSG_RESET\n"));
2369
2370 HGCMService::Reset();
2371
2372 HGCMMsgMainReset *pMsg = (HGCMMsgMainReset *)pMsgCore;
2373 if (!pMsg->fForShutdown)
2374 HGCMService::BroadcastNotify(HGCMNOTIFYEVENT_RESET);
2375 } break;
2376
2377 case HGCM_MSG_LOADSTATE:
2378 {
2379 HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pMsgCore;
2380
2381 LogFlowFunc(("HGCM_MSG_LOADSTATE\n"));
2382
2383 rc = HGCMService::LoadState(pMsg->pSSM, pMsg->uVersion);
2384 } break;
2385
2386 case HGCM_MSG_SAVESTATE:
2387 {
2388 HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pMsgCore;
2389
2390 LogFlowFunc(("HGCM_MSG_SAVESTATE\n"));
2391
2392 rc = HGCMService::SaveState(pMsg->pSSM);
2393 } break;
2394
2395 case HGCM_MSG_QUIT:
2396 {
2397 HGCMMsgMainQuit *pMsg = (HGCMMsgMainQuit *)pMsgCore;
2398 LogFlowFunc(("HGCM_MSG_QUIT\n"));
2399
2400 HGCMService::UnloadAll(pMsg->fUvmIsInvalid);
2401
2402 fQuit = true;
2403 } break;
2404
2405 case HGCM_MSG_REGEXT:
2406 {
2407 HGCMMsgMainRegisterExtension *pMsg = (HGCMMsgMainRegisterExtension *)pMsgCore;
2408
2409 LogFlowFunc(("HGCM_MSG_REGEXT\n"));
2410
2411 /* Allocate the handle data. */
2412 HGCMSVCEXTHANDLE handle = (HGCMSVCEXTHANDLE)RTMemAllocZ(sizeof(struct _HGCMSVCEXTHANDLEDATA)
2413 + strlen(pMsg->pszServiceName)
2414 + sizeof(char));
2415
2416 if (handle == NULL)
2417 {
2418 rc = VERR_NO_MEMORY;
2419 }
2420 else
2421 {
2422 handle->pszServiceName = (char *)((uint8_t *)handle + sizeof(struct _HGCMSVCEXTHANDLEDATA));
2423 strcpy(handle->pszServiceName, pMsg->pszServiceName);
2424
2425 HGCMService *pService;
2426 rc = HGCMService::ResolveService(&pService, handle->pszServiceName);
2427
2428 if (RT_SUCCESS(rc))
2429 {
2430 pService->RegisterExtension(handle, pMsg->pfnExtension, pMsg->pvExtension);
2431
2432 pService->ReleaseService();
2433 }
2434
2435 if (RT_FAILURE(rc))
2436 {
2437 RTMemFree(handle);
2438 }
2439 else
2440 {
2441 *pMsg->pHandle = handle;
2442 }
2443 }
2444 } break;
2445
2446 case HGCM_MSG_UNREGEXT:
2447 {
2448 HGCMMsgMainUnregisterExtension *pMsg = (HGCMMsgMainUnregisterExtension *)pMsgCore;
2449
2450 LogFlowFunc(("HGCM_MSG_UNREGEXT\n"));
2451
2452 HGCMService *pService;
2453 rc = HGCMService::ResolveService(&pService, pMsg->handle->pszServiceName);
2454
2455 if (RT_SUCCESS(rc))
2456 {
2457 pService->UnregisterExtension(pMsg->handle);
2458
2459 pService->ReleaseService();
2460 }
2461
2462 RTMemFree(pMsg->handle);
2463 } break;
2464
2465 default:
2466 {
2467 AssertMsgFailed(("hgcmThread: Unsupported message number %08X!!!\n", u32MsgId));
2468 rc = VERR_NOT_SUPPORTED;
2469 } break;
2470 }
2471
2472 /* Complete the message processing. */
2473 hgcmMsgComplete(pMsgCore, rc);
2474
2475 LogFlowFunc(("message processed %Rrc\n", rc));
2476 }
2477}
2478
2479
2480/*
2481 * The HGCM API.
2482 */
2483
2484/** The main hgcm thread. */
2485static HGCMThread *g_pHgcmThread = 0;
2486
2487/*
2488 * Public HGCM functions.
2489 *
2490 * hgcmGuest* - called as a result of the guest HGCM requests.
2491 * hgcmHost* - called by the host.
2492 */
2493
2494/* Load a HGCM service from the specified library.
2495 * Assign the specified name to the service.
2496 *
2497 * @param pszServiceLibrary The library to be loaded.
2498 * @param pszServiceName The name to be assigned to the service.
2499 * @param pUVM The user mode VM handle (for statistics and such).
2500 * @param pHgcmPort The HGCM port on the VMMDev device (for session ID and such).
2501 * @return VBox rc.
2502 */
2503int HGCMHostLoad(const char *pszServiceLibrary,
2504 const char *pszServiceName,
2505 PUVM pUVM,
2506 PPDMIHGCMPORT pHgcmPort)
2507{
2508 LogFlowFunc(("lib = %s, name = %s\n", pszServiceLibrary, pszServiceName));
2509
2510 if (!pszServiceLibrary || !pszServiceName)
2511 {
2512 return VERR_INVALID_PARAMETER;
2513 }
2514
2515 /* Forward the request to the main hgcm thread. */
2516 HGCMMsgCore *pCoreMsg;
2517 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_LOAD, hgcmMainMessageAlloc);
2518
2519 if (RT_SUCCESS(rc))
2520 {
2521 /* Initialize the message. Since the message is synchronous, use the supplied pointers. */
2522 HGCMMsgMainLoad *pMsg = (HGCMMsgMainLoad *)pCoreMsg;
2523
2524 pMsg->pszServiceLibrary = pszServiceLibrary;
2525 pMsg->pszServiceName = pszServiceName;
2526 pMsg->pUVM = pUVM;
2527 pMsg->pHgcmPort = pHgcmPort;
2528
2529 rc = hgcmMsgSend(pMsg);
2530 }
2531
2532 LogFlowFunc(("rc = %Rrc\n", rc));
2533 return rc;
2534}
2535
2536/* Register a HGCM service extension.
2537 *
2538 * @param pHandle Returned handle for the registered extension.
2539 * @param pszServiceName The name of the service.
2540 * @param pfnExtension The extension entry point (callback).
2541 * @param pvExtension The extension pointer.
2542 * @return VBox rc.
2543 */
2544int HGCMHostRegisterServiceExtension(HGCMSVCEXTHANDLE *pHandle,
2545 const char *pszServiceName,
2546 PFNHGCMSVCEXT pfnExtension,
2547 void *pvExtension)
2548{
2549 LogFlowFunc(("pHandle = %p, name = %s, pfn = %p, rv = %p\n", pHandle, pszServiceName, pfnExtension, pvExtension));
2550
2551 if (!pHandle || !pszServiceName || !pfnExtension)
2552 {
2553 return VERR_INVALID_PARAMETER;
2554 }
2555
2556 /* Forward the request to the main hgcm thread. */
2557 HGCMMsgCore *pCoreMsg;
2558 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_REGEXT, hgcmMainMessageAlloc);
2559
2560 if (RT_SUCCESS(rc))
2561 {
2562 /* Initialize the message. Since the message is synchronous, use the supplied pointers. */
2563 HGCMMsgMainRegisterExtension *pMsg = (HGCMMsgMainRegisterExtension *)pCoreMsg;
2564
2565 pMsg->pHandle = pHandle;
2566 pMsg->pszServiceName = pszServiceName;
2567 pMsg->pfnExtension = pfnExtension;
2568 pMsg->pvExtension = pvExtension;
2569
2570 rc = hgcmMsgSend(pMsg);
2571 }
2572
2573 LogFlowFunc(("*pHandle = %p, rc = %Rrc\n", *pHandle, rc));
2574 return rc;
2575}
2576
2577void HGCMHostUnregisterServiceExtension(HGCMSVCEXTHANDLE handle)
2578{
2579 LogFlowFunc(("handle = %p\n", handle));
2580
2581 /* Forward the request to the main hgcm thread. */
2582 HGCMMsgCore *pCoreMsg;
2583 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_UNREGEXT, hgcmMainMessageAlloc);
2584
2585 if (RT_SUCCESS(rc))
2586 {
2587 /* Initialize the message. */
2588 HGCMMsgMainUnregisterExtension *pMsg = (HGCMMsgMainUnregisterExtension *)pCoreMsg;
2589
2590 pMsg->handle = handle;
2591
2592 rc = hgcmMsgSend(pMsg);
2593 }
2594
2595 LogFlowFunc(("rc = %Rrc\n", rc));
2596 return;
2597}
2598
2599/* Find a service and inform it about a client connection, create a client handle.
2600 *
2601 * @param pHGCMPort The port to be used for completion confirmation.
2602 * @param pCmd The VBox HGCM context.
2603 * @param pszServiceName The name of the service to be connected to.
2604 * @param pu32ClientId Where the store the created client handle.
2605 * @return VBox rc.
2606 */
2607int HGCMGuestConnect(PPDMIHGCMPORT pHGCMPort,
2608 PVBOXHGCMCMD pCmd,
2609 const char *pszServiceName,
2610 uint32_t *pu32ClientId)
2611{
2612 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, name = %s, pu32ClientId = %p\n",
2613 pHGCMPort, pCmd, pszServiceName, pu32ClientId));
2614
2615 if (pHGCMPort == NULL || pCmd == NULL || pszServiceName == NULL || pu32ClientId == NULL)
2616 {
2617 return VERR_INVALID_PARAMETER;
2618 }
2619
2620 /* Forward the request to the main hgcm thread. */
2621 HGCMMsgCore *pCoreMsg;
2622 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_CONNECT, hgcmMainMessageAlloc);
2623
2624 if (RT_SUCCESS(rc))
2625 {
2626 /* Initialize the message. Since 'pszServiceName' and 'pu32ClientId'
2627 * will not be deallocated by the caller until the message is completed,
2628 * use the supplied pointers.
2629 */
2630 HGCMMsgMainConnect *pMsg = (HGCMMsgMainConnect *)pCoreMsg;
2631
2632 pMsg->pHGCMPort = pHGCMPort;
2633 pMsg->pCmd = pCmd;
2634 pMsg->pszServiceName = pszServiceName;
2635 pMsg->pu32ClientId = pu32ClientId;
2636
2637 rc = hgcmMsgPost(pMsg, hgcmMsgCompletionCallback);
2638 }
2639
2640 LogFlowFunc(("rc = %Rrc\n", rc));
2641 return rc;
2642}
2643
2644/* Tell a service that the client is disconnecting, destroy the client handle.
2645 *
2646 * @param pHGCMPort The port to be used for completion confirmation.
2647 * @param pCmd The VBox HGCM context.
2648 * @param u32ClientId The client handle to be disconnected and deleted.
2649 * @return VBox rc.
2650 */
2651int HGCMGuestDisconnect(PPDMIHGCMPORT pHGCMPort,
2652 PVBOXHGCMCMD pCmd,
2653 uint32_t u32ClientId)
2654{
2655 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, u32ClientId = %d\n",
2656 pHGCMPort, pCmd, u32ClientId));
2657
2658 if (!pHGCMPort || !pCmd || !u32ClientId)
2659 {
2660 return VERR_INVALID_PARAMETER;
2661 }
2662
2663 /* Forward the request to the main hgcm thread. */
2664 HGCMMsgCore *pCoreMsg;
2665 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_DISCONNECT, hgcmMainMessageAlloc);
2666
2667 if (RT_SUCCESS(rc))
2668 {
2669 /* Initialize the message. */
2670 HGCMMsgMainDisconnect *pMsg = (HGCMMsgMainDisconnect *)pCoreMsg;
2671
2672 pMsg->pCmd = pCmd;
2673 pMsg->pHGCMPort = pHGCMPort;
2674 pMsg->u32ClientId = u32ClientId;
2675
2676 rc = hgcmMsgPost(pMsg, hgcmMsgCompletionCallback);
2677 }
2678
2679 LogFlowFunc(("rc = %Rrc\n", rc));
2680 return rc;
2681}
2682
2683/** Helper to send either HGCM_MSG_SAVESTATE or HGCM_MSG_LOADSTATE messages to the main HGCM thread.
2684 *
2685 * @param pSSM The SSM handle.
2686 * @param idMsg The message to be sent: HGCM_MSG_SAVESTATE or HGCM_MSG_LOADSTATE.
2687 * @param uVersion The state version being loaded.
2688 * @return VBox rc.
2689 */
2690static int hgcmHostLoadSaveState(PSSMHANDLE pSSM, uint32_t idMsg, uint32_t uVersion)
2691{
2692 LogFlowFunc(("pSSM = %p, idMsg = %d, uVersion = uVersion\n", pSSM, idMsg));
2693
2694 HGCMMsgCore *pCoreMsg;
2695 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, idMsg, hgcmMainMessageAlloc);
2696
2697 if (RT_SUCCESS(rc))
2698 {
2699 HGCMMsgMainLoadSaveState *pMsg = (HGCMMsgMainLoadSaveState *)pCoreMsg;
2700 AssertRelease(pMsg);
2701
2702 pMsg->pSSM = pSSM;
2703 pMsg->uVersion = uVersion;
2704
2705 rc = hgcmMsgSend(pMsg);
2706 }
2707
2708 LogFlowFunc(("rc = %Rrc\n", rc));
2709 return rc;
2710}
2711
2712/** Save the state of services.
2713 *
2714 * @param pSSM The SSM handle.
2715 * @return VBox rc.
2716 */
2717int HGCMHostSaveState(PSSMHANDLE pSSM)
2718{
2719 return hgcmHostLoadSaveState(pSSM, HGCM_MSG_SAVESTATE, HGCM_SAVED_STATE_VERSION);
2720}
2721
2722/** Load the state of services.
2723 *
2724 * @param pSSM The SSM handle.
2725 * @param uVersion The state version being loaded.
2726 * @return VBox rc.
2727 */
2728int HGCMHostLoadState(PSSMHANDLE pSSM, uint32_t uVersion)
2729{
2730 return hgcmHostLoadSaveState(pSSM, HGCM_MSG_LOADSTATE, uVersion);
2731}
2732
2733/** The guest calls the service.
2734 *
2735 * @param pHGCMPort The port to be used for completion confirmation.
2736 * @param pCmd The VBox HGCM context.
2737 * @param u32ClientId The client handle.
2738 * @param u32Function The function number.
2739 * @param cParms Number of parameters.
2740 * @param paParms Pointer to array of parameters.
2741 * @param tsArrival The STAM_GET_TS() value when the request arrived.
2742 * @return VBox rc.
2743 */
2744int HGCMGuestCall(PPDMIHGCMPORT pHGCMPort,
2745 PVBOXHGCMCMD pCmd,
2746 uint32_t u32ClientId,
2747 uint32_t u32Function,
2748 uint32_t cParms,
2749 VBOXHGCMSVCPARM *paParms,
2750 uint64_t tsArrival)
2751{
2752 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, u32ClientId = %d, u32Function = %d, cParms = %d, paParms = %p\n",
2753 pHGCMPort, pCmd, u32ClientId, u32Function, cParms, paParms));
2754
2755 if (!pHGCMPort || !pCmd || u32ClientId == 0)
2756 {
2757 return VERR_INVALID_PARAMETER;
2758 }
2759
2760 int rc = VERR_HGCM_INVALID_CLIENT_ID;
2761
2762 /* Resolve the client handle to the client instance pointer. */
2763 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(u32ClientId, HGCMOBJ_CLIENT);
2764
2765 if (pClient)
2766 {
2767 AssertRelease(pClient->pService);
2768
2769 /* Forward the message to the service thread. */
2770 rc = pClient->pService->GuestCall(pHGCMPort, pCmd, u32ClientId, pClient, u32Function, cParms, paParms, tsArrival);
2771
2772 hgcmObjDereference(pClient);
2773 }
2774
2775 LogFlowFunc(("rc = %Rrc\n", rc));
2776 return rc;
2777}
2778
2779/** The guest cancelled a request (call, connect, disconnect)
2780 *
2781 * @param pHGCMPort The port to be used for completion confirmation.
2782 * @param pCmd The VBox HGCM context.
2783 * @param idClient The client handle.
2784 */
2785void HGCMGuestCancelled(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t idClient)
2786{
2787 LogFlowFunc(("pHGCMPort = %p, pCmd = %p, idClient = %d\n", pHGCMPort, pCmd, idClient));
2788 AssertReturnVoid(pHGCMPort);
2789 AssertReturnVoid(pCmd);
2790 AssertReturnVoid(idClient != 0);
2791
2792 /* Resolve the client handle to the client instance pointer. */
2793 HGCMClient *pClient = (HGCMClient *)hgcmObjReference(idClient, HGCMOBJ_CLIENT);
2794
2795 if (pClient)
2796 {
2797 AssertRelease(pClient->pService);
2798
2799 /* Forward the message to the service thread. */
2800 pClient->pService->GuestCancelled(pHGCMPort, pCmd, idClient);
2801
2802 hgcmObjDereference(pClient);
2803 }
2804
2805 LogFlowFunc(("returns\n"));
2806}
2807
2808/** The host calls the service.
2809 *
2810 * @param pszServiceName The service name to be called.
2811 * @param u32Function The function number.
2812 * @param cParms Number of parameters.
2813 * @param paParms Pointer to array of parameters.
2814 * @return VBox rc.
2815 */
2816int HGCMHostCall(const char *pszServiceName,
2817 uint32_t u32Function,
2818 uint32_t cParms,
2819 VBOXHGCMSVCPARM *paParms)
2820{
2821 LogFlowFunc(("name = %s, u32Function = %d, cParms = %d, paParms = %p\n",
2822 pszServiceName, u32Function, cParms, paParms));
2823
2824 if (!pszServiceName)
2825 {
2826 return VERR_INVALID_PARAMETER;
2827 }
2828
2829 /* Host calls go to main HGCM thread that resolves the service name to the
2830 * service instance pointer and then, using the service pointer, forwards
2831 * the message to the service thread.
2832 * So it is slow but host calls are intended mostly for configuration and
2833 * other non-time-critical functions.
2834 */
2835 HGCMMsgCore *pCoreMsg;
2836 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_HOSTCALL, hgcmMainMessageAlloc);
2837
2838 if (RT_SUCCESS(rc))
2839 {
2840 HGCMMsgMainHostCall *pMsg = (HGCMMsgMainHostCall *)pCoreMsg;
2841
2842 pMsg->pszServiceName = (char *)pszServiceName;
2843 pMsg->u32Function = u32Function;
2844 pMsg->cParms = cParms;
2845 pMsg->paParms = paParms;
2846
2847 rc = hgcmMsgSend(pMsg);
2848 }
2849
2850 LogFlowFunc(("rc = %Rrc\n", rc));
2851 return rc;
2852}
2853
2854/** Posts a notification event to all services.
2855 *
2856 * @param enmEvent The notification event.
2857 * @return VBox rc.
2858 */
2859int HGCMBroadcastEvent(HGCMNOTIFYEVENT enmEvent)
2860{
2861 LogFlowFunc(("enmEvent=%d\n", enmEvent));
2862
2863 HGCMMsgCore *pCoreMsg;
2864 int rc = hgcmMsgAlloc(g_pHgcmThread, &pCoreMsg, HGCM_MSG_BRD_NOTIFY, hgcmMainMessageAlloc);
2865
2866 if (RT_SUCCESS(rc))
2867 {
2868 HGCMMsgMainBroadcastNotify *pMsg = (HGCMMsgMainBroadcastNotify *)pCoreMsg;
2869
2870 pMsg->enmEvent = enmEvent;
2871
2872 rc = hgcmMsgPost(pMsg, NULL);
2873 }
2874
2875 LogFlowFunc(("rc = %Rrc\n", rc));
2876 return rc;
2877}
2878
2879
2880int HGCMHostReset(bool fForShutdown)
2881{
2882 LogFlowFunc(("\n"));
2883
2884 /* Disconnect all clients.
2885 */
2886
2887 HGCMMsgCore *pMsgCore;
2888 int rc = hgcmMsgAlloc(g_pHgcmThread, &pMsgCore, HGCM_MSG_RESET, hgcmMainMessageAlloc);
2889
2890 if (RT_SUCCESS(rc))
2891 {
2892 HGCMMsgMainReset *pMsg = (HGCMMsgMainReset *)pMsgCore;
2893
2894 pMsg->fForShutdown = fForShutdown;
2895
2896 rc = hgcmMsgSend(pMsg);
2897 }
2898
2899 LogFlowFunc(("rc = %Rrc\n", rc));
2900 return rc;
2901}
2902
2903int HGCMHostInit(void)
2904{
2905 LogFlowFunc(("\n"));
2906
2907 int rc = hgcmThreadInit();
2908
2909 if (RT_SUCCESS(rc))
2910 {
2911 /*
2912 * Start main HGCM thread.
2913 */
2914
2915 rc = hgcmThreadCreate(&g_pHgcmThread, "MainHGCMthread", hgcmThread, NULL /*pvUser*/, NULL /*pszStatsSubDir*/, NULL /*pUVM*/);
2916
2917 if (RT_FAILURE(rc))
2918 LogRel(("Failed to start HGCM thread. HGCM services will be unavailable!!! rc = %Rrc\n", rc));
2919 }
2920
2921 LogFlowFunc(("rc = %Rrc\n", rc));
2922 return rc;
2923}
2924
2925int HGCMHostShutdown(bool fUvmIsInvalid /*= false*/)
2926{
2927 LogFlowFunc(("\n"));
2928
2929 /*
2930 * Do HGCMReset and then unload all services.
2931 */
2932
2933 int rc = HGCMHostReset(true /*fForShutdown*/);
2934
2935 if (RT_SUCCESS(rc))
2936 {
2937 /* Send the quit message to the main hgcmThread. */
2938 HGCMMsgCore *pMsgCore;
2939 rc = hgcmMsgAlloc(g_pHgcmThread, &pMsgCore, HGCM_MSG_QUIT, hgcmMainMessageAlloc);
2940
2941 if (RT_SUCCESS(rc))
2942 {
2943 HGCMMsgMainQuit *pMsg = (HGCMMsgMainQuit *)pMsgCore;
2944 pMsg->fUvmIsInvalid = fUvmIsInvalid;
2945
2946 rc = hgcmMsgSend(pMsg);
2947
2948 if (RT_SUCCESS(rc))
2949 {
2950 /* Wait for the thread termination. */
2951 hgcmThreadWait(g_pHgcmThread);
2952 g_pHgcmThread = NULL;
2953
2954 hgcmThreadUninit();
2955 }
2956 }
2957 }
2958
2959 LogFlowFunc(("rc = %Rrc\n", rc));
2960 return rc;
2961}
2962
Note: See TracBrowser for help on using the repository browser.

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