VirtualBox

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

Last change on this file since 103415 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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