VirtualBox

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

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

HGCM,HostServices: Extended VBOXHGCMSVCFNTABLE with client and call limits. Tried to pick reasonable values for all services. bugref:9379

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