VirtualBox

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

Last change on this file since 98278 was 98278, checked in by vboxsync, 16 months ago

Main/src-client: Some more rc -> hrc/vrc stuff found by grep. A build fix. bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use