VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp@ 75705

Last change on this file since 75705 was 75705, checked in by vboxsync, 6 years ago

VBoxGuest/darwin: Implemented straight forward interrupt handling. Enabled test signing. Adjustments. bugref:4686

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 163.2 KB
Line 
1/* $Id: VBoxGuest.cpp 75705 2018-11-25 01:44:41Z vboxsync $ */
2/** @file
3 * VBoxGuest - Guest Additions Driver, Common Code.
4 */
5
6/*
7 * Copyright (C) 2007-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/** @page pg_vbdrv VBoxGuest
28 *
29 * VBoxGuest is the device driver for VMMDev.
30 *
31 * The device driver is shipped as part of the guest additions. It has roots in
32 * the host VMM support driver (usually known as VBoxDrv), so fixes in platform
33 * specific code may apply to both drivers.
34 *
35 * The common code lives in VBoxGuest.cpp and is compiled both as C++ and C.
36 * The VBoxGuest.cpp source file shall not contain platform specific code,
37 * though it must occationally do a few \#ifdef RT_OS_XXX tests to cater for
38 * platform differences. Though, in those cases, it is common that more than
39 * one platform needs special handling.
40 *
41 * On most platforms the device driver should create two device nodes, one for
42 * full (unrestricted) access to the feature set, and one which only provides a
43 * restrict set of functions. These are generally referred to as 'vboxguest'
44 * and 'vboxuser' respectively. Currently, this two device approach is only
45 * implemented on Linux!
46 *
47 */
48
49
50/*********************************************************************************************************************************
51* Header Files *
52*********************************************************************************************************************************/
53#define LOG_GROUP LOG_GROUP_DEFAULT
54#include "VBoxGuestInternal.h"
55#include <VBox/VMMDev.h> /* for VMMDEV_RAM_SIZE */
56#include <VBox/log.h>
57#include <VBox/HostServices/GuestPropertySvc.h>
58#include <iprt/ctype.h>
59#include <iprt/mem.h>
60#include <iprt/time.h>
61#include <iprt/memobj.h>
62#include <iprt/asm.h>
63#include <iprt/asm-amd64-x86.h>
64#include <iprt/string.h>
65#include <iprt/process.h>
66#include <iprt/assert.h>
67#include <iprt/param.h>
68#include <iprt/timer.h>
69#ifdef VBOX_WITH_HGCM
70# include <iprt/thread.h>
71#endif
72#include "version-generated.h"
73#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
74# include "revision-generated.h"
75#endif
76#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
77# include <iprt/rand.h>
78#endif
79
80
81/*********************************************************************************************************************************
82* Defined Constants And Macros *
83*********************************************************************************************************************************/
84#define VBOXGUEST_ACQUIRE_STYLE_EVENTS (VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
85
86
87/*********************************************************************************************************************************
88* Internal Functions *
89*********************************************************************************************************************************/
90#ifdef VBOX_WITH_HGCM
91static DECLCALLBACK(int) vgdrvHgcmAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdrNonVolatile, void *pvUser, uint32_t u32User);
92#endif
93static int vgdrvIoCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession);
94static void vgdrvBitUsageTrackerClear(PVBOXGUESTBITUSAGETRACER pTracker);
95static uint32_t vgdrvGetAllowedEventMaskForSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession);
96static int vgdrvResetEventFilterOnHost(PVBOXGUESTDEVEXT pDevExt, uint32_t fFixedEvents);
97static int vgdrvResetMouseStatusOnHost(PVBOXGUESTDEVEXT pDevExt);
98static int vgdrvResetCapabilitiesOnHost(PVBOXGUESTDEVEXT pDevExt);
99static int vgdrvSetSessionEventFilter(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
100 uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination);
101static int vgdrvSetSessionMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
102 uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination);
103static int vgdrvSetSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
104 uint32_t fOrMask, uint32_t fNoMask,
105 uint32_t *pfSessionCaps, uint32_t *pfGlobalCaps, bool fSessionTermination);
106static int vgdrvAcquireSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
107 uint32_t fOrMask, uint32_t fNotMask, uint32_t fFlags, bool fSessionTermination);
108static int vgdrvDispatchEventsLocked(PVBOXGUESTDEVEXT pDevExt, uint32_t fEvents);
109
110
111/*********************************************************************************************************************************
112* Global Variables *
113*********************************************************************************************************************************/
114static const uint32_t g_cbChangeMemBalloonReq = RT_UOFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
115
116#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
117/**
118 * Drag in the rest of IRPT since we share it with the
119 * rest of the kernel modules on Solaris.
120 */
121PFNRT g_apfnVBoxGuestIPRTDeps[] =
122{
123 /* VirtioNet */
124 (PFNRT)RTRandBytes,
125 /* RTSemMutex* */
126 (PFNRT)RTSemMutexCreate,
127 (PFNRT)RTSemMutexDestroy,
128 (PFNRT)RTSemMutexRequest,
129 (PFNRT)RTSemMutexRequestNoResume,
130 (PFNRT)RTSemMutexRequestDebug,
131 (PFNRT)RTSemMutexRequestNoResumeDebug,
132 (PFNRT)RTSemMutexRelease,
133 (PFNRT)RTSemMutexIsOwned,
134 NULL
135};
136#endif /* RT_OS_DARWIN || RT_OS_SOLARIS */
137
138
139/**
140 * Reserves memory in which the VMM can relocate any guest mappings
141 * that are floating around.
142 *
143 * This operation is a little bit tricky since the VMM might not accept
144 * just any address because of address clashes between the three contexts
145 * it operates in, so use a small stack to perform this operation.
146 *
147 * @returns VBox status code (ignored).
148 * @param pDevExt The device extension.
149 */
150static int vgdrvInitFixateGuestMappings(PVBOXGUESTDEVEXT pDevExt)
151{
152 /*
153 * Query the required space.
154 */
155 VMMDevReqHypervisorInfo *pReq;
156 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
157 if (RT_FAILURE(rc))
158 return rc;
159 pReq->hypervisorStart = 0;
160 pReq->hypervisorSize = 0;
161 rc = VbglR0GRPerform(&pReq->header);
162 if (RT_FAILURE(rc)) /* this shouldn't happen! */
163 {
164 VbglR0GRFree(&pReq->header);
165 return rc;
166 }
167
168 /*
169 * The VMM will report back if there is nothing it wants to map, like for
170 * instance in VT-x and AMD-V mode.
171 */
172 if (pReq->hypervisorSize == 0)
173 Log(("vgdrvInitFixateGuestMappings: nothing to do\n"));
174 else
175 {
176 /*
177 * We have to try several times since the host can be picky
178 * about certain addresses.
179 */
180 RTR0MEMOBJ hFictive = NIL_RTR0MEMOBJ;
181 uint32_t cbHypervisor = pReq->hypervisorSize;
182 RTR0MEMOBJ ahTries[5];
183 uint32_t iTry;
184 bool fBitched = false;
185 Log(("vgdrvInitFixateGuestMappings: cbHypervisor=%#x\n", cbHypervisor));
186 for (iTry = 0; iTry < RT_ELEMENTS(ahTries); iTry++)
187 {
188 /*
189 * Reserve space, or if that isn't supported, create a object for
190 * some fictive physical memory and map that in to kernel space.
191 *
192 * To make the code a bit uglier, most systems cannot help with
193 * 4MB alignment, so we have to deal with that in addition to
194 * having two ways of getting the memory.
195 */
196 uint32_t uAlignment = _4M;
197 RTR0MEMOBJ hObj;
198 rc = RTR0MemObjReserveKernel(&hObj, (void *)-1, RT_ALIGN_32(cbHypervisor, _4M), uAlignment);
199 if (rc == VERR_NOT_SUPPORTED)
200 {
201 uAlignment = PAGE_SIZE;
202 rc = RTR0MemObjReserveKernel(&hObj, (void *)-1, RT_ALIGN_32(cbHypervisor, _4M) + _4M, uAlignment);
203 }
204 /*
205 * If both RTR0MemObjReserveKernel calls above failed because either not supported or
206 * not implemented at all at the current platform, try to map the memory object into the
207 * virtual kernel space.
208 */
209 if (rc == VERR_NOT_SUPPORTED)
210 {
211 if (hFictive == NIL_RTR0MEMOBJ)
212 {
213 rc = RTR0MemObjEnterPhys(&hObj, VBOXGUEST_HYPERVISOR_PHYSICAL_START, cbHypervisor + _4M, RTMEM_CACHE_POLICY_DONT_CARE);
214 if (RT_FAILURE(rc))
215 break;
216 hFictive = hObj;
217 }
218 uAlignment = _4M;
219 rc = RTR0MemObjMapKernel(&hObj, hFictive, (void *)-1, uAlignment, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
220 if (rc == VERR_NOT_SUPPORTED)
221 {
222 uAlignment = PAGE_SIZE;
223 rc = RTR0MemObjMapKernel(&hObj, hFictive, (void *)-1, uAlignment, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
224 }
225 }
226 if (RT_FAILURE(rc))
227 {
228 LogRel(("VBoxGuest: Failed to reserve memory for the hypervisor: rc=%Rrc (cbHypervisor=%#x uAlignment=%#x iTry=%u)\n",
229 rc, cbHypervisor, uAlignment, iTry));
230 fBitched = true;
231 break;
232 }
233
234 /*
235 * Try set it.
236 */
237 pReq->header.requestType = VMMDevReq_SetHypervisorInfo;
238 pReq->header.rc = VERR_INTERNAL_ERROR;
239 pReq->hypervisorSize = cbHypervisor;
240 pReq->hypervisorStart = (RTGCPTR32)(uintptr_t)RTR0MemObjAddress(hObj);
241 if ( uAlignment == PAGE_SIZE
242 && pReq->hypervisorStart & (_4M - 1))
243 pReq->hypervisorStart = RT_ALIGN_32(pReq->hypervisorStart, _4M);
244 AssertMsg(RT_ALIGN_32(pReq->hypervisorStart, _4M) == pReq->hypervisorStart, ("%#x\n", pReq->hypervisorStart));
245
246 rc = VbglR0GRPerform(&pReq->header);
247 if (RT_SUCCESS(rc))
248 {
249 pDevExt->hGuestMappings = hFictive != NIL_RTR0MEMOBJ ? hFictive : hObj;
250 Log(("VBoxGuest: %p LB %#x; uAlignment=%#x iTry=%u hGuestMappings=%p (%s)\n",
251 RTR0MemObjAddress(pDevExt->hGuestMappings),
252 RTR0MemObjSize(pDevExt->hGuestMappings),
253 uAlignment, iTry, pDevExt->hGuestMappings, hFictive != NIL_RTR0PTR ? "fictive" : "reservation"));
254 break;
255 }
256 ahTries[iTry] = hObj;
257 }
258
259 /*
260 * Cleanup failed attempts.
261 */
262 while (iTry-- > 0)
263 RTR0MemObjFree(ahTries[iTry], false /* fFreeMappings */);
264 if ( RT_FAILURE(rc)
265 && hFictive != NIL_RTR0PTR)
266 RTR0MemObjFree(hFictive, false /* fFreeMappings */);
267 if (RT_FAILURE(rc) && !fBitched)
268 LogRel(("VBoxGuest: Warning: failed to reserve %#d of memory for guest mappings.\n", cbHypervisor));
269 }
270 VbglR0GRFree(&pReq->header);
271
272 /*
273 * We ignore failed attempts for now.
274 */
275 return VINF_SUCCESS;
276}
277
278
279/**
280 * Undo what vgdrvInitFixateGuestMappings did.
281 *
282 * @param pDevExt The device extension.
283 */
284static void vgdrvTermUnfixGuestMappings(PVBOXGUESTDEVEXT pDevExt)
285{
286 if (pDevExt->hGuestMappings != NIL_RTR0PTR)
287 {
288 /*
289 * Tell the host that we're going to free the memory we reserved for
290 * it, the free it up. (Leak the memory if anything goes wrong here.)
291 */
292 VMMDevReqHypervisorInfo *pReq;
293 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
294 if (RT_SUCCESS(rc))
295 {
296 pReq->hypervisorStart = 0;
297 pReq->hypervisorSize = 0;
298 rc = VbglR0GRPerform(&pReq->header);
299 VbglR0GRFree(&pReq->header);
300 }
301 if (RT_SUCCESS(rc))
302 {
303 rc = RTR0MemObjFree(pDevExt->hGuestMappings, true /* fFreeMappings */);
304 AssertRC(rc);
305 }
306 else
307 LogRel(("vgdrvTermUnfixGuestMappings: Failed to unfix the guest mappings! rc=%Rrc\n", rc));
308
309 pDevExt->hGuestMappings = NIL_RTR0MEMOBJ;
310 }
311}
312
313
314
315/**
316 * Report the guest information to the host.
317 *
318 * @returns IPRT status code.
319 * @param enmOSType The OS type to report.
320 */
321static int vgdrvReportGuestInfo(VBOXOSTYPE enmOSType)
322{
323 /*
324 * Allocate and fill in the two guest info reports.
325 */
326 VMMDevReportGuestInfo2 *pReqInfo2 = NULL;
327 VMMDevReportGuestInfo *pReqInfo1 = NULL;
328 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqInfo2, sizeof (VMMDevReportGuestInfo2), VMMDevReq_ReportGuestInfo2);
329 Log(("vgdrvReportGuestInfo: VbglR0GRAlloc VMMDevReportGuestInfo2 completed with rc=%Rrc\n", rc));
330 if (RT_SUCCESS(rc))
331 {
332 pReqInfo2->guestInfo.additionsMajor = VBOX_VERSION_MAJOR;
333 pReqInfo2->guestInfo.additionsMinor = VBOX_VERSION_MINOR;
334 pReqInfo2->guestInfo.additionsBuild = VBOX_VERSION_BUILD;
335 pReqInfo2->guestInfo.additionsRevision = VBOX_SVN_REV;
336 pReqInfo2->guestInfo.additionsFeatures = VBOXGSTINFO2_F_REQUESTOR_INFO;
337 RTStrCopy(pReqInfo2->guestInfo.szName, sizeof(pReqInfo2->guestInfo.szName), VBOX_VERSION_STRING);
338
339 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqInfo1, sizeof (VMMDevReportGuestInfo), VMMDevReq_ReportGuestInfo);
340 Log(("vgdrvReportGuestInfo: VbglR0GRAlloc VMMDevReportGuestInfo completed with rc=%Rrc\n", rc));
341 if (RT_SUCCESS(rc))
342 {
343 pReqInfo1->guestInfo.interfaceVersion = VMMDEV_VERSION;
344 pReqInfo1->guestInfo.osType = enmOSType;
345
346 /*
347 * There are two protocols here:
348 * 1. Info2 + Info1. Supported by >=3.2.51.
349 * 2. Info1 and optionally Info2. The old protocol.
350 *
351 * We try protocol 1 first. It will fail with VERR_NOT_SUPPORTED
352 * if not supported by the VMMDev (message ordering requirement).
353 */
354 rc = VbglR0GRPerform(&pReqInfo2->header);
355 Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo2 completed with rc=%Rrc\n", rc));
356 if (RT_SUCCESS(rc))
357 {
358 rc = VbglR0GRPerform(&pReqInfo1->header);
359 Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo completed with rc=%Rrc\n", rc));
360 }
361 else if ( rc == VERR_NOT_SUPPORTED
362 || rc == VERR_NOT_IMPLEMENTED)
363 {
364 rc = VbglR0GRPerform(&pReqInfo1->header);
365 Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo completed with rc=%Rrc\n", rc));
366 if (RT_SUCCESS(rc))
367 {
368 rc = VbglR0GRPerform(&pReqInfo2->header);
369 Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo2 completed with rc=%Rrc\n", rc));
370 if (rc == VERR_NOT_IMPLEMENTED)
371 rc = VINF_SUCCESS;
372 }
373 }
374 VbglR0GRFree(&pReqInfo1->header);
375 }
376 VbglR0GRFree(&pReqInfo2->header);
377 }
378
379 return rc;
380}
381
382
383/**
384 * Report the guest driver status to the host.
385 *
386 * @returns IPRT status code.
387 * @param fActive Flag whether the driver is now active or not.
388 */
389static int vgdrvReportDriverStatus(bool fActive)
390{
391 /*
392 * Report guest status of the VBox driver to the host.
393 */
394 VMMDevReportGuestStatus *pReq2 = NULL;
395 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq2, sizeof(*pReq2), VMMDevReq_ReportGuestStatus);
396 Log(("vgdrvReportDriverStatus: VbglR0GRAlloc VMMDevReportGuestStatus completed with rc=%Rrc\n", rc));
397 if (RT_SUCCESS(rc))
398 {
399 pReq2->guestStatus.facility = VBoxGuestFacilityType_VBoxGuestDriver;
400 pReq2->guestStatus.status = fActive ?
401 VBoxGuestFacilityStatus_Active
402 : VBoxGuestFacilityStatus_Inactive;
403 pReq2->guestStatus.flags = 0;
404 rc = VbglR0GRPerform(&pReq2->header);
405 Log(("vgdrvReportDriverStatus: VbglR0GRPerform VMMDevReportGuestStatus completed with fActive=%d, rc=%Rrc\n",
406 fActive ? 1 : 0, rc));
407 if (rc == VERR_NOT_IMPLEMENTED) /* Compatibility with older hosts. */
408 rc = VINF_SUCCESS;
409 VbglR0GRFree(&pReq2->header);
410 }
411
412 return rc;
413}
414
415
416/** @name Memory Ballooning
417 * @{
418 */
419
420/**
421 * Inflate the balloon by one chunk represented by an R0 memory object.
422 *
423 * The caller owns the balloon mutex.
424 *
425 * @returns IPRT status code.
426 * @param pMemObj Pointer to the R0 memory object.
427 * @param pReq The pre-allocated request for performing the VMMDev call.
428 */
429static int vgdrvBalloonInflate(PRTR0MEMOBJ pMemObj, VMMDevChangeMemBalloon *pReq)
430{
431 uint32_t iPage;
432 int rc;
433
434 for (iPage = 0; iPage < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; iPage++)
435 {
436 RTHCPHYS phys = RTR0MemObjGetPagePhysAddr(*pMemObj, iPage);
437 pReq->aPhysPage[iPage] = phys;
438 }
439
440 pReq->fInflate = true;
441 pReq->header.size = g_cbChangeMemBalloonReq;
442 pReq->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
443
444 rc = VbglR0GRPerform(&pReq->header);
445 if (RT_FAILURE(rc))
446 LogRel(("vgdrvBalloonInflate: VbglR0GRPerform failed. rc=%Rrc\n", rc));
447 return rc;
448}
449
450
451/**
452 * Deflate the balloon by one chunk - info the host and free the memory object.
453 *
454 * The caller owns the balloon mutex.
455 *
456 * @returns IPRT status code.
457 * @param pMemObj Pointer to the R0 memory object.
458 * The memory object will be freed afterwards.
459 * @param pReq The pre-allocated request for performing the VMMDev call.
460 */
461static int vgdrvBalloonDeflate(PRTR0MEMOBJ pMemObj, VMMDevChangeMemBalloon *pReq)
462{
463 uint32_t iPage;
464 int rc;
465
466 for (iPage = 0; iPage < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; iPage++)
467 {
468 RTHCPHYS phys = RTR0MemObjGetPagePhysAddr(*pMemObj, iPage);
469 pReq->aPhysPage[iPage] = phys;
470 }
471
472 pReq->fInflate = false;
473 pReq->header.size = g_cbChangeMemBalloonReq;
474 pReq->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
475
476 rc = VbglR0GRPerform(&pReq->header);
477 if (RT_FAILURE(rc))
478 {
479 LogRel(("vgdrvBalloonDeflate: VbglR0GRPerform failed. rc=%Rrc\n", rc));
480 return rc;
481 }
482
483 rc = RTR0MemObjFree(*pMemObj, true);
484 if (RT_FAILURE(rc))
485 {
486 LogRel(("vgdrvBalloonDeflate: RTR0MemObjFree(%p,true) -> %Rrc; this is *BAD*!\n", *pMemObj, rc));
487 return rc;
488 }
489
490 *pMemObj = NIL_RTR0MEMOBJ;
491 return VINF_SUCCESS;
492}
493
494
495/**
496 * Inflate/deflate the memory balloon and notify the host.
497 *
498 * This is a worker used by vgdrvIoCtl_CheckMemoryBalloon - it takes the mutex.
499 *
500 * @returns VBox status code.
501 * @param pDevExt The device extension.
502 * @param cBalloonChunks The new size of the balloon in chunks of 1MB.
503 * @param pfHandleInR3 Where to return the handle-in-ring3 indicator
504 * (VINF_SUCCESS if set).
505 */
506static int vgdrvSetBalloonSizeKernel(PVBOXGUESTDEVEXT pDevExt, uint32_t cBalloonChunks, bool *pfHandleInR3)
507{
508 int rc = VINF_SUCCESS;
509
510 if (pDevExt->MemBalloon.fUseKernelAPI)
511 {
512 VMMDevChangeMemBalloon *pReq;
513 uint32_t i;
514
515 if (cBalloonChunks > pDevExt->MemBalloon.cMaxChunks)
516 {
517 LogRel(("vgdrvSetBalloonSizeKernel: illegal balloon size %u (max=%u)\n",
518 cBalloonChunks, pDevExt->MemBalloon.cMaxChunks));
519 return VERR_INVALID_PARAMETER;
520 }
521
522 if (cBalloonChunks == pDevExt->MemBalloon.cMaxChunks)
523 return VINF_SUCCESS; /* nothing to do */
524
525 if ( cBalloonChunks > pDevExt->MemBalloon.cChunks
526 && !pDevExt->MemBalloon.paMemObj)
527 {
528 pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAllocZ(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks);
529 if (!pDevExt->MemBalloon.paMemObj)
530 {
531 LogRel(("vgdrvSetBalloonSizeKernel: no memory for paMemObj!\n"));
532 return VERR_NO_MEMORY;
533 }
534 }
535
536 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, g_cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon);
537 if (RT_FAILURE(rc))
538 return rc;
539
540 if (cBalloonChunks > pDevExt->MemBalloon.cChunks)
541 {
542 /* inflate */
543 for (i = pDevExt->MemBalloon.cChunks; i < cBalloonChunks; i++)
544 {
545 rc = RTR0MemObjAllocPhysNC(&pDevExt->MemBalloon.paMemObj[i],
546 VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, NIL_RTHCPHYS);
547 if (RT_FAILURE(rc))
548 {
549 if (rc == VERR_NOT_SUPPORTED)
550 {
551 /* not supported -- fall back to the R3-allocated memory. */
552 rc = VINF_SUCCESS;
553 pDevExt->MemBalloon.fUseKernelAPI = false;
554 Assert(pDevExt->MemBalloon.cChunks == 0);
555 Log(("VBoxGuestSetBalloonSizeKernel: PhysNC allocs not supported, falling back to R3 allocs.\n"));
556 }
557 /* else if (rc == VERR_NO_MEMORY || rc == VERR_NO_PHYS_MEMORY):
558 * cannot allocate more memory => don't try further, just stop here */
559 /* else: XXX what else can fail? VERR_MEMOBJ_INIT_FAILED for instance. just stop. */
560 break;
561 }
562
563 rc = vgdrvBalloonInflate(&pDevExt->MemBalloon.paMemObj[i], pReq);
564 if (RT_FAILURE(rc))
565 {
566 Log(("vboxGuestSetBalloonSize(inflate): failed, rc=%Rrc!\n", rc));
567 RTR0MemObjFree(pDevExt->MemBalloon.paMemObj[i], true);
568 pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ;
569 break;
570 }
571 pDevExt->MemBalloon.cChunks++;
572 }
573 }
574 else
575 {
576 /* deflate */
577 for (i = pDevExt->MemBalloon.cChunks; i-- > cBalloonChunks;)
578 {
579 rc = vgdrvBalloonDeflate(&pDevExt->MemBalloon.paMemObj[i], pReq);
580 if (RT_FAILURE(rc))
581 {
582 Log(("vboxGuestSetBalloonSize(deflate): failed, rc=%Rrc!\n", rc));
583 break;
584 }
585 pDevExt->MemBalloon.cChunks--;
586 }
587 }
588
589 VbglR0GRFree(&pReq->header);
590 }
591
592 /*
593 * Set the handle-in-ring3 indicator. When set Ring-3 will have to work
594 * the balloon changes via the other API.
595 */
596 *pfHandleInR3 = pDevExt->MemBalloon.fUseKernelAPI ? false : true;
597
598 return rc;
599}
600
601
602/**
603 * Inflate/deflate the balloon by one chunk.
604 *
605 * Worker for vgdrvIoCtl_ChangeMemoryBalloon - it takes the mutex.
606 *
607 * @returns VBox status code.
608 * @param pDevExt The device extension.
609 * @param pSession The session.
610 * @param pvChunk The address of the chunk to add to / remove from the
611 * balloon. (user space address)
612 * @param fInflate Inflate if true, deflate if false.
613 */
614static int vgdrvSetBalloonSizeFromUser(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, RTR3PTR pvChunk, bool fInflate)
615{
616 VMMDevChangeMemBalloon *pReq;
617 PRTR0MEMOBJ pMemObj = NULL;
618 int rc = VINF_SUCCESS;
619 uint32_t i;
620 RT_NOREF1(pSession);
621
622 if (fInflate)
623 {
624 if ( pDevExt->MemBalloon.cChunks > pDevExt->MemBalloon.cMaxChunks - 1
625 || pDevExt->MemBalloon.cMaxChunks == 0 /* If called without first querying. */)
626 {
627 LogRel(("vgdrvSetBalloonSizeFromUser: cannot inflate balloon, already have %u chunks (max=%u)\n",
628 pDevExt->MemBalloon.cChunks, pDevExt->MemBalloon.cMaxChunks));
629 return VERR_INVALID_PARAMETER;
630 }
631
632 if (!pDevExt->MemBalloon.paMemObj)
633 {
634 pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAlloc(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks);
635 if (!pDevExt->MemBalloon.paMemObj)
636 {
637 LogRel(("vgdrvSetBalloonSizeFromUser: no memory for paMemObj!\n"));
638 return VERR_NO_MEMORY;
639 }
640 for (i = 0; i < pDevExt->MemBalloon.cMaxChunks; i++)
641 pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ;
642 }
643 }
644 else
645 {
646 if (pDevExt->MemBalloon.cChunks == 0)
647 {
648 AssertMsgFailed(("vgdrvSetBalloonSizeFromUser: cannot decrease balloon, already at size 0\n"));
649 return VERR_INVALID_PARAMETER;
650 }
651 }
652
653 /*
654 * Enumerate all memory objects and check if the object is already registered.
655 */
656 for (i = 0; i < pDevExt->MemBalloon.cMaxChunks; i++)
657 {
658 if ( fInflate
659 && !pMemObj
660 && pDevExt->MemBalloon.paMemObj[i] == NIL_RTR0MEMOBJ)
661 pMemObj = &pDevExt->MemBalloon.paMemObj[i]; /* found free object pointer */
662 if (RTR0MemObjAddressR3(pDevExt->MemBalloon.paMemObj[i]) == pvChunk)
663 {
664 if (fInflate)
665 return VERR_ALREADY_EXISTS; /* don't provide the same memory twice */
666 pMemObj = &pDevExt->MemBalloon.paMemObj[i];
667 break;
668 }
669 }
670 if (!pMemObj)
671 {
672 if (fInflate)
673 {
674 /* no free object pointer found -- should not happen */
675 return VERR_NO_MEMORY;
676 }
677
678 /* cannot free this memory as it wasn't provided before */
679 return VERR_NOT_FOUND;
680 }
681
682 /*
683 * Try inflate / default the balloon as requested.
684 */
685 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, g_cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon);
686 if (RT_FAILURE(rc))
687 return rc;
688 pReq->header.fRequestor = pSession->fRequestor;
689
690 if (fInflate)
691 {
692 rc = RTR0MemObjLockUser(pMemObj, pvChunk, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE,
693 RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
694 if (RT_SUCCESS(rc))
695 {
696 rc = vgdrvBalloonInflate(pMemObj, pReq);
697 if (RT_SUCCESS(rc))
698 pDevExt->MemBalloon.cChunks++;
699 else
700 {
701 Log(("vgdrvSetBalloonSizeFromUser(inflate): failed, rc=%Rrc!\n", rc));
702 RTR0MemObjFree(*pMemObj, true);
703 *pMemObj = NIL_RTR0MEMOBJ;
704 }
705 }
706 }
707 else
708 {
709 rc = vgdrvBalloonDeflate(pMemObj, pReq);
710 if (RT_SUCCESS(rc))
711 pDevExt->MemBalloon.cChunks--;
712 else
713 Log(("vgdrvSetBalloonSizeFromUser(deflate): failed, rc=%Rrc!\n", rc));
714 }
715
716 VbglR0GRFree(&pReq->header);
717 return rc;
718}
719
720
721/**
722 * Cleanup the memory balloon of a session.
723 *
724 * Will request the balloon mutex, so it must be valid and the caller must not
725 * own it already.
726 *
727 * @param pDevExt The device extension.
728 * @param pSession The session. Can be NULL at unload.
729 */
730static void vgdrvCloseMemBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
731{
732 RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx);
733 if ( pDevExt->MemBalloon.pOwner == pSession
734 || pSession == NULL /*unload*/)
735 {
736 if (pDevExt->MemBalloon.paMemObj)
737 {
738 VMMDevChangeMemBalloon *pReq;
739 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, g_cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon);
740 if (RT_SUCCESS(rc))
741 {
742 /* fRequestor is kernel here, as we're cleaning up. */
743
744 uint32_t i;
745 for (i = pDevExt->MemBalloon.cChunks; i-- > 0;)
746 {
747 rc = vgdrvBalloonDeflate(&pDevExt->MemBalloon.paMemObj[i], pReq);
748 if (RT_FAILURE(rc))
749 {
750 LogRel(("vgdrvCloseMemBalloon: Deflate failed with rc=%Rrc. Will leak %u chunks.\n",
751 rc, pDevExt->MemBalloon.cChunks));
752 break;
753 }
754 pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ;
755 pDevExt->MemBalloon.cChunks--;
756 }
757 VbglR0GRFree(&pReq->header);
758 }
759 else
760 LogRel(("vgdrvCloseMemBalloon: Failed to allocate VMMDev request buffer (rc=%Rrc). Will leak %u chunks.\n",
761 rc, pDevExt->MemBalloon.cChunks));
762 RTMemFree(pDevExt->MemBalloon.paMemObj);
763 pDevExt->MemBalloon.paMemObj = NULL;
764 }
765
766 pDevExt->MemBalloon.pOwner = NULL;
767 }
768 RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx);
769}
770
771/** @} */
772
773
774
775/** @name Heartbeat
776 * @{
777 */
778
779/**
780 * Sends heartbeat to host.
781 *
782 * @returns VBox status code.
783 */
784static int vgdrvHeartbeatSend(PVBOXGUESTDEVEXT pDevExt)
785{
786 int rc;
787 if (pDevExt->pReqGuestHeartbeat)
788 {
789 rc = VbglR0GRPerform(pDevExt->pReqGuestHeartbeat);
790 Log3(("vgdrvHeartbeatSend: VbglR0GRPerform vgdrvHeartbeatSend completed with rc=%Rrc\n", rc));
791 }
792 else
793 rc = VERR_INVALID_STATE;
794 return rc;
795}
796
797
798/**
799 * Callback for heartbeat timer.
800 */
801static DECLCALLBACK(void) vgdrvHeartbeatTimerHandler(PRTTIMER hTimer, void *pvUser, uint64_t iTick)
802{
803 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
804 int rc;
805 AssertReturnVoid(pDevExt);
806
807 rc = vgdrvHeartbeatSend(pDevExt);
808 if (RT_FAILURE(rc))
809 Log(("HB Timer: vgdrvHeartbeatSend failed: rc=%Rrc\n", rc));
810
811 NOREF(hTimer); NOREF(iTick);
812}
813
814
815/**
816 * Configure the host to check guest's heartbeat
817 * and get heartbeat interval from the host.
818 *
819 * @returns VBox status code.
820 * @param pDevExt The device extension.
821 * @param fEnabled Set true to enable guest heartbeat checks on host.
822 */
823static int vgdrvHeartbeatHostConfigure(PVBOXGUESTDEVEXT pDevExt, bool fEnabled)
824{
825 VMMDevReqHeartbeat *pReq;
826 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_HeartbeatConfigure);
827 Log(("vgdrvHeartbeatHostConfigure: VbglR0GRAlloc vgdrvHeartbeatHostConfigure completed with rc=%Rrc\n", rc));
828 if (RT_SUCCESS(rc))
829 {
830 pReq->fEnabled = fEnabled;
831 pReq->cNsInterval = 0;
832 rc = VbglR0GRPerform(&pReq->header);
833 Log(("vgdrvHeartbeatHostConfigure: VbglR0GRPerform vgdrvHeartbeatHostConfigure completed with rc=%Rrc\n", rc));
834 pDevExt->cNsHeartbeatInterval = pReq->cNsInterval;
835 VbglR0GRFree(&pReq->header);
836 }
837 return rc;
838}
839
840
841/**
842 * Initializes the heartbeat timer.
843 *
844 * This feature may be disabled by the host.
845 *
846 * @returns VBox status (ignored).
847 * @param pDevExt The device extension.
848 */
849static int vgdrvHeartbeatInit(PVBOXGUESTDEVEXT pDevExt)
850{
851 /*
852 * Make sure that heartbeat checking is disabled.
853 */
854 int rc = vgdrvHeartbeatHostConfigure(pDevExt, false);
855 if (RT_SUCCESS(rc))
856 {
857 rc = vgdrvHeartbeatHostConfigure(pDevExt, true);
858 if (RT_SUCCESS(rc))
859 {
860 /*
861 * Preallocate the request to use it from the timer callback because:
862 * 1) on Windows VbglR0GRAlloc must be called at IRQL <= APC_LEVEL
863 * and the timer callback runs at DISPATCH_LEVEL;
864 * 2) avoid repeated allocations.
865 */
866 rc = VbglR0GRAlloc(&pDevExt->pReqGuestHeartbeat, sizeof(*pDevExt->pReqGuestHeartbeat), VMMDevReq_GuestHeartbeat);
867 if (RT_SUCCESS(rc))
868 {
869 LogRel(("vgdrvHeartbeatInit: Setting up heartbeat to trigger every %RU64 milliseconds\n",
870 pDevExt->cNsHeartbeatInterval / RT_NS_1MS));
871 rc = RTTimerCreateEx(&pDevExt->pHeartbeatTimer, pDevExt->cNsHeartbeatInterval, 0 /*fFlags*/,
872 (PFNRTTIMER)vgdrvHeartbeatTimerHandler, pDevExt);
873 if (RT_SUCCESS(rc))
874 {
875 rc = RTTimerStart(pDevExt->pHeartbeatTimer, 0);
876 if (RT_SUCCESS(rc))
877 return VINF_SUCCESS;
878
879 LogRel(("vgdrvHeartbeatInit: Heartbeat timer failed to start, rc=%Rrc\n", rc));
880 }
881 else
882 LogRel(("vgdrvHeartbeatInit: Failed to create heartbeat timer: %Rrc\n", rc));
883
884 VbglR0GRFree(pDevExt->pReqGuestHeartbeat);
885 pDevExt->pReqGuestHeartbeat = NULL;
886 }
887 else
888 LogRel(("vgdrvHeartbeatInit: VbglR0GRAlloc(VMMDevReq_GuestHeartbeat): %Rrc\n", rc));
889
890 LogRel(("vgdrvHeartbeatInit: Failed to set up the timer, guest heartbeat is disabled\n"));
891 vgdrvHeartbeatHostConfigure(pDevExt, false);
892 }
893 else
894 LogRel(("vgdrvHeartbeatInit: Failed to configure host for heartbeat checking: rc=%Rrc\n", rc));
895 }
896 return rc;
897}
898
899/** @} */
900
901
902/**
903 * Helper to reinit the VMMDev communication after hibernation.
904 *
905 * @returns VBox status code.
906 * @param pDevExt The device extension.
907 * @param enmOSType The OS type.
908 *
909 * @todo Call this on all platforms, not just windows.
910 */
911int VGDrvCommonReinitDevExtAfterHibernation(PVBOXGUESTDEVEXT pDevExt, VBOXOSTYPE enmOSType)
912{
913 int rc = vgdrvReportGuestInfo(enmOSType);
914 if (RT_SUCCESS(rc))
915 {
916 rc = vgdrvReportDriverStatus(true /* Driver is active */);
917 if (RT_FAILURE(rc))
918 Log(("VGDrvCommonReinitDevExtAfterHibernation: could not report guest driver status, rc=%Rrc\n", rc));
919 }
920 else
921 Log(("VGDrvCommonReinitDevExtAfterHibernation: could not report guest information to host, rc=%Rrc\n", rc));
922 LogFlow(("VGDrvCommonReinitDevExtAfterHibernation: returned with rc=%Rrc\n", rc));
923 RT_NOREF1(pDevExt);
924 return rc;
925}
926
927
928/**
929 * Initializes the release logger (debug is implicit), if configured.
930 *
931 * @returns IPRT status code.
932 */
933int VGDrvCommonInitLoggers(void)
934{
935#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER
936 /*
937 * Create the release log.
938 */
939 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
940 PRTLOGGER pRelLogger;
941 int rc = RTLogCreate(&pRelLogger, 0 /*fFlags*/, "all", "VBOXGUEST_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
942 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
943 if (RT_SUCCESS(rc))
944 RTLogRelSetDefaultInstance(pRelLogger);
945 /** @todo Add native hook for getting logger config parameters and setting
946 * them. On linux we should use the module parameter stuff... */
947 return rc;
948#else
949 return VINF_SUCCESS;
950#endif
951}
952
953
954/**
955 * Destroys the loggers.
956 */
957void VGDrvCommonDestroyLoggers(void)
958{
959#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER
960 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
961 RTLogDestroy(RTLogSetDefaultInstance(NULL));
962#endif
963}
964
965
966/**
967 * Initialize the device extension fundament.
968 *
969 * There are no device resources at this point, VGDrvCommonInitDevExtResources
970 * should be called when they are available.
971 *
972 * @returns VBox status code.
973 * @param pDevExt The device extension to init.
974 */
975int VGDrvCommonInitDevExtFundament(PVBOXGUESTDEVEXT pDevExt)
976{
977 int rc;
978 AssertMsg( pDevExt->uInitState != VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT
979 && pDevExt->uInitState != VBOXGUESTDEVEXT_INIT_STATE_RESOURCES, ("uInitState=%#x\n", pDevExt->uInitState));
980
981 /*
982 * Initialize the data.
983 */
984 pDevExt->IOPortBase = UINT16_MAX;
985 pDevExt->pVMMDevMemory = NULL;
986 pDevExt->hGuestMappings = NIL_RTR0MEMOBJ;
987 pDevExt->EventSpinlock = NIL_RTSPINLOCK;
988 pDevExt->fHostFeatures = 0;
989 pDevExt->pIrqAckEvents = NULL;
990 pDevExt->PhysIrqAckEvents = NIL_RTCCPHYS;
991 RTListInit(&pDevExt->WaitList);
992#ifdef VBOX_WITH_HGCM
993 RTListInit(&pDevExt->HGCMWaitList);
994#endif
995#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
996 RTListInit(&pDevExt->WakeUpList);
997#endif
998 RTListInit(&pDevExt->WokenUpList);
999 RTListInit(&pDevExt->FreeList);
1000 RTListInit(&pDevExt->SessionList);
1001 pDevExt->cSessions = 0;
1002 pDevExt->fLoggingEnabled = false;
1003 pDevExt->f32PendingEvents = 0;
1004 pDevExt->u32MousePosChangedSeq = 0;
1005 pDevExt->SessionSpinlock = NIL_RTSPINLOCK;
1006 pDevExt->MemBalloon.hMtx = NIL_RTSEMFASTMUTEX;
1007 pDevExt->MemBalloon.cChunks = 0;
1008 pDevExt->MemBalloon.cMaxChunks = 0;
1009 pDevExt->MemBalloon.fUseKernelAPI = true;
1010 pDevExt->MemBalloon.paMemObj = NULL;
1011 pDevExt->MemBalloon.pOwner = NULL;
1012 pDevExt->pfnMouseNotifyCallback = NULL;
1013 pDevExt->pvMouseNotifyCallbackArg = NULL;
1014 pDevExt->pReqGuestHeartbeat = NULL;
1015
1016 pDevExt->fFixedEvents = 0;
1017 vgdrvBitUsageTrackerClear(&pDevExt->EventFilterTracker);
1018 pDevExt->fEventFilterHost = UINT32_MAX; /* forces a report */
1019
1020 vgdrvBitUsageTrackerClear(&pDevExt->MouseStatusTracker);
1021 pDevExt->fMouseStatusHost = UINT32_MAX; /* forces a report */
1022
1023 pDevExt->fAcquireModeGuestCaps = 0;
1024 pDevExt->fSetModeGuestCaps = 0;
1025 pDevExt->fAcquiredGuestCaps = 0;
1026 vgdrvBitUsageTrackerClear(&pDevExt->SetGuestCapsTracker);
1027 pDevExt->fGuestCapsHost = UINT32_MAX; /* forces a report */
1028
1029 /*
1030 * Create the wait and session spinlocks as well as the ballooning mutex.
1031 */
1032 rc = RTSpinlockCreate(&pDevExt->EventSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestEvent");
1033 if (RT_SUCCESS(rc))
1034 {
1035 rc = RTSpinlockCreate(&pDevExt->SessionSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestSession");
1036 if (RT_SUCCESS(rc))
1037 {
1038 rc = RTSemFastMutexCreate(&pDevExt->MemBalloon.hMtx);
1039 if (RT_SUCCESS(rc))
1040 {
1041 pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT;
1042 return VINF_SUCCESS;
1043 }
1044
1045 LogRel(("VGDrvCommonInitDevExt: failed to create mutex, rc=%Rrc!\n", rc));
1046 RTSpinlockDestroy(pDevExt->SessionSpinlock);
1047 }
1048 else
1049 LogRel(("VGDrvCommonInitDevExt: failed to create spinlock, rc=%Rrc!\n", rc));
1050 RTSpinlockDestroy(pDevExt->EventSpinlock);
1051 }
1052 else
1053 LogRel(("VGDrvCommonInitDevExt: failed to create spinlock, rc=%Rrc!\n", rc));
1054
1055 pDevExt->uInitState = 0;
1056 return rc;
1057}
1058
1059
1060/**
1061 * Counter to VGDrvCommonInitDevExtFundament.
1062 *
1063 * @param pDevExt The device extension.
1064 */
1065void VGDrvCommonDeleteDevExtFundament(PVBOXGUESTDEVEXT pDevExt)
1066{
1067 int rc2;
1068 AssertMsgReturnVoid(pDevExt->uInitState == VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT, ("uInitState=%#x\n", pDevExt->uInitState));
1069 pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_DELETED;
1070
1071 rc2 = RTSemFastMutexDestroy(pDevExt->MemBalloon.hMtx); AssertRC(rc2);
1072 rc2 = RTSpinlockDestroy(pDevExt->EventSpinlock); AssertRC(rc2);
1073 rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2);
1074}
1075
1076
1077/**
1078 * Initializes the VBoxGuest device extension resource parts.
1079 *
1080 * The native code locates the VMMDev on the PCI bus and retrieve the MMIO and
1081 * I/O port ranges, this function will take care of mapping the MMIO memory (if
1082 * present). Upon successful return the native code should set up the interrupt
1083 * handler.
1084 *
1085 * @returns VBox status code.
1086 *
1087 * @param pDevExt The device extension. Allocated by the native code.
1088 * @param IOPortBase The base of the I/O port range.
1089 * @param pvMMIOBase The base of the MMIO memory mapping.
1090 * This is optional, pass NULL if not present.
1091 * @param cbMMIO The size of the MMIO memory mapping.
1092 * This is optional, pass 0 if not present.
1093 * @param enmOSType The guest OS type to report to the VMMDev.
1094 * @param fFixedEvents Events that will be enabled upon init and no client
1095 * will ever be allowed to mask.
1096 */
1097int VGDrvCommonInitDevExtResources(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase,
1098 void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fFixedEvents)
1099{
1100 int rc;
1101 AssertMsgReturn(pDevExt->uInitState == VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT, ("uInitState=%#x\n", pDevExt->uInitState),
1102 VERR_INVALID_STATE);
1103
1104 /*
1105 * If there is an MMIO region validate the version and size.
1106 */
1107 if (pvMMIOBase)
1108 {
1109 VMMDevMemory *pVMMDev = (VMMDevMemory *)pvMMIOBase;
1110 Assert(cbMMIO);
1111 if ( pVMMDev->u32Version == VMMDEV_MEMORY_VERSION
1112 && pVMMDev->u32Size >= 32
1113 && pVMMDev->u32Size <= cbMMIO)
1114 {
1115 pDevExt->pVMMDevMemory = pVMMDev;
1116 Log(("VGDrvCommonInitDevExtResources: VMMDevMemory: mapping=%p size=%#RX32 (%#RX32) version=%#RX32\n",
1117 pVMMDev, pVMMDev->u32Size, cbMMIO, pVMMDev->u32Version));
1118 }
1119 else /* try live without it. */
1120 LogRel(("VGDrvCommonInitDevExtResources: Bogus VMMDev memory; u32Version=%RX32 (expected %RX32) u32Size=%RX32 (expected <= %RX32)\n",
1121 pVMMDev->u32Version, VMMDEV_MEMORY_VERSION, pVMMDev->u32Size, cbMMIO));
1122 }
1123
1124 /*
1125 * Initialize the guest library and report the guest info back to VMMDev,
1126 * set the interrupt control filter mask, and fixate the guest mappings
1127 * made by the VMM.
1128 */
1129 pDevExt->IOPortBase = IOPortBase;
1130 rc = VbglR0InitPrimary(pDevExt->IOPortBase, (VMMDevMemory *)pDevExt->pVMMDevMemory, &pDevExt->fHostFeatures);
1131 if (RT_SUCCESS(rc))
1132 {
1133 VMMDevRequestHeader *pAckReq = NULL;
1134 rc = VbglR0GRAlloc(&pAckReq, sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents);
1135 if (RT_SUCCESS(rc))
1136 {
1137 pDevExt->PhysIrqAckEvents = VbglR0PhysHeapGetPhysAddr(pAckReq);
1138 Assert(pDevExt->PhysIrqAckEvents != 0);
1139 ASMCompilerBarrier(); /* linux + solaris already have IRQs hooked up at this point, so take care. */
1140 pDevExt->pIrqAckEvents = (VMMDevEvents *)pAckReq;
1141
1142 rc = vgdrvReportGuestInfo(enmOSType);
1143 if (RT_SUCCESS(rc))
1144 {
1145 /*
1146 * Set the fixed event and make sure the host doesn't have any lingering
1147 * the guest capabilities or mouse status bits set.
1148 */
1149#ifdef VBOX_WITH_HGCM
1150 fFixedEvents |= VMMDEV_EVENT_HGCM;
1151#endif
1152 pDevExt->fFixedEvents = fFixedEvents;
1153 rc = vgdrvResetEventFilterOnHost(pDevExt, fFixedEvents);
1154 if (RT_SUCCESS(rc))
1155 {
1156 rc = vgdrvResetCapabilitiesOnHost(pDevExt);
1157 if (RT_SUCCESS(rc))
1158 {
1159 rc = vgdrvResetMouseStatusOnHost(pDevExt);
1160 if (RT_SUCCESS(rc))
1161 {
1162 /*
1163 * Initialize stuff which may fail without requiring the driver init to fail.
1164 */
1165 vgdrvInitFixateGuestMappings(pDevExt);
1166 vgdrvHeartbeatInit(pDevExt);
1167
1168 /*
1169 * Done!
1170 */
1171 rc = vgdrvReportDriverStatus(true /* Driver is active */);
1172 if (RT_FAILURE(rc))
1173 LogRel(("VGDrvCommonInitDevExtResources: VBoxReportGuestDriverStatus failed, rc=%Rrc\n", rc));
1174
1175 pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_RESOURCES;
1176 LogFlowFunc(("VGDrvCommonInitDevExtResources: returns success\n"));
1177 return VINF_SUCCESS;
1178 }
1179 LogRel(("VGDrvCommonInitDevExtResources: failed to clear mouse status: rc=%Rrc\n", rc));
1180 }
1181 else
1182 LogRel(("VGDrvCommonInitDevExtResources: failed to clear guest capabilities: rc=%Rrc\n", rc));
1183 }
1184 else
1185 LogRel(("VGDrvCommonInitDevExtResources: failed to set fixed event filter: rc=%Rrc\n", rc));
1186 pDevExt->fFixedEvents = 0;
1187 }
1188 else
1189 LogRel(("VGDrvCommonInitDevExtResources: vgdrvReportGuestInfo failed: rc=%Rrc\n", rc));
1190 VbglR0GRFree((VMMDevRequestHeader *)pDevExt->pIrqAckEvents);
1191 }
1192 else
1193 LogRel(("VGDrvCommonInitDevExtResources: VbglR0GRAlloc failed: rc=%Rrc\n", rc));
1194
1195 VbglR0TerminatePrimary();
1196 }
1197 else
1198 LogRel(("VGDrvCommonInitDevExtResources: VbglR0InitPrimary failed: rc=%Rrc\n", rc));
1199 pDevExt->IOPortBase = UINT16_MAX;
1200 return rc;
1201}
1202
1203
1204/**
1205 * Deletes all the items in a wait chain.
1206 * @param pList The head of the chain.
1207 */
1208static void vgdrvDeleteWaitList(PRTLISTNODE pList)
1209{
1210 while (!RTListIsEmpty(pList))
1211 {
1212 int rc2;
1213 PVBOXGUESTWAIT pWait = RTListGetFirst(pList, VBOXGUESTWAIT, ListNode);
1214 RTListNodeRemove(&pWait->ListNode);
1215
1216 rc2 = RTSemEventMultiDestroy(pWait->Event); AssertRC(rc2);
1217 pWait->Event = NIL_RTSEMEVENTMULTI;
1218 pWait->pSession = NULL;
1219 RTMemFree(pWait);
1220 }
1221}
1222
1223
1224/**
1225 * Counter to VGDrvCommonInitDevExtResources.
1226 *
1227 * @param pDevExt The device extension.
1228 */
1229void VGDrvCommonDeleteDevExtResources(PVBOXGUESTDEVEXT pDevExt)
1230{
1231 Log(("VGDrvCommonDeleteDevExtResources:\n"));
1232 AssertMsgReturnVoid(pDevExt->uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES, ("uInitState=%#x\n", pDevExt->uInitState));
1233 pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT;
1234
1235 /*
1236 * Stop and destroy HB timer and disable host heartbeat checking.
1237 */
1238 if (pDevExt->pHeartbeatTimer)
1239 {
1240 RTTimerDestroy(pDevExt->pHeartbeatTimer);
1241 vgdrvHeartbeatHostConfigure(pDevExt, false);
1242 }
1243
1244 VbglR0GRFree(pDevExt->pReqGuestHeartbeat);
1245 pDevExt->pReqGuestHeartbeat = NULL;
1246
1247 /*
1248 * Clean up the bits that involves the host first.
1249 */
1250 vgdrvTermUnfixGuestMappings(pDevExt);
1251 if (!RTListIsEmpty(&pDevExt->SessionList))
1252 {
1253 LogRelFunc(("session list not empty!\n"));
1254 RTListInit(&pDevExt->SessionList);
1255 }
1256
1257 /*
1258 * Update the host flags (mouse status etc) not to reflect this session.
1259 */
1260 pDevExt->fFixedEvents = 0;
1261 vgdrvResetEventFilterOnHost(pDevExt, 0 /*fFixedEvents*/);
1262 vgdrvResetCapabilitiesOnHost(pDevExt);
1263 vgdrvResetMouseStatusOnHost(pDevExt);
1264
1265 vgdrvCloseMemBalloon(pDevExt, (PVBOXGUESTSESSION)NULL);
1266
1267 /*
1268 * No more IRQs.
1269 */
1270 pDevExt->pIrqAckEvents = NULL; /* Will be freed by VbglR0TerminatePrimary. */
1271 ASMAtomicWriteU32(&pDevExt->fHostFeatures, 0);
1272
1273 /*
1274 * Cleanup all the other resources.
1275 */
1276 vgdrvDeleteWaitList(&pDevExt->WaitList);
1277#ifdef VBOX_WITH_HGCM
1278 vgdrvDeleteWaitList(&pDevExt->HGCMWaitList);
1279#endif
1280#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1281 vgdrvDeleteWaitList(&pDevExt->WakeUpList);
1282#endif
1283 vgdrvDeleteWaitList(&pDevExt->WokenUpList);
1284 vgdrvDeleteWaitList(&pDevExt->FreeList);
1285
1286 VbglR0TerminatePrimary();
1287
1288
1289 pDevExt->pVMMDevMemory = NULL;
1290 pDevExt->IOPortBase = 0;
1291}
1292
1293
1294/**
1295 * Initializes the VBoxGuest device extension when the device driver is loaded.
1296 *
1297 * The native code locates the VMMDev on the PCI bus and retrieve the MMIO and
1298 * I/O port ranges, this function will take care of mapping the MMIO memory (if
1299 * present). Upon successful return the native code should set up the interrupt
1300 * handler.
1301 *
1302 * Instead of calling this method, the host specific code choose to perform a
1303 * more granular initialization using:
1304 * 1. VGDrvCommonInitLoggers
1305 * 2. VGDrvCommonInitDevExtFundament
1306 * 3. VGDrvCommonInitDevExtResources
1307 *
1308 * @returns VBox status code.
1309 *
1310 * @param pDevExt The device extension. Allocated by the native code.
1311 * @param IOPortBase The base of the I/O port range.
1312 * @param pvMMIOBase The base of the MMIO memory mapping.
1313 * This is optional, pass NULL if not present.
1314 * @param cbMMIO The size of the MMIO memory mapping.
1315 * This is optional, pass 0 if not present.
1316 * @param enmOSType The guest OS type to report to the VMMDev.
1317 * @param fFixedEvents Events that will be enabled upon init and no client
1318 * will ever be allowed to mask.
1319 */
1320int VGDrvCommonInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase,
1321 void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fFixedEvents)
1322{
1323 int rc;
1324 VGDrvCommonInitLoggers();
1325
1326 rc = VGDrvCommonInitDevExtFundament(pDevExt);
1327 if (RT_SUCCESS(rc))
1328 {
1329 rc = VGDrvCommonInitDevExtResources(pDevExt, IOPortBase, pvMMIOBase, cbMMIO, enmOSType, fFixedEvents);
1330 if (RT_SUCCESS(rc))
1331 return rc;
1332
1333 VGDrvCommonDeleteDevExtFundament(pDevExt);
1334 }
1335 VGDrvCommonDestroyLoggers();
1336 return rc; /* (failed) */
1337}
1338
1339
1340/**
1341 * Checks if the given option can be taken to not mean 'false'.
1342 *
1343 * @returns true or false accordingly.
1344 * @param pszValue The value to consider.
1345 */
1346bool VBDrvCommonIsOptionValueTrue(const char *pszValue)
1347{
1348 if (pszValue)
1349 {
1350 char ch;
1351 while ( (ch = *pszValue) != '\0'
1352 && RT_C_IS_SPACE(ch))
1353 pszValue++;
1354
1355 return ch != '\0'
1356 && ch != 'n' /* no */
1357 && ch != 'N' /* NO */
1358 && ch != 'd' /* disabled */
1359 && ch != 'f' /* false*/
1360 && ch != 'F' /* FALSE */
1361 && ch != 'D' /* DISABLED */
1362 && ( (ch != 'o' && ch != 'O') /* off, OFF, Off */
1363 || (pszValue[1] != 'f' && pszValue[1] != 'F') )
1364 && (ch != '0' || pszValue[1] != '\0') /* '0' */
1365 ;
1366 }
1367 return false;
1368}
1369
1370
1371/**
1372 * Processes a option.
1373 *
1374 * This will let the OS specific code have a go at it too.
1375 *
1376 * @param pDevExt The device extension.
1377 * @param pszName The option name, sans prefix.
1378 * @param pszValue The option value.
1379 */
1380void VGDrvCommonProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1381{
1382 Log(("VGDrvCommonProcessOption: pszName='%s' pszValue='%s'\n", pszName, pszValue));
1383
1384 if ( RTStrICmpAscii(pszName, "r3_log_to_host") == 0
1385 || RTStrICmpAscii(pszName, "LoggingEnabled") == 0 /*legacy*/ )
1386 pDevExt->fLoggingEnabled = VBDrvCommonIsOptionValueTrue(pszValue);
1387 else if ( RTStrNICmpAscii(pszName, RT_STR_TUPLE("log")) == 0
1388 || RTStrNICmpAscii(pszName, RT_STR_TUPLE("dbg_log")) == 0)
1389 {
1390 bool const fLogRel = *pszName == 'd' || *pszName == 'D';
1391 const char *pszSubName = &pszName[fLogRel ? 4 + 3 : 3];
1392 if ( !*pszSubName
1393 || RTStrICmpAscii(pszSubName, "_flags") == 0
1394 || RTStrICmpAscii(pszSubName, "_dest") == 0)
1395 {
1396 PRTLOGGER pLogger = fLogRel ? RTLogRelGetDefaultInstance() : RTLogDefaultInstance();
1397 if (pLogger)
1398 {
1399 if (!*pszSubName)
1400 RTLogGroupSettings(pLogger, pszValue);
1401 else if (RTStrICmpAscii(pszSubName, "_flags"))
1402 RTLogFlags(pLogger, pszValue);
1403 else
1404 RTLogDestinations(pLogger, pszValue);
1405 }
1406 }
1407 else if (!VGDrvNativeProcessOption(pDevExt, pszName, pszValue))
1408 LogRel(("VBoxGuest: Ignoring unknown option '%s' (value '%s')\n", pszName, pszValue));
1409 }
1410 else if (!VGDrvNativeProcessOption(pDevExt, pszName, pszValue))
1411 LogRel(("VBoxGuest: Ignoring unknown option '%s' (value '%s')\n", pszName, pszValue));
1412}
1413
1414
1415/**
1416 * Read driver configuration from the host.
1417 *
1418 * This involves connecting to the guest properties service, which means that
1419 * interrupts needs to work and that the calling thread must be able to block.
1420 *
1421 * @param pDevExt The device extension.
1422 */
1423void VGDrvCommonProcessOptionsFromHost(PVBOXGUESTDEVEXT pDevExt)
1424{
1425 /*
1426 * Create a kernel session without our selves, then connect to the HGCM service.
1427 */
1428 PVBOXGUESTSESSION pSession;
1429 int rc = VGDrvCommonCreateKernelSession(pDevExt, &pSession);
1430 if (RT_SUCCESS(rc))
1431 {
1432 union
1433 {
1434 VBGLIOCHGCMCONNECT Connect;
1435 VBGLIOCHGCMDISCONNECT Disconnect;
1436 GuestPropMsgEnumProperties EnumMsg;
1437 } uBuf;
1438
1439 RT_ZERO(uBuf.Connect);
1440 VBGLREQHDR_INIT(&uBuf.Connect.Hdr, HGCM_CONNECT);
1441 uBuf.Connect.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
1442 RTStrCopy(uBuf.Connect.u.In.Loc.u.host.achName, sizeof(uBuf.Connect.u.In.Loc.u.host.achName),
1443 "VBoxGuestPropSvc"); /** @todo Add a define to the header for the name. */
1444 rc = VGDrvCommonIoCtl(VBGL_IOCTL_HGCM_CONNECT, pDevExt, pSession, &uBuf.Connect.Hdr, sizeof(uBuf.Connect));
1445 if (RT_SUCCESS(rc))
1446 {
1447 static const char g_szzPattern[] = "/VirtualBox/GuestAdd/VBoxGuest/*\0";
1448 uint32_t const idClient = uBuf.Connect.u.Out.idClient;
1449 char *pszzStrings = NULL;
1450 uint32_t cbStrings;
1451
1452 /*
1453 * Enumerate all the relevant properties. We try with a 1KB buffer, but
1454 * will double it until we get what we want or go beyond 16KB.
1455 */
1456 for (cbStrings = _1K; cbStrings <= _16K; cbStrings *= 2)
1457 {
1458 pszzStrings = (char *)RTMemAllocZ(cbStrings);
1459 if (pszzStrings)
1460 {
1461 VBGL_HGCM_HDR_INIT(&uBuf.EnumMsg.hdr, idClient, GUEST_PROP_FN_ENUM_PROPS, 3);
1462
1463 uBuf.EnumMsg.patterns.type = VMMDevHGCMParmType_LinAddr;
1464 uBuf.EnumMsg.patterns.u.Pointer.size = sizeof(g_szzPattern);
1465 uBuf.EnumMsg.patterns.u.Pointer.u.linearAddr = (uintptr_t)g_szzPattern;
1466
1467 uBuf.EnumMsg.strings.type = VMMDevHGCMParmType_LinAddr;
1468 uBuf.EnumMsg.strings.u.Pointer.size = cbStrings;
1469 uBuf.EnumMsg.strings.u.Pointer.u.linearAddr = (uintptr_t)pszzStrings;
1470
1471 uBuf.EnumMsg.size.type = VMMDevHGCMParmType_32bit;
1472 uBuf.EnumMsg.size.u.value32 = 0;
1473
1474 rc = VGDrvCommonIoCtl(VBGL_IOCTL_HGCM_CALL(sizeof(uBuf.EnumMsg)), pDevExt, pSession,
1475 &uBuf.EnumMsg.hdr.Hdr, sizeof(uBuf.EnumMsg));
1476 if (RT_SUCCESS(rc))
1477 {
1478 if ( uBuf.EnumMsg.size.type == VMMDevHGCMParmType_32bit
1479 && uBuf.EnumMsg.size.u.value32 <= cbStrings
1480 && uBuf.EnumMsg.size.u.value32 > 0)
1481 cbStrings = uBuf.EnumMsg.size.u.value32;
1482 Log(("VGDrvCommonReadConfigurationFromHost: GUEST_PROP_FN_ENUM_PROPS -> %#x bytes (cbStrings=%#x)\n",
1483 uBuf.EnumMsg.size.u.value32, cbStrings));
1484 break;
1485 }
1486
1487 RTMemFree(pszzStrings);
1488 pszzStrings = NULL;
1489 }
1490 else
1491 {
1492 LogRel(("VGDrvCommonReadConfigurationFromHost: failed to allocate %#x bytes\n", cbStrings));
1493 break;
1494 }
1495 }
1496
1497 /*
1498 * Disconnect and destroy the session.
1499 */
1500 VBGLREQHDR_INIT(&uBuf.Disconnect.Hdr, HGCM_DISCONNECT);
1501 uBuf.Disconnect.u.In.idClient = idClient;
1502 VGDrvCommonIoCtl(VBGL_IOCTL_HGCM_DISCONNECT, pDevExt, pSession, &uBuf.Disconnect.Hdr, sizeof(uBuf.Disconnect));
1503
1504 VGDrvCommonCloseSession(pDevExt, pSession);
1505
1506 /*
1507 * Process the properties if we got any.
1508 *
1509 * The string buffer contains packed strings in groups of four - name, value,
1510 * timestamp (as a decimal string) and flags. It is terminated by four empty
1511 * strings. Layout:
1512 * Name\0Value\0Timestamp\0Flags\0
1513 */
1514 if (pszzStrings)
1515 {
1516 uint32_t off;
1517 for (off = 0; off < cbStrings; off++)
1518 {
1519 /*
1520 * Parse the four fields, checking that it's all plain ASCII w/o any control characters.
1521 */
1522 const char *apszFields[4] = { NULL, NULL, NULL, NULL };
1523 bool fValidFields = true;
1524 unsigned iField;
1525 for (iField = 0; iField < RT_ELEMENTS(apszFields); iField++)
1526 {
1527 apszFields[0] = &pszzStrings[off];
1528 while (off < cbStrings)
1529 {
1530 char ch = pszzStrings[off++];
1531 if ((unsigned)ch < 0x20U || (unsigned)ch > 0x7fU)
1532 {
1533 if (!ch)
1534 break;
1535 if (fValidFields)
1536 Log(("VGDrvCommonReadConfigurationFromHost: Invalid char %#x at %#x (field %u)\n",
1537 ch, off - 1, iField));
1538 fValidFields = false;
1539 }
1540 }
1541 }
1542 if ( off <= cbStrings
1543 && fValidFields
1544 && *apszFields[0] != '\0')
1545 {
1546 /*
1547 * Validate and convert the flags to integer, then process the option.
1548 */
1549 uint32_t fFlags = 0;
1550 rc = GuestPropValidateFlags(apszFields[3], &fFlags);
1551 if (RT_SUCCESS(rc))
1552 {
1553 if (fFlags & GUEST_PROP_F_RDONLYGUEST)
1554 {
1555 apszFields[0] += sizeof(g_szzPattern) - 2;
1556 VGDrvCommonProcessOption(pDevExt, apszFields[0], apszFields[1]);
1557 }
1558 else
1559 LogRel(("VBoxGuest: Ignoring '%s' as it does not have RDONLYGUEST set\n", apszFields[0]));
1560 }
1561 else
1562 LogRel(("VBoxGuest: Invalid flags '%s' for '%s': %Rrc\n", apszFields[2], apszFields[0], rc));
1563 }
1564 else if (off < cbStrings)
1565 {
1566 LogRel(("VBoxGuest: Malformed guest properties enum result!\n"));
1567 Log(("VBoxGuest: off=%#x cbStrings=%#x\n%.*Rhxd\n", off, cbStrings, cbStrings, pszzStrings));
1568 break;
1569 }
1570 else if (!fValidFields)
1571 LogRel(("VBoxGuest: Ignoring %.*Rhxs as it has invalid characters in one or more fields\n",
1572 (int)strlen(apszFields[0]), apszFields[0]));
1573 else
1574 break;
1575 }
1576
1577 RTMemFree(pszzStrings);
1578 }
1579 else
1580 LogRel(("VGDrvCommonReadConfigurationFromHost: failed to enumerate '%s': %Rrc\n", g_szzPattern, rc));
1581
1582 }
1583 else
1584 LogRel(("VGDrvCommonReadConfigurationFromHost: failed to connect: %Rrc\n", rc));
1585 }
1586 else
1587 LogRel(("VGDrvCommonReadConfigurationFromHost: failed to connect: %Rrc\n", rc));
1588}
1589
1590
1591/**
1592 * Destroys the VBoxGuest device extension.
1593 *
1594 * The native code should call this before the driver is unloaded,
1595 * but don't call this on shutdown.
1596 *
1597 * @param pDevExt The device extension.
1598 */
1599void VGDrvCommonDeleteDevExt(PVBOXGUESTDEVEXT pDevExt)
1600{
1601 Log(("VGDrvCommonDeleteDevExt:\n"));
1602 Log(("VBoxGuest: The additions driver is terminating.\n"));
1603 VGDrvCommonDeleteDevExtResources(pDevExt);
1604 VGDrvCommonDeleteDevExtFundament(pDevExt);
1605 VGDrvCommonDestroyLoggers();
1606}
1607
1608
1609/**
1610 * Creates a VBoxGuest user session.
1611 *
1612 * The native code calls this when a ring-3 client opens the device.
1613 * Use VGDrvCommonCreateKernelSession when a ring-0 client connects.
1614 *
1615 * @returns VBox status code.
1616 * @param pDevExt The device extension.
1617 * @param fRequestor VMMDEV_REQUESTOR_XXX.
1618 * @param ppSession Where to store the session on success.
1619 */
1620int VGDrvCommonCreateUserSession(PVBOXGUESTDEVEXT pDevExt, uint32_t fRequestor, PVBOXGUESTSESSION *ppSession)
1621{
1622 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
1623 if (RT_UNLIKELY(!pSession))
1624 {
1625 LogRel(("VGDrvCommonCreateUserSession: no memory!\n"));
1626 return VERR_NO_MEMORY;
1627 }
1628
1629 pSession->Process = RTProcSelf();
1630 pSession->R0Process = RTR0ProcHandleSelf();
1631 pSession->pDevExt = pDevExt;
1632 pSession->fRequestor = fRequestor;
1633 pSession->fUserSession = RT_BOOL(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE);
1634 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1635 RTListAppend(&pDevExt->SessionList, &pSession->ListNode);
1636 pDevExt->cSessions++;
1637 RTSpinlockRelease(pDevExt->SessionSpinlock);
1638
1639 *ppSession = pSession;
1640 LogFlow(("VGDrvCommonCreateUserSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
1641 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
1642 return VINF_SUCCESS;
1643}
1644
1645
1646/**
1647 * Creates a VBoxGuest kernel session.
1648 *
1649 * The native code calls this when a ring-0 client connects to the device.
1650 * Use VGDrvCommonCreateUserSession when a ring-3 client opens the device.
1651 *
1652 * @returns VBox status code.
1653 * @param pDevExt The device extension.
1654 * @param ppSession Where to store the session on success.
1655 */
1656int VGDrvCommonCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession)
1657{
1658 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
1659 if (RT_UNLIKELY(!pSession))
1660 {
1661 LogRel(("VGDrvCommonCreateKernelSession: no memory!\n"));
1662 return VERR_NO_MEMORY;
1663 }
1664
1665 pSession->Process = NIL_RTPROCESS;
1666 pSession->R0Process = NIL_RTR0PROCESS;
1667 pSession->pDevExt = pDevExt;
1668 pSession->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV_OTHER
1669 | VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
1670 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1671 RTListAppend(&pDevExt->SessionList, &pSession->ListNode);
1672 pDevExt->cSessions++;
1673 RTSpinlockRelease(pDevExt->SessionSpinlock);
1674
1675 *ppSession = pSession;
1676 LogFlow(("VGDrvCommonCreateKernelSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
1677 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
1678 return VINF_SUCCESS;
1679}
1680
1681
1682/**
1683 * Closes a VBoxGuest session.
1684 *
1685 * @param pDevExt The device extension.
1686 * @param pSession The session to close (and free).
1687 */
1688void VGDrvCommonCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
1689{
1690#ifdef VBOX_WITH_HGCM
1691 unsigned i;
1692#endif
1693 LogFlow(("VGDrvCommonCloseSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
1694 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
1695
1696 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1697 RTListNodeRemove(&pSession->ListNode);
1698 pDevExt->cSessions--;
1699 RTSpinlockRelease(pDevExt->SessionSpinlock);
1700 vgdrvAcquireSessionCapabilities(pDevExt, pSession, 0, UINT32_MAX, VBGL_IOC_AGC_FLAGS_DEFAULT, true /*fSessionTermination*/);
1701 vgdrvSetSessionCapabilities(pDevExt, pSession, 0 /*fOrMask*/, UINT32_MAX /*fNotMask*/,
1702 NULL /*pfSessionCaps*/, NULL /*pfGlobalCaps*/, true /*fSessionTermination*/);
1703 vgdrvSetSessionEventFilter(pDevExt, pSession, 0 /*fOrMask*/, UINT32_MAX /*fNotMask*/, true /*fSessionTermination*/);
1704 vgdrvSetSessionMouseStatus(pDevExt, pSession, 0 /*fOrMask*/, UINT32_MAX /*fNotMask*/, true /*fSessionTermination*/);
1705
1706 vgdrvIoCtl_CancelAllWaitEvents(pDevExt, pSession);
1707
1708#ifdef VBOX_WITH_HGCM
1709 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1710 if (pSession->aHGCMClientIds[i])
1711 {
1712 uint32_t idClient = pSession->aHGCMClientIds[i];
1713 pSession->aHGCMClientIds[i] = 0;
1714 Log(("VGDrvCommonCloseSession: disconnecting client id %#RX32\n", idClient));
1715 VbglR0HGCMInternalDisconnect(idClient, VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV,
1716 vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1717 }
1718#endif
1719
1720 pSession->pDevExt = NULL;
1721 pSession->Process = NIL_RTPROCESS;
1722 pSession->R0Process = NIL_RTR0PROCESS;
1723 vgdrvCloseMemBalloon(pDevExt, pSession);
1724 RTMemFree(pSession);
1725}
1726
1727
1728/**
1729 * Allocates a wait-for-event entry.
1730 *
1731 * @returns The wait-for-event entry.
1732 * @param pDevExt The device extension.
1733 * @param pSession The session that's allocating this. Can be NULL.
1734 */
1735static PVBOXGUESTWAIT vgdrvWaitAlloc(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
1736{
1737 /*
1738 * Allocate it one way or the other.
1739 */
1740 PVBOXGUESTWAIT pWait = RTListGetFirst(&pDevExt->FreeList, VBOXGUESTWAIT, ListNode);
1741 if (pWait)
1742 {
1743 RTSpinlockAcquire(pDevExt->EventSpinlock);
1744
1745 pWait = RTListGetFirst(&pDevExt->FreeList, VBOXGUESTWAIT, ListNode);
1746 if (pWait)
1747 RTListNodeRemove(&pWait->ListNode);
1748
1749 RTSpinlockRelease(pDevExt->EventSpinlock);
1750 }
1751 if (!pWait)
1752 {
1753 int rc;
1754
1755 pWait = (PVBOXGUESTWAIT)RTMemAlloc(sizeof(*pWait));
1756 if (!pWait)
1757 {
1758 LogRelMax(32, ("vgdrvWaitAlloc: out-of-memory!\n"));
1759 return NULL;
1760 }
1761
1762 rc = RTSemEventMultiCreate(&pWait->Event);
1763 if (RT_FAILURE(rc))
1764 {
1765 LogRelMax(32, ("vgdrvWaitAlloc: RTSemEventMultiCreate failed with rc=%Rrc!\n", rc));
1766 RTMemFree(pWait);
1767 return NULL;
1768 }
1769
1770 pWait->ListNode.pNext = NULL;
1771 pWait->ListNode.pPrev = NULL;
1772 }
1773
1774 /*
1775 * Zero members just as an precaution.
1776 */
1777 pWait->fReqEvents = 0;
1778 pWait->fResEvents = 0;
1779#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1780 pWait->fPendingWakeUp = false;
1781 pWait->fFreeMe = false;
1782#endif
1783 pWait->pSession = pSession;
1784#ifdef VBOX_WITH_HGCM
1785 pWait->pHGCMReq = NULL;
1786#endif
1787 RTSemEventMultiReset(pWait->Event);
1788 return pWait;
1789}
1790
1791
1792/**
1793 * Frees the wait-for-event entry.
1794 *
1795 * The caller must own the wait spinlock !
1796 * The entry must be in a list!
1797 *
1798 * @param pDevExt The device extension.
1799 * @param pWait The wait-for-event entry to free.
1800 */
1801static void vgdrvWaitFreeLocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait)
1802{
1803 pWait->fReqEvents = 0;
1804 pWait->fResEvents = 0;
1805#ifdef VBOX_WITH_HGCM
1806 pWait->pHGCMReq = NULL;
1807#endif
1808#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1809 Assert(!pWait->fFreeMe);
1810 if (pWait->fPendingWakeUp)
1811 pWait->fFreeMe = true;
1812 else
1813#endif
1814 {
1815 RTListNodeRemove(&pWait->ListNode);
1816 RTListAppend(&pDevExt->FreeList, &pWait->ListNode);
1817 }
1818}
1819
1820
1821/**
1822 * Frees the wait-for-event entry.
1823 *
1824 * @param pDevExt The device extension.
1825 * @param pWait The wait-for-event entry to free.
1826 */
1827static void vgdrvWaitFreeUnlocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait)
1828{
1829 RTSpinlockAcquire(pDevExt->EventSpinlock);
1830 vgdrvWaitFreeLocked(pDevExt, pWait);
1831 RTSpinlockRelease(pDevExt->EventSpinlock);
1832}
1833
1834
1835#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1836/**
1837 * Processes the wake-up list.
1838 *
1839 * All entries in the wake-up list gets signalled and moved to the woken-up
1840 * list.
1841 * At least on Windows this function can be invoked concurrently from
1842 * different VCPUs. So, be thread-safe.
1843 *
1844 * @param pDevExt The device extension.
1845 */
1846void VGDrvCommonWaitDoWakeUps(PVBOXGUESTDEVEXT pDevExt)
1847{
1848 if (!RTListIsEmpty(&pDevExt->WakeUpList))
1849 {
1850 RTSpinlockAcquire(pDevExt->EventSpinlock);
1851 for (;;)
1852 {
1853 int rc;
1854 PVBOXGUESTWAIT pWait = RTListGetFirst(&pDevExt->WakeUpList, VBOXGUESTWAIT, ListNode);
1855 if (!pWait)
1856 break;
1857 /* Prevent other threads from accessing pWait when spinlock is released. */
1858 RTListNodeRemove(&pWait->ListNode);
1859
1860 pWait->fPendingWakeUp = true;
1861 RTSpinlockRelease(pDevExt->EventSpinlock);
1862
1863 rc = RTSemEventMultiSignal(pWait->Event);
1864 AssertRC(rc);
1865
1866 RTSpinlockAcquire(pDevExt->EventSpinlock);
1867 Assert(pWait->ListNode.pNext == NULL && pWait->ListNode.pPrev == NULL);
1868 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
1869 pWait->fPendingWakeUp = false;
1870 if (RT_LIKELY(!pWait->fFreeMe))
1871 { /* likely */ }
1872 else
1873 {
1874 pWait->fFreeMe = false;
1875 vgdrvWaitFreeLocked(pDevExt, pWait);
1876 }
1877 }
1878 RTSpinlockRelease(pDevExt->EventSpinlock);
1879 }
1880}
1881#endif /* VBOXGUEST_USE_DEFERRED_WAKE_UP */
1882
1883
1884/**
1885 * Implements the fast (no input or output) type of IOCtls.
1886 *
1887 * This is currently just a placeholder stub inherited from the support driver code.
1888 *
1889 * @returns VBox status code.
1890 * @param iFunction The IOCtl function number.
1891 * @param pDevExt The device extension.
1892 * @param pSession The session.
1893 */
1894int VGDrvCommonIoCtlFast(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
1895{
1896 LogFlow(("VGDrvCommonIoCtlFast: iFunction=%#x pDevExt=%p pSession=%p\n", iFunction, pDevExt, pSession));
1897
1898 NOREF(iFunction);
1899 NOREF(pDevExt);
1900 NOREF(pSession);
1901 return VERR_NOT_SUPPORTED;
1902}
1903
1904
1905/**
1906 * Gets the driver I/O control interface version, maybe adjusting it for
1907 * backwards compatibility.
1908 *
1909 * The adjusting is currently not implemented as we only have one major I/O
1910 * control interface version out there to support. This is something we will
1911 * implement as needed.
1912 *
1913 * returns IPRT status code.
1914 * @param pDevExt The device extension.
1915 * @param pSession The session.
1916 * @param pReq The request info.
1917 */
1918static int vgdrvIoCtl_DriverVersionInfo(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCDRIVERVERSIONINFO pReq)
1919{
1920 int rc;
1921 LogFlow(("VBGL_IOCTL_DRIVER_VERSION_INFO: uReqVersion=%#x uMinVersion=%#x uReserved1=%#x uReserved2=%#x\n",
1922 pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, pReq->u.In.uReserved1, pReq->u.In.uReserved2));
1923 RT_NOREF2(pDevExt, pSession);
1924
1925 /*
1926 * Input validation.
1927 */
1928 if ( pReq->u.In.uMinVersion <= pReq->u.In.uReqVersion
1929 && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(pReq->u.In.uReqVersion))
1930 {
1931 /*
1932 * Match the version.
1933 * The current logic is very simple, match the major interface version.
1934 */
1935 if ( pReq->u.In.uMinVersion <= VBGL_IOC_VERSION
1936 && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(VBGL_IOC_VERSION))
1937 rc = VINF_SUCCESS;
1938 else
1939 {
1940 LogRel(("VBGL_IOCTL_DRIVER_VERSION_INFO: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
1941 pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, VBGL_IOC_VERSION));
1942 rc = VERR_VERSION_MISMATCH;
1943 }
1944 }
1945 else
1946 {
1947 LogRel(("VBGL_IOCTL_DRIVER_VERSION_INFO: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n",
1948 pReq->u.In.uMinVersion, pReq->u.In.uReqVersion));
1949 rc = VERR_INVALID_PARAMETER;
1950 }
1951
1952 pReq->u.Out.uSessionVersion = RT_SUCCESS(rc) ? VBGL_IOC_VERSION : UINT32_MAX;
1953 pReq->u.Out.uDriverVersion = VBGL_IOC_VERSION;
1954 pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
1955 pReq->u.Out.uReserved1 = 0;
1956 pReq->u.Out.uReserved2 = 0;
1957 return rc;
1958}
1959
1960
1961/**
1962 * Similar to vgdrvIoCtl_DriverVersionInfo, except its for IDC.
1963 *
1964 * returns IPRT status code.
1965 * @param pDevExt The device extension.
1966 * @param pSession The session.
1967 * @param pReq The request info.
1968 */
1969static int vgdrvIoCtl_IdcConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCIDCCONNECT pReq)
1970{
1971 int rc;
1972 LogFlow(("VBGL_IOCTL_IDC_CONNECT: u32MagicCookie=%#x uReqVersion=%#x uMinVersion=%#x uReserved=%#x\n",
1973 pReq->u.In.u32MagicCookie, pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, pReq->u.In.uReserved));
1974 Assert(pSession != NULL);
1975 RT_NOREF(pDevExt);
1976
1977 /*
1978 * Input validation.
1979 */
1980 if (pReq->u.In.u32MagicCookie == VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE)
1981 {
1982 if ( pReq->u.In.uMinVersion <= pReq->u.In.uReqVersion
1983 && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(pReq->u.In.uReqVersion))
1984 {
1985 /*
1986 * Match the version.
1987 * The current logic is very simple, match the major interface version.
1988 */
1989 if ( pReq->u.In.uMinVersion <= VBGL_IOC_VERSION
1990 && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(VBGL_IOC_VERSION))
1991 {
1992 pReq->u.Out.pvSession = pSession;
1993 pReq->u.Out.uSessionVersion = VBGL_IOC_VERSION;
1994 pReq->u.Out.uDriverVersion = VBGL_IOC_VERSION;
1995 pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
1996 pReq->u.Out.uReserved1 = 0;
1997 pReq->u.Out.pvReserved2 = NULL;
1998 return VINF_SUCCESS;
1999
2000 }
2001 LogRel(("VBGL_IOCTL_IDC_CONNECT: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
2002 pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, VBGL_IOC_VERSION));
2003 rc = VERR_VERSION_MISMATCH;
2004 }
2005 else
2006 {
2007 LogRel(("VBGL_IOCTL_IDC_CONNECT: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n",
2008 pReq->u.In.uMinVersion, pReq->u.In.uReqVersion));
2009 rc = VERR_INVALID_PARAMETER;
2010 }
2011
2012 pReq->u.Out.pvSession = NULL;
2013 pReq->u.Out.uSessionVersion = UINT32_MAX;
2014 pReq->u.Out.uDriverVersion = VBGL_IOC_VERSION;
2015 pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
2016 pReq->u.Out.uReserved1 = 0;
2017 pReq->u.Out.pvReserved2 = NULL;
2018 }
2019 else
2020 {
2021 LogRel(("VBGL_IOCTL_IDC_CONNECT: u32MagicCookie=%#x expected %#x!\n",
2022 pReq->u.In.u32MagicCookie, VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE));
2023 rc = VERR_INVALID_PARAMETER;
2024 }
2025 return rc;
2026}
2027
2028
2029/**
2030 * Counterpart to vgdrvIoCtl_IdcConnect, destroys the session.
2031 *
2032 * returns IPRT status code.
2033 * @param pDevExt The device extension.
2034 * @param pSession The session.
2035 * @param pReq The request info.
2036 */
2037static int vgdrvIoCtl_IdcDisconnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCIDCDISCONNECT pReq)
2038{
2039 LogFlow(("VBGL_IOCTL_IDC_DISCONNECT: pvSession=%p vs pSession=%p\n", pReq->u.In.pvSession, pSession));
2040 RT_NOREF(pDevExt);
2041 Assert(pSession != NULL);
2042
2043 if (pReq->u.In.pvSession == pSession)
2044 {
2045 VGDrvCommonCloseSession(pDevExt, pSession);
2046 return VINF_SUCCESS;
2047 }
2048 LogRel(("VBGL_IOCTL_IDC_DISCONNECT: In.pvSession=%p is not equal to pSession=%p!\n", pReq->u.In.pvSession, pSession));
2049 return VERR_INVALID_PARAMETER;
2050}
2051
2052
2053/**
2054 * Return the VMM device I/O info.
2055 *
2056 * returns IPRT status code.
2057 * @param pDevExt The device extension.
2058 * @param pInfo The request info.
2059 * @note Ring-0 only, caller checked.
2060 */
2061static int vgdrvIoCtl_GetVMMDevIoInfo(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCGETVMMDEVIOINFO pInfo)
2062{
2063 LogFlow(("VBGL_IOCTL_GET_VMMDEV_IO_INFO\n"));
2064
2065 pInfo->u.Out.IoPort = pDevExt->IOPortBase;
2066 pInfo->u.Out.pvVmmDevMapping = pDevExt->pVMMDevMemory;
2067 pInfo->u.Out.auPadding[0] = 0;
2068#if HC_ARCH_BITS != 32
2069 pInfo->u.Out.auPadding[1] = 0;
2070 pInfo->u.Out.auPadding[2] = 0;
2071#endif
2072 return VINF_SUCCESS;
2073}
2074
2075
2076/**
2077 * Set the callback for the kernel mouse handler.
2078 *
2079 * returns IPRT status code.
2080 * @param pDevExt The device extension.
2081 * @param pNotify The new callback information.
2082 */
2083int vgdrvIoCtl_SetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
2084{
2085 LogFlow(("VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK: pfnNotify=%p pvUser=%p\n", pNotify->u.In.pfnNotify, pNotify->u.In.pvUser));
2086
2087#ifdef VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT
2088 VGDrvNativeSetMouseNotifyCallback(pDevExt, pNotify);
2089#else
2090 RTSpinlockAcquire(pDevExt->EventSpinlock);
2091 pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
2092 pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
2093 RTSpinlockRelease(pDevExt->EventSpinlock);
2094#endif
2095 return VINF_SUCCESS;
2096}
2097
2098
2099/**
2100 * Worker vgdrvIoCtl_WaitEvent.
2101 *
2102 * The caller enters the spinlock, we leave it.
2103 *
2104 * @returns VINF_SUCCESS if we've left the spinlock and can return immediately.
2105 */
2106DECLINLINE(int) vbdgCheckWaitEventCondition(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2107 PVBGLIOCWAITFOREVENTS pInfo, int iEvent, const uint32_t fReqEvents)
2108{
2109 uint32_t fMatches = pDevExt->f32PendingEvents & fReqEvents;
2110 if (fMatches & VBOXGUEST_ACQUIRE_STYLE_EVENTS)
2111 fMatches &= vgdrvGetAllowedEventMaskForSession(pDevExt, pSession);
2112 if (fMatches || pSession->fPendingCancelWaitEvents)
2113 {
2114 ASMAtomicAndU32(&pDevExt->f32PendingEvents, ~fMatches);
2115 RTSpinlockRelease(pDevExt->EventSpinlock);
2116
2117 pInfo->u.Out.fEvents = fMatches;
2118 if (fReqEvents & ~((uint32_t)1 << iEvent))
2119 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x\n", pInfo->u.Out.fEvents));
2120 else
2121 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x/%d\n", pInfo->u.Out.fEvents, iEvent));
2122 pSession->fPendingCancelWaitEvents = false;
2123 return VINF_SUCCESS;
2124 }
2125
2126 RTSpinlockRelease(pDevExt->EventSpinlock);
2127 return VERR_TIMEOUT;
2128}
2129
2130
2131static int vgdrvIoCtl_WaitForEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2132 PVBGLIOCWAITFOREVENTS pInfo, bool fInterruptible)
2133{
2134 uint32_t const cMsTimeout = pInfo->u.In.cMsTimeOut;
2135 const uint32_t fReqEvents = pInfo->u.In.fEvents;
2136 uint32_t fResEvents;
2137 int iEvent;
2138 PVBOXGUESTWAIT pWait;
2139 int rc;
2140
2141 pInfo->u.Out.fEvents = 0; /* Note! This overwrites pInfo->u.In.* fields! */
2142
2143 /*
2144 * Copy and verify the input mask.
2145 */
2146 iEvent = ASMBitFirstSetU32(fReqEvents) - 1;
2147 if (RT_UNLIKELY(iEvent < 0))
2148 {
2149 LogRel(("VBOXGUEST_IOCTL_WAITEVENT: Invalid input mask %#x!!\n", fReqEvents));
2150 return VERR_INVALID_PARAMETER;
2151 }
2152
2153 /*
2154 * Check the condition up front, before doing the wait-for-event allocations.
2155 */
2156 RTSpinlockAcquire(pDevExt->EventSpinlock);
2157 rc = vbdgCheckWaitEventCondition(pDevExt, pSession, pInfo, iEvent, fReqEvents);
2158 if (rc == VINF_SUCCESS)
2159 return rc;
2160
2161 if (!cMsTimeout)
2162 {
2163 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns VERR_TIMEOUT\n"));
2164 return VERR_TIMEOUT;
2165 }
2166
2167 pWait = vgdrvWaitAlloc(pDevExt, pSession);
2168 if (!pWait)
2169 return VERR_NO_MEMORY;
2170 pWait->fReqEvents = fReqEvents;
2171
2172 /*
2173 * We've got the wait entry now, re-enter the spinlock and check for the condition.
2174 * If the wait condition is met, return.
2175 * Otherwise enter into the list and go to sleep waiting for the ISR to signal us.
2176 */
2177 RTSpinlockAcquire(pDevExt->EventSpinlock);
2178 RTListAppend(&pDevExt->WaitList, &pWait->ListNode);
2179 rc = vbdgCheckWaitEventCondition(pDevExt, pSession, pInfo, iEvent, fReqEvents);
2180 if (rc == VINF_SUCCESS)
2181 {
2182 vgdrvWaitFreeUnlocked(pDevExt, pWait);
2183 return rc;
2184 }
2185
2186 if (fInterruptible)
2187 rc = RTSemEventMultiWaitNoResume(pWait->Event, cMsTimeout == UINT32_MAX ? RT_INDEFINITE_WAIT : cMsTimeout);
2188 else
2189 rc = RTSemEventMultiWait(pWait->Event, cMsTimeout == UINT32_MAX ? RT_INDEFINITE_WAIT : cMsTimeout);
2190
2191 /*
2192 * There is one special case here and that's when the semaphore is
2193 * destroyed upon device driver unload. This shouldn't happen of course,
2194 * but in case it does, just get out of here ASAP.
2195 */
2196 if (rc == VERR_SEM_DESTROYED)
2197 return rc;
2198
2199 /*
2200 * Unlink the wait item and dispose of it.
2201 */
2202 RTSpinlockAcquire(pDevExt->EventSpinlock);
2203 fResEvents = pWait->fResEvents;
2204 vgdrvWaitFreeLocked(pDevExt, pWait);
2205 RTSpinlockRelease(pDevExt->EventSpinlock);
2206
2207 /*
2208 * Now deal with the return code.
2209 */
2210 if ( fResEvents
2211 && fResEvents != UINT32_MAX)
2212 {
2213 pInfo->u.Out.fEvents = fResEvents;
2214 if (fReqEvents & ~((uint32_t)1 << iEvent))
2215 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x\n", pInfo->u.Out.fEvents));
2216 else
2217 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x/%d\n", pInfo->u.Out.fEvents, iEvent));
2218 rc = VINF_SUCCESS;
2219 }
2220 else if ( fResEvents == UINT32_MAX
2221 || rc == VERR_INTERRUPTED)
2222 {
2223 rc = VERR_INTERRUPTED;
2224 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns VERR_INTERRUPTED\n"));
2225 }
2226 else if (rc == VERR_TIMEOUT)
2227 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns VERR_TIMEOUT (2)\n"));
2228 else
2229 {
2230 if (RT_SUCCESS(rc))
2231 {
2232 LogRelMax(32, ("VBOXGUEST_IOCTL_WAITEVENT: returns %Rrc but no events!\n", rc));
2233 rc = VERR_INTERNAL_ERROR;
2234 }
2235 LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %Rrc\n", rc));
2236 }
2237
2238 return rc;
2239}
2240
2241
2242/** @todo the semantics of this IoCtl have been tightened, so that no calls to
2243 * VBOXGUEST_IOCTL_WAITEVENT are allowed in a session after it has been
2244 * called. Change the code to make calls to VBOXGUEST_IOCTL_WAITEVENT made
2245 * after that to return VERR_INTERRUPTED or something appropriate. */
2246static int vgdrvIoCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
2247{
2248 PVBOXGUESTWAIT pWait;
2249 PVBOXGUESTWAIT pSafe;
2250 int rc = 0;
2251 /* Was as least one WAITEVENT in process for this session? If not we
2252 * set a flag that the next call should be interrupted immediately. This
2253 * is needed so that a user thread can reliably interrupt another one in a
2254 * WAITEVENT loop. */
2255 bool fCancelledOne = false;
2256
2257 LogFlow(("VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS\n"));
2258
2259 /*
2260 * Walk the event list and wake up anyone with a matching session.
2261 */
2262 RTSpinlockAcquire(pDevExt->EventSpinlock);
2263 RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode)
2264 {
2265 if (pWait->pSession == pSession)
2266 {
2267 fCancelledOne = true;
2268 pWait->fResEvents = UINT32_MAX;
2269 RTListNodeRemove(&pWait->ListNode);
2270#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
2271 RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode);
2272#else
2273 rc |= RTSemEventMultiSignal(pWait->Event);
2274 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
2275#endif
2276 }
2277 }
2278 if (!fCancelledOne)
2279 pSession->fPendingCancelWaitEvents = true;
2280 RTSpinlockRelease(pDevExt->EventSpinlock);
2281 Assert(rc == 0);
2282 NOREF(rc);
2283
2284#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
2285 VGDrvCommonWaitDoWakeUps(pDevExt);
2286#endif
2287
2288 return VINF_SUCCESS;
2289}
2290
2291
2292/**
2293 * Checks if the VMM request is allowed in the context of the given session.
2294 *
2295 * @returns VINF_SUCCESS or VERR_PERMISSION_DENIED.
2296 * @param pDevExt The device extension.
2297 * @param pSession The calling session.
2298 * @param enmType The request type.
2299 * @param pReqHdr The request.
2300 */
2301static int vgdrvCheckIfVmmReqIsAllowed(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VMMDevRequestType enmType,
2302 VMMDevRequestHeader const *pReqHdr)
2303{
2304 /*
2305 * Categorize the request being made.
2306 */
2307 /** @todo This need quite some more work! */
2308 enum
2309 {
2310 kLevel_Invalid, kLevel_NoOne, kLevel_OnlyVBoxGuest, kLevel_OnlyKernel, kLevel_TrustedUsers, kLevel_AllUsers
2311 } enmRequired;
2312 RT_NOREF1(pDevExt);
2313
2314 switch (enmType)
2315 {
2316 /*
2317 * Deny access to anything we don't know or provide specialized I/O controls for.
2318 */
2319#ifdef VBOX_WITH_HGCM
2320 case VMMDevReq_HGCMConnect:
2321 case VMMDevReq_HGCMDisconnect:
2322# ifdef VBOX_WITH_64_BITS_GUESTS
2323 case VMMDevReq_HGCMCall32:
2324 case VMMDevReq_HGCMCall64:
2325# else
2326 case VMMDevReq_HGCMCall:
2327# endif /* VBOX_WITH_64_BITS_GUESTS */
2328 case VMMDevReq_HGCMCancel:
2329 case VMMDevReq_HGCMCancel2:
2330#endif /* VBOX_WITH_HGCM */
2331 case VMMDevReq_SetGuestCapabilities:
2332 default:
2333 enmRequired = kLevel_NoOne;
2334 break;
2335
2336 /*
2337 * There are a few things only this driver can do (and it doesn't use
2338 * the VMMRequst I/O control route anyway, but whatever).
2339 */
2340 case VMMDevReq_ReportGuestInfo:
2341 case VMMDevReq_ReportGuestInfo2:
2342 case VMMDevReq_GetHypervisorInfo:
2343 case VMMDevReq_SetHypervisorInfo:
2344 case VMMDevReq_RegisterPatchMemory:
2345 case VMMDevReq_DeregisterPatchMemory:
2346 case VMMDevReq_GetMemBalloonChangeRequest:
2347 enmRequired = kLevel_OnlyVBoxGuest;
2348 break;
2349
2350 /*
2351 * Trusted users apps only.
2352 */
2353 case VMMDevReq_QueryCredentials:
2354 case VMMDevReq_ReportCredentialsJudgement:
2355 case VMMDevReq_RegisterSharedModule:
2356 case VMMDevReq_UnregisterSharedModule:
2357 case VMMDevReq_WriteCoreDump:
2358 case VMMDevReq_GetCpuHotPlugRequest:
2359 case VMMDevReq_SetCpuHotPlugStatus:
2360 case VMMDevReq_CheckSharedModules:
2361 case VMMDevReq_GetPageSharingStatus:
2362 case VMMDevReq_DebugIsPageShared:
2363 case VMMDevReq_ReportGuestStats:
2364 case VMMDevReq_ReportGuestUserState:
2365 case VMMDevReq_GetStatisticsChangeRequest:
2366 case VMMDevReq_ChangeMemBalloon:
2367 enmRequired = kLevel_TrustedUsers;
2368 break;
2369
2370 /*
2371 * Anyone.
2372 */
2373 case VMMDevReq_GetMouseStatus:
2374 case VMMDevReq_SetMouseStatus:
2375 case VMMDevReq_SetPointerShape:
2376 case VMMDevReq_GetHostVersion:
2377 case VMMDevReq_Idle:
2378 case VMMDevReq_GetHostTime:
2379 case VMMDevReq_SetPowerStatus:
2380 case VMMDevReq_AcknowledgeEvents:
2381 case VMMDevReq_CtlGuestFilterMask:
2382 case VMMDevReq_ReportGuestStatus:
2383 case VMMDevReq_GetDisplayChangeRequest:
2384 case VMMDevReq_VideoModeSupported:
2385 case VMMDevReq_GetHeightReduction:
2386 case VMMDevReq_GetDisplayChangeRequest2:
2387 case VMMDevReq_VideoModeSupported2:
2388 case VMMDevReq_VideoAccelEnable:
2389 case VMMDevReq_VideoAccelFlush:
2390 case VMMDevReq_VideoSetVisibleRegion:
2391 case VMMDevReq_GetDisplayChangeRequestEx:
2392 case VMMDevReq_GetDisplayChangeRequestMulti:
2393 case VMMDevReq_GetSeamlessChangeRequest:
2394 case VMMDevReq_GetVRDPChangeRequest:
2395 case VMMDevReq_LogString:
2396 case VMMDevReq_GetSessionId:
2397 enmRequired = kLevel_AllUsers;
2398 break;
2399
2400 /*
2401 * Depends on the request parameters...
2402 */
2403 /** @todo this have to be changed into an I/O control and the facilities
2404 * tracked in the session so they can automatically be failed when the
2405 * session terminates without reporting the new status.
2406 *
2407 * The information presented by IGuest is not reliable without this! */
2408 case VMMDevReq_ReportGuestCapabilities:
2409 switch (((VMMDevReportGuestStatus const *)pReqHdr)->guestStatus.facility)
2410 {
2411 case VBoxGuestFacilityType_All:
2412 case VBoxGuestFacilityType_VBoxGuestDriver:
2413 enmRequired = kLevel_OnlyVBoxGuest;
2414 break;
2415 case VBoxGuestFacilityType_VBoxService:
2416 enmRequired = kLevel_TrustedUsers;
2417 break;
2418 case VBoxGuestFacilityType_VBoxTrayClient:
2419 case VBoxGuestFacilityType_Seamless:
2420 case VBoxGuestFacilityType_Graphics:
2421 default:
2422 enmRequired = kLevel_AllUsers;
2423 break;
2424 }
2425 break;
2426 }
2427
2428 /*
2429 * Check against the session.
2430 */
2431 switch (enmRequired)
2432 {
2433 default:
2434 case kLevel_NoOne:
2435 break;
2436 case kLevel_OnlyVBoxGuest:
2437 case kLevel_OnlyKernel:
2438 if (pSession->R0Process == NIL_RTR0PROCESS)
2439 return VINF_SUCCESS;
2440 break;
2441 case kLevel_TrustedUsers:
2442 if (pSession->fUserSession)
2443 break;
2444 case kLevel_AllUsers:
2445 return VINF_SUCCESS;
2446 }
2447
2448 return VERR_PERMISSION_DENIED;
2449}
2450
2451static int vgdrvIoCtl_VMMDevRequest(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2452 VMMDevRequestHeader *pReqHdr, size_t cbData)
2453{
2454 int rc;
2455 VMMDevRequestHeader *pReqCopy;
2456
2457 /*
2458 * Validate the header and request size.
2459 */
2460 const VMMDevRequestType enmType = pReqHdr->requestType;
2461 const uint32_t cbReq = pReqHdr->size;
2462 const uint32_t cbMinSize = (uint32_t)vmmdevGetRequestSize(enmType);
2463
2464 LogFlow(("VBOXGUEST_IOCTL_VMMREQUEST: type %d\n", pReqHdr->requestType));
2465
2466 if (cbReq < cbMinSize)
2467 {
2468 LogRel(("VBOXGUEST_IOCTL_VMMREQUEST: invalid hdr size %#x, expected >= %#x; type=%#x!!\n",
2469 cbReq, cbMinSize, enmType));
2470 return VERR_INVALID_PARAMETER;
2471 }
2472 if (cbReq > cbData)
2473 {
2474 LogRel(("VBOXGUEST_IOCTL_VMMREQUEST: invalid size %#x, expected >= %#x (hdr); type=%#x!!\n",
2475 cbData, cbReq, enmType));
2476 return VERR_INVALID_PARAMETER;
2477 }
2478 rc = VbglGR0Verify(pReqHdr, cbData);
2479 if (RT_FAILURE(rc))
2480 {
2481 Log(("VBOXGUEST_IOCTL_VMMREQUEST: invalid header: size %#x, expected >= %#x (hdr); type=%#x; rc=%Rrc!!\n",
2482 cbData, cbReq, enmType, rc));
2483 return rc;
2484 }
2485
2486 rc = vgdrvCheckIfVmmReqIsAllowed(pDevExt, pSession, enmType, pReqHdr);
2487 if (RT_FAILURE(rc))
2488 {
2489 Log(("VBOXGUEST_IOCTL_VMMREQUEST: Operation not allowed! type=%#x rc=%Rrc\n", enmType, rc));
2490 return rc;
2491 }
2492
2493 /*
2494 * Make a copy of the request in the physical memory heap so
2495 * the VBoxGuestLibrary can more easily deal with the request.
2496 * (This is really a waste of time since the OS or the OS specific
2497 * code has already buffered or locked the input/output buffer, but
2498 * it does makes things a bit simpler wrt to phys address.)
2499 */
2500 rc = VbglR0GRAlloc(&pReqCopy, cbReq, enmType);
2501 if (RT_FAILURE(rc))
2502 {
2503 Log(("VBOXGUEST_IOCTL_VMMREQUEST: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n",
2504 cbReq, cbReq, rc));
2505 return rc;
2506 }
2507 memcpy(pReqCopy, pReqHdr, cbReq);
2508 Assert(pReqCopy->reserved1 == cbReq);
2509 pReqCopy->reserved1 = 0; /* VGDrvCommonIoCtl or caller sets cbOut, so clear it. */
2510 pReqCopy->fRequestor = pSession->fRequestor;
2511
2512 if (enmType == VMMDevReq_GetMouseStatus) /* clear poll condition. */
2513 pSession->u32MousePosChangedSeq = ASMAtomicUoReadU32(&pDevExt->u32MousePosChangedSeq);
2514
2515 rc = VbglR0GRPerform(pReqCopy);
2516 if ( RT_SUCCESS(rc)
2517 && RT_SUCCESS(pReqCopy->rc))
2518 {
2519 Assert(rc != VINF_HGCM_ASYNC_EXECUTE);
2520 Assert(pReqCopy->rc != VINF_HGCM_ASYNC_EXECUTE);
2521
2522 memcpy(pReqHdr, pReqCopy, cbReq);
2523 pReqHdr->reserved1 = cbReq; /* preserve cbOut */
2524 }
2525 else if (RT_FAILURE(rc))
2526 Log(("VBOXGUEST_IOCTL_VMMREQUEST: VbglR0GRPerform - rc=%Rrc!\n", rc));
2527 else
2528 {
2529 Log(("VBOXGUEST_IOCTL_VMMREQUEST: request execution failed; VMMDev rc=%Rrc!\n", pReqCopy->rc));
2530 rc = pReqCopy->rc;
2531 }
2532
2533 VbglR0GRFree(pReqCopy);
2534 return rc;
2535}
2536
2537
2538#ifdef VBOX_WITH_HGCM
2539
2540AssertCompile(RT_INDEFINITE_WAIT == (uint32_t)RT_INDEFINITE_WAIT); /* assumed by code below */
2541
2542/** Worker for vgdrvHgcmAsyncWaitCallback*. */
2543static int vgdrvHgcmAsyncWaitCallbackWorker(VMMDevHGCMRequestHeader volatile *pHdr, PVBOXGUESTDEVEXT pDevExt,
2544 bool fInterruptible, uint32_t cMillies)
2545{
2546 int rc;
2547
2548 /*
2549 * Check to see if the condition was met by the time we got here.
2550 *
2551 * We create a simple poll loop here for dealing with out-of-memory
2552 * conditions since the caller isn't necessarily able to deal with
2553 * us returning too early.
2554 */
2555 PVBOXGUESTWAIT pWait;
2556 for (;;)
2557 {
2558 RTSpinlockAcquire(pDevExt->EventSpinlock);
2559 if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0)
2560 {
2561 RTSpinlockRelease(pDevExt->EventSpinlock);
2562 return VINF_SUCCESS;
2563 }
2564 RTSpinlockRelease(pDevExt->EventSpinlock);
2565
2566 pWait = vgdrvWaitAlloc(pDevExt, NULL);
2567 if (pWait)
2568 break;
2569 if (fInterruptible)
2570 return VERR_INTERRUPTED;
2571 RTThreadSleep(1);
2572 }
2573 pWait->fReqEvents = VMMDEV_EVENT_HGCM;
2574 pWait->pHGCMReq = pHdr;
2575
2576 /*
2577 * Re-enter the spinlock and re-check for the condition.
2578 * If the condition is met, return.
2579 * Otherwise link us into the HGCM wait list and go to sleep.
2580 */
2581 RTSpinlockAcquire(pDevExt->EventSpinlock);
2582 RTListAppend(&pDevExt->HGCMWaitList, &pWait->ListNode);
2583 if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0)
2584 {
2585 vgdrvWaitFreeLocked(pDevExt, pWait);
2586 RTSpinlockRelease(pDevExt->EventSpinlock);
2587 return VINF_SUCCESS;
2588 }
2589 RTSpinlockRelease(pDevExt->EventSpinlock);
2590
2591 if (fInterruptible)
2592 rc = RTSemEventMultiWaitNoResume(pWait->Event, cMillies);
2593 else
2594 rc = RTSemEventMultiWait(pWait->Event, cMillies);
2595 if (rc == VERR_SEM_DESTROYED)
2596 return rc;
2597
2598 /*
2599 * Unlink, free and return.
2600 */
2601 if ( RT_FAILURE(rc)
2602 && rc != VERR_TIMEOUT
2603 && ( !fInterruptible
2604 || rc != VERR_INTERRUPTED))
2605 LogRel(("vgdrvHgcmAsyncWaitCallback: wait failed! %Rrc\n", rc));
2606
2607 vgdrvWaitFreeUnlocked(pDevExt, pWait);
2608 return rc;
2609}
2610
2611
2612/**
2613 * This is a callback for dealing with async waits.
2614 *
2615 * It operates in a manner similar to vgdrvIoCtl_WaitEvent.
2616 */
2617static DECLCALLBACK(int) vgdrvHgcmAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdr, void *pvUser, uint32_t u32User)
2618{
2619 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
2620 LogFlow(("vgdrvHgcmAsyncWaitCallback: requestType=%d\n", pHdr->header.requestType));
2621 return vgdrvHgcmAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr, pDevExt,
2622 false /* fInterruptible */, u32User /* cMillies */);
2623}
2624
2625
2626/**
2627 * This is a callback for dealing with async waits with a timeout.
2628 *
2629 * It operates in a manner similar to vgdrvIoCtl_WaitEvent.
2630 */
2631static DECLCALLBACK(int) vgdrvHgcmAsyncWaitCallbackInterruptible(VMMDevHGCMRequestHeader *pHdr, void *pvUser, uint32_t u32User)
2632{
2633 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
2634 LogFlow(("vgdrvHgcmAsyncWaitCallbackInterruptible: requestType=%d\n", pHdr->header.requestType));
2635 return vgdrvHgcmAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr, pDevExt,
2636 true /* fInterruptible */, u32User /* cMillies */);
2637}
2638
2639
2640static int vgdrvIoCtl_HGCMConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCONNECT pInfo)
2641{
2642 int rc;
2643 HGCMCLIENTID idClient = 0;
2644
2645 /*
2646 * The VbglHGCMConnect call will invoke the callback if the HGCM
2647 * call is performed in an ASYNC fashion. The function is not able
2648 * to deal with cancelled requests.
2649 */
2650 Log(("VBOXGUEST_IOCTL_HGCM_CONNECT: %.128s\n",
2651 pInfo->u.In.Loc.type == VMMDevHGCMLoc_LocalHost || pInfo->u.In.Loc.type == VMMDevHGCMLoc_LocalHost_Existing
2652 ? pInfo->u.In.Loc.u.host.achName : "<not local host>"));
2653
2654 rc = VbglR0HGCMInternalConnect(&pInfo->u.In.Loc, pSession->fRequestor, &idClient,
2655 vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
2656 Log(("VBOXGUEST_IOCTL_HGCM_CONNECT: idClient=%RX32 (rc=%Rrc)\n", idClient, rc));
2657 if (RT_SUCCESS(rc))
2658 {
2659 /*
2660 * Append the client id to the client id table.
2661 * If the table has somehow become filled up, we'll disconnect the session.
2662 */
2663 unsigned i;
2664 RTSpinlockAcquire(pDevExt->SessionSpinlock);
2665 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
2666 if (!pSession->aHGCMClientIds[i])
2667 {
2668 pSession->aHGCMClientIds[i] = idClient;
2669 break;
2670 }
2671 RTSpinlockRelease(pDevExt->SessionSpinlock);
2672 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
2673 {
2674 LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CONNECT: too many HGCMConnect calls for one session!\n"));
2675 VbglR0HGCMInternalDisconnect(idClient, pSession->fRequestor, vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
2676
2677 pInfo->u.Out.idClient = 0;
2678 return VERR_TOO_MANY_OPEN_FILES;
2679 }
2680 }
2681 pInfo->u.Out.idClient = idClient;
2682 return rc;
2683}
2684
2685
2686static int vgdrvIoCtl_HGCMDisconnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMDISCONNECT pInfo)
2687{
2688 /*
2689 * Validate the client id and invalidate its entry while we're in the call.
2690 */
2691 int rc;
2692 const uint32_t idClient = pInfo->u.In.idClient;
2693 unsigned i;
2694 RTSpinlockAcquire(pDevExt->SessionSpinlock);
2695 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
2696 if (pSession->aHGCMClientIds[i] == idClient)
2697 {
2698 pSession->aHGCMClientIds[i] = UINT32_MAX;
2699 break;
2700 }
2701 RTSpinlockRelease(pDevExt->SessionSpinlock);
2702 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
2703 {
2704 LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_DISCONNECT: idClient=%RX32\n", idClient));
2705 return VERR_INVALID_HANDLE;
2706 }
2707
2708 /*
2709 * The VbglHGCMConnect call will invoke the callback if the HGCM
2710 * call is performed in an ASYNC fashion. The function is not able
2711 * to deal with cancelled requests.
2712 */
2713 Log(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: idClient=%RX32\n", idClient));
2714 rc = VbglR0HGCMInternalDisconnect(idClient, pSession->fRequestor, vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
2715 LogFlow(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: rc=%Rrc\n", rc));
2716
2717 /* Update the client id array according to the result. */
2718 RTSpinlockAcquire(pDevExt->SessionSpinlock);
2719 if (pSession->aHGCMClientIds[i] == UINT32_MAX)
2720 pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) ? 0 : idClient;
2721 RTSpinlockRelease(pDevExt->SessionSpinlock);
2722
2723 return rc;
2724}
2725
2726
2727static int vgdrvIoCtl_HGCMCallInner(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCALL pInfo,
2728 uint32_t cMillies, bool fInterruptible, bool f32bit, bool fUserData,
2729 size_t cbExtra, size_t cbData)
2730{
2731 const uint32_t u32ClientId = pInfo->u32ClientID;
2732 uint32_t fFlags;
2733 size_t cbActual;
2734 unsigned i;
2735 int rc;
2736
2737 /*
2738 * Some more validations.
2739 */
2740 if (RT_LIKELY(pInfo->cParms <= VMMDEV_MAX_HGCM_PARMS)) /* (Just make sure it doesn't overflow the next check.) */
2741 { /* likely */}
2742 else
2743 {
2744 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cParm=%RX32 is not sane\n", pInfo->cParms));
2745 return VERR_INVALID_PARAMETER;
2746 }
2747
2748 cbActual = cbExtra + sizeof(*pInfo);
2749#ifdef RT_ARCH_AMD64
2750 if (f32bit)
2751 cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter32);
2752 else
2753#endif
2754 cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter);
2755 if (RT_LIKELY(cbData >= cbActual))
2756 { /* likely */}
2757 else
2758 {
2759 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cbData=%#zx (%zu) required size is %#zx (%zu)\n",
2760 cbData, cbData, cbActual, cbActual));
2761 return VERR_INVALID_PARAMETER;
2762 }
2763 pInfo->Hdr.cbOut = (uint32_t)cbActual;
2764
2765 /*
2766 * Validate the client id.
2767 */
2768 RTSpinlockAcquire(pDevExt->SessionSpinlock);
2769 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
2770 if (pSession->aHGCMClientIds[i] == u32ClientId)
2771 break;
2772 RTSpinlockRelease(pDevExt->SessionSpinlock);
2773 if (RT_LIKELY(i < RT_ELEMENTS(pSession->aHGCMClientIds)))
2774 { /* likely */}
2775 else
2776 {
2777 LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CALL: Invalid handle. u32Client=%RX32\n", u32ClientId));
2778 return VERR_INVALID_HANDLE;
2779 }
2780
2781 /*
2782 * The VbglHGCMCall call will invoke the callback if the HGCM
2783 * call is performed in an ASYNC fashion. This function can
2784 * deal with cancelled requests, so we let user more requests
2785 * be interruptible (should add a flag for this later I guess).
2786 */
2787 LogFlow(("VBOXGUEST_IOCTL_HGCM_CALL: u32Client=%RX32\n", pInfo->u32ClientID));
2788 fFlags = !fUserData && pSession->R0Process == NIL_RTR0PROCESS ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
2789 uint32_t cbInfo = (uint32_t)(cbData - cbExtra);
2790#ifdef RT_ARCH_AMD64
2791 if (f32bit)
2792 {
2793 if (fInterruptible)
2794 rc = VbglR0HGCMInternalCall32(pInfo, cbInfo, fFlags, pSession->fRequestor,
2795 vgdrvHgcmAsyncWaitCallbackInterruptible, pDevExt, cMillies);
2796 else
2797 rc = VbglR0HGCMInternalCall32(pInfo, cbInfo, fFlags, pSession->fRequestor,
2798 vgdrvHgcmAsyncWaitCallback, pDevExt, cMillies);
2799 }
2800 else
2801#endif
2802 {
2803 if (fInterruptible)
2804 rc = VbglR0HGCMInternalCall(pInfo, cbInfo, fFlags, pSession->fRequestor,
2805 vgdrvHgcmAsyncWaitCallbackInterruptible, pDevExt, cMillies);
2806 else
2807 rc = VbglR0HGCMInternalCall(pInfo, cbInfo, fFlags, pSession->fRequestor,
2808 vgdrvHgcmAsyncWaitCallback, pDevExt, cMillies);
2809 }
2810 if (RT_SUCCESS(rc))
2811 {
2812 rc = pInfo->Hdr.rc;
2813 LogFlow(("VBOXGUEST_IOCTL_HGCM_CALL: result=%Rrc\n", rc));
2814 }
2815 else
2816 {
2817 if ( rc != VERR_INTERRUPTED
2818 && rc != VERR_TIMEOUT)
2819 LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CALL: %s Failed. rc=%Rrc (Hdr.rc=%Rrc).\n", f32bit ? "32" : "64", rc, pInfo->Hdr.rc));
2820 else
2821 Log(("VBOXGUEST_IOCTL_HGCM_CALL: %s Failed. rc=%Rrc (Hdr.rc=%Rrc).\n", f32bit ? "32" : "64", rc, pInfo->Hdr.rc));
2822 }
2823 return rc;
2824}
2825
2826
2827static int vgdrvIoCtl_HGCMCallWrapper(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCALL pInfo,
2828 bool f32bit, bool fUserData, size_t cbData)
2829{
2830 return vgdrvIoCtl_HGCMCallInner(pDevExt, pSession, pInfo, pInfo->cMsTimeout,
2831 pInfo->fInterruptible || pSession->R0Process != NIL_RTR0PROCESS,
2832 f32bit, fUserData, 0 /*cbExtra*/, cbData);
2833}
2834
2835
2836/**
2837 * Handles a fast HGCM call from another driver.
2838 *
2839 * The driver has provided a fully assembled HGCM call request and all we need
2840 * to do is send it to the host and do the wait processing.
2841 *
2842 * @returns VBox status code of the request submission part.
2843 * @param pDevExt The device extension.
2844 * @param pCallReq The call request.
2845 */
2846static int vgdrvIoCtl_HGCMFastCall(PVBOXGUESTDEVEXT pDevExt, VBGLIOCIDCHGCMFASTCALL volatile *pCallReq)
2847{
2848 VMMDevHGCMCall volatile *pHgcmCall = (VMMDevHGCMCall volatile *)(pCallReq + 1);
2849 int rc;
2850
2851 /*
2852 * Check out the physical address.
2853 */
2854 Assert((pCallReq->GCPhysReq & PAGE_OFFSET_MASK) == ((uintptr_t)pHgcmCall & PAGE_OFFSET_MASK));
2855
2856 AssertReturn(!pCallReq->fInterruptible, VERR_NOT_IMPLEMENTED);
2857
2858 /*
2859 * Submit the request.
2860 */
2861 Log(("vgdrvIoCtl_HGCMFastCall -> host\n"));
2862 ASMOutU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST, (uint32_t)pCallReq->GCPhysReq);
2863
2864 /* Make the compiler aware that the host has changed memory. */
2865 ASMCompilerBarrier();
2866
2867 pCallReq->Hdr.rc = rc = pHgcmCall->header.header.rc;
2868 Log(("vgdrvIoCtl_HGCMFastCall -> %Rrc (header rc=%Rrc)\n", rc, pHgcmCall->header.result));
2869
2870 /*
2871 * The host is likely to engage in asynchronous execution of HGCM, unless it fails.
2872 */
2873 if (rc == VINF_HGCM_ASYNC_EXECUTE)
2874 {
2875 rc = vgdrvHgcmAsyncWaitCallbackWorker(&pHgcmCall->header, pDevExt, false /* fInterruptible */, RT_INDEFINITE_WAIT);
2876 if (pHgcmCall->header.fu32Flags & VBOX_HGCM_REQ_DONE)
2877 {
2878 Assert(!(pHgcmCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED));
2879 rc = VINF_SUCCESS;
2880 }
2881 else
2882 {
2883 /*
2884 * Timeout and interrupt scenarios are messy and requires
2885 * cancelation, so implement later.
2886 */
2887 AssertReleaseMsgFailed(("rc=%Rrc\n", rc));
2888 }
2889 }
2890 else
2891 Assert((pHgcmCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) || RT_FAILURE_NP(rc));
2892
2893 Log(("vgdrvIoCtl_HGCMFastCall: rc=%Rrc result=%Rrc fu32Flags=%#x\n", rc, pHgcmCall->header.result, pHgcmCall->header.fu32Flags));
2894 return rc;
2895
2896}
2897
2898#endif /* VBOX_WITH_HGCM */
2899
2900/**
2901 * Handle VBGL_IOCTL_CHECK_BALLOON from R3.
2902 *
2903 * Ask the host for the size of the balloon and try to set it accordingly. If
2904 * this approach fails because it's not supported, return with fHandleInR3 set
2905 * and let the user land supply memory we can lock via the other ioctl.
2906 *
2907 * @returns VBox status code.
2908 *
2909 * @param pDevExt The device extension.
2910 * @param pSession The session.
2911 * @param pInfo The output buffer.
2912 */
2913static int vgdrvIoCtl_CheckMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCCHECKBALLOON pInfo)
2914{
2915 VMMDevGetMemBalloonChangeRequest *pReq;
2916 int rc;
2917
2918 LogFlow(("VBGL_IOCTL_CHECK_BALLOON:\n"));
2919 rc = RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx);
2920 AssertRCReturn(rc, rc);
2921
2922 /*
2923 * The first user trying to query/change the balloon becomes the
2924 * owner and owns it until the session is closed (vgdrvCloseMemBalloon).
2925 */
2926 if ( pDevExt->MemBalloon.pOwner != pSession
2927 && pDevExt->MemBalloon.pOwner == NULL)
2928 pDevExt->MemBalloon.pOwner = pSession;
2929
2930 if (pDevExt->MemBalloon.pOwner == pSession)
2931 {
2932 /*
2933 * This is a response to that event. Setting this bit means that
2934 * we request the value from the host and change the guest memory
2935 * balloon according to this value.
2936 */
2937 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest);
2938 if (RT_SUCCESS(rc))
2939 {
2940 pReq->header.fRequestor = pSession->fRequestor;
2941 pReq->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
2942 rc = VbglR0GRPerform(&pReq->header);
2943 if (RT_SUCCESS(rc))
2944 {
2945 Assert(pDevExt->MemBalloon.cMaxChunks == pReq->cPhysMemChunks || pDevExt->MemBalloon.cMaxChunks == 0);
2946 pDevExt->MemBalloon.cMaxChunks = pReq->cPhysMemChunks;
2947
2948 pInfo->u.Out.cBalloonChunks = pReq->cBalloonChunks;
2949 pInfo->u.Out.fHandleInR3 = false;
2950 pInfo->u.Out.afPadding[0] = false;
2951 pInfo->u.Out.afPadding[1] = false;
2952 pInfo->u.Out.afPadding[2] = false;
2953
2954 rc = vgdrvSetBalloonSizeKernel(pDevExt, pReq->cBalloonChunks, &pInfo->u.Out.fHandleInR3);
2955 /* Ignore various out of memory failures. */
2956 if ( rc == VERR_NO_MEMORY
2957 || rc == VERR_NO_PHYS_MEMORY
2958 || rc == VERR_NO_CONT_MEMORY)
2959 rc = VINF_SUCCESS;
2960 }
2961 else
2962 LogRel(("VBGL_IOCTL_CHECK_BALLOON: VbglR0GRPerform failed. rc=%Rrc\n", rc));
2963 VbglR0GRFree(&pReq->header);
2964 }
2965 }
2966 else
2967 rc = VERR_PERMISSION_DENIED;
2968
2969 RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx);
2970 LogFlow(("VBGL_IOCTL_CHECK_BALLOON returns %Rrc\n", rc));
2971 return rc;
2972}
2973
2974
2975/**
2976 * Handle a request for changing the memory balloon.
2977 *
2978 * @returns VBox status code.
2979 *
2980 * @param pDevExt The device extention.
2981 * @param pSession The session.
2982 * @param pInfo The change request structure (input).
2983 */
2984static int vgdrvIoCtl_ChangeMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCCHANGEBALLOON pInfo)
2985{
2986 int rc;
2987 LogFlow(("VBGL_IOCTL_CHANGE_BALLOON: fInflate=%RTbool u64ChunkAddr=%p\n", pInfo->u.In.fInflate, pInfo->u.In.pvChunk));
2988 if ( pInfo->u.In.abPadding[0]
2989 || pInfo->u.In.abPadding[1]
2990 || pInfo->u.In.abPadding[2]
2991 || pInfo->u.In.abPadding[3]
2992 || pInfo->u.In.abPadding[4]
2993 || pInfo->u.In.abPadding[5]
2994 || pInfo->u.In.abPadding[6]
2995#if ARCH_BITS == 32
2996 || pInfo->u.In.abPadding[7]
2997 || pInfo->u.In.abPadding[8]
2998 || pInfo->u.In.abPadding[9]
2999#endif
3000 )
3001 {
3002 Log(("VBGL_IOCTL_CHANGE_BALLOON: Padding isn't all zero: %.*Rhxs\n", sizeof(pInfo->u.In.abPadding), pInfo->u.In.abPadding));
3003 return VERR_INVALID_PARAMETER;
3004 }
3005
3006 rc = RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx);
3007 AssertRCReturn(rc, rc);
3008
3009 if (!pDevExt->MemBalloon.fUseKernelAPI)
3010 {
3011 /*
3012 * The first user trying to query/change the balloon becomes the
3013 * owner and owns it until the session is closed (vgdrvCloseMemBalloon).
3014 */
3015 if ( pDevExt->MemBalloon.pOwner != pSession
3016 && pDevExt->MemBalloon.pOwner == NULL)
3017 pDevExt->MemBalloon.pOwner = pSession;
3018
3019 if (pDevExt->MemBalloon.pOwner == pSession)
3020 rc = vgdrvSetBalloonSizeFromUser(pDevExt, pSession, pInfo->u.In.pvChunk, pInfo->u.In.fInflate != false);
3021 else
3022 rc = VERR_PERMISSION_DENIED;
3023 }
3024 else
3025 rc = VERR_PERMISSION_DENIED;
3026
3027 RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx);
3028 return rc;
3029}
3030
3031
3032/**
3033 * Handle a request for writing a core dump of the guest on the host.
3034 *
3035 * @returns VBox status code.
3036 *
3037 * @param pDevExt The device extension.
3038 * @param pSession The session.
3039 * @param pInfo The output buffer.
3040 */
3041static int vgdrvIoCtl_WriteCoreDump(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCWRITECOREDUMP pInfo)
3042{
3043 VMMDevReqWriteCoreDump *pReq = NULL;
3044 int rc;
3045 LogFlow(("VBOXGUEST_IOCTL_WRITE_CORE_DUMP\n"));
3046 RT_NOREF1(pDevExt);
3047
3048 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_WriteCoreDump);
3049 if (RT_SUCCESS(rc))
3050 {
3051 pReq->header.fRequestor = pSession->fRequestor;
3052 pReq->fFlags = pInfo->u.In.fFlags;
3053 rc = VbglR0GRPerform(&pReq->header);
3054 if (RT_FAILURE(rc))
3055 Log(("VBOXGUEST_IOCTL_WRITE_CORE_DUMP: VbglR0GRPerform failed, rc=%Rrc!\n", rc));
3056
3057 VbglR0GRFree(&pReq->header);
3058 }
3059 else
3060 Log(("VBOXGUEST_IOCTL_WRITE_CORE_DUMP: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n",
3061 sizeof(*pReq), sizeof(*pReq), rc));
3062 return rc;
3063}
3064
3065
3066/**
3067 * Guest backdoor logging.
3068 *
3069 * @returns VBox status code.
3070 *
3071 * @param pDevExt The device extension.
3072 * @param pch The log message (need not be NULL terminated).
3073 * @param cbData Size of the buffer.
3074 * @param fUserSession Copy of VBOXGUESTSESSION::fUserSession for the
3075 * call. True normal user, false root user.
3076 */
3077static int vgdrvIoCtl_Log(PVBOXGUESTDEVEXT pDevExt, const char *pch, size_t cbData, bool fUserSession)
3078{
3079 if (pDevExt->fLoggingEnabled)
3080 RTLogBackdoorPrintf("%.*s", cbData, pch);
3081 else if (!fUserSession)
3082 LogRel(("%.*s", cbData, pch));
3083 else
3084 Log(("%.*s", cbData, pch));
3085 return VINF_SUCCESS;
3086}
3087
3088
3089/** @name Guest Capabilities, Mouse Status and Event Filter
3090 * @{
3091 */
3092
3093/**
3094 * Clears a bit usage tracker (init time).
3095 *
3096 * @param pTracker The tracker to clear.
3097 */
3098static void vgdrvBitUsageTrackerClear(PVBOXGUESTBITUSAGETRACER pTracker)
3099{
3100 uint32_t iBit;
3101 AssertCompile(sizeof(pTracker->acPerBitUsage) == 32 * sizeof(uint32_t));
3102
3103 for (iBit = 0; iBit < 32; iBit++)
3104 pTracker->acPerBitUsage[iBit] = 0;
3105 pTracker->fMask = 0;
3106}
3107
3108
3109#ifdef VBOX_STRICT
3110/**
3111 * Checks that pTracker->fMask is correct and that the usage values are within
3112 * the valid range.
3113 *
3114 * @param pTracker The tracker.
3115 * @param cMax Max valid usage value.
3116 * @param pszWhat Identifies the tracker in assertions.
3117 */
3118static void vgdrvBitUsageTrackerCheckMask(PCVBOXGUESTBITUSAGETRACER pTracker, uint32_t cMax, const char *pszWhat)
3119{
3120 uint32_t fMask = 0;
3121 uint32_t iBit;
3122 AssertCompile(sizeof(pTracker->acPerBitUsage) == 32 * sizeof(uint32_t));
3123
3124 for (iBit = 0; iBit < 32; iBit++)
3125 if (pTracker->acPerBitUsage[iBit])
3126 {
3127 fMask |= RT_BIT_32(iBit);
3128 AssertMsg(pTracker->acPerBitUsage[iBit] <= cMax,
3129 ("%s: acPerBitUsage[%u]=%#x cMax=%#x\n", pszWhat, iBit, pTracker->acPerBitUsage[iBit], cMax));
3130 }
3131
3132 AssertMsg(fMask == pTracker->fMask, ("%s: %#x vs %#x\n", pszWhat, fMask, pTracker->fMask));
3133}
3134#endif
3135
3136
3137/**
3138 * Applies a change to the bit usage tracker.
3139 *
3140 *
3141 * @returns true if the mask changed, false if not.
3142 * @param pTracker The bit usage tracker.
3143 * @param fChanged The bits to change.
3144 * @param fPrevious The previous value of the bits.
3145 * @param cMax The max valid usage value for assertions.
3146 * @param pszWhat Identifies the tracker in assertions.
3147 */
3148static bool vgdrvBitUsageTrackerChange(PVBOXGUESTBITUSAGETRACER pTracker, uint32_t fChanged, uint32_t fPrevious,
3149 uint32_t cMax, const char *pszWhat)
3150{
3151 bool fGlobalChange = false;
3152 AssertCompile(sizeof(pTracker->acPerBitUsage) == 32 * sizeof(uint32_t));
3153
3154 while (fChanged)
3155 {
3156 uint32_t const iBit = ASMBitFirstSetU32(fChanged) - 1;
3157 uint32_t const fBitMask = RT_BIT_32(iBit);
3158 Assert(iBit < 32); Assert(fBitMask & fChanged);
3159
3160 if (fBitMask & fPrevious)
3161 {
3162 pTracker->acPerBitUsage[iBit] -= 1;
3163 AssertMsg(pTracker->acPerBitUsage[iBit] <= cMax,
3164 ("%s: acPerBitUsage[%u]=%#x cMax=%#x\n", pszWhat, iBit, pTracker->acPerBitUsage[iBit], cMax));
3165 if (pTracker->acPerBitUsage[iBit] == 0)
3166 {
3167 fGlobalChange = true;
3168 pTracker->fMask &= ~fBitMask;
3169 }
3170 }
3171 else
3172 {
3173 pTracker->acPerBitUsage[iBit] += 1;
3174 AssertMsg(pTracker->acPerBitUsage[iBit] > 0 && pTracker->acPerBitUsage[iBit] <= cMax,
3175 ("pTracker->acPerBitUsage[%u]=%#x cMax=%#x\n", pszWhat, iBit, pTracker->acPerBitUsage[iBit], cMax));
3176 if (pTracker->acPerBitUsage[iBit] == 1)
3177 {
3178 fGlobalChange = true;
3179 pTracker->fMask |= fBitMask;
3180 }
3181 }
3182
3183 fChanged &= ~fBitMask;
3184 }
3185
3186#ifdef VBOX_STRICT
3187 vgdrvBitUsageTrackerCheckMask(pTracker, cMax, pszWhat);
3188#endif
3189 NOREF(pszWhat); NOREF(cMax);
3190 return fGlobalChange;
3191}
3192
3193
3194/**
3195 * Init and termination worker for resetting the (host) event filter on the host
3196 *
3197 * @returns VBox status code.
3198 * @param pDevExt The device extension.
3199 * @param fFixedEvents Fixed events (init time).
3200 */
3201static int vgdrvResetEventFilterOnHost(PVBOXGUESTDEVEXT pDevExt, uint32_t fFixedEvents)
3202{
3203 VMMDevCtlGuestFilterMask *pReq;
3204 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
3205 if (RT_SUCCESS(rc))
3206 {
3207 pReq->u32NotMask = UINT32_MAX & ~fFixedEvents;
3208 pReq->u32OrMask = fFixedEvents;
3209 rc = VbglR0GRPerform(&pReq->header);
3210 if (RT_FAILURE(rc))
3211 LogRelFunc(("failed with rc=%Rrc\n", rc));
3212 VbglR0GRFree(&pReq->header);
3213 }
3214 RT_NOREF1(pDevExt);
3215 return rc;
3216}
3217
3218
3219/**
3220 * Changes the event filter mask for the given session.
3221 *
3222 * This is called in response to VBGL_IOCTL_CHANGE_FILTER_MASK as well as to do
3223 * session cleanup.
3224 *
3225 * @returns VBox status code.
3226 * @param pDevExt The device extension.
3227 * @param pSession The session.
3228 * @param fOrMask The events to add.
3229 * @param fNotMask The events to remove.
3230 * @param fSessionTermination Set if we're called by the session cleanup code.
3231 * This tweaks the error handling so we perform
3232 * proper session cleanup even if the host
3233 * misbehaves.
3234 *
3235 * @remarks Takes the session spinlock.
3236 */
3237static int vgdrvSetSessionEventFilter(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
3238 uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination)
3239{
3240 VMMDevCtlGuestFilterMask *pReq;
3241 uint32_t fChanged;
3242 uint32_t fPrevious;
3243 int rc;
3244
3245 /*
3246 * Preallocate a request buffer so we can do all in one go without leaving the spinlock.
3247 */
3248 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
3249 if (RT_SUCCESS(rc))
3250 { /* nothing */ }
3251 else if (!fSessionTermination)
3252 {
3253 LogRel(("vgdrvSetSessionFilterMask: VbglR0GRAlloc failure: %Rrc\n", rc));
3254 return rc;
3255 }
3256 else
3257 pReq = NULL; /* Ignore failure, we must do session cleanup. */
3258
3259
3260 RTSpinlockAcquire(pDevExt->SessionSpinlock);
3261
3262 /*
3263 * Apply the changes to the session mask.
3264 */
3265 fPrevious = pSession->fEventFilter;
3266 pSession->fEventFilter |= fOrMask;
3267 pSession->fEventFilter &= ~fNotMask;
3268
3269 /*
3270 * If anything actually changed, update the global usage counters.
3271 */
3272 fChanged = fPrevious ^ pSession->fEventFilter;
3273 LogFlow(("vgdrvSetSessionEventFilter: Session->fEventFilter: %#x -> %#x (changed %#x)\n",
3274 fPrevious, pSession->fEventFilter, fChanged));
3275 if (fChanged)
3276 {
3277 bool fGlobalChange = vgdrvBitUsageTrackerChange(&pDevExt->EventFilterTracker, fChanged, fPrevious,
3278 pDevExt->cSessions, "EventFilterTracker");
3279
3280 /*
3281 * If there are global changes, update the event filter on the host.
3282 */
3283 if (fGlobalChange || pDevExt->fEventFilterHost == UINT32_MAX)
3284 {
3285 Assert(pReq || fSessionTermination);
3286 if (pReq)
3287 {
3288 pReq->u32OrMask = pDevExt->fFixedEvents | pDevExt->EventFilterTracker.fMask;
3289 if (pReq->u32OrMask == pDevExt->fEventFilterHost)
3290 rc = VINF_SUCCESS;
3291 else
3292 {
3293 pDevExt->fEventFilterHost = pReq->u32OrMask;
3294 pReq->u32NotMask = ~pReq->u32OrMask;
3295 rc = VbglR0GRPerform(&pReq->header);
3296 if (RT_FAILURE(rc))
3297 {
3298 /*
3299 * Failed, roll back (unless it's session termination time).
3300 */
3301 pDevExt->fEventFilterHost = UINT32_MAX;
3302 if (!fSessionTermination)
3303 {
3304 vgdrvBitUsageTrackerChange(&pDevExt->EventFilterTracker, fChanged, pSession->fEventFilter,
3305 pDevExt->cSessions, "EventFilterTracker");
3306 pSession->fEventFilter = fPrevious;
3307 }
3308 }
3309 }
3310 }
3311 else
3312 rc = VINF_SUCCESS;
3313 }
3314 }
3315
3316 RTSpinlockRelease(pDevExt->SessionSpinlock);
3317 if (pReq)
3318 VbglR0GRFree(&pReq->header);
3319 return rc;
3320}
3321
3322
3323/**
3324 * Handle VBGL_IOCTL_CHANGE_FILTER_MASK.
3325 *
3326 * @returns VBox status code.
3327 *
3328 * @param pDevExt The device extension.
3329 * @param pSession The session.
3330 * @param pInfo The request.
3331 */
3332static int vgdrvIoCtl_ChangeFilterMask(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCCHANGEFILTERMASK pInfo)
3333{
3334 LogFlow(("VBGL_IOCTL_CHANGE_FILTER_MASK: or=%#x not=%#x\n", pInfo->u.In.fOrMask, pInfo->u.In.fNotMask));
3335
3336 if ((pInfo->u.In.fOrMask | pInfo->u.In.fNotMask) & ~VMMDEV_EVENT_VALID_EVENT_MASK)
3337 {
3338 Log(("VBGL_IOCTL_CHANGE_FILTER_MASK: or=%#x not=%#x: Invalid masks!\n", pInfo->u.In.fOrMask, pInfo->u.In.fNotMask));
3339 return VERR_INVALID_PARAMETER;
3340 }
3341
3342 return vgdrvSetSessionEventFilter(pDevExt, pSession, pInfo->u.In.fOrMask, pInfo->u.In.fNotMask, false /*fSessionTermination*/);
3343}
3344
3345
3346/**
3347 * Init and termination worker for set mouse feature status to zero on the host.
3348 *
3349 * @returns VBox status code.
3350 * @param pDevExt The device extension.
3351 */
3352static int vgdrvResetMouseStatusOnHost(PVBOXGUESTDEVEXT pDevExt)
3353{
3354 VMMDevReqMouseStatus *pReq;
3355 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetMouseStatus);
3356 if (RT_SUCCESS(rc))
3357 {
3358 pReq->mouseFeatures = 0;
3359 pReq->pointerXPos = 0;
3360 pReq->pointerYPos = 0;
3361 rc = VbglR0GRPerform(&pReq->header);
3362 if (RT_FAILURE(rc))
3363 LogRelFunc(("failed with rc=%Rrc\n", rc));
3364 VbglR0GRFree(&pReq->header);
3365 }
3366 RT_NOREF1(pDevExt);
3367 return rc;
3368}
3369
3370
3371/**
3372 * Changes the mouse status mask for the given session.
3373 *
3374 * This is called in response to VBOXGUEST_IOCTL_SET_MOUSE_STATUS as well as to
3375 * do session cleanup.
3376 *
3377 * @returns VBox status code.
3378 * @param pDevExt The device extension.
3379 * @param pSession The session.
3380 * @param fOrMask The status flags to add.
3381 * @param fNotMask The status flags to remove.
3382 * @param fSessionTermination Set if we're called by the session cleanup code.
3383 * This tweaks the error handling so we perform
3384 * proper session cleanup even if the host
3385 * misbehaves.
3386 *
3387 * @remarks Takes the session spinlock.
3388 */
3389static int vgdrvSetSessionMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
3390 uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination)
3391{
3392 VMMDevReqMouseStatus *pReq;
3393 uint32_t fChanged;
3394 uint32_t fPrevious;
3395 int rc;
3396
3397 /*
3398 * Preallocate a request buffer so we can do all in one go without leaving the spinlock.
3399 */
3400 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetMouseStatus);
3401 if (RT_SUCCESS(rc))
3402 {
3403 if (!fSessionTermination)
3404 pReq->header.fRequestor = pSession->fRequestor;
3405 }
3406 else if (!fSessionTermination)
3407 {
3408 LogRel(("vgdrvSetSessionMouseStatus: VbglR0GRAlloc failure: %Rrc\n", rc));
3409 return rc;
3410 }
3411 else
3412 pReq = NULL; /* Ignore failure, we must do session cleanup. */
3413
3414
3415 RTSpinlockAcquire(pDevExt->SessionSpinlock);
3416
3417 /*
3418 * Apply the changes to the session mask.
3419 */
3420 fPrevious = pSession->fMouseStatus;
3421 pSession->fMouseStatus |= fOrMask;
3422 pSession->fMouseStatus &= ~fNotMask;
3423
3424 /*
3425 * If anything actually changed, update the global usage counters.
3426 */
3427 fChanged = fPrevious ^ pSession->fMouseStatus;
3428 if (fChanged)
3429 {
3430 bool fGlobalChange = vgdrvBitUsageTrackerChange(&pDevExt->MouseStatusTracker, fChanged, fPrevious,
3431 pDevExt->cSessions, "MouseStatusTracker");
3432
3433 /*
3434 * If there are global changes, update the event filter on the host.
3435 */
3436 if (fGlobalChange || pDevExt->fMouseStatusHost == UINT32_MAX)
3437 {
3438 Assert(pReq || fSessionTermination);
3439 if (pReq)
3440 {
3441 pReq->mouseFeatures = pDevExt->MouseStatusTracker.fMask;
3442 if (pReq->mouseFeatures == pDevExt->fMouseStatusHost)
3443 rc = VINF_SUCCESS;
3444 else
3445 {
3446 pDevExt->fMouseStatusHost = pReq->mouseFeatures;
3447 pReq->pointerXPos = 0;
3448 pReq->pointerYPos = 0;
3449 rc = VbglR0GRPerform(&pReq->header);
3450 if (RT_FAILURE(rc))
3451 {
3452 /*
3453 * Failed, roll back (unless it's session termination time).
3454 */
3455 pDevExt->fMouseStatusHost = UINT32_MAX;
3456 if (!fSessionTermination)
3457 {
3458 vgdrvBitUsageTrackerChange(&pDevExt->MouseStatusTracker, fChanged, pSession->fMouseStatus,
3459 pDevExt->cSessions, "MouseStatusTracker");
3460 pSession->fMouseStatus = fPrevious;
3461 }
3462 }
3463 }
3464 }
3465 else
3466 rc = VINF_SUCCESS;
3467 }
3468 }
3469
3470 RTSpinlockRelease(pDevExt->SessionSpinlock);
3471 if (pReq)
3472 VbglR0GRFree(&pReq->header);
3473 return rc;
3474}
3475
3476
3477/**
3478 * Sets the mouse status features for this session and updates them globally.
3479 *
3480 * @returns VBox status code.
3481 *
3482 * @param pDevExt The device extention.
3483 * @param pSession The session.
3484 * @param fFeatures New bitmap of enabled features.
3485 */
3486static int vgdrvIoCtl_SetMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fFeatures)
3487{
3488 LogFlow(("VBGL_IOCTL_SET_MOUSE_STATUS: features=%#x\n", fFeatures));
3489
3490 if (fFeatures & ~VMMDEV_MOUSE_GUEST_MASK)
3491 return VERR_INVALID_PARAMETER;
3492
3493 return vgdrvSetSessionMouseStatus(pDevExt, pSession, fFeatures, ~fFeatures, false /*fSessionTermination*/);
3494}
3495
3496
3497/**
3498 * Return the mask of VMM device events that this session is allowed to see (wrt
3499 * to "acquire" mode guest capabilities).
3500 *
3501 * The events associated with guest capabilities in "acquire" mode will be
3502 * restricted to sessions which has acquired the respective capabilities.
3503 * If someone else tries to wait for acquired events, they won't be woken up
3504 * when the event becomes pending. Should some other thread in the session
3505 * acquire the capability while the corresponding event is pending, the waiting
3506 * thread will woken up.
3507 *
3508 * @returns Mask of events valid for the given session.
3509 * @param pDevExt The device extension.
3510 * @param pSession The session.
3511 *
3512 * @remarks Needs only be called when dispatching events in the
3513 * VBOXGUEST_ACQUIRE_STYLE_EVENTS mask.
3514 */
3515static uint32_t vgdrvGetAllowedEventMaskForSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
3516{
3517 uint32_t fAcquireModeGuestCaps;
3518 uint32_t fAcquiredGuestCaps;
3519 uint32_t fAllowedEvents;
3520
3521 /*
3522 * Note! Reads pSession->fAcquiredGuestCaps and pDevExt->fAcquireModeGuestCaps
3523 * WITHOUT holding VBOXGUESTDEVEXT::SessionSpinlock.
3524 */
3525 fAcquireModeGuestCaps = ASMAtomicUoReadU32(&pDevExt->fAcquireModeGuestCaps);
3526 if (fAcquireModeGuestCaps == 0)
3527 return VMMDEV_EVENT_VALID_EVENT_MASK;
3528 fAcquiredGuestCaps = ASMAtomicUoReadU32(&pSession->fAcquiredGuestCaps);
3529
3530 /*
3531 * Calculate which events to allow according to the cap config and caps
3532 * acquired by the session.
3533 */
3534 fAllowedEvents = VMMDEV_EVENT_VALID_EVENT_MASK;
3535 if ( !(fAcquiredGuestCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS)
3536 && (fAcquireModeGuestCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS))
3537 fAllowedEvents &= ~VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
3538
3539 if ( !(fAcquiredGuestCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS)
3540 && (fAcquireModeGuestCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS))
3541 fAllowedEvents &= ~VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
3542
3543 return fAllowedEvents;
3544}
3545
3546
3547/**
3548 * Init and termination worker for set guest capabilities to zero on the host.
3549 *
3550 * @returns VBox status code.
3551 * @param pDevExt The device extension.
3552 */
3553static int vgdrvResetCapabilitiesOnHost(PVBOXGUESTDEVEXT pDevExt)
3554{
3555 VMMDevReqGuestCapabilities2 *pReq;
3556 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities);
3557 if (RT_SUCCESS(rc))
3558 {
3559 pReq->u32NotMask = UINT32_MAX;
3560 pReq->u32OrMask = 0;
3561 rc = VbglR0GRPerform(&pReq->header);
3562
3563 if (RT_FAILURE(rc))
3564 LogRelFunc(("failed with rc=%Rrc\n", rc));
3565 VbglR0GRFree(&pReq->header);
3566 }
3567 RT_NOREF1(pDevExt);
3568 return rc;
3569}
3570
3571
3572/**
3573 * Sets the guest capabilities to the host while holding the lock.
3574 *
3575 * This will ASSUME that we're the ones in charge of the mask, so
3576 * we'll simply clear all bits we don't set.
3577 *
3578 * @returns VBox status code.
3579 * @param pDevExt The device extension.
3580 * @param pReq The request.
3581 */
3582static int vgdrvUpdateCapabilitiesOnHostWithReqAndLock(PVBOXGUESTDEVEXT pDevExt, VMMDevReqGuestCapabilities2 *pReq)
3583{
3584 int rc;
3585
3586 pReq->u32OrMask = pDevExt->fAcquiredGuestCaps | pDevExt->SetGuestCapsTracker.fMask;
3587 if (pReq->u32OrMask == pDevExt->fGuestCapsHost)
3588 rc = VINF_SUCCESS;
3589 else
3590 {
3591 pDevExt->fGuestCapsHost = pReq->u32OrMask;
3592 pReq->u32NotMask = ~pReq->u32OrMask;
3593 rc = VbglR0GRPerform(&pReq->header);
3594 if (RT_FAILURE(rc))
3595 pDevExt->fGuestCapsHost = UINT32_MAX;
3596 }
3597
3598 return rc;
3599}
3600
3601
3602/**
3603 * Switch a set of capabilities into "acquire" mode and (maybe) acquire them for
3604 * the given session.
3605 *
3606 * This is called in response to VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE as well as
3607 * to do session cleanup.
3608 *
3609 * @returns VBox status code.
3610 * @param pDevExt The device extension.
3611 * @param pSession The session.
3612 * @param fOrMask The capabilities to add .
3613 * @param fNotMask The capabilities to remove. Ignored in
3614 * VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE.
3615 * @param fFlags Confusing operation modifier.
3616 * VBOXGUESTCAPSACQUIRE_FLAGS_NONE means to both
3617 * configure and acquire/release the capabilities.
3618 * VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE
3619 * means only configure capabilities in the
3620 * @a fOrMask capabilities for "acquire" mode.
3621 * @param fSessionTermination Set if we're called by the session cleanup code.
3622 * This tweaks the error handling so we perform
3623 * proper session cleanup even if the host
3624 * misbehaves.
3625 *
3626 * @remarks Takes both the session and event spinlocks.
3627 */
3628static int vgdrvAcquireSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
3629 uint32_t fOrMask, uint32_t fNotMask, uint32_t fFlags,
3630 bool fSessionTermination)
3631{
3632 uint32_t fCurrentOwnedCaps;
3633 uint32_t fSessionRemovedCaps;
3634 uint32_t fSessionAddedCaps;
3635 uint32_t fOtherConflictingCaps;
3636 VMMDevReqGuestCapabilities2 *pReq = NULL;
3637 int rc;
3638
3639
3640 /*
3641 * Validate and adjust input.
3642 */
3643 if (fOrMask & ~( VMMDEV_GUEST_SUPPORTS_SEAMLESS
3644 | VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING
3645 | VMMDEV_GUEST_SUPPORTS_GRAPHICS ) )
3646 {
3647 LogRel(("vgdrvAcquireSessionCapabilities: invalid fOrMask=%#x (pSession=%p fNotMask=%#x fFlags=%#x)\n",
3648 fOrMask, pSession, fNotMask, fFlags));
3649 return VERR_INVALID_PARAMETER;
3650 }
3651
3652 if ((fFlags & ~VBGL_IOC_AGC_FLAGS_VALID_MASK) != 0)
3653 {
3654 LogRel(("vgdrvAcquireSessionCapabilities: invalid fFlags=%#x (pSession=%p fOrMask=%#x fNotMask=%#x)\n",
3655 fFlags, pSession, fOrMask, fNotMask));
3656 return VERR_INVALID_PARAMETER;
3657 }
3658 Assert(!fOrMask || !fSessionTermination);
3659
3660 /* The fNotMask no need to have all values valid, invalid ones will simply be ignored. */
3661 fNotMask &= ~fOrMask;
3662
3663 /*
3664 * Preallocate a update request if we're about to do more than just configure
3665 * the capability mode.
3666 */
3667 if (!(fFlags & VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE))
3668 {
3669 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities);
3670 if (RT_SUCCESS(rc))
3671 {
3672 if (!fSessionTermination)
3673 pReq->header.fRequestor = pSession->fRequestor;
3674 }
3675 else if (!fSessionTermination)
3676 {
3677 LogRel(("vgdrvAcquireSessionCapabilities: pSession=%p fOrMask=%#x fNotMask=%#x fFlags=%#x: VbglR0GRAlloc failure: %Rrc\n",
3678 pSession, fOrMask, fNotMask, fFlags, rc));
3679 return rc;
3680 }
3681 else
3682 pReq = NULL; /* Ignore failure, we must do session cleanup. */
3683 }
3684
3685 /*
3686 * Try switch the capabilities in the OR mask into "acquire" mode.
3687 *
3688 * Note! We currently ignore anyone which may already have "set" the capabilities
3689 * in fOrMask. Perhaps not the best way to handle it, but it's simple...
3690 */
3691 RTSpinlockAcquire(pDevExt->EventSpinlock);
3692
3693 if (!(pDevExt->fSetModeGuestCaps & fOrMask))
3694 pDevExt->fAcquireModeGuestCaps |= fOrMask;
3695 else
3696 {
3697 RTSpinlockRelease(pDevExt->EventSpinlock);
3698
3699 if (pReq)
3700 VbglR0GRFree(&pReq->header);
3701 AssertMsgFailed(("Trying to change caps mode: %#x\n", fOrMask));
3702 LogRel(("vgdrvAcquireSessionCapabilities: pSession=%p fOrMask=%#x fNotMask=%#x fFlags=%#x: calling caps acquire for set caps\n",
3703 pSession, fOrMask, fNotMask, fFlags));
3704 return VERR_INVALID_STATE;
3705 }
3706
3707 /*
3708 * If we only wanted to switch the capabilities into "acquire" mode, we're done now.
3709 */
3710 if (fFlags & VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE)
3711 {
3712 RTSpinlockRelease(pDevExt->EventSpinlock);
3713
3714 Assert(!pReq);
3715 Log(("vgdrvAcquireSessionCapabilities: pSession=%p fOrMask=%#x fNotMask=%#x fFlags=%#x: configured acquire caps: 0x%x\n",
3716 pSession, fOrMask, fNotMask, fFlags));
3717 return VINF_SUCCESS;
3718 }
3719 Assert(pReq || fSessionTermination);
3720
3721 /*
3722 * Caller wants to acquire/release the capabilities too.
3723 *
3724 * Note! The mode change of the capabilities above won't be reverted on
3725 * failure, this is intentional.
3726 */
3727 fCurrentOwnedCaps = pSession->fAcquiredGuestCaps;
3728 fSessionRemovedCaps = fCurrentOwnedCaps & fNotMask;
3729 fSessionAddedCaps = fOrMask & ~fCurrentOwnedCaps;
3730 fOtherConflictingCaps = pDevExt->fAcquiredGuestCaps & ~fCurrentOwnedCaps;
3731 fOtherConflictingCaps &= fSessionAddedCaps;
3732
3733 if (!fOtherConflictingCaps)
3734 {
3735 if (fSessionAddedCaps)
3736 {
3737 pSession->fAcquiredGuestCaps |= fSessionAddedCaps;
3738 pDevExt->fAcquiredGuestCaps |= fSessionAddedCaps;
3739 }
3740
3741 if (fSessionRemovedCaps)
3742 {
3743 pSession->fAcquiredGuestCaps &= ~fSessionRemovedCaps;
3744 pDevExt->fAcquiredGuestCaps &= ~fSessionRemovedCaps;
3745 }
3746
3747 /*
3748 * If something changes (which is very likely), tell the host.
3749 */
3750 if (fSessionAddedCaps || fSessionRemovedCaps || pDevExt->fGuestCapsHost == UINT32_MAX)
3751 {
3752 Assert(pReq || fSessionTermination);
3753 if (pReq)
3754 {
3755 rc = vgdrvUpdateCapabilitiesOnHostWithReqAndLock(pDevExt, pReq);
3756 if (RT_FAILURE(rc) && !fSessionTermination)
3757 {
3758 /* Failed, roll back. */
3759 if (fSessionAddedCaps)
3760 {
3761 pSession->fAcquiredGuestCaps &= ~fSessionAddedCaps;
3762 pDevExt->fAcquiredGuestCaps &= ~fSessionAddedCaps;
3763 }
3764 if (fSessionRemovedCaps)
3765 {
3766 pSession->fAcquiredGuestCaps |= fSessionRemovedCaps;
3767 pDevExt->fAcquiredGuestCaps |= fSessionRemovedCaps;
3768 }
3769
3770 RTSpinlockRelease(pDevExt->EventSpinlock);
3771 LogRel(("vgdrvAcquireSessionCapabilities: vgdrvUpdateCapabilitiesOnHostWithReqAndLock failed: rc=%Rrc\n", rc));
3772 VbglR0GRFree(&pReq->header);
3773 return rc;
3774 }
3775 }
3776 }
3777 }
3778 else
3779 {
3780 RTSpinlockRelease(pDevExt->EventSpinlock);
3781
3782 Log(("vgdrvAcquireSessionCapabilities: Caps %#x were busy\n", fOtherConflictingCaps));
3783 VbglR0GRFree(&pReq->header);
3784 return VERR_RESOURCE_BUSY;
3785 }
3786
3787 RTSpinlockRelease(pDevExt->EventSpinlock);
3788 if (pReq)
3789 VbglR0GRFree(&pReq->header);
3790
3791 /*
3792 * If we added a capability, check if that means some other thread in our
3793 * session should be unblocked because there are events pending.
3794 *
3795 * HACK ALERT! When the seamless support capability is added we generate a
3796 * seamless change event so that the ring-3 client can sync with
3797 * the seamless state. Although this introduces a spurious
3798 * wakeups of the ring-3 client, it solves the problem of client
3799 * state inconsistency in multiuser environment (on Windows).
3800 */
3801 if (fSessionAddedCaps)
3802 {
3803 uint32_t fGenFakeEvents = 0;
3804 if (fSessionAddedCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS)
3805 fGenFakeEvents |= VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
3806
3807 RTSpinlockAcquire(pDevExt->EventSpinlock);
3808 if (fGenFakeEvents || pDevExt->f32PendingEvents)
3809 vgdrvDispatchEventsLocked(pDevExt, fGenFakeEvents);
3810 RTSpinlockRelease(pDevExt->EventSpinlock);
3811
3812#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
3813 VGDrvCommonWaitDoWakeUps(pDevExt);
3814#endif
3815 }
3816
3817 return VINF_SUCCESS;
3818}
3819
3820
3821/**
3822 * Handle VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES.
3823 *
3824 * @returns VBox status code.
3825 *
3826 * @param pDevExt The device extension.
3827 * @param pSession The session.
3828 * @param pAcquire The request.
3829 */
3830static int vgdrvIoCtl_GuestCapsAcquire(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCACQUIREGUESTCAPS pAcquire)
3831{
3832 int rc;
3833 LogFlow(("VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES: or=%#x not=%#x flags=%#x\n",
3834 pAcquire->u.In.fOrMask, pAcquire->u.In.fNotMask, pAcquire->u.In.fFlags));
3835
3836 rc = vgdrvAcquireSessionCapabilities(pDevExt, pSession, pAcquire->u.In.fOrMask, pAcquire->u.In.fNotMask,
3837 pAcquire->u.In.fFlags, false /*fSessionTermination*/);
3838 if (RT_FAILURE(rc))
3839 LogRel(("VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES failed rc=%Rrc\n", rc));
3840 return rc;
3841}
3842
3843
3844/**
3845 * Sets the guest capabilities for a session.
3846 *
3847 * @returns VBox status code.
3848 * @param pDevExt The device extension.
3849 * @param pSession The session.
3850 * @param fOrMask The capabilities to add.
3851 * @param fNotMask The capabilities to remove.
3852 * @param pfSessionCaps Where to return the guest capabilities reported
3853 * for this session. Optional.
3854 * @param pfGlobalCaps Where to return the guest capabilities reported
3855 * for all the sessions. Optional.
3856 *
3857 * @param fSessionTermination Set if we're called by the session cleanup code.
3858 * This tweaks the error handling so we perform
3859 * proper session cleanup even if the host
3860 * misbehaves.
3861 *
3862 * @remarks Takes the session spinlock.
3863 */
3864static int vgdrvSetSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
3865 uint32_t fOrMask, uint32_t fNotMask, uint32_t *pfSessionCaps, uint32_t *pfGlobalCaps,
3866 bool fSessionTermination)
3867{
3868 /*
3869 * Preallocate a request buffer so we can do all in one go without leaving the spinlock.
3870 */
3871 VMMDevReqGuestCapabilities2 *pReq;
3872 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities);
3873 if (RT_SUCCESS(rc))
3874 {
3875 if (!fSessionTermination)
3876 pReq->header.fRequestor = pSession->fRequestor;
3877 }
3878 else if (!fSessionTermination)
3879 {
3880 if (pfSessionCaps)
3881 *pfSessionCaps = UINT32_MAX;
3882 if (pfGlobalCaps)
3883 *pfGlobalCaps = UINT32_MAX;
3884 LogRel(("vgdrvSetSessionCapabilities: VbglR0GRAlloc failure: %Rrc\n", rc));
3885 return rc;
3886 }
3887 else
3888 pReq = NULL; /* Ignore failure, we must do session cleanup. */
3889
3890
3891 RTSpinlockAcquire(pDevExt->SessionSpinlock);
3892
3893#ifndef VBOXGUEST_DISREGARD_ACQUIRE_MODE_GUEST_CAPS
3894 /*
3895 * Capabilities in "acquire" mode cannot be set via this API.
3896 * (Acquire mode is only used on windows at the time of writing.)
3897 */
3898 if (!(fOrMask & pDevExt->fAcquireModeGuestCaps))
3899#endif
3900 {
3901 /*
3902 * Apply the changes to the session mask.
3903 */
3904 uint32_t fChanged;
3905 uint32_t fPrevious = pSession->fCapabilities;
3906 pSession->fCapabilities |= fOrMask;
3907 pSession->fCapabilities &= ~fNotMask;
3908
3909 /*
3910 * If anything actually changed, update the global usage counters.
3911 */
3912 fChanged = fPrevious ^ pSession->fCapabilities;
3913 if (fChanged)
3914 {
3915 bool fGlobalChange = vgdrvBitUsageTrackerChange(&pDevExt->SetGuestCapsTracker, fChanged, fPrevious,
3916 pDevExt->cSessions, "SetGuestCapsTracker");
3917
3918 /*
3919 * If there are global changes, update the capabilities on the host.
3920 */
3921 if (fGlobalChange || pDevExt->fGuestCapsHost == UINT32_MAX)
3922 {
3923 Assert(pReq || fSessionTermination);
3924 if (pReq)
3925 {
3926 rc = vgdrvUpdateCapabilitiesOnHostWithReqAndLock(pDevExt, pReq);
3927
3928 /* On failure, roll back (unless it's session termination time). */
3929 if (RT_FAILURE(rc) && !fSessionTermination)
3930 {
3931 vgdrvBitUsageTrackerChange(&pDevExt->SetGuestCapsTracker, fChanged, pSession->fCapabilities,
3932 pDevExt->cSessions, "SetGuestCapsTracker");
3933 pSession->fCapabilities = fPrevious;
3934 }
3935 }
3936 }
3937 }
3938 }
3939#ifndef VBOXGUEST_DISREGARD_ACQUIRE_MODE_GUEST_CAPS
3940 else
3941 rc = VERR_RESOURCE_BUSY;
3942#endif
3943
3944 if (pfSessionCaps)
3945 *pfSessionCaps = pSession->fCapabilities;
3946 if (pfGlobalCaps)
3947 *pfGlobalCaps = pDevExt->fAcquiredGuestCaps | pDevExt->SetGuestCapsTracker.fMask;
3948
3949 RTSpinlockRelease(pDevExt->SessionSpinlock);
3950 if (pReq)
3951 VbglR0GRFree(&pReq->header);
3952 return rc;
3953}
3954
3955
3956/**
3957 * Handle VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES.
3958 *
3959 * @returns VBox status code.
3960 *
3961 * @param pDevExt The device extension.
3962 * @param pSession The session.
3963 * @param pInfo The request.
3964 */
3965static int vgdrvIoCtl_SetCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCSETGUESTCAPS pInfo)
3966{
3967 int rc;
3968 LogFlow(("VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES: or=%#x not=%#x\n", pInfo->u.In.fOrMask, pInfo->u.In.fNotMask));
3969
3970 if (!((pInfo->u.In.fOrMask | pInfo->u.In.fNotMask) & ~VMMDEV_GUEST_CAPABILITIES_MASK))
3971 rc = vgdrvSetSessionCapabilities(pDevExt, pSession, pInfo->u.In.fOrMask, pInfo->u.In.fNotMask,
3972 &pInfo->u.Out.fSessionCaps, &pInfo->u.Out.fGlobalCaps, false /*fSessionTermination*/);
3973 else
3974 rc = VERR_INVALID_PARAMETER;
3975
3976 return rc;
3977}
3978
3979/** @} */
3980
3981
3982/**
3983 * Common IOCtl for user to kernel and kernel to kernel communication.
3984 *
3985 * This function only does the basic validation and then invokes
3986 * worker functions that takes care of each specific function.
3987 *
3988 * @returns VBox status code.
3989 *
3990 * @param iFunction The requested function.
3991 * @param pDevExt The device extension.
3992 * @param pSession The client session.
3993 * @param pReqHdr Pointer to the request. This always starts with
3994 * a request common header.
3995 * @param cbReq The max size of the request buffer.
3996 */
3997int VGDrvCommonIoCtl(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLREQHDR pReqHdr, size_t cbReq)
3998{
3999 uintptr_t const iFunctionStripped = VBGL_IOCTL_CODE_STRIPPED(iFunction);
4000 int rc;
4001
4002 LogFlow(("VGDrvCommonIoCtl: iFunction=%#x pDevExt=%p pSession=%p pReqHdr=%p cbReq=%zu\n",
4003 iFunction, pDevExt, pSession, pReqHdr, cbReq));
4004
4005 /*
4006 * Define some helper macros to simplify validation.
4007 */
4008#define REQ_CHECK_SIZES_EX(Name, cbInExpect, cbOutExpect) \
4009 do { \
4010 if (RT_LIKELY( pReqHdr->cbIn == (cbInExpect) \
4011 && ( pReqHdr->cbOut == (cbOutExpect) \
4012 || ((cbInExpect) == (cbOutExpect) && pReqHdr->cbOut == 0) ) )) \
4013 { /* likely */ } \
4014 else \
4015 { \
4016 Log(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld. cbOut=%ld expected %ld.\n", \
4017 (long)pReqHdr->cbIn, (long)(cbInExpect), (long)pReqHdr->cbOut, (long)(cbOutExpect))); \
4018 return pReqHdr->rc = VERR_INVALID_PARAMETER; \
4019 } \
4020 } while (0)
4021
4022#define REQ_CHECK_SIZES(Name) REQ_CHECK_SIZES_EX(Name, Name ## _SIZE_IN, Name ## _SIZE_OUT)
4023
4024#define REQ_CHECK_SIZE_IN(Name, cbInExpect) \
4025 do { \
4026 if (RT_LIKELY(pReqHdr->cbIn == (cbInExpect))) \
4027 { /* likely */ } \
4028 else \
4029 { \
4030 Log(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld.\n", \
4031 (long)pReqHdr->cbIn, (long)(cbInExpect))); \
4032 return pReqHdr->rc = VERR_INVALID_PARAMETER; \
4033 } \
4034 } while (0)
4035
4036#define REQ_CHECK_SIZE_OUT(Name, cbOutExpect) \
4037 do { \
4038 if (RT_LIKELY( pReqHdr->cbOut == (cbOutExpect) \
4039 || (pReqHdr->cbOut == 0 && pReqHdr->cbIn == (cbOutExpect)))) \
4040 { /* likely */ } \
4041 else \
4042 { \
4043 Log(( #Name ": Invalid input/output sizes. cbOut=%ld (%ld) expected %ld.\n", \
4044 (long)pReqHdr->cbOut, (long)pReqHdr->cbIn, (long)(cbOutExpect))); \
4045 return pReqHdr->rc = VERR_INVALID_PARAMETER; \
4046 } \
4047 } while (0)
4048
4049#define REQ_CHECK_EXPR(Name, expr) \
4050 do { \
4051 if (RT_LIKELY(!!(expr))) \
4052 { /* likely */ } \
4053 else \
4054 { \
4055 Log(( #Name ": %s\n", #expr)); \
4056 return pReqHdr->rc = VERR_INVALID_PARAMETER; \
4057 } \
4058 } while (0)
4059
4060#define REQ_CHECK_EXPR_FMT(expr, fmt) \
4061 do { \
4062 if (RT_LIKELY(!!(expr))) \
4063 { /* likely */ } \
4064 else \
4065 { \
4066 Log( fmt ); \
4067 return pReqHdr->rc = VERR_INVALID_PARAMETER; \
4068 } \
4069 } while (0)
4070
4071#define REQ_CHECK_RING0(mnemonic) \
4072 do { \
4073 if (pSession->R0Process != NIL_RTR0PROCESS) \
4074 { \
4075 LogFunc((mnemonic ": Ring-0 only, caller is %RTproc/%p\n", \
4076 pSession->Process, (uintptr_t)pSession->R0Process)); \
4077 return pReqHdr->rc = VERR_PERMISSION_DENIED; \
4078 } \
4079 } while (0)
4080
4081
4082 /*
4083 * Validate the request.
4084 */
4085 if (RT_LIKELY(cbReq >= sizeof(*pReqHdr)))
4086 { /* likely */ }
4087 else
4088 {
4089 Log(("VGDrvCommonIoCtl: Bad ioctl request size; cbReq=%#lx\n", (long)cbReq));
4090 return VERR_INVALID_PARAMETER;
4091 }
4092
4093 if (pReqHdr->cbOut == 0)
4094 pReqHdr->cbOut = pReqHdr->cbIn;
4095
4096 if (RT_LIKELY( pReqHdr->uVersion == VBGLREQHDR_VERSION
4097 && pReqHdr->cbIn >= sizeof(*pReqHdr)
4098 && pReqHdr->cbIn <= cbReq
4099 && pReqHdr->cbOut >= sizeof(*pReqHdr)
4100 && pReqHdr->cbOut <= cbReq))
4101 { /* likely */ }
4102 else
4103 {
4104 Log(("VGDrvCommonIoCtl: Bad ioctl request header; cbIn=%#lx cbOut=%#lx version=%#lx\n",
4105 (long)pReqHdr->cbIn, (long)pReqHdr->cbOut, (long)pReqHdr->uVersion));
4106 return VERR_INVALID_PARAMETER;
4107 }
4108
4109 if (RT_LIKELY(RT_VALID_PTR(pSession)))
4110 { /* likely */ }
4111 else
4112 {
4113 Log(("VGDrvCommonIoCtl: Invalid pSession value %p (ioctl=%#x)\n", pSession, iFunction));
4114 return VERR_INVALID_PARAMETER;
4115 }
4116
4117
4118 /*
4119 * Deal with variably sized requests first.
4120 */
4121 rc = VINF_SUCCESS;
4122 if ( iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_VMMDEV_REQUEST(0))
4123 || iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_VMMDEV_REQUEST_BIG) )
4124 {
4125 REQ_CHECK_EXPR(VBGL_IOCTL_VMMDEV_REQUEST, pReqHdr->uType != VBGLREQHDR_TYPE_DEFAULT);
4126 REQ_CHECK_EXPR_FMT(pReqHdr->cbIn == pReqHdr->cbOut,
4127 ("VBGL_IOCTL_VMMDEV_REQUEST: cbIn=%ld != cbOut=%ld\n", (long)pReqHdr->cbIn, (long)pReqHdr->cbOut));
4128 pReqHdr->rc = vgdrvIoCtl_VMMDevRequest(pDevExt, pSession, (VMMDevRequestHeader *)pReqHdr, cbReq);
4129 }
4130 else if (RT_LIKELY(pReqHdr->uType == VBGLREQHDR_TYPE_DEFAULT))
4131 {
4132 if (iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_LOG(0)))
4133 {
4134 REQ_CHECK_SIZE_OUT(VBGL_IOCTL_LOG, VBGL_IOCTL_LOG_SIZE_OUT);
4135 pReqHdr->rc = vgdrvIoCtl_Log(pDevExt, &((PVBGLIOCLOG)pReqHdr)->u.In.szMsg[0], pReqHdr->cbIn - sizeof(VBGLREQHDR),
4136 pSession->fUserSession);
4137 }
4138#ifdef VBOX_WITH_HGCM
4139 else if (iFunction == VBGL_IOCTL_IDC_HGCM_FAST_CALL) /* (is variable size, but we don't bother encoding it) */
4140 {
4141 REQ_CHECK_RING0("VBGL_IOCTL_IDC_HGCM_FAST_CALL");
4142 REQ_CHECK_EXPR(VBGL_IOCTL_IDC_HGCM_FAST_CALL, cbReq >= sizeof(VBGLIOCIDCHGCMFASTCALL) + sizeof(VMMDevHGCMCall));
4143 vgdrvIoCtl_HGCMFastCall(pDevExt, (VBGLIOCIDCHGCMFASTCALL volatile *)pReqHdr);
4144 }
4145 else if ( iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL(0))
4146# if ARCH_BITS == 64
4147 || iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL_32(0))
4148# endif
4149 )
4150 {
4151 REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn >= sizeof(VBGLIOCHGCMCALL));
4152 REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn == pReqHdr->cbOut);
4153 pReqHdr->rc = vgdrvIoCtl_HGCMCallWrapper(pDevExt, pSession, (PVBGLIOCHGCMCALL)pReqHdr,
4154 iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL_32(0)),
4155 false /*fUserData*/, cbReq);
4156 }
4157 else if (iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(0)))
4158 {
4159 REQ_CHECK_RING0("VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA");
4160 REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn >= sizeof(VBGLIOCHGCMCALL));
4161 REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn == pReqHdr->cbOut);
4162 pReqHdr->rc = vgdrvIoCtl_HGCMCallWrapper(pDevExt, pSession, (PVBGLIOCHGCMCALL)pReqHdr,
4163 ARCH_BITS == 32, true /*fUserData*/, cbReq);
4164 }
4165#endif /* VBOX_WITH_HGCM */
4166 else
4167 {
4168 switch (iFunction)
4169 {
4170 /*
4171 * Ring-0 only:
4172 */
4173 case VBGL_IOCTL_IDC_CONNECT:
4174 REQ_CHECK_RING0("VBGL_IOCL_IDC_CONNECT");
4175 REQ_CHECK_SIZES(VBGL_IOCTL_IDC_CONNECT);
4176 pReqHdr->rc = vgdrvIoCtl_IdcConnect(pDevExt, pSession, (PVBGLIOCIDCCONNECT)pReqHdr);
4177 break;
4178
4179 case VBGL_IOCTL_IDC_DISCONNECT:
4180 REQ_CHECK_RING0("VBGL_IOCTL_IDC_DISCONNECT");
4181 REQ_CHECK_SIZES(VBGL_IOCTL_IDC_DISCONNECT);
4182 pReqHdr->rc = vgdrvIoCtl_IdcDisconnect(pDevExt, pSession, (PVBGLIOCIDCDISCONNECT)pReqHdr);
4183 break;
4184
4185 case VBGL_IOCTL_GET_VMMDEV_IO_INFO:
4186 REQ_CHECK_RING0("GET_VMMDEV_IO_INFO");
4187 REQ_CHECK_SIZES(VBGL_IOCTL_GET_VMMDEV_IO_INFO);
4188 pReqHdr->rc = vgdrvIoCtl_GetVMMDevIoInfo(pDevExt, (PVBGLIOCGETVMMDEVIOINFO)pReqHdr);
4189 break;
4190
4191 case VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK:
4192 REQ_CHECK_RING0("SET_MOUSE_NOTIFY_CALLBACK");
4193 REQ_CHECK_SIZES(VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK);
4194 pReqHdr->rc = vgdrvIoCtl_SetMouseNotifyCallback(pDevExt, (PVBGLIOCSETMOUSENOTIFYCALLBACK)pReqHdr);
4195 break;
4196
4197 /*
4198 * Ring-3 only:
4199 */
4200 case VBGL_IOCTL_DRIVER_VERSION_INFO:
4201 REQ_CHECK_SIZES(VBGL_IOCTL_DRIVER_VERSION_INFO);
4202 pReqHdr->rc = vgdrvIoCtl_DriverVersionInfo(pDevExt, pSession, (PVBGLIOCDRIVERVERSIONINFO)pReqHdr);
4203 break;
4204
4205 /*
4206 * Both ring-3 and ring-0:
4207 */
4208 case VBGL_IOCTL_WAIT_FOR_EVENTS:
4209 REQ_CHECK_SIZES(VBGL_IOCTL_WAIT_FOR_EVENTS);
4210 pReqHdr->rc = vgdrvIoCtl_WaitForEvents(pDevExt, pSession, (VBGLIOCWAITFOREVENTS *)pReqHdr,
4211 pSession->R0Process != NIL_RTR0PROCESS);
4212 break;
4213
4214 case VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS:
4215 REQ_CHECK_SIZES(VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS);
4216 pReqHdr->rc = vgdrvIoCtl_CancelAllWaitEvents(pDevExt, pSession);
4217 break;
4218
4219 case VBGL_IOCTL_CHANGE_FILTER_MASK:
4220 REQ_CHECK_SIZES(VBGL_IOCTL_CHANGE_FILTER_MASK);
4221 pReqHdr->rc = vgdrvIoCtl_ChangeFilterMask(pDevExt, pSession, (PVBGLIOCCHANGEFILTERMASK)pReqHdr);
4222 break;
4223
4224#ifdef VBOX_WITH_HGCM
4225 case VBGL_IOCTL_HGCM_CONNECT:
4226 REQ_CHECK_SIZES(VBGL_IOCTL_HGCM_CONNECT);
4227 pReqHdr->rc = vgdrvIoCtl_HGCMConnect(pDevExt, pSession, (PVBGLIOCHGCMCONNECT)pReqHdr);
4228 break;
4229
4230 case VBGL_IOCTL_HGCM_DISCONNECT:
4231 REQ_CHECK_SIZES(VBGL_IOCTL_HGCM_DISCONNECT);
4232 pReqHdr->rc = vgdrvIoCtl_HGCMDisconnect(pDevExt, pSession, (PVBGLIOCHGCMDISCONNECT)pReqHdr);
4233 break;
4234#endif
4235
4236 case VBGL_IOCTL_CHECK_BALLOON:
4237 REQ_CHECK_SIZES(VBGL_IOCTL_CHECK_BALLOON);
4238 pReqHdr->rc = vgdrvIoCtl_CheckMemoryBalloon(pDevExt, pSession, (PVBGLIOCCHECKBALLOON)pReqHdr);
4239 break;
4240
4241 case VBGL_IOCTL_CHANGE_BALLOON:
4242 REQ_CHECK_SIZES(VBGL_IOCTL_CHANGE_BALLOON);
4243 pReqHdr->rc = vgdrvIoCtl_ChangeMemoryBalloon(pDevExt, pSession, (PVBGLIOCCHANGEBALLOON)pReqHdr);
4244 break;
4245
4246 case VBGL_IOCTL_WRITE_CORE_DUMP:
4247 REQ_CHECK_SIZES(VBGL_IOCTL_WRITE_CORE_DUMP);
4248 pReqHdr->rc = vgdrvIoCtl_WriteCoreDump(pDevExt, pSession, (PVBGLIOCWRITECOREDUMP)pReqHdr);
4249 break;
4250
4251 case VBGL_IOCTL_SET_MOUSE_STATUS:
4252 REQ_CHECK_SIZES(VBGL_IOCTL_SET_MOUSE_STATUS);
4253 pReqHdr->rc = vgdrvIoCtl_SetMouseStatus(pDevExt, pSession, ((PVBGLIOCSETMOUSESTATUS)pReqHdr)->u.In.fStatus);
4254 break;
4255
4256 case VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES:
4257 REQ_CHECK_SIZES(VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES);
4258 pReqHdr->rc = vgdrvIoCtl_GuestCapsAcquire(pDevExt, pSession, (PVBGLIOCACQUIREGUESTCAPS)pReqHdr);
4259 break;
4260
4261 case VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES:
4262 REQ_CHECK_SIZES(VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES);
4263 pReqHdr->rc = vgdrvIoCtl_SetCapabilities(pDevExt, pSession, (PVBGLIOCSETGUESTCAPS)pReqHdr);
4264 break;
4265
4266#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
4267 case VBGL_IOCTL_DPC_LATENCY_CHECKER:
4268 REQ_CHECK_SIZES(VBGL_IOCTL_DPC_LATENCY_CHECKER);
4269 pReqHdr->rc = VGDrvNtIOCtl_DpcLatencyChecker();
4270 break;
4271#endif
4272
4273 default:
4274 {
4275 LogRel(("VGDrvCommonIoCtl: Unknown request iFunction=%#x (stripped %#x) cbReq=%#x\n",
4276 iFunction, iFunctionStripped, cbReq));
4277 pReqHdr->rc = rc = VERR_NOT_SUPPORTED;
4278 break;
4279 }
4280 }
4281 }
4282 }
4283 else
4284 {
4285 Log(("VGDrvCommonIoCtl: uType=%#x, expected default (ioctl=%#x)\n", pReqHdr->uType, iFunction));
4286 return VERR_INVALID_PARAMETER;
4287 }
4288
4289 LogFlow(("VGDrvCommonIoCtl: returns %Rrc (req: rc=%Rrc cbOut=%#x)\n", rc, pReqHdr->rc, pReqHdr->cbOut));
4290 return rc;
4291}
4292
4293
4294/**
4295 * Used by VGDrvCommonISR as well as the acquire guest capability code.
4296 *
4297 * @returns VINF_SUCCESS on success. On failure, ORed together
4298 * RTSemEventMultiSignal errors (completes processing despite errors).
4299 * @param pDevExt The VBoxGuest device extension.
4300 * @param fEvents The events to dispatch.
4301 */
4302static int vgdrvDispatchEventsLocked(PVBOXGUESTDEVEXT pDevExt, uint32_t fEvents)
4303{
4304 PVBOXGUESTWAIT pWait;
4305 PVBOXGUESTWAIT pSafe;
4306 int rc = VINF_SUCCESS;
4307
4308 fEvents |= pDevExt->f32PendingEvents;
4309
4310 RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode)
4311 {
4312 uint32_t fHandledEvents = pWait->fReqEvents & fEvents;
4313 if ( fHandledEvents != 0
4314 && !pWait->fResEvents)
4315 {
4316 /* Does this one wait on any of the events we're dispatching? We do a quick
4317 check first, then deal with VBOXGUEST_ACQUIRE_STYLE_EVENTS as applicable. */
4318 if (fHandledEvents & VBOXGUEST_ACQUIRE_STYLE_EVENTS)
4319 fHandledEvents &= vgdrvGetAllowedEventMaskForSession(pDevExt, pWait->pSession);
4320 if (fHandledEvents)
4321 {
4322 pWait->fResEvents = pWait->fReqEvents & fEvents & fHandledEvents;
4323 fEvents &= ~pWait->fResEvents;
4324 RTListNodeRemove(&pWait->ListNode);
4325#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
4326 RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode);
4327#else
4328 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
4329 rc |= RTSemEventMultiSignal(pWait->Event);
4330#endif
4331 if (!fEvents)
4332 break;
4333 }
4334 }
4335 }
4336
4337 ASMAtomicWriteU32(&pDevExt->f32PendingEvents, fEvents);
4338 return rc;
4339}
4340
4341
4342/**
4343 * Simply checks whether the IRQ is ours or not, does not do any interrupt
4344 * procesing.
4345 *
4346 * @returns true if it was our interrupt, false if it wasn't.
4347 * @param pDevExt The VBoxGuest device extension.
4348 */
4349bool VGDrvCommonIsOurIRQ(PVBOXGUESTDEVEXT pDevExt)
4350{
4351 VMMDevMemory volatile *pVMMDevMemory;
4352 bool fOurIrq;
4353
4354 RTSpinlockAcquire(pDevExt->EventSpinlock);
4355 pVMMDevMemory = pDevExt->pVMMDevMemory;
4356 fOurIrq = pVMMDevMemory ? pVMMDevMemory->V.V1_04.fHaveEvents : false;
4357 RTSpinlockRelease(pDevExt->EventSpinlock);
4358
4359 return fOurIrq;
4360}
4361
4362
4363/**
4364 * Common interrupt service routine.
4365 *
4366 * This deals with events and with waking up thread waiting for those events.
4367 *
4368 * @returns true if it was our interrupt, false if it wasn't.
4369 * @param pDevExt The VBoxGuest device extension.
4370 */
4371bool VGDrvCommonISR(PVBOXGUESTDEVEXT pDevExt)
4372{
4373 VMMDevEvents volatile *pReq;
4374 bool fMousePositionChanged = false;
4375 int rc = 0;
4376 VMMDevMemory volatile *pVMMDevMemory;
4377 bool fOurIrq;
4378
4379 /*
4380 * Make sure we've initialized the device extension.
4381 */
4382 if (RT_LIKELY(pDevExt->fHostFeatures & VMMDEV_HVF_FAST_IRQ_ACK))
4383 pReq = NULL;
4384 else if (RT_LIKELY((pReq = pDevExt->pIrqAckEvents) != NULL))
4385 { /* likely */ }
4386 else
4387 return false;
4388
4389 /*
4390 * Enter the spinlock and check if it's our IRQ or not.
4391 */
4392 RTSpinlockAcquire(pDevExt->EventSpinlock);
4393 pVMMDevMemory = pDevExt->pVMMDevMemory;
4394 fOurIrq = pVMMDevMemory ? pVMMDevMemory->V.V1_04.fHaveEvents : false;
4395 if (fOurIrq)
4396 {
4397 /*
4398 * Acknowledge events.
4399 * We don't use VbglR0GRPerform here as it may take another spinlocks.
4400 */
4401 uint32_t fEvents;
4402 if (!pReq)
4403 {
4404 fEvents = ASMInU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST_FAST);
4405 ASMCompilerBarrier(); /* paranoia */
4406 rc = fEvents != UINT32_MAX ? VINF_SUCCESS : VERR_INTERNAL_ERROR;
4407 }
4408 else
4409 {
4410 pReq->header.rc = VERR_INTERNAL_ERROR;
4411 pReq->events = 0;
4412 ASMCompilerBarrier();
4413 ASMOutU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST, (uint32_t)pDevExt->PhysIrqAckEvents);
4414 ASMCompilerBarrier(); /* paranoia */
4415 fEvents = pReq->events;
4416 rc = pReq->header.rc;
4417 }
4418 if (RT_SUCCESS(rc))
4419 {
4420 Log3(("VGDrvCommonISR: acknowledge events succeeded %#RX32\n", fEvents));
4421
4422 /*
4423 * VMMDEV_EVENT_MOUSE_POSITION_CHANGED can only be polled for.
4424 */
4425 if (fEvents & VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
4426 {
4427 fMousePositionChanged = true;
4428 fEvents &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED;
4429#if !defined(VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT)
4430 if (pDevExt->pfnMouseNotifyCallback)
4431 pDevExt->pfnMouseNotifyCallback(pDevExt->pvMouseNotifyCallbackArg);
4432#endif
4433 }
4434
4435#ifdef VBOX_WITH_HGCM
4436 /*
4437 * The HGCM event/list is kind of different in that we evaluate all entries.
4438 */
4439 if (fEvents & VMMDEV_EVENT_HGCM)
4440 {
4441 PVBOXGUESTWAIT pWait;
4442 PVBOXGUESTWAIT pSafe;
4443 RTListForEachSafe(&pDevExt->HGCMWaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode)
4444 {
4445 if (pWait->pHGCMReq->fu32Flags & VBOX_HGCM_REQ_DONE)
4446 {
4447 pWait->fResEvents = VMMDEV_EVENT_HGCM;
4448 RTListNodeRemove(&pWait->ListNode);
4449# ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
4450 RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode);
4451# else
4452 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
4453 rc |= RTSemEventMultiSignal(pWait->Event);
4454# endif
4455 }
4456 }
4457 fEvents &= ~VMMDEV_EVENT_HGCM;
4458 }
4459#endif
4460
4461 /*
4462 * Normal FIFO waiter evaluation.
4463 */
4464 rc |= vgdrvDispatchEventsLocked(pDevExt, fEvents);
4465 }
4466 else /* something is serious wrong... */
4467 Log(("VGDrvCommonISR: acknowledge events failed rc=%Rrc (events=%#x)!!\n", rc, fEvents));
4468 }
4469 else
4470 Log3(("VGDrvCommonISR: not ours\n"));
4471
4472 RTSpinlockRelease(pDevExt->EventSpinlock);
4473
4474 /*
4475 * Execute the mouse notification callback here if it cannot be executed while
4476 * holding the interrupt safe spinlock, see @bugref{8639}.
4477 */
4478#if defined(VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT) && !defined(RT_OS_WINDOWS) /* (Windows does this in the Dpc callback) */
4479 if ( fMousePositionChanged
4480 && pDevExt->pfnMouseNotifyCallback)
4481 pDevExt->pfnMouseNotifyCallback(pDevExt->pvMouseNotifyCallbackArg);
4482#endif
4483
4484#if defined(VBOXGUEST_USE_DEFERRED_WAKE_UP) && !defined(RT_OS_WINDOWS)
4485 /*
4486 * Do wake-ups.
4487 * Note. On Windows this isn't possible at this IRQL, so a DPC will take
4488 * care of it. Same on darwin, doing it in the work loop callback.
4489 */
4490 VGDrvCommonWaitDoWakeUps(pDevExt);
4491#endif
4492
4493 /*
4494 * Work the poll and async notification queues on OSes that implements that.
4495 * (Do this outside the spinlock to prevent some recursive spinlocking.)
4496 */
4497 if (fMousePositionChanged)
4498 {
4499 ASMAtomicIncU32(&pDevExt->u32MousePosChangedSeq);
4500 VGDrvNativeISRMousePollEvent(pDevExt);
4501 }
4502
4503 AssertMsg(rc == 0, ("rc=%#x (%d)\n", rc, rc));
4504 return fOurIrq;
4505}
4506
Note: See TracBrowser for help on using the repository browser.

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