VirtualBox

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

Last change on this file was 100363, checked in by vboxsync, 10 months ago

Additions/common/VBoxGuest: Fix crash when the MMIO request path is not available, regression introduced with r157982, bugref:10457

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

© 2023 Oracle
ContactPrivacy policyTerms of Use