VirtualBox

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

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

HGCM,HostServices: Set the default limits before calling the service load function. bugref:9379

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

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