VirtualBox

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

Last change on this file since 41943 was 41722, checked in by vboxsync, 13 years ago

Additions/common/VBoxGuest: fixed copyright dates and licence headers and removed a copyright text that is not relevant to this code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.9 KB
Line 
1/* $Id: VBoxGuest.cpp 41722 2012-06-14 19:49:31Z vboxsync $ */
2/** @file
3 * VBoxGuest - Guest Additions Driver, Common Code.
4 */
5
6/*
7 * Copyright (C) 2007-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP LOG_GROUP_DEFAULT
32#include "VBoxGuestInternal.h"
33#include "VBoxGuest2.h"
34#include <VBox/VMMDev.h> /* for VMMDEV_RAM_SIZE */
35#include <VBox/log.h>
36#include <iprt/mem.h>
37#include <iprt/time.h>
38#include <iprt/memobj.h>
39#include <iprt/asm.h>
40#include <iprt/asm-amd64-x86.h>
41#include <iprt/string.h>
42#include <iprt/process.h>
43#include <iprt/assert.h>
44#include <iprt/param.h>
45#ifdef VBOX_WITH_HGCM
46# include <iprt/thread.h>
47#endif
48#include "version-generated.h"
49#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
50# include "revision-generated.h"
51#endif
52#ifdef RT_OS_WINDOWS
53# ifndef CTL_CODE
54# include <Windows.h>
55# endif
56#endif
57#if defined(RT_OS_SOLARIS)
58# include <iprt/rand.h>
59#endif
60
61
62/*******************************************************************************
63* Internal Functions *
64*******************************************************************************/
65#ifdef VBOX_WITH_HGCM
66static DECLCALLBACK(int) VBoxGuestHGCMAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdrNonVolatile, void *pvUser, uint32_t u32User);
67#endif
68#ifdef DEBUG
69static void testSetMouseStatus(void);
70#endif
71static int VBoxGuestCommonIOCtl_SetMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fFeatures);
72
73
74/*******************************************************************************
75* Global Variables *
76*******************************************************************************/
77static const size_t cbChangeMemBalloonReq = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
78
79#if defined(RT_OS_SOLARIS)
80/**
81 * Drag in the rest of IRPT since we share it with the
82 * rest of the kernel modules on Solaris.
83 */
84PFNRT g_apfnVBoxGuestIPRTDeps[] =
85{
86 /* VirtioNet */
87 (PFNRT)RTRandBytes,
88 NULL
89};
90#endif /* RT_OS_SOLARIS */
91
92
93/**
94 * Reserves memory in which the VMM can relocate any guest mappings
95 * that are floating around.
96 *
97 * This operation is a little bit tricky since the VMM might not accept
98 * just any address because of address clashes between the three contexts
99 * it operates in, so use a small stack to perform this operation.
100 *
101 * @returns VBox status code (ignored).
102 * @param pDevExt The device extension.
103 */
104static int vboxGuestInitFixateGuestMappings(PVBOXGUESTDEVEXT pDevExt)
105{
106 /*
107 * Query the required space.
108 */
109 VMMDevReqHypervisorInfo *pReq;
110 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
111 if (RT_FAILURE(rc))
112 return rc;
113 pReq->hypervisorStart = 0;
114 pReq->hypervisorSize = 0;
115 rc = VbglGRPerform(&pReq->header);
116 if (RT_FAILURE(rc)) /* this shouldn't happen! */
117 {
118 VbglGRFree(&pReq->header);
119 return rc;
120 }
121
122 /*
123 * The VMM will report back if there is nothing it wants to map, like for
124 * instance in VT-x and AMD-V mode.
125 */
126 if (pReq->hypervisorSize == 0)
127 Log(("vboxGuestInitFixateGuestMappings: nothing to do\n"));
128 else
129 {
130 /*
131 * We have to try several times since the host can be picky
132 * about certain addresses.
133 */
134 RTR0MEMOBJ hFictive = NIL_RTR0MEMOBJ;
135 uint32_t cbHypervisor = pReq->hypervisorSize;
136 RTR0MEMOBJ ahTries[5];
137 uint32_t iTry;
138 bool fBitched = false;
139 Log(("vboxGuestInitFixateGuestMappings: cbHypervisor=%#x\n", cbHypervisor));
140 for (iTry = 0; iTry < RT_ELEMENTS(ahTries); iTry++)
141 {
142 /*
143 * Reserve space, or if that isn't supported, create a object for
144 * some fictive physical memory and map that in to kernel space.
145 *
146 * To make the code a bit uglier, most systems cannot help with
147 * 4MB alignment, so we have to deal with that in addition to
148 * having two ways of getting the memory.
149 */
150 uint32_t uAlignment = _4M;
151 RTR0MEMOBJ hObj;
152 rc = RTR0MemObjReserveKernel(&hObj, (void *)-1, RT_ALIGN_32(cbHypervisor, _4M), uAlignment);
153 if (rc == VERR_NOT_SUPPORTED)
154 {
155 uAlignment = PAGE_SIZE;
156 rc = RTR0MemObjReserveKernel(&hObj, (void *)-1, RT_ALIGN_32(cbHypervisor, _4M) + _4M, uAlignment);
157 }
158 /*
159 * If both RTR0MemObjReserveKernel calls above failed because either not supported or
160 * not implemented at all at the current platform, try to map the memory object into the
161 * virtual kernel space.
162 */
163 if (rc == VERR_NOT_SUPPORTED)
164 {
165 if (hFictive == NIL_RTR0MEMOBJ)
166 {
167 rc = RTR0MemObjEnterPhys(&hObj, VBOXGUEST_HYPERVISOR_PHYSICAL_START, cbHypervisor + _4M, RTMEM_CACHE_POLICY_DONT_CARE);
168 if (RT_FAILURE(rc))
169 break;
170 hFictive = hObj;
171 }
172 uAlignment = _4M;
173 rc = RTR0MemObjMapKernel(&hObj, hFictive, (void *)-1, uAlignment, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
174 if (rc == VERR_NOT_SUPPORTED)
175 {
176 uAlignment = PAGE_SIZE;
177 rc = RTR0MemObjMapKernel(&hObj, hFictive, (void *)-1, uAlignment, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
178 }
179 }
180 if (RT_FAILURE(rc))
181 {
182 LogRel(("VBoxGuest: Failed to reserve memory for the hypervisor: rc=%Rrc (cbHypervisor=%#x uAlignment=%#x iTry=%u)\n",
183 rc, cbHypervisor, uAlignment, iTry));
184 fBitched = true;
185 break;
186 }
187
188 /*
189 * Try set it.
190 */
191 pReq->header.requestType = VMMDevReq_SetHypervisorInfo;
192 pReq->header.rc = VERR_INTERNAL_ERROR;
193 pReq->hypervisorSize = cbHypervisor;
194 pReq->hypervisorStart = (uintptr_t)RTR0MemObjAddress(hObj);
195 if ( uAlignment == PAGE_SIZE
196 && pReq->hypervisorStart & (_4M - 1))
197 pReq->hypervisorStart = RT_ALIGN_32(pReq->hypervisorStart, _4M);
198 AssertMsg(RT_ALIGN_32(pReq->hypervisorStart, _4M) == pReq->hypervisorStart, ("%#x\n", pReq->hypervisorStart));
199
200 rc = VbglGRPerform(&pReq->header);
201 if (RT_SUCCESS(rc))
202 {
203 pDevExt->hGuestMappings = hFictive != NIL_RTR0MEMOBJ ? hFictive : hObj;
204 Log(("VBoxGuest: %p LB %#x; uAlignment=%#x iTry=%u hGuestMappings=%p (%s)\n",
205 RTR0MemObjAddress(pDevExt->hGuestMappings),
206 RTR0MemObjSize(pDevExt->hGuestMappings),
207 uAlignment, iTry, pDevExt->hGuestMappings, hFictive != NIL_RTR0PTR ? "fictive" : "reservation"));
208 break;
209 }
210 ahTries[iTry] = hObj;
211 }
212
213 /*
214 * Cleanup failed attempts.
215 */
216 while (iTry-- > 0)
217 RTR0MemObjFree(ahTries[iTry], false /* fFreeMappings */);
218 if ( RT_FAILURE(rc)
219 && hFictive != NIL_RTR0PTR)
220 RTR0MemObjFree(hFictive, false /* fFreeMappings */);
221 if (RT_FAILURE(rc) && !fBitched)
222 LogRel(("VBoxGuest: Warning: failed to reserve %#d of memory for guest mappings.\n", cbHypervisor));
223 }
224 VbglGRFree(&pReq->header);
225
226 /*
227 * We ignore failed attempts for now.
228 */
229 return VINF_SUCCESS;
230}
231
232
233/**
234 * Undo what vboxGuestInitFixateGuestMappings did.
235 *
236 * @param pDevExt The device extension.
237 */
238static void vboxGuestTermUnfixGuestMappings(PVBOXGUESTDEVEXT pDevExt)
239{
240 if (pDevExt->hGuestMappings != NIL_RTR0PTR)
241 {
242 /*
243 * Tell the host that we're going to free the memory we reserved for
244 * it, the free it up. (Leak the memory if anything goes wrong here.)
245 */
246 VMMDevReqHypervisorInfo *pReq;
247 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
248 if (RT_SUCCESS(rc))
249 {
250 pReq->hypervisorStart = 0;
251 pReq->hypervisorSize = 0;
252 rc = VbglGRPerform(&pReq->header);
253 VbglGRFree(&pReq->header);
254 }
255 if (RT_SUCCESS(rc))
256 {
257 rc = RTR0MemObjFree(pDevExt->hGuestMappings, true /* fFreeMappings */);
258 AssertRC(rc);
259 }
260 else
261 LogRel(("vboxGuestTermUnfixGuestMappings: Failed to unfix the guest mappings! rc=%Rrc\n", rc));
262
263 pDevExt->hGuestMappings = NIL_RTR0MEMOBJ;
264 }
265}
266
267
268/**
269 * Sets the interrupt filter mask during initialization and termination.
270 *
271 * This will ASSUME that we're the ones in carge over the mask, so
272 * we'll simply clear all bits we don't set.
273 *
274 * @returns VBox status code (ignored).
275 * @param pDevExt The device extension.
276 * @param fMask The new mask.
277 */
278static int vboxGuestSetFilterMask(PVBOXGUESTDEVEXT pDevExt, uint32_t fMask)
279{
280 VMMDevCtlGuestFilterMask *pReq;
281 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
282 if (RT_SUCCESS(rc))
283 {
284 pReq->u32OrMask = fMask;
285 pReq->u32NotMask = ~fMask;
286 rc = VbglGRPerform(&pReq->header);
287 if (RT_FAILURE(rc))
288 LogRel(("vboxGuestSetFilterMask: failed with rc=%Rrc\n", rc));
289 VbglGRFree(&pReq->header);
290 }
291 return rc;
292}
293
294
295/**
296 * Inflate the balloon by one chunk represented by an R0 memory object.
297 *
298 * The caller owns the balloon mutex.
299 *
300 * @returns IPRT status code.
301 * @param pMemObj Pointer to the R0 memory object.
302 * @param pReq The pre-allocated request for performing the VMMDev call.
303 */
304static int vboxGuestBalloonInflate(PRTR0MEMOBJ pMemObj, VMMDevChangeMemBalloon *pReq)
305{
306 uint32_t iPage;
307 int rc;
308
309 for (iPage = 0; iPage < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; iPage++)
310 {
311 RTHCPHYS phys = RTR0MemObjGetPagePhysAddr(*pMemObj, iPage);
312 pReq->aPhysPage[iPage] = phys;
313 }
314
315 pReq->fInflate = true;
316 pReq->header.size = cbChangeMemBalloonReq;
317 pReq->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
318
319 rc = VbglGRPerform(&pReq->header);
320 if (RT_FAILURE(rc))
321 LogRel(("vboxGuestBalloonInflate: VbglGRPerform failed. rc=%Rrc\n", rc));
322 return rc;
323}
324
325
326/**
327 * Deflate the balloon by one chunk - info the host and free the memory object.
328 *
329 * The caller owns the balloon mutex.
330 *
331 * @returns IPRT status code.
332 * @param pMemObj Pointer to the R0 memory object.
333 * The memory object will be freed afterwards.
334 * @param pReq The pre-allocated request for performing the VMMDev call.
335 */
336static int vboxGuestBalloonDeflate(PRTR0MEMOBJ pMemObj, VMMDevChangeMemBalloon *pReq)
337{
338 uint32_t iPage;
339 int rc;
340
341 for (iPage = 0; iPage < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; iPage++)
342 {
343 RTHCPHYS phys = RTR0MemObjGetPagePhysAddr(*pMemObj, iPage);
344 pReq->aPhysPage[iPage] = phys;
345 }
346
347 pReq->fInflate = false;
348 pReq->header.size = cbChangeMemBalloonReq;
349 pReq->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
350
351 rc = VbglGRPerform(&pReq->header);
352 if (RT_FAILURE(rc))
353 {
354 LogRel(("vboxGuestBalloonDeflate: VbglGRPerform failed. rc=%Rrc\n", rc));
355 return rc;
356 }
357
358 rc = RTR0MemObjFree(*pMemObj, true);
359 if (RT_FAILURE(rc))
360 {
361 LogRel(("vboxGuestBalloonDeflate: RTR0MemObjFree(%p,true) -> %Rrc; this is *BAD*!\n", *pMemObj, rc));
362 return rc;
363 }
364
365 *pMemObj = NIL_RTR0MEMOBJ;
366 return VINF_SUCCESS;
367}
368
369
370/**
371 * Inflate/deflate the memory balloon and notify the host.
372 *
373 * This is a worker used by VBoxGuestCommonIOCtl_CheckMemoryBalloon - it takes
374 * the mutex.
375 *
376 * @returns VBox status code.
377 * @param pDevExt The device extension.
378 * @param pSession The session.
379 * @param cBalloonChunks The new size of the balloon in chunks of 1MB.
380 * @param pfHandleInR3 Where to return the handle-in-ring3 indicator
381 * (VINF_SUCCESS if set).
382 */
383static int vboxGuestSetBalloonSizeKernel(PVBOXGUESTDEVEXT pDevExt, uint32_t cBalloonChunks, uint32_t *pfHandleInR3)
384{
385 int rc = VINF_SUCCESS;
386
387 if (pDevExt->MemBalloon.fUseKernelAPI)
388 {
389 VMMDevChangeMemBalloon *pReq;
390 uint32_t i;
391
392 if (cBalloonChunks > pDevExt->MemBalloon.cMaxChunks)
393 {
394 LogRel(("vboxGuestSetBalloonSizeKernel: illegal balloon size %u (max=%u)\n",
395 cBalloonChunks, pDevExt->MemBalloon.cMaxChunks));
396 return VERR_INVALID_PARAMETER;
397 }
398
399 if (cBalloonChunks == pDevExt->MemBalloon.cMaxChunks)
400 return VINF_SUCCESS; /* nothing to do */
401
402 if ( cBalloonChunks > pDevExt->MemBalloon.cChunks
403 && !pDevExt->MemBalloon.paMemObj)
404 {
405 pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAllocZ(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks);
406 if (!pDevExt->MemBalloon.paMemObj)
407 {
408 LogRel(("VBoxGuestSetBalloonSizeKernel: no memory for paMemObj!\n"));
409 return VERR_NO_MEMORY;
410 }
411 }
412
413 rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon);
414 if (RT_FAILURE(rc))
415 return rc;
416
417 if (cBalloonChunks > pDevExt->MemBalloon.cChunks)
418 {
419 /* inflate */
420 for (i = pDevExt->MemBalloon.cChunks; i < cBalloonChunks; i++)
421 {
422 rc = RTR0MemObjAllocPhysNC(&pDevExt->MemBalloon.paMemObj[i],
423 VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, NIL_RTHCPHYS);
424 if (RT_FAILURE(rc))
425 {
426 if (rc == VERR_NOT_SUPPORTED)
427 {
428 /* not supported -- fall back to the R3-allocated memory. */
429 rc = VINF_SUCCESS;
430 pDevExt->MemBalloon.fUseKernelAPI = false;
431 Assert(pDevExt->MemBalloon.cChunks == 0);
432 Log(("VBoxGuestSetBalloonSizeKernel: PhysNC allocs not supported, falling back to R3 allocs.\n"));
433 }
434 /* else if (rc == VERR_NO_MEMORY || rc == VERR_NO_PHYS_MEMORY):
435 * cannot allocate more memory => don't try further, just stop here */
436 /* else: XXX what else can fail? VERR_MEMOBJ_INIT_FAILED for instance. just stop. */
437 break;
438 }
439
440 rc = vboxGuestBalloonInflate(&pDevExt->MemBalloon.paMemObj[i], pReq);
441 if (RT_FAILURE(rc))
442 {
443 Log(("vboxGuestSetBalloonSize(inflate): failed, rc=%Rrc!\n", rc));
444 RTR0MemObjFree(pDevExt->MemBalloon.paMemObj[i], true);
445 pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ;
446 break;
447 }
448 pDevExt->MemBalloon.cChunks++;
449 }
450 }
451 else
452 {
453 /* deflate */
454 for (i = pDevExt->MemBalloon.cChunks; i-- > cBalloonChunks;)
455 {
456 rc = vboxGuestBalloonDeflate(&pDevExt->MemBalloon.paMemObj[i], pReq);
457 if (RT_FAILURE(rc))
458 {
459 Log(("vboxGuestSetBalloonSize(deflate): failed, rc=%Rrc!\n", rc));
460 break;
461 }
462 pDevExt->MemBalloon.cChunks--;
463 }
464 }
465
466 VbglGRFree(&pReq->header);
467 }
468
469 /*
470 * Set the handle-in-ring3 indicator. When set Ring-3 will have to work
471 * the balloon changes via the other API.
472 */
473 *pfHandleInR3 = pDevExt->MemBalloon.fUseKernelAPI ? false : true;
474
475 return rc;
476}
477
478
479/**
480 * Helper to reinit the VBoxVMM communication after hibernation.
481 *
482 * @returns VBox status code.
483 * @param pDevExt The device extension.
484 * @param enmOSType The OS type.
485 */
486int VBoxGuestReinitDevExtAfterHibernation(PVBOXGUESTDEVEXT pDevExt, VBOXOSTYPE enmOSType)
487{
488 int rc = VBoxGuestReportGuestInfo(enmOSType);
489 if (RT_SUCCESS(rc))
490 {
491 rc = VBoxGuestReportDriverStatus(true /* Driver is active */);
492 if (RT_FAILURE(rc))
493 Log(("VBoxGuest::VBoxGuestReinitDevExtAfterHibernation: could not report guest driver status, rc=%Rrc\n", rc));
494 }
495 else
496 Log(("VBoxGuest::VBoxGuestReinitDevExtAfterHibernation: could not report guest information to host, rc=%Rrc\n", rc));
497 Log(("VBoxGuest::VBoxGuestReinitDevExtAfterHibernation: returned with rc=%Rrc\n", rc));
498 return rc;
499}
500
501
502/**
503 * Inflate/deflate the balloon by one chunk.
504 *
505 * Worker for VBoxGuestCommonIOCtl_ChangeMemoryBalloon - it takes the mutex.
506 *
507 * @returns VBox status code.
508 * @param pDevExt The device extension.
509 * @param pSession The session.
510 * @param u64ChunkAddr The address of the chunk to add to / remove from the
511 * balloon.
512 * @param fInflate Inflate if true, deflate if false.
513 */
514static int vboxGuestSetBalloonSizeFromUser(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
515 uint64_t u64ChunkAddr, bool fInflate)
516{
517 VMMDevChangeMemBalloon *pReq;
518 int rc = VINF_SUCCESS;
519 uint32_t i;
520 PRTR0MEMOBJ pMemObj = NULL;
521
522 if (fInflate)
523 {
524 if ( pDevExt->MemBalloon.cChunks > pDevExt->MemBalloon.cMaxChunks - 1
525 || pDevExt->MemBalloon.cMaxChunks == 0 /* If called without first querying. */)
526 {
527 LogRel(("vboxGuestSetBalloonSize: cannot inflate balloon, already have %u chunks (max=%u)\n",
528 pDevExt->MemBalloon.cChunks, pDevExt->MemBalloon.cMaxChunks));
529 return VERR_INVALID_PARAMETER;
530 }
531
532 if (!pDevExt->MemBalloon.paMemObj)
533 {
534 pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAlloc(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks);
535 if (!pDevExt->MemBalloon.paMemObj)
536 {
537 LogRel(("VBoxGuestSetBalloonSizeFromUser: no memory for paMemObj!\n"));
538 return VERR_NO_MEMORY;
539 }
540 for (i = 0; i < pDevExt->MemBalloon.cMaxChunks; i++)
541 pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ;
542 }
543 }
544 else
545 {
546 if (pDevExt->MemBalloon.cChunks == 0)
547 {
548 AssertMsgFailed(("vboxGuestSetBalloonSize: cannot decrease balloon, already at size 0\n"));
549 return VERR_INVALID_PARAMETER;
550 }
551 }
552
553 /*
554 * Enumerate all memory objects and check if the object is already registered.
555 */
556 for (i = 0; i < pDevExt->MemBalloon.cMaxChunks; i++)
557 {
558 if ( fInflate
559 && !pMemObj
560 && pDevExt->MemBalloon.paMemObj[i] == NIL_RTR0MEMOBJ)
561 pMemObj = &pDevExt->MemBalloon.paMemObj[i]; /* found free object pointer */
562 if (RTR0MemObjAddressR3(pDevExt->MemBalloon.paMemObj[i]) == u64ChunkAddr)
563 {
564 if (fInflate)
565 return VERR_ALREADY_EXISTS; /* don't provide the same memory twice */
566 pMemObj = &pDevExt->MemBalloon.paMemObj[i];
567 break;
568 }
569 }
570 if (!pMemObj)
571 {
572 if (fInflate)
573 {
574 /* no free object pointer found -- should not happen */
575 return VERR_NO_MEMORY;
576 }
577
578 /* cannot free this memory as it wasn't provided before */
579 return VERR_NOT_FOUND;
580 }
581
582 /*
583 * Try inflate / default the balloon as requested.
584 */
585 rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon);
586 if (RT_FAILURE(rc))
587 return rc;
588
589 if (fInflate)
590 {
591 rc = RTR0MemObjLockUser(pMemObj, (RTR3PTR)u64ChunkAddr, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE,
592 RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
593 if (RT_SUCCESS(rc))
594 {
595 rc = vboxGuestBalloonInflate(pMemObj, pReq);
596 if (RT_SUCCESS(rc))
597 pDevExt->MemBalloon.cChunks++;
598 else
599 {
600 Log(("vboxGuestSetBalloonSize(inflate): failed, rc=%Rrc!\n", rc));
601 RTR0MemObjFree(*pMemObj, true);
602 *pMemObj = NIL_RTR0MEMOBJ;
603 }
604 }
605 }
606 else
607 {
608 rc = vboxGuestBalloonDeflate(pMemObj, pReq);
609 if (RT_SUCCESS(rc))
610 pDevExt->MemBalloon.cChunks--;
611 else
612 Log(("vboxGuestSetBalloonSize(deflate): failed, rc=%Rrc!\n", rc));
613 }
614
615 VbglGRFree(&pReq->header);
616 return rc;
617}
618
619
620/**
621 * Cleanup the memory balloon of a session.
622 *
623 * Will request the balloon mutex, so it must be valid and the caller must not
624 * own it already.
625 *
626 * @param pDevExt The device extension.
627 * @param pDevExt The session. Can be NULL at unload.
628 */
629static void vboxGuestCloseMemBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
630{
631 RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx);
632 if ( pDevExt->MemBalloon.pOwner == pSession
633 || pSession == NULL /*unload*/)
634 {
635 if (pDevExt->MemBalloon.paMemObj)
636 {
637 VMMDevChangeMemBalloon *pReq;
638 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon);
639 if (RT_SUCCESS(rc))
640 {
641 uint32_t i;
642 for (i = pDevExt->MemBalloon.cChunks; i-- > 0;)
643 {
644 rc = vboxGuestBalloonDeflate(&pDevExt->MemBalloon.paMemObj[i], pReq);
645 if (RT_FAILURE(rc))
646 {
647 LogRel(("vboxGuestCloseMemBalloon: Deflate failed with rc=%Rrc. Will leak %u chunks.\n",
648 rc, pDevExt->MemBalloon.cChunks));
649 break;
650 }
651 pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ;
652 pDevExt->MemBalloon.cChunks--;
653 }
654 VbglGRFree(&pReq->header);
655 }
656 else
657 LogRel(("vboxGuestCloseMemBalloon: Failed to allocate VMMDev request buffer (rc=%Rrc). Will leak %u chunks.\n",
658 rc, pDevExt->MemBalloon.cChunks));
659 RTMemFree(pDevExt->MemBalloon.paMemObj);
660 pDevExt->MemBalloon.paMemObj = NULL;
661 }
662
663 pDevExt->MemBalloon.pOwner = NULL;
664 }
665 RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx);
666}
667
668
669/**
670 * Initializes the VBoxGuest device extension when the
671 * device driver is loaded.
672 *
673 * The native code locates the VMMDev on the PCI bus and retrieve
674 * the MMIO and I/O port ranges, this function will take care of
675 * mapping the MMIO memory (if present). Upon successful return
676 * the native code should set up the interrupt handler.
677 *
678 * @returns VBox status code.
679 *
680 * @param pDevExt The device extension. Allocated by the native code.
681 * @param IOPortBase The base of the I/O port range.
682 * @param pvMMIOBase The base of the MMIO memory mapping.
683 * This is optional, pass NULL if not present.
684 * @param cbMMIO The size of the MMIO memory mapping.
685 * This is optional, pass 0 if not present.
686 * @param enmOSType The guest OS type to report to the VMMDev.
687 * @param fFixedEvents Events that will be enabled upon init and no client
688 * will ever be allowed to mask.
689 */
690int VBoxGuestInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase,
691 void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fFixedEvents)
692{
693 int rc, rc2;
694 unsigned i;
695
696 /*
697 * Adjust fFixedEvents.
698 */
699#ifdef VBOX_WITH_HGCM
700 fFixedEvents |= VMMDEV_EVENT_HGCM;
701#endif
702
703 /*
704 * Initialize the data.
705 */
706 pDevExt->IOPortBase = IOPortBase;
707 pDevExt->pVMMDevMemory = NULL;
708 pDevExt->fFixedEvents = fFixedEvents;
709 pDevExt->hGuestMappings = NIL_RTR0MEMOBJ;
710 pDevExt->EventSpinlock = NIL_RTSPINLOCK;
711 pDevExt->pIrqAckEvents = NULL;
712 pDevExt->PhysIrqAckEvents = NIL_RTCCPHYS;
713 RTListInit(&pDevExt->WaitList);
714#ifdef VBOX_WITH_HGCM
715 RTListInit(&pDevExt->HGCMWaitList);
716#endif
717#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
718 RTListInit(&pDevExt->WakeUpList);
719#endif
720 RTListInit(&pDevExt->WokenUpList);
721 RTListInit(&pDevExt->FreeList);
722#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
723 pDevExt->fVRDPEnabled = false;
724#endif
725 pDevExt->fLoggingEnabled = false;
726 pDevExt->f32PendingEvents = 0;
727 pDevExt->u32MousePosChangedSeq = 0;
728 pDevExt->SessionSpinlock = NIL_RTSPINLOCK;
729 pDevExt->MemBalloon.hMtx = NIL_RTSEMFASTMUTEX;
730 pDevExt->MemBalloon.cChunks = 0;
731 pDevExt->MemBalloon.cMaxChunks = 0;
732 pDevExt->MemBalloon.fUseKernelAPI = true;
733 pDevExt->MemBalloon.paMemObj = NULL;
734 pDevExt->MemBalloon.pOwner = NULL;
735 for (i = 0; i < RT_ELEMENTS(pDevExt->acMouseFeatureUsage); ++i)
736 pDevExt->acMouseFeatureUsage[i] = 0;
737 pDevExt->fMouseStatus = 0;
738 pDevExt->MouseNotifyCallback.pfnNotify = NULL;
739 pDevExt->MouseNotifyCallback.pvUser = NULL;
740 pDevExt->cISR = 0;
741
742 /*
743 * If there is an MMIO region validate the version and size.
744 */
745 if (pvMMIOBase)
746 {
747 VMMDevMemory *pVMMDev = (VMMDevMemory *)pvMMIOBase;
748 Assert(cbMMIO);
749 if ( pVMMDev->u32Version == VMMDEV_MEMORY_VERSION
750 && pVMMDev->u32Size >= 32
751 && pVMMDev->u32Size <= cbMMIO)
752 {
753 pDevExt->pVMMDevMemory = pVMMDev;
754 Log(("VBoxGuestInitDevExt: VMMDevMemory: mapping=%p size=%#RX32 (%#RX32) version=%#RX32\n",
755 pVMMDev, pVMMDev->u32Size, cbMMIO, pVMMDev->u32Version));
756 }
757 else /* try live without it. */
758 LogRel(("VBoxGuestInitDevExt: Bogus VMMDev memory; u32Version=%RX32 (expected %RX32) u32Size=%RX32 (expected <= %RX32)\n",
759 pVMMDev->u32Version, VMMDEV_MEMORY_VERSION, pVMMDev->u32Size, cbMMIO));
760 }
761
762 /*
763 * Create the wait and session spinlocks as well as the ballooning mutex.
764 */
765 rc = RTSpinlockCreate(&pDevExt->EventSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestEvent");
766 if (RT_SUCCESS(rc))
767 rc = RTSpinlockCreate(&pDevExt->SessionSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestSession");
768 if (RT_FAILURE(rc))
769 {
770 LogRel(("VBoxGuestInitDevExt: failed to create spinlock, rc=%Rrc!\n", rc));
771 if (pDevExt->EventSpinlock != NIL_RTSPINLOCK)
772 RTSpinlockDestroy(pDevExt->EventSpinlock);
773 return rc;
774 }
775
776 rc = RTSemFastMutexCreate(&pDevExt->MemBalloon.hMtx);
777 if (RT_FAILURE(rc))
778 {
779 LogRel(("VBoxGuestInitDevExt: failed to create mutex, rc=%Rrc!\n", rc));
780 RTSpinlockDestroy(pDevExt->SessionSpinlock);
781 RTSpinlockDestroy(pDevExt->EventSpinlock);
782 return rc;
783 }
784
785 /*
786 * Initialize the guest library and report the guest info back to VMMDev,
787 * set the interrupt control filter mask, and fixate the guest mappings
788 * made by the VMM.
789 */
790 rc = VbglInit(pDevExt->IOPortBase, (VMMDevMemory *)pDevExt->pVMMDevMemory);
791 if (RT_SUCCESS(rc))
792 {
793 rc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->pIrqAckEvents, sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents);
794 if (RT_SUCCESS(rc))
795 {
796 pDevExt->PhysIrqAckEvents = VbglPhysHeapGetPhysAddr(pDevExt->pIrqAckEvents);
797 Assert(pDevExt->PhysIrqAckEvents != 0);
798
799 rc = VBoxGuestReportGuestInfo(enmOSType);
800 if (RT_SUCCESS(rc))
801 {
802 rc = vboxGuestSetFilterMask(pDevExt, fFixedEvents);
803 if (RT_SUCCESS(rc))
804 {
805 /*
806 * Disable guest graphics capability by default. The guest specific
807 * graphics driver will re-enable this when it is necessary.
808 */
809 rc = VBoxGuestSetGuestCapabilities(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS);
810 if (RT_SUCCESS(rc))
811 {
812 vboxGuestInitFixateGuestMappings(pDevExt);
813
814#ifdef DEBUG
815 testSetMouseStatus(); /* Other tests? */
816#endif
817
818 rc = VBoxGuestReportDriverStatus(true /* Driver is active */);
819 if (RT_FAILURE(rc))
820 LogRel(("VBoxGuestInitDevExt: VBoxReportGuestDriverStatus failed, rc=%Rrc\n", rc));
821
822 Log(("VBoxGuestInitDevExt: returns success\n"));
823 return VINF_SUCCESS;
824 }
825
826 LogRel(("VBoxGuestInitDevExt: VBoxGuestSetGuestCapabilities failed, rc=%Rrc\n", rc));
827 }
828 else
829 LogRel(("VBoxGuestInitDevExt: vboxGuestSetFilterMask failed, rc=%Rrc\n", rc));
830 }
831 else
832 LogRel(("VBoxGuestInitDevExt: VBoxReportGuestInfo failed, rc=%Rrc\n", rc));
833 VbglGRFree((VMMDevRequestHeader *)pDevExt->pIrqAckEvents);
834 }
835 else
836 LogRel(("VBoxGuestInitDevExt: VBoxGRAlloc failed, rc=%Rrc\n", rc));
837
838 VbglTerminate();
839 }
840 else
841 LogRel(("VBoxGuestInitDevExt: VbglInit failed, rc=%Rrc\n", rc));
842
843 rc2 = RTSemFastMutexDestroy(pDevExt->MemBalloon.hMtx); AssertRC(rc2);
844 rc2 = RTSpinlockDestroy(pDevExt->EventSpinlock); AssertRC(rc2);
845 rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2);
846 return rc; /* (failed) */
847}
848
849
850/**
851 * Deletes all the items in a wait chain.
852 * @param pList The head of the chain.
853 */
854static void VBoxGuestDeleteWaitList(PRTLISTNODE pList)
855{
856 while (!RTListIsEmpty(pList))
857 {
858 int rc2;
859 PVBOXGUESTWAIT pWait = RTListGetFirst(pList, VBOXGUESTWAIT, ListNode);
860 RTListNodeRemove(&pWait->ListNode);
861
862 rc2 = RTSemEventMultiDestroy(pWait->Event); AssertRC(rc2);
863 pWait->Event = NIL_RTSEMEVENTMULTI;
864 pWait->pSession = NULL;
865 RTMemFree(pWait);
866 }
867}
868
869
870/**
871 * Destroys the VBoxGuest device extension.
872 *
873 * The native code should call this before the driver is loaded,
874 * but don't call this on shutdown.
875 *
876 * @param pDevExt The device extension.
877 */
878void VBoxGuestDeleteDevExt(PVBOXGUESTDEVEXT pDevExt)
879{
880 int rc2;
881 Log(("VBoxGuestDeleteDevExt:\n"));
882 Log(("VBoxGuest: The additions driver is terminating.\n"));
883
884 /*
885 * Clean up the bits that involves the host first.
886 */
887 vboxGuestTermUnfixGuestMappings(pDevExt);
888 VBoxGuestSetGuestCapabilities(0, UINT32_MAX); /* clears all capabilities */
889 vboxGuestSetFilterMask(pDevExt, 0); /* filter all events */
890 vboxGuestCloseMemBalloon(pDevExt, (PVBOXGUESTSESSION)NULL);
891
892 /*
893 * Cleanup all the other resources.
894 */
895 rc2 = RTSpinlockDestroy(pDevExt->EventSpinlock); AssertRC(rc2);
896 rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2);
897 rc2 = RTSemFastMutexDestroy(pDevExt->MemBalloon.hMtx); AssertRC(rc2);
898
899 VBoxGuestDeleteWaitList(&pDevExt->WaitList);
900#ifdef VBOX_WITH_HGCM
901 VBoxGuestDeleteWaitList(&pDevExt->HGCMWaitList);
902#endif
903#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
904 VBoxGuestDeleteWaitList(&pDevExt->WakeUpList);
905#endif
906 VBoxGuestDeleteWaitList(&pDevExt->WokenUpList);
907 VBoxGuestDeleteWaitList(&pDevExt->FreeList);
908
909 VbglTerminate();
910
911 pDevExt->pVMMDevMemory = NULL;
912
913 pDevExt->IOPortBase = 0;
914 pDevExt->pIrqAckEvents = NULL;
915}
916
917
918/**
919 * Creates a VBoxGuest user session.
920 *
921 * The native code calls this when a ring-3 client opens the device.
922 * Use VBoxGuestCreateKernelSession when a ring-0 client connects.
923 *
924 * @returns VBox status code.
925 * @param pDevExt The device extension.
926 * @param ppSession Where to store the session on success.
927 */
928int VBoxGuestCreateUserSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession)
929{
930 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
931 if (RT_UNLIKELY(!pSession))
932 {
933 LogRel(("VBoxGuestCreateUserSession: no memory!\n"));
934 return VERR_NO_MEMORY;
935 }
936
937 pSession->Process = RTProcSelf();
938 pSession->R0Process = RTR0ProcHandleSelf();
939 pSession->pDevExt = pDevExt;
940
941 *ppSession = pSession;
942 LogFlow(("VBoxGuestCreateUserSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
943 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
944 return VINF_SUCCESS;
945}
946
947
948/**
949 * Creates a VBoxGuest kernel session.
950 *
951 * The native code calls this when a ring-0 client connects to the device.
952 * Use VBoxGuestCreateUserSession when a ring-3 client opens the device.
953 *
954 * @returns VBox status code.
955 * @param pDevExt The device extension.
956 * @param ppSession Where to store the session on success.
957 */
958int VBoxGuestCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession)
959{
960 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
961 if (RT_UNLIKELY(!pSession))
962 {
963 LogRel(("VBoxGuestCreateKernelSession: no memory!\n"));
964 return VERR_NO_MEMORY;
965 }
966
967 pSession->Process = NIL_RTPROCESS;
968 pSession->R0Process = NIL_RTR0PROCESS;
969 pSession->pDevExt = pDevExt;
970
971 *ppSession = pSession;
972 LogFlow(("VBoxGuestCreateKernelSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
973 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
974 return VINF_SUCCESS;
975}
976
977
978
979/**
980 * Closes a VBoxGuest session.
981 *
982 * @param pDevExt The device extension.
983 * @param pSession The session to close (and free).
984 */
985void VBoxGuestCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
986{
987 unsigned i; NOREF(i);
988 Log(("VBoxGuestCloseSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
989 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
990
991#ifdef VBOX_WITH_HGCM
992 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
993 if (pSession->aHGCMClientIds[i])
994 {
995 VBoxGuestHGCMDisconnectInfo Info;
996 Info.result = 0;
997 Info.u32ClientID = pSession->aHGCMClientIds[i];
998 pSession->aHGCMClientIds[i] = 0;
999 Log(("VBoxGuestCloseSession: disconnecting client id %#RX32\n", Info.u32ClientID));
1000 VbglR0HGCMInternalDisconnect(&Info, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1001 }
1002#endif
1003
1004 pSession->pDevExt = NULL;
1005 pSession->Process = NIL_RTPROCESS;
1006 pSession->R0Process = NIL_RTR0PROCESS;
1007 vboxGuestCloseMemBalloon(pDevExt, pSession);
1008 /* Reset any mouse status flags which the session may have set. */
1009 VBoxGuestCommonIOCtl_SetMouseStatus(pDevExt, pSession, 0);
1010 RTMemFree(pSession);
1011}
1012
1013
1014/**
1015 * Allocates a wait-for-event entry.
1016 *
1017 * @returns The wait-for-event entry.
1018 * @param pDevExt The device extension.
1019 * @param pSession The session that's allocating this. Can be NULL.
1020 */
1021static PVBOXGUESTWAIT VBoxGuestWaitAlloc(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
1022{
1023 /*
1024 * Allocate it one way or the other.
1025 */
1026 PVBOXGUESTWAIT pWait = RTListGetFirst(&pDevExt->FreeList, VBOXGUESTWAIT, ListNode);
1027 if (pWait)
1028 {
1029 RTSpinlockAcquire(pDevExt->EventSpinlock);
1030
1031 pWait = RTListGetFirst(&pDevExt->FreeList, VBOXGUESTWAIT, ListNode);
1032 if (pWait)
1033 RTListNodeRemove(&pWait->ListNode);
1034
1035 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1036 }
1037 if (!pWait)
1038 {
1039 static unsigned s_cErrors = 0;
1040 int rc;
1041
1042 pWait = (PVBOXGUESTWAIT)RTMemAlloc(sizeof(*pWait));
1043 if (!pWait)
1044 {
1045 if (s_cErrors++ < 32)
1046 LogRel(("VBoxGuestWaitAlloc: out-of-memory!\n"));
1047 return NULL;
1048 }
1049
1050 rc = RTSemEventMultiCreate(&pWait->Event);
1051 if (RT_FAILURE(rc))
1052 {
1053 if (s_cErrors++ < 32)
1054 LogRel(("VBoxGuestCommonIOCtl: RTSemEventMultiCreate failed with rc=%Rrc!\n", rc));
1055 RTMemFree(pWait);
1056 return NULL;
1057 }
1058
1059 pWait->ListNode.pNext = NULL;
1060 pWait->ListNode.pPrev = NULL;
1061 }
1062
1063 /*
1064 * Zero members just as an precaution.
1065 */
1066 pWait->fReqEvents = 0;
1067 pWait->fResEvents = 0;
1068#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1069 pWait->fPendingWakeUp = false;
1070 pWait->fFreeMe = false;
1071#endif
1072 pWait->pSession = pSession;
1073#ifdef VBOX_WITH_HGCM
1074 pWait->pHGCMReq = NULL;
1075#endif
1076 RTSemEventMultiReset(pWait->Event);
1077 return pWait;
1078}
1079
1080
1081/**
1082 * Frees the wait-for-event entry.
1083 *
1084 * The caller must own the wait spinlock !
1085 * The entry must be in a list!
1086 *
1087 * @param pDevExt The device extension.
1088 * @param pWait The wait-for-event entry to free.
1089 */
1090static void VBoxGuestWaitFreeLocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait)
1091{
1092 pWait->fReqEvents = 0;
1093 pWait->fResEvents = 0;
1094#ifdef VBOX_WITH_HGCM
1095 pWait->pHGCMReq = NULL;
1096#endif
1097#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1098 Assert(!pWait->fFreeMe);
1099 if (pWait->fPendingWakeUp)
1100 pWait->fFreeMe = true;
1101 else
1102#endif
1103 {
1104 RTListNodeRemove(&pWait->ListNode);
1105 RTListAppend(&pDevExt->FreeList, &pWait->ListNode);
1106 }
1107}
1108
1109
1110/**
1111 * Frees the wait-for-event entry.
1112 *
1113 * @param pDevExt The device extension.
1114 * @param pWait The wait-for-event entry to free.
1115 */
1116static void VBoxGuestWaitFreeUnlocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait)
1117{
1118 RTSpinlockAcquire(pDevExt->EventSpinlock);
1119 VBoxGuestWaitFreeLocked(pDevExt, pWait);
1120 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1121}
1122
1123
1124#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1125/**
1126 * Processes the wake-up list.
1127 *
1128 * All entries in the wake-up list gets signalled and moved to the woken-up
1129 * list.
1130 *
1131 * @param pDevExt The device extension.
1132 */
1133void VBoxGuestWaitDoWakeUps(PVBOXGUESTDEVEXT pDevExt)
1134{
1135 if (!RTListIsEmpty(&pDevExt->WakeUpList))
1136 {
1137 RTSpinlockAcquire(pDevExt->EventSpinlock);
1138 for (;;)
1139 {
1140 int rc;
1141 PVBOXGUESTWAIT pWait = RTListGetFirst(&pDevExt->WakeUpList, VBOXGUESTWAIT, ListNode);
1142 if (!pWait)
1143 break;
1144 pWait->fPendingWakeUp = true;
1145 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1146
1147 rc = RTSemEventMultiSignal(pWait->Event);
1148 AssertRC(rc);
1149
1150 RTSpinlockAcquire(pDevExt->EventSpinlock);
1151 pWait->fPendingWakeUp = false;
1152 if (!pWait->fFreeMe)
1153 {
1154 RTListNodeRemove(&pWait->ListNode);
1155 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
1156 }
1157 else
1158 {
1159 pWait->fFreeMe = false;
1160 VBoxGuestWaitFreeLocked(pDevExt, pWait);
1161 }
1162 }
1163 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1164 }
1165}
1166#endif /* VBOXGUEST_USE_DEFERRED_WAKE_UP */
1167
1168
1169/**
1170 * Modifies the guest capabilities.
1171 *
1172 * Should be called during driver init and termination.
1173 *
1174 * @returns VBox status code.
1175 * @param fOr The Or mask (what to enable).
1176 * @param fNot The Not mask (what to disable).
1177 */
1178int VBoxGuestSetGuestCapabilities(uint32_t fOr, uint32_t fNot)
1179{
1180 VMMDevReqGuestCapabilities2 *pReq;
1181 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities);
1182 if (RT_FAILURE(rc))
1183 {
1184 Log(("VBoxGuestSetGuestCapabilities: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n",
1185 sizeof(*pReq), sizeof(*pReq), rc));
1186 return rc;
1187 }
1188
1189 pReq->u32OrMask = fOr;
1190 pReq->u32NotMask = fNot;
1191
1192 rc = VbglGRPerform(&pReq->header);
1193 if (RT_FAILURE(rc))
1194 Log(("VBoxGuestSetGuestCapabilities: VbglGRPerform failed, rc=%Rrc!\n", rc));
1195
1196 VbglGRFree(&pReq->header);
1197 return rc;
1198}
1199
1200
1201/**
1202 * Implements the fast (no input or output) type of IOCtls.
1203 *
1204 * This is currently just a placeholder stub inherited from the support driver code.
1205 *
1206 * @returns VBox status code.
1207 * @param iFunction The IOCtl function number.
1208 * @param pDevExt The device extension.
1209 * @param pSession The session.
1210 */
1211int VBoxGuestCommonIOCtlFast(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
1212{
1213 Log(("VBoxGuestCommonIOCtlFast: iFunction=%#x pDevExt=%p pSession=%p\n", iFunction, pDevExt, pSession));
1214
1215 NOREF(iFunction);
1216 NOREF(pDevExt);
1217 NOREF(pSession);
1218 return VERR_NOT_SUPPORTED;
1219}
1220
1221
1222/**
1223 * Return the VMM device port.
1224 *
1225 * returns IPRT status code.
1226 * @param pDevExt The device extension.
1227 * @param pInfo The request info.
1228 * @param pcbDataReturned (out) contains the number of bytes to return.
1229 */
1230static int VBoxGuestCommonIOCtl_GetVMMDevPort(PVBOXGUESTDEVEXT pDevExt, VBoxGuestPortInfo *pInfo, size_t *pcbDataReturned)
1231{
1232 Log(("VBoxGuestCommonIOCtl: GETVMMDEVPORT\n"));
1233 pInfo->portAddress = pDevExt->IOPortBase;
1234 pInfo->pVMMDevMemory = (VMMDevMemory *)pDevExt->pVMMDevMemory;
1235 if (pcbDataReturned)
1236 *pcbDataReturned = sizeof(*pInfo);
1237 return VINF_SUCCESS;
1238}
1239
1240
1241#ifndef RT_OS_WINDOWS
1242/**
1243 * Set the callback for the kernel mouse handler.
1244 *
1245 * returns IPRT status code.
1246 * @param pDevExt The device extension.
1247 * @param pNotify The new callback information.
1248 * @note This function takes the session spinlock to update the callback
1249 * information, but the interrupt handler will not do this. To make
1250 * sure that the interrupt handler sees a consistent structure, we
1251 * set the function pointer to NULL before updating the data and only
1252 * set it to the correct value once the data is updated. Since the
1253 * interrupt handler executes atomically this ensures that the data is
1254 * valid if the function pointer is non-NULL.
1255 */
1256int VBoxGuestCommonIOCtl_SetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, VBoxGuestMouseSetNotifyCallback *pNotify)
1257{
1258 Log(("VBoxGuestCommonIOCtl: SET_MOUSE_NOTIFY_CALLBACK\n"));
1259
1260 RTSpinlockAcquire(pDevExt->EventSpinlock);
1261 pDevExt->MouseNotifyCallback = *pNotify;
1262 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1263
1264 /* Make sure no active ISR is referencing the old data - hacky but should be
1265 * effective. */
1266 while (pDevExt->cISR > 0)
1267 ASMNopPause();
1268
1269 return VINF_SUCCESS;
1270}
1271#endif
1272
1273
1274/**
1275 * Worker VBoxGuestCommonIOCtl_WaitEvent.
1276 *
1277 * The caller enters the spinlock, we leave it.
1278 *
1279 * @returns VINF_SUCCESS if we've left the spinlock and can return immediately.
1280 */
1281DECLINLINE(int) WaitEventCheckCondition(PVBOXGUESTDEVEXT pDevExt, VBoxGuestWaitEventInfo *pInfo,
1282 int iEvent, const uint32_t fReqEvents)
1283{
1284 uint32_t fMatches = pDevExt->f32PendingEvents & fReqEvents;
1285 if (fMatches)
1286 {
1287 ASMAtomicAndU32(&pDevExt->f32PendingEvents, ~fMatches);
1288 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1289
1290 pInfo->u32EventFlagsOut = fMatches;
1291 pInfo->u32Result = VBOXGUEST_WAITEVENT_OK;
1292 if (fReqEvents & ~((uint32_t)1 << iEvent))
1293 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x\n", pInfo->u32EventFlagsOut));
1294 else
1295 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x/%d\n", pInfo->u32EventFlagsOut, iEvent));
1296 return VINF_SUCCESS;
1297 }
1298 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1299 return VERR_TIMEOUT;
1300}
1301
1302
1303static int VBoxGuestCommonIOCtl_WaitEvent(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1304 VBoxGuestWaitEventInfo *pInfo, size_t *pcbDataReturned, bool fInterruptible)
1305{
1306 const uint32_t fReqEvents = pInfo->u32EventMaskIn;
1307 uint32_t fResEvents;
1308 int iEvent;
1309 PVBOXGUESTWAIT pWait;
1310 int rc;
1311
1312 pInfo->u32EventFlagsOut = 0;
1313 pInfo->u32Result = VBOXGUEST_WAITEVENT_ERROR;
1314 if (pcbDataReturned)
1315 *pcbDataReturned = sizeof(*pInfo);
1316
1317 /*
1318 * Copy and verify the input mask.
1319 */
1320 iEvent = ASMBitFirstSetU32(fReqEvents) - 1;
1321 if (RT_UNLIKELY(iEvent < 0))
1322 {
1323 Log(("VBoxGuestCommonIOCtl: WAITEVENT: Invalid input mask %#x!!\n", fReqEvents));
1324 return VERR_INVALID_PARAMETER;
1325 }
1326
1327 /*
1328 * Check the condition up front, before doing the wait-for-event allocations.
1329 */
1330 RTSpinlockAcquire(pDevExt->EventSpinlock);
1331 rc = WaitEventCheckCondition(pDevExt, pInfo, iEvent, fReqEvents);
1332 if (rc == VINF_SUCCESS)
1333 return rc;
1334
1335 if (!pInfo->u32TimeoutIn)
1336 {
1337 pInfo->u32Result = VBOXGUEST_WAITEVENT_TIMEOUT;
1338 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns VERR_TIMEOUT\n"));
1339 return VERR_TIMEOUT;
1340 }
1341
1342 pWait = VBoxGuestWaitAlloc(pDevExt, pSession);
1343 if (!pWait)
1344 return VERR_NO_MEMORY;
1345 pWait->fReqEvents = fReqEvents;
1346
1347 /*
1348 * We've got the wait entry now, re-enter the spinlock and check for the condition.
1349 * If the wait condition is met, return.
1350 * Otherwise enter into the list and go to sleep waiting for the ISR to signal us.
1351 */
1352 RTSpinlockAcquire(pDevExt->EventSpinlock);
1353 RTListAppend(&pDevExt->WaitList, &pWait->ListNode);
1354 rc = WaitEventCheckCondition(pDevExt, pInfo, iEvent, fReqEvents);
1355 if (rc == VINF_SUCCESS)
1356 {
1357 VBoxGuestWaitFreeUnlocked(pDevExt, pWait);
1358 return rc;
1359 }
1360
1361 if (fInterruptible)
1362 rc = RTSemEventMultiWaitNoResume(pWait->Event,
1363 pInfo->u32TimeoutIn == UINT32_MAX ? RT_INDEFINITE_WAIT : pInfo->u32TimeoutIn);
1364 else
1365 rc = RTSemEventMultiWait(pWait->Event,
1366 pInfo->u32TimeoutIn == UINT32_MAX ? RT_INDEFINITE_WAIT : pInfo->u32TimeoutIn);
1367
1368 /*
1369 * There is one special case here and that's when the semaphore is
1370 * destroyed upon device driver unload. This shouldn't happen of course,
1371 * but in case it does, just get out of here ASAP.
1372 */
1373 if (rc == VERR_SEM_DESTROYED)
1374 return rc;
1375
1376 /*
1377 * Unlink the wait item and dispose of it.
1378 */
1379 RTSpinlockAcquire(pDevExt->EventSpinlock);
1380 fResEvents = pWait->fResEvents;
1381 VBoxGuestWaitFreeLocked(pDevExt, pWait);
1382 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1383
1384 /*
1385 * Now deal with the return code.
1386 */
1387 if ( fResEvents
1388 && fResEvents != UINT32_MAX)
1389 {
1390 pInfo->u32EventFlagsOut = fResEvents;
1391 pInfo->u32Result = VBOXGUEST_WAITEVENT_OK;
1392 if (fReqEvents & ~((uint32_t)1 << iEvent))
1393 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x\n", pInfo->u32EventFlagsOut));
1394 else
1395 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x/%d\n", pInfo->u32EventFlagsOut, iEvent));
1396 rc = VINF_SUCCESS;
1397 }
1398 else if ( fResEvents == UINT32_MAX
1399 || rc == VERR_INTERRUPTED)
1400 {
1401 pInfo->u32Result = VBOXGUEST_WAITEVENT_INTERRUPTED;
1402 rc = VERR_INTERRUPTED;
1403 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns VERR_INTERRUPTED\n"));
1404 }
1405 else if (rc == VERR_TIMEOUT)
1406 {
1407 pInfo->u32Result = VBOXGUEST_WAITEVENT_TIMEOUT;
1408 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns VERR_TIMEOUT (2)\n"));
1409 }
1410 else
1411 {
1412 if (RT_SUCCESS(rc))
1413 {
1414 static unsigned s_cErrors = 0;
1415 if (s_cErrors++ < 32)
1416 LogRel(("VBoxGuestCommonIOCtl: WAITEVENT: returns %Rrc but no events!\n", rc));
1417 rc = VERR_INTERNAL_ERROR;
1418 }
1419 pInfo->u32Result = VBOXGUEST_WAITEVENT_ERROR;
1420 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %Rrc\n", rc));
1421 }
1422
1423 return rc;
1424}
1425
1426
1427static int VBoxGuestCommonIOCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
1428{
1429 PVBOXGUESTWAIT pWait;
1430 PVBOXGUESTWAIT pSafe;
1431 int rc = 0;
1432
1433 Log(("VBoxGuestCommonIOCtl: CANCEL_ALL_WAITEVENTS\n"));
1434
1435 /*
1436 * Walk the event list and wake up anyone with a matching session.
1437 */
1438 RTSpinlockAcquire(pDevExt->EventSpinlock);
1439 RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode)
1440 {
1441 if (pWait->pSession == pSession)
1442 {
1443 pWait->fResEvents = UINT32_MAX;
1444 RTListNodeRemove(&pWait->ListNode);
1445#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1446 RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode);
1447#else
1448 rc |= RTSemEventMultiSignal(pWait->Event);
1449 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
1450#endif
1451 }
1452 }
1453 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1454 Assert(rc == 0);
1455
1456#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
1457 VBoxGuestWaitDoWakeUps(pDevExt);
1458#endif
1459
1460 return VINF_SUCCESS;
1461}
1462
1463/**
1464 * Checks if the VMM request is allowed in the context of the given session.
1465 *
1466 * @returns VINF_SUCCESS or VERR_PERMISSION_DENIED.
1467 * @param pSession The calling session.
1468 * @param enmType The request type.
1469 * @param pReqHdr The request.
1470 */
1471static int VBoxGuestCheckIfVMMReqAllowed(PVBOXGUESTSESSION pSession, VMMDevRequestType enmType,
1472 VMMDevRequestHeader const *pReqHdr)
1473{
1474 /*
1475 * Categorize the request being made.
1476 */
1477 /** @todo This need quite some more work! */
1478 enum
1479 {
1480 kLevel_Invalid, kLevel_NoOne, kLevel_OnlyVBoxGuest, kLevel_OnlyKernel, kLevel_TrustedUsers, kLevel_AllUsers
1481 } enmRequired;
1482 switch (enmType)
1483 {
1484 /*
1485 * Deny access to anything we don't know or provide specialized I/O controls for.
1486 */
1487#ifdef VBOX_WITH_HGCM
1488 case VMMDevReq_HGCMConnect:
1489 case VMMDevReq_HGCMDisconnect:
1490# ifdef VBOX_WITH_64_BITS_GUESTS
1491 case VMMDevReq_HGCMCall32:
1492 case VMMDevReq_HGCMCall64:
1493# else
1494 case VMMDevReq_HGCMCall:
1495# endif /* VBOX_WITH_64_BITS_GUESTS */
1496 case VMMDevReq_HGCMCancel:
1497 case VMMDevReq_HGCMCancel2:
1498#endif /* VBOX_WITH_HGCM */
1499 default:
1500 enmRequired = kLevel_NoOne;
1501 break;
1502
1503 /*
1504 * There are a few things only this driver can do (and it doesn't use
1505 * the VMMRequst I/O control route anyway, but whatever).
1506 */
1507 case VMMDevReq_ReportGuestInfo:
1508 case VMMDevReq_ReportGuestInfo2:
1509 case VMMDevReq_GetHypervisorInfo:
1510 case VMMDevReq_SetHypervisorInfo:
1511 case VMMDevReq_RegisterPatchMemory:
1512 case VMMDevReq_DeregisterPatchMemory:
1513 case VMMDevReq_GetMemBalloonChangeRequest:
1514 enmRequired = kLevel_OnlyVBoxGuest;
1515 break;
1516
1517 /*
1518 * Trusted users apps only.
1519 */
1520 case VMMDevReq_QueryCredentials:
1521 case VMMDevReq_ReportCredentialsJudgement:
1522 case VMMDevReq_RegisterSharedModule:
1523 case VMMDevReq_UnregisterSharedModule:
1524 case VMMDevReq_WriteCoreDump:
1525 case VMMDevReq_GetCpuHotPlugRequest:
1526 case VMMDevReq_SetCpuHotPlugStatus:
1527 case VMMDevReq_CheckSharedModules:
1528 case VMMDevReq_GetPageSharingStatus:
1529 case VMMDevReq_DebugIsPageShared:
1530 case VMMDevReq_ReportGuestStats:
1531 case VMMDevReq_GetStatisticsChangeRequest:
1532 case VMMDevReq_ChangeMemBalloon:
1533 enmRequired = kLevel_TrustedUsers;
1534 break;
1535
1536 /*
1537 * Anyone.
1538 */
1539 case VMMDevReq_GetMouseStatus:
1540 case VMMDevReq_SetMouseStatus:
1541 case VMMDevReq_SetPointerShape:
1542 case VMMDevReq_GetHostVersion:
1543 case VMMDevReq_Idle:
1544 case VMMDevReq_GetHostTime:
1545 case VMMDevReq_SetPowerStatus:
1546 case VMMDevReq_AcknowledgeEvents:
1547 case VMMDevReq_CtlGuestFilterMask:
1548 case VMMDevReq_ReportGuestStatus:
1549 case VMMDevReq_GetDisplayChangeRequest:
1550 case VMMDevReq_VideoModeSupported:
1551 case VMMDevReq_GetHeightReduction:
1552 case VMMDevReq_GetDisplayChangeRequest2:
1553 case VMMDevReq_SetGuestCapabilities:
1554 case VMMDevReq_VideoModeSupported2:
1555 case VMMDevReq_VideoAccelEnable:
1556 case VMMDevReq_VideoAccelFlush:
1557 case VMMDevReq_VideoSetVisibleRegion:
1558 case VMMDevReq_GetSeamlessChangeRequest:
1559 case VMMDevReq_GetVRDPChangeRequest:
1560 case VMMDevReq_LogString:
1561 case VMMDevReq_GetSessionId:
1562 enmRequired = kLevel_AllUsers;
1563 break;
1564
1565 /*
1566 * Depends on the request parameters...
1567 */
1568 /** @todo this have to be changed into an I/O control and the facilities
1569 * tracked in the session so they can automatically be failed when the
1570 * session terminates without reporting the new status.
1571 *
1572 * The information presented by IGuest is not reliable without this! */
1573 case VMMDevReq_ReportGuestCapabilities:
1574 switch (((VMMDevReportGuestStatus const *)pReqHdr)->guestStatus.facility)
1575 {
1576 case VBoxGuestFacilityType_All:
1577 case VBoxGuestFacilityType_VBoxGuestDriver:
1578 enmRequired = kLevel_OnlyVBoxGuest;
1579 break;
1580 case VBoxGuestFacilityType_VBoxService:
1581 enmRequired = kLevel_TrustedUsers;
1582 break;
1583 case VBoxGuestFacilityType_VBoxTrayClient:
1584 case VBoxGuestFacilityType_Seamless:
1585 case VBoxGuestFacilityType_Graphics:
1586 default:
1587 enmRequired = kLevel_AllUsers;
1588 break;
1589 }
1590 break;
1591 }
1592
1593 /*
1594 * Check against the session.
1595 */
1596 switch (enmRequired)
1597 {
1598 default:
1599 case kLevel_NoOne:
1600 break;
1601 case kLevel_OnlyVBoxGuest:
1602 case kLevel_OnlyKernel:
1603 if (pSession->R0Process == NIL_RTR0PROCESS)
1604 return VINF_SUCCESS;
1605 break;
1606 case kLevel_TrustedUsers:
1607 case kLevel_AllUsers:
1608 return VINF_SUCCESS;
1609 }
1610
1611 return VERR_PERMISSION_DENIED;
1612}
1613
1614static int VBoxGuestCommonIOCtl_VMMRequest(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1615 VMMDevRequestHeader *pReqHdr, size_t cbData, size_t *pcbDataReturned)
1616{
1617 int rc;
1618 VMMDevRequestHeader *pReqCopy;
1619
1620 /*
1621 * Validate the header and request size.
1622 */
1623 const VMMDevRequestType enmType = pReqHdr->requestType;
1624 const uint32_t cbReq = pReqHdr->size;
1625 const uint32_t cbMinSize = vmmdevGetRequestSize(enmType);
1626
1627 Log(("VBoxGuestCommonIOCtl: VMMREQUEST type %d\n", pReqHdr->requestType));
1628
1629 if (cbReq < cbMinSize)
1630 {
1631 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid hdr size %#x, expected >= %#x; type=%#x!!\n",
1632 cbReq, cbMinSize, enmType));
1633 return VERR_INVALID_PARAMETER;
1634 }
1635 if (cbReq > cbData)
1636 {
1637 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid size %#x, expected >= %#x (hdr); type=%#x!!\n",
1638 cbData, cbReq, enmType));
1639 return VERR_INVALID_PARAMETER;
1640 }
1641 rc = VbglGRVerify(pReqHdr, cbData);
1642 if (RT_FAILURE(rc))
1643 {
1644 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid header: size %#x, expected >= %#x (hdr); type=%#x; rc=%Rrc!!\n",
1645 cbData, cbReq, enmType, rc));
1646 return rc;
1647 }
1648
1649 rc = VBoxGuestCheckIfVMMReqAllowed(pSession, enmType, pReqHdr);
1650 if (RT_FAILURE(rc))
1651 {
1652 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: Operation not allowed! type=%#x rc=%Rrc\n", enmType, rc));
1653 return rc;
1654 }
1655
1656 /*
1657 * Make a copy of the request in the physical memory heap so
1658 * the VBoxGuestLibrary can more easily deal with the request.
1659 * (This is really a waste of time since the OS or the OS specific
1660 * code has already buffered or locked the input/output buffer, but
1661 * it does makes things a bit simpler wrt to phys address.)
1662 */
1663 rc = VbglGRAlloc(&pReqCopy, cbReq, enmType);
1664 if (RT_FAILURE(rc))
1665 {
1666 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n",
1667 cbReq, cbReq, rc));
1668 return rc;
1669 }
1670 memcpy(pReqCopy, pReqHdr, cbReq);
1671
1672 if (enmType == VMMDevReq_GetMouseStatus) /* clear poll condition. */
1673 pSession->u32MousePosChangedSeq = ASMAtomicUoReadU32(&pDevExt->u32MousePosChangedSeq);
1674
1675 rc = VbglGRPerform(pReqCopy);
1676 if ( RT_SUCCESS(rc)
1677 && RT_SUCCESS(pReqCopy->rc))
1678 {
1679 Assert(rc != VINF_HGCM_ASYNC_EXECUTE);
1680 Assert(pReqCopy->rc != VINF_HGCM_ASYNC_EXECUTE);
1681
1682 memcpy(pReqHdr, pReqCopy, cbReq);
1683 if (pcbDataReturned)
1684 *pcbDataReturned = cbReq;
1685 }
1686 else if (RT_FAILURE(rc))
1687 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: VbglGRPerform - rc=%Rrc!\n", rc));
1688 else
1689 {
1690 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: request execution failed; VMMDev rc=%Rrc!\n", pReqCopy->rc));
1691 rc = pReqCopy->rc;
1692 }
1693
1694 VbglGRFree(pReqCopy);
1695 return rc;
1696}
1697
1698
1699static int VBoxGuestCommonIOCtl_CtlFilterMask(PVBOXGUESTDEVEXT pDevExt, VBoxGuestFilterMaskInfo *pInfo)
1700{
1701 VMMDevCtlGuestFilterMask *pReq;
1702 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
1703 if (RT_FAILURE(rc))
1704 {
1705 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n",
1706 sizeof(*pReq), sizeof(*pReq), rc));
1707 return rc;
1708 }
1709
1710 pReq->u32OrMask = pInfo->u32OrMask;
1711 pReq->u32NotMask = pInfo->u32NotMask;
1712 pReq->u32NotMask &= ~pDevExt->fFixedEvents; /* don't permit these to be cleared! */
1713 rc = VbglGRPerform(&pReq->header);
1714 if (RT_FAILURE(rc))
1715 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: VbglGRPerform failed, rc=%Rrc!\n", rc));
1716
1717 VbglGRFree(&pReq->header);
1718 return rc;
1719}
1720
1721#ifdef VBOX_WITH_HGCM
1722
1723AssertCompile(RT_INDEFINITE_WAIT == (uint32_t)RT_INDEFINITE_WAIT); /* assumed by code below */
1724
1725/** Worker for VBoxGuestHGCMAsyncWaitCallback*. */
1726static int VBoxGuestHGCMAsyncWaitCallbackWorker(VMMDevHGCMRequestHeader volatile *pHdr, PVBOXGUESTDEVEXT pDevExt,
1727 bool fInterruptible, uint32_t cMillies)
1728{
1729 int rc;
1730
1731 /*
1732 * Check to see if the condition was met by the time we got here.
1733 *
1734 * We create a simple poll loop here for dealing with out-of-memory
1735 * conditions since the caller isn't necessarily able to deal with
1736 * us returning too early.
1737 */
1738 PVBOXGUESTWAIT pWait;
1739 for (;;)
1740 {
1741 RTSpinlockAcquire(pDevExt->EventSpinlock);
1742 if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0)
1743 {
1744 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1745 return VINF_SUCCESS;
1746 }
1747 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1748
1749 pWait = VBoxGuestWaitAlloc(pDevExt, NULL);
1750 if (pWait)
1751 break;
1752 if (fInterruptible)
1753 return VERR_INTERRUPTED;
1754 RTThreadSleep(1);
1755 }
1756 pWait->fReqEvents = VMMDEV_EVENT_HGCM;
1757 pWait->pHGCMReq = pHdr;
1758
1759 /*
1760 * Re-enter the spinlock and re-check for the condition.
1761 * If the condition is met, return.
1762 * Otherwise link us into the HGCM wait list and go to sleep.
1763 */
1764 RTSpinlockAcquire(pDevExt->EventSpinlock);
1765 RTListAppend(&pDevExt->HGCMWaitList, &pWait->ListNode);
1766 if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0)
1767 {
1768 VBoxGuestWaitFreeLocked(pDevExt, pWait);
1769 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1770 return VINF_SUCCESS;
1771 }
1772 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
1773
1774 if (fInterruptible)
1775 rc = RTSemEventMultiWaitNoResume(pWait->Event, cMillies);
1776 else
1777 rc = RTSemEventMultiWait(pWait->Event, cMillies);
1778 if (rc == VERR_SEM_DESTROYED)
1779 return rc;
1780
1781 /*
1782 * Unlink, free and return.
1783 */
1784 if ( RT_FAILURE(rc)
1785 && rc != VERR_TIMEOUT
1786 && ( !fInterruptible
1787 || rc != VERR_INTERRUPTED))
1788 LogRel(("VBoxGuestHGCMAsyncWaitCallback: wait failed! %Rrc\n", rc));
1789
1790 VBoxGuestWaitFreeUnlocked(pDevExt, pWait);
1791 return rc;
1792}
1793
1794
1795/**
1796 * This is a callback for dealing with async waits.
1797 *
1798 * It operates in a manner similar to VBoxGuestCommonIOCtl_WaitEvent.
1799 */
1800static DECLCALLBACK(int) VBoxGuestHGCMAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdr, void *pvUser, uint32_t u32User)
1801{
1802 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
1803 Log(("VBoxGuestHGCMAsyncWaitCallback: requestType=%d\n", pHdr->header.requestType));
1804 return VBoxGuestHGCMAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr,
1805 pDevExt,
1806 false /* fInterruptible */,
1807 u32User /* cMillies */);
1808}
1809
1810
1811/**
1812 * This is a callback for dealing with async waits with a timeout.
1813 *
1814 * It operates in a manner similar to VBoxGuestCommonIOCtl_WaitEvent.
1815 */
1816static DECLCALLBACK(int) VBoxGuestHGCMAsyncWaitCallbackInterruptible(VMMDevHGCMRequestHeader *pHdr,
1817 void *pvUser, uint32_t u32User)
1818{
1819 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
1820 Log(("VBoxGuestHGCMAsyncWaitCallbackInterruptible: requestType=%d\n", pHdr->header.requestType));
1821 return VBoxGuestHGCMAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr,
1822 pDevExt,
1823 true /* fInterruptible */,
1824 u32User /* cMillies */ );
1825
1826}
1827
1828
1829static int VBoxGuestCommonIOCtl_HGCMConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1830 VBoxGuestHGCMConnectInfo *pInfo, size_t *pcbDataReturned)
1831{
1832 int rc;
1833
1834 /*
1835 * The VbglHGCMConnect call will invoke the callback if the HGCM
1836 * call is performed in an ASYNC fashion. The function is not able
1837 * to deal with cancelled requests.
1838 */
1839 Log(("VBoxGuestCommonIOCtl: HGCM_CONNECT: %.128s\n",
1840 pInfo->Loc.type == VMMDevHGCMLoc_LocalHost || pInfo->Loc.type == VMMDevHGCMLoc_LocalHost_Existing
1841 ? pInfo->Loc.u.host.achName : "<not local host>"));
1842
1843 rc = VbglR0HGCMInternalConnect(pInfo, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1844 if (RT_SUCCESS(rc))
1845 {
1846 Log(("VBoxGuestCommonIOCtl: HGCM_CONNECT: u32Client=%RX32 result=%Rrc (rc=%Rrc)\n",
1847 pInfo->u32ClientID, pInfo->result, rc));
1848 if (RT_SUCCESS(pInfo->result))
1849 {
1850 /*
1851 * Append the client id to the client id table.
1852 * If the table has somehow become filled up, we'll disconnect the session.
1853 */
1854 unsigned i;
1855 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1856 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1857 if (!pSession->aHGCMClientIds[i])
1858 {
1859 pSession->aHGCMClientIds[i] = pInfo->u32ClientID;
1860 break;
1861 }
1862 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock);
1863 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1864 {
1865 static unsigned s_cErrors = 0;
1866 VBoxGuestHGCMDisconnectInfo Info;
1867
1868 if (s_cErrors++ < 32)
1869 LogRel(("VBoxGuestCommonIOCtl: HGCM_CONNECT: too many HGCMConnect calls for one session!\n"));
1870
1871 Info.result = 0;
1872 Info.u32ClientID = pInfo->u32ClientID;
1873 VbglR0HGCMInternalDisconnect(&Info, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1874 return VERR_TOO_MANY_OPEN_FILES;
1875 }
1876 }
1877 if (pcbDataReturned)
1878 *pcbDataReturned = sizeof(*pInfo);
1879 }
1880 return rc;
1881}
1882
1883
1884static int VBoxGuestCommonIOCtl_HGCMDisconnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VBoxGuestHGCMDisconnectInfo *pInfo,
1885 size_t *pcbDataReturned)
1886{
1887 /*
1888 * Validate the client id and invalidate its entry while we're in the call.
1889 */
1890 int rc;
1891 const uint32_t u32ClientId = pInfo->u32ClientID;
1892 unsigned i;
1893 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1894 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1895 if (pSession->aHGCMClientIds[i] == u32ClientId)
1896 {
1897 pSession->aHGCMClientIds[i] = UINT32_MAX;
1898 break;
1899 }
1900 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock);
1901 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1902 {
1903 static unsigned s_cErrors = 0;
1904 if (s_cErrors++ > 32)
1905 LogRel(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", u32ClientId));
1906 return VERR_INVALID_HANDLE;
1907 }
1908
1909 /*
1910 * The VbglHGCMConnect call will invoke the callback if the HGCM
1911 * call is performed in an ASYNC fashion. The function is not able
1912 * to deal with cancelled requests.
1913 */
1914 Log(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", pInfo->u32ClientID));
1915 rc = VbglR0HGCMInternalDisconnect(pInfo, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1916 if (RT_SUCCESS(rc))
1917 {
1918 Log(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: result=%Rrc\n", pInfo->result));
1919 if (pcbDataReturned)
1920 *pcbDataReturned = sizeof(*pInfo);
1921 }
1922
1923 /* Update the client id array according to the result. */
1924 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1925 if (pSession->aHGCMClientIds[i] == UINT32_MAX)
1926 pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) && RT_SUCCESS(pInfo->result) ? 0 : u32ClientId;
1927 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock);
1928
1929 return rc;
1930}
1931
1932
1933static int VBoxGuestCommonIOCtl_HGCMCall(PVBOXGUESTDEVEXT pDevExt,
1934 PVBOXGUESTSESSION pSession,
1935 VBoxGuestHGCMCallInfo *pInfo,
1936 uint32_t cMillies, bool fInterruptible, bool f32bit, bool fUserData,
1937 size_t cbExtra, size_t cbData, size_t *pcbDataReturned)
1938{
1939 const uint32_t u32ClientId = pInfo->u32ClientID;
1940 uint32_t fFlags;
1941 size_t cbActual;
1942 unsigned i;
1943 int rc;
1944
1945 /*
1946 * Some more validations.
1947 */
1948 if (pInfo->cParms > 4096) /* (Just make sure it doesn't overflow the next check.) */
1949 {
1950 LogRel(("VBoxGuestCommonIOCtl: HGCM_CALL: cParm=%RX32 is not sane\n", pInfo->cParms));
1951 return VERR_INVALID_PARAMETER;
1952 }
1953
1954 cbActual = cbExtra + sizeof(*pInfo);
1955#ifdef RT_ARCH_AMD64
1956 if (f32bit)
1957 cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter32);
1958 else
1959#endif
1960 cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter);
1961 if (cbData < cbActual)
1962 {
1963 LogRel(("VBoxGuestCommonIOCtl: HGCM_CALL: cbData=%#zx (%zu) required size is %#zx (%zu)\n",
1964 cbData, cbActual));
1965 return VERR_INVALID_PARAMETER;
1966 }
1967
1968 /*
1969 * Validate the client id.
1970 */
1971 RTSpinlockAcquire(pDevExt->SessionSpinlock);
1972 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1973 if (pSession->aHGCMClientIds[i] == u32ClientId)
1974 break;
1975 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock);
1976 if (RT_UNLIKELY(i >= RT_ELEMENTS(pSession->aHGCMClientIds)))
1977 {
1978 static unsigned s_cErrors = 0;
1979 if (s_cErrors++ > 32)
1980 LogRel(("VBoxGuestCommonIOCtl: HGCM_CALL: Invalid handle. u32Client=%RX32\n", u32ClientId));
1981 return VERR_INVALID_HANDLE;
1982 }
1983
1984 /*
1985 * The VbglHGCMCall call will invoke the callback if the HGCM
1986 * call is performed in an ASYNC fashion. This function can
1987 * deal with cancelled requests, so we let user more requests
1988 * be interruptible (should add a flag for this later I guess).
1989 */
1990 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: u32Client=%RX32\n", pInfo->u32ClientID));
1991 fFlags = !fUserData && pSession->R0Process == NIL_RTR0PROCESS ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1992#ifdef RT_ARCH_AMD64
1993 if (f32bit)
1994 {
1995 if (fInterruptible)
1996 rc = VbglR0HGCMInternalCall32(pInfo, cbData - cbExtra, fFlags, VBoxGuestHGCMAsyncWaitCallbackInterruptible, pDevExt, cMillies);
1997 else
1998 rc = VbglR0HGCMInternalCall32(pInfo, cbData - cbExtra, fFlags, VBoxGuestHGCMAsyncWaitCallback, pDevExt, cMillies);
1999 }
2000 else
2001#endif
2002 {
2003 if (fInterruptible)
2004 rc = VbglR0HGCMInternalCall(pInfo, cbData - cbExtra, fFlags, VBoxGuestHGCMAsyncWaitCallbackInterruptible, pDevExt, cMillies);
2005 else
2006 rc = VbglR0HGCMInternalCall(pInfo, cbData - cbExtra, fFlags, VBoxGuestHGCMAsyncWaitCallback, pDevExt, cMillies);
2007 }
2008 if (RT_SUCCESS(rc))
2009 {
2010 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: result=%Rrc\n", pInfo->result));
2011 if (pcbDataReturned)
2012 *pcbDataReturned = cbActual;
2013 }
2014 else
2015 {
2016 if ( rc != VERR_INTERRUPTED
2017 && rc != VERR_TIMEOUT)
2018 {
2019 static unsigned s_cErrors = 0;
2020 if (s_cErrors++ < 32)
2021 LogRel(("VBoxGuestCommonIOCtl: HGCM_CALL: %s Failed. rc=%Rrc.\n", f32bit ? "32" : "64", rc));
2022 }
2023 else
2024 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: %s Failed. rc=%Rrc.\n", f32bit ? "32" : "64", rc));
2025 }
2026 return rc;
2027}
2028
2029
2030#endif /* VBOX_WITH_HGCM */
2031
2032/**
2033 * Handle VBOXGUEST_IOCTL_CHECK_BALLOON from R3.
2034 *
2035 * Ask the host for the size of the balloon and try to set it accordingly. If
2036 * this approach fails because it's not supported, return with fHandleInR3 set
2037 * and let the user land supply memory we can lock via the other ioctl.
2038 *
2039 * @returns VBox status code.
2040 *
2041 * @param pDevExt The device extension.
2042 * @param pSession The session.
2043 * @param pInfo The output buffer.
2044 * @param pcbDataReturned Where to store the amount of returned data. Can
2045 * be NULL.
2046 */
2047static int VBoxGuestCommonIOCtl_CheckMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2048 VBoxGuestCheckBalloonInfo *pInfo, size_t *pcbDataReturned)
2049{
2050 VMMDevGetMemBalloonChangeRequest *pReq;
2051 int rc;
2052
2053 Log(("VBoxGuestCommonIOCtl: CHECK_MEMORY_BALLOON\n"));
2054 rc = RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx);
2055 AssertRCReturn(rc, rc);
2056
2057 /*
2058 * The first user trying to query/change the balloon becomes the
2059 * owner and owns it until the session is closed (vboxGuestCloseMemBalloon).
2060 */
2061 if ( pDevExt->MemBalloon.pOwner != pSession
2062 && pDevExt->MemBalloon.pOwner == NULL)
2063 pDevExt->MemBalloon.pOwner = pSession;
2064
2065 if (pDevExt->MemBalloon.pOwner == pSession)
2066 {
2067 rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest);
2068 if (RT_SUCCESS(rc))
2069 {
2070 /*
2071 * This is a response to that event. Setting this bit means that
2072 * we request the value from the host and change the guest memory
2073 * balloon according to this value.
2074 */
2075 pReq->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
2076 rc = VbglGRPerform(&pReq->header);
2077 if (RT_SUCCESS(rc))
2078 {
2079 Assert(pDevExt->MemBalloon.cMaxChunks == pReq->cPhysMemChunks || pDevExt->MemBalloon.cMaxChunks == 0);
2080 pDevExt->MemBalloon.cMaxChunks = pReq->cPhysMemChunks;
2081
2082 pInfo->cBalloonChunks = pReq->cBalloonChunks;
2083 pInfo->fHandleInR3 = false;
2084
2085 rc = vboxGuestSetBalloonSizeKernel(pDevExt, pReq->cBalloonChunks, &pInfo->fHandleInR3);
2086 /* Ignore various out of memory failures. */
2087 if ( rc == VERR_NO_MEMORY
2088 || rc == VERR_NO_PHYS_MEMORY
2089 || rc == VERR_NO_CONT_MEMORY)
2090 rc = VINF_SUCCESS;
2091
2092 if (pcbDataReturned)
2093 *pcbDataReturned = sizeof(VBoxGuestCheckBalloonInfo);
2094 }
2095 else
2096 LogRel(("VBoxGuestCommonIOCtl: CHECK_MEMORY_BALLOON: VbglGRPerform failed. rc=%Rrc\n", rc));
2097 VbglGRFree(&pReq->header);
2098 }
2099 }
2100 else
2101 rc = VERR_PERMISSION_DENIED;
2102
2103 RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx);
2104 Log(("VBoxGuestCommonIOCtl: CHECK_MEMORY_BALLOON returns %Rrc\n", rc));
2105 return rc;
2106}
2107
2108
2109/**
2110 * Handle a request for changing the memory balloon.
2111 *
2112 * @returns VBox status code.
2113 *
2114 * @param pDevExt The device extention.
2115 * @param pSession The session.
2116 * @param pInfo The change request structure (input).
2117 * @param pcbDataReturned Where to store the amount of returned data. Can
2118 * be NULL.
2119 */
2120static int VBoxGuestCommonIOCtl_ChangeMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2121 VBoxGuestChangeBalloonInfo *pInfo, size_t *pcbDataReturned)
2122{
2123 int rc = RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx);
2124 AssertRCReturn(rc, rc);
2125
2126 if (!pDevExt->MemBalloon.fUseKernelAPI)
2127 {
2128 /*
2129 * The first user trying to query/change the balloon becomes the
2130 * owner and owns it until the session is closed (vboxGuestCloseMemBalloon).
2131 */
2132 if ( pDevExt->MemBalloon.pOwner != pSession
2133 && pDevExt->MemBalloon.pOwner == NULL)
2134 pDevExt->MemBalloon.pOwner = pSession;
2135
2136 if (pDevExt->MemBalloon.pOwner == pSession)
2137 {
2138 rc = vboxGuestSetBalloonSizeFromUser(pDevExt, pSession, pInfo->u64ChunkAddr, !!pInfo->fInflate);
2139 if (pcbDataReturned)
2140 *pcbDataReturned = 0;
2141 }
2142 else
2143 rc = VERR_PERMISSION_DENIED;
2144 }
2145 else
2146 rc = VERR_PERMISSION_DENIED;
2147
2148 RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx);
2149 return rc;
2150}
2151
2152
2153/**
2154 * Handle a request for writing a core dump of the guest on the host.
2155 *
2156 * @returns VBox status code.
2157 *
2158 * @param pDevExt The device extension.
2159 * @param pInfo The output buffer.
2160 */
2161static int VBoxGuestCommonIOCtl_WriteCoreDump(PVBOXGUESTDEVEXT pDevExt, VBoxGuestWriteCoreDump *pInfo)
2162{
2163 VMMDevReqWriteCoreDump *pReq = NULL;
2164 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_WriteCoreDump);
2165 if (RT_FAILURE(rc))
2166 {
2167 Log(("VBoxGuestCommonIOCtl: WRITE_CORE_DUMP: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n",
2168 sizeof(*pReq), sizeof(*pReq), rc));
2169 return rc;
2170 }
2171
2172 pReq->fFlags = pInfo->fFlags;
2173 rc = VbglGRPerform(&pReq->header);
2174 if (RT_FAILURE(rc))
2175 Log(("VBoxGuestCommonIOCtl: WRITE_CORE_DUMP: VbglGRPerform failed, rc=%Rrc!\n", rc));
2176
2177 VbglGRFree(&pReq->header);
2178 return rc;
2179}
2180
2181
2182#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
2183/**
2184 * Enables the VRDP session and saves its session ID.
2185 *
2186 * @returns VBox status code.
2187 *
2188 * @param pDevExt The device extention.
2189 * @param pSession The session.
2190 */
2191static int VBoxGuestCommonIOCtl_EnableVRDPSession(VBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
2192{
2193 /* Nothing to do here right now, since this only is supported on Windows at the moment. */
2194 return VERR_NOT_IMPLEMENTED;
2195}
2196
2197
2198/**
2199 * Disables the VRDP session.
2200 *
2201 * @returns VBox status code.
2202 *
2203 * @param pDevExt The device extention.
2204 * @param pSession The session.
2205 */
2206static int VBoxGuestCommonIOCtl_DisableVRDPSession(VBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
2207{
2208 /* Nothing to do here right now, since this only is supported on Windows at the moment. */
2209 return VERR_NOT_IMPLEMENTED;
2210}
2211#endif /* VBOX_WITH_VRDP_SESSION_HANDLING */
2212
2213#ifdef DEBUG
2214/** Unit test SetMouseStatus instead of really executing the request. */
2215static bool g_test_fSetMouseStatus = false;
2216/** When unit testing SetMouseStatus, the fake RC for the GR to return. */
2217static int g_test_SetMouseStatusGRRC;
2218/** When unit testing SetMouseStatus this will be set to the status passed to
2219 * the GR. */
2220static uint32_t g_test_statusSetMouseStatus;
2221#endif
2222
2223static int vboxguestcommonSetMouseStatus(uint32_t fFeatures)
2224{
2225 VMMDevReqMouseStatus *pReq;
2226 int rc;
2227
2228 LogRelFlowFunc(("fFeatures=%u\n", (int) fFeatures));
2229 rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetMouseStatus);
2230 if (RT_SUCCESS(rc))
2231 {
2232 pReq->mouseFeatures = fFeatures;
2233 pReq->pointerXPos = 0;
2234 pReq->pointerYPos = 0;
2235#ifdef DEBUG
2236 if (g_test_fSetMouseStatus)
2237 {
2238 g_test_statusSetMouseStatus = pReq->mouseFeatures;
2239 rc = g_test_SetMouseStatusGRRC;
2240 }
2241 else
2242#endif
2243 rc = VbglGRPerform(&pReq->header);
2244 VbglGRFree(&pReq->header);
2245 }
2246 LogRelFlowFunc(("rc=%Rrc\n", rc));
2247 return rc;
2248}
2249
2250
2251/**
2252 * Sets the mouse status features for this session and updates them
2253 * globally. We aim to ensure that if several threads call this in
2254 * parallel the most recent status will always end up being set.
2255 *
2256 * @returns VBox status code.
2257 *
2258 * @param pDevExt The device extention.
2259 * @param pSession The session.
2260 * @param fFeatures New bitmap of enabled features.
2261 */
2262static int VBoxGuestCommonIOCtl_SetMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fFeatures)
2263{
2264 uint32_t fNewDevExtStatus = 0;
2265 unsigned i;
2266 int rc;
2267 /* Exit early if nothing has changed - hack to work around the
2268 * Windows Additions not using the common code. */
2269 bool fNoAction;
2270
2271 RTSpinlockAcquire(pDevExt->SessionSpinlock);
2272
2273 for (i = 0; i < sizeof(fFeatures) * 8; i++)
2274 {
2275 if (RT_BIT_32(i) & VMMDEV_MOUSE_GUEST_MASK)
2276 {
2277 if ( (RT_BIT_32(i) & fFeatures)
2278 && !(RT_BIT_32(i) & pSession->fMouseStatus))
2279 pDevExt->acMouseFeatureUsage[i]++;
2280 else if ( !(RT_BIT_32(i) & fFeatures)
2281 && (RT_BIT_32(i) & pSession->fMouseStatus))
2282 pDevExt->acMouseFeatureUsage[i]--;
2283 }
2284 if (pDevExt->acMouseFeatureUsage[i] > 0)
2285 fNewDevExtStatus |= RT_BIT_32(i);
2286 }
2287
2288 pSession->fMouseStatus = fFeatures & VMMDEV_MOUSE_GUEST_MASK;
2289 fNoAction = (pDevExt->fMouseStatus == fNewDevExtStatus);
2290 pDevExt->fMouseStatus = fNewDevExtStatus;
2291
2292 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock);
2293 if (fNoAction)
2294 return VINF_SUCCESS;
2295
2296 do
2297 {
2298 fNewDevExtStatus = pDevExt->fMouseStatus;
2299 rc = vboxguestcommonSetMouseStatus(fNewDevExtStatus);
2300 } while ( RT_SUCCESS(rc)
2301 && fNewDevExtStatus != pDevExt->fMouseStatus);
2302
2303 return rc;
2304}
2305
2306
2307#ifdef DEBUG
2308/** Unit test for the SET_MOUSE_STATUS IoCtl. Since this is closely tied to
2309 * the code in question it probably makes most sense to keep it next to the
2310 * code. */
2311static void testSetMouseStatus(void)
2312{
2313 uint32_t u32Data;
2314 int rc;
2315 RTSPINLOCK Spinlock;
2316
2317 g_test_fSetMouseStatus = true;
2318 rc = RTSpinlockCreate(&Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestTest");
2319 AssertRCReturnVoid(rc);
2320 {
2321 VBOXGUESTDEVEXT DevExt = { 0 };
2322 VBOXGUESTSESSION Session = { 0 };
2323
2324 g_test_statusSetMouseStatus = ~0;
2325 g_test_SetMouseStatusGRRC = VINF_SUCCESS;
2326 DevExt.SessionSpinlock = Spinlock;
2327 u32Data = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
2328 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2329 &Session, &u32Data, sizeof(u32Data), NULL);
2330 AssertRCSuccess(rc);
2331 AssertMsg( g_test_statusSetMouseStatus
2332 == VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE,
2333 ("Actual status: 0x%x\n", g_test_statusSetMouseStatus));
2334 DevExt.acMouseFeatureUsage[ASMBitFirstSetU32(VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR) - 1] = 1;
2335 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2336 &Session, &u32Data, sizeof(u32Data), NULL);
2337 AssertRCSuccess(rc);
2338 AssertMsg( g_test_statusSetMouseStatus
2339 == ( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
2340 | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR),
2341 ("Actual status: 0x%x\n", g_test_statusSetMouseStatus));
2342 u32Data = VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE; /* Can't change this */
2343 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2344 &Session, &u32Data, sizeof(u32Data), NULL);
2345 AssertRCSuccess(rc);
2346 AssertMsg( g_test_statusSetMouseStatus
2347 == VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR,
2348 ("Actual status: 0x%x\n", g_test_statusSetMouseStatus));
2349 u32Data = VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR;
2350 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2351 &Session, &u32Data, sizeof(u32Data), NULL);
2352 AssertRCSuccess(rc);
2353 AssertMsg( g_test_statusSetMouseStatus
2354 == VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR,
2355 ("Actual status: 0x%x\n", g_test_statusSetMouseStatus));
2356 u32Data = 0;
2357 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2358 &Session, &u32Data, sizeof(u32Data), NULL);
2359 AssertRCSuccess(rc);
2360 AssertMsg( g_test_statusSetMouseStatus
2361 == VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR,
2362 ("Actual status: 0x%x\n", g_test_statusSetMouseStatus));
2363 AssertMsg(DevExt.acMouseFeatureUsage[ASMBitFirstSetU32(VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR) - 1] == 1,
2364 ("Actual value: %d\n", DevExt.acMouseFeatureUsage[ASMBitFirstSetU32(VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR)]));
2365 g_test_SetMouseStatusGRRC = VERR_UNRESOLVED_ERROR;
2366 /* This should succeed as the host request should not be made
2367 * since nothing has changed. */
2368 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2369 &Session, &u32Data, sizeof(u32Data), NULL);
2370 AssertRCSuccess(rc);
2371 /* This should fail with VERR_UNRESOLVED_ERROR as set above. */
2372 u32Data = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
2373 rc = VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &DevExt,
2374 &Session, &u32Data, sizeof(u32Data), NULL);
2375 AssertMsg(rc == VERR_UNRESOLVED_ERROR, ("rc == %Rrc\n", rc));
2376 /* Untested paths: out of memory; race setting status to host */
2377 }
2378 RTSpinlockDestroy(Spinlock);
2379 g_test_fSetMouseStatus = false;
2380}
2381#endif
2382
2383
2384/**
2385 * Guest backdoor logging.
2386 *
2387 * @returns VBox status code.
2388 *
2389 * @param pDevExt The device extension.
2390 * @param pch The log message (need not be NULL terminated).
2391 * @param cbData Size of the buffer.
2392 * @param pcbDataReturned Where to store the amount of returned data. Can be NULL.
2393 */
2394static int VBoxGuestCommonIOCtl_Log(PVBOXGUESTDEVEXT pDevExt, const char *pch, size_t cbData, size_t *pcbDataReturned)
2395{
2396 NOREF(pch);
2397 NOREF(cbData);
2398 if (pDevExt->fLoggingEnabled)
2399 RTLogBackdoorPrintf("%.*s", cbData, pch);
2400 else
2401 Log(("%.*s", cbData, pch));
2402 if (pcbDataReturned)
2403 *pcbDataReturned = 0;
2404 return VINF_SUCCESS;
2405}
2406
2407
2408/**
2409 * Common IOCtl for user to kernel and kernel to kernel communication.
2410 *
2411 * This function only does the basic validation and then invokes
2412 * worker functions that takes care of each specific function.
2413 *
2414 * @returns VBox status code.
2415 *
2416 * @param iFunction The requested function.
2417 * @param pDevExt The device extension.
2418 * @param pSession The client session.
2419 * @param pvData The input/output data buffer. Can be NULL depending on the function.
2420 * @param cbData The max size of the data buffer.
2421 * @param pcbDataReturned Where to store the amount of returned data. Can be NULL.
2422 */
2423int VBoxGuestCommonIOCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2424 void *pvData, size_t cbData, size_t *pcbDataReturned)
2425{
2426 int rc;
2427 Log(("VBoxGuestCommonIOCtl: iFunction=%#x pDevExt=%p pSession=%p pvData=%p cbData=%zu\n",
2428 iFunction, pDevExt, pSession, pvData, cbData));
2429
2430 /*
2431 * Make sure the returned data size is set to zero.
2432 */
2433 if (pcbDataReturned)
2434 *pcbDataReturned = 0;
2435
2436 /*
2437 * Define some helper macros to simplify validation.
2438 */
2439#define CHECKRET_RING0(mnemonic) \
2440 do { \
2441 if (pSession->R0Process != NIL_RTR0PROCESS) \
2442 { \
2443 LogFunc((mnemonic ": Ring-0 only, caller is %RTproc/%p\n", \
2444 pSession->Process, (uintptr_t)pSession->R0Process)); \
2445 return VERR_PERMISSION_DENIED; \
2446 } \
2447 } while (0)
2448#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
2449 do { \
2450 if (cbData < (cbMin)) \
2451 { \
2452 LogFunc((mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
2453 cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
2454 return VERR_BUFFER_OVERFLOW; \
2455 } \
2456 if ((cbMin) != 0 && !VALID_PTR(pvData)) \
2457 { \
2458 LogFunc((mnemonic ": Invalid pointer %p\n", pvData)); \
2459 return VERR_INVALID_POINTER; \
2460 } \
2461 } while (0)
2462#define CHECKRET_SIZE(mnemonic, cb) \
2463 do { \
2464 if (cbData != (cb)) \
2465 { \
2466 LogFunc((mnemonic ": cbData=%#zx (%zu) expected is %#zx (%zu)\n", \
2467 cbData, cbData, (size_t)(cb), (size_t)(cb))); \
2468 return VERR_BUFFER_OVERFLOW; \
2469 } \
2470 if ((cb) != 0 && !VALID_PTR(pvData)) \
2471 { \
2472 LogFunc((mnemonic ": Invalid pointer %p\n", pvData)); \
2473 return VERR_INVALID_POINTER; \
2474 } \
2475 } while (0)
2476
2477
2478 /*
2479 * Deal with variably sized requests first.
2480 */
2481 rc = VINF_SUCCESS;
2482 if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_VMMREQUEST(0)))
2483 {
2484 CHECKRET_MIN_SIZE("VMMREQUEST", sizeof(VMMDevRequestHeader));
2485 rc = VBoxGuestCommonIOCtl_VMMRequest(pDevExt, pSession, (VMMDevRequestHeader *)pvData, cbData, pcbDataReturned);
2486 }
2487#ifdef VBOX_WITH_HGCM
2488 /*
2489 * These ones are a bit tricky.
2490 */
2491 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL(0)))
2492 {
2493 bool fInterruptible = pSession->R0Process != NIL_RTR0PROCESS;
2494 CHECKRET_MIN_SIZE("HGCM_CALL", sizeof(VBoxGuestHGCMCallInfo));
2495 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, (VBoxGuestHGCMCallInfo *)pvData, RT_INDEFINITE_WAIT,
2496 fInterruptible, false /*f32bit*/, false /* fUserData */,
2497 0, cbData, pcbDataReturned);
2498 }
2499 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_TIMED(0)))
2500 {
2501 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pvData;
2502 CHECKRET_MIN_SIZE("HGCM_CALL_TIMED", sizeof(VBoxGuestHGCMCallInfoTimed));
2503 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, &pInfo->info, pInfo->u32Timeout,
2504 !!pInfo->fInterruptible || pSession->R0Process != NIL_RTR0PROCESS,
2505 false /*f32bit*/, false /* fUserData */,
2506 RT_OFFSETOF(VBoxGuestHGCMCallInfoTimed, info), cbData, pcbDataReturned);
2507 }
2508 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_USERDATA(0)))
2509 {
2510 bool fInterruptible = true;
2511 CHECKRET_MIN_SIZE("HGCM_CALL", sizeof(VBoxGuestHGCMCallInfo));
2512 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, (VBoxGuestHGCMCallInfo *)pvData, RT_INDEFINITE_WAIT,
2513 fInterruptible, false /*f32bit*/, true /* fUserData */,
2514 0, cbData, pcbDataReturned);
2515 }
2516# ifdef RT_ARCH_AMD64
2517 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_32(0)))
2518 {
2519 bool fInterruptible = pSession->R0Process != NIL_RTR0PROCESS;
2520 CHECKRET_MIN_SIZE("HGCM_CALL", sizeof(VBoxGuestHGCMCallInfo));
2521 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, (VBoxGuestHGCMCallInfo *)pvData, RT_INDEFINITE_WAIT,
2522 fInterruptible, true /*f32bit*/, false /* fUserData */,
2523 0, cbData, pcbDataReturned);
2524 }
2525 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_TIMED_32(0)))
2526 {
2527 CHECKRET_MIN_SIZE("HGCM_CALL_TIMED", sizeof(VBoxGuestHGCMCallInfoTimed));
2528 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pvData;
2529 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, &pInfo->info, pInfo->u32Timeout,
2530 !!pInfo->fInterruptible || pSession->R0Process != NIL_RTR0PROCESS,
2531 true /*f32bit*/, false /* fUserData */,
2532 RT_OFFSETOF(VBoxGuestHGCMCallInfoTimed, info), cbData, pcbDataReturned);
2533 }
2534# endif
2535#endif /* VBOX_WITH_HGCM */
2536 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_LOG(0)))
2537 {
2538 CHECKRET_MIN_SIZE("LOG", 1);
2539 rc = VBoxGuestCommonIOCtl_Log(pDevExt, (char *)pvData, cbData, pcbDataReturned);
2540 }
2541 else
2542 {
2543 switch (iFunction)
2544 {
2545 case VBOXGUEST_IOCTL_GETVMMDEVPORT:
2546 CHECKRET_RING0("GETVMMDEVPORT");
2547 CHECKRET_MIN_SIZE("GETVMMDEVPORT", sizeof(VBoxGuestPortInfo));
2548 rc = VBoxGuestCommonIOCtl_GetVMMDevPort(pDevExt, (VBoxGuestPortInfo *)pvData, pcbDataReturned);
2549 break;
2550
2551#ifndef RT_OS_WINDOWS /* Windows has its own implementation of this. */
2552 case VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK:
2553 CHECKRET_RING0("SET_MOUSE_NOTIFY_CALLBACK");
2554 CHECKRET_SIZE("SET_MOUSE_NOTIFY_CALLBACK", sizeof(VBoxGuestMouseSetNotifyCallback));
2555 rc = VBoxGuestCommonIOCtl_SetMouseNotifyCallback(pDevExt, (VBoxGuestMouseSetNotifyCallback *)pvData);
2556 break;
2557#endif
2558
2559 case VBOXGUEST_IOCTL_WAITEVENT:
2560 CHECKRET_MIN_SIZE("WAITEVENT", sizeof(VBoxGuestWaitEventInfo));
2561 rc = VBoxGuestCommonIOCtl_WaitEvent(pDevExt, pSession, (VBoxGuestWaitEventInfo *)pvData,
2562 pcbDataReturned, pSession->R0Process != NIL_RTR0PROCESS);
2563 break;
2564
2565 case VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS:
2566 if (cbData != 0)
2567 rc = VERR_INVALID_PARAMETER;
2568 rc = VBoxGuestCommonIOCtl_CancelAllWaitEvents(pDevExt, pSession);
2569 break;
2570
2571 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
2572 CHECKRET_MIN_SIZE("CTL_FILTER_MASK", sizeof(VBoxGuestFilterMaskInfo));
2573 rc = VBoxGuestCommonIOCtl_CtlFilterMask(pDevExt, (VBoxGuestFilterMaskInfo *)pvData);
2574 break;
2575
2576#ifdef VBOX_WITH_HGCM
2577 case VBOXGUEST_IOCTL_HGCM_CONNECT:
2578# ifdef RT_ARCH_AMD64
2579 case VBOXGUEST_IOCTL_HGCM_CONNECT_32:
2580# endif
2581 CHECKRET_MIN_SIZE("HGCM_CONNECT", sizeof(VBoxGuestHGCMConnectInfo));
2582 rc = VBoxGuestCommonIOCtl_HGCMConnect(pDevExt, pSession, (VBoxGuestHGCMConnectInfo *)pvData, pcbDataReturned);
2583 break;
2584
2585 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
2586# ifdef RT_ARCH_AMD64
2587 case VBOXGUEST_IOCTL_HGCM_DISCONNECT_32:
2588# endif
2589 CHECKRET_MIN_SIZE("HGCM_DISCONNECT", sizeof(VBoxGuestHGCMDisconnectInfo));
2590 rc = VBoxGuestCommonIOCtl_HGCMDisconnect(pDevExt, pSession, (VBoxGuestHGCMDisconnectInfo *)pvData, pcbDataReturned);
2591 break;
2592#endif /* VBOX_WITH_HGCM */
2593
2594 case VBOXGUEST_IOCTL_CHECK_BALLOON:
2595 CHECKRET_MIN_SIZE("CHECK_MEMORY_BALLOON", sizeof(VBoxGuestCheckBalloonInfo));
2596 rc = VBoxGuestCommonIOCtl_CheckMemoryBalloon(pDevExt, pSession, (VBoxGuestCheckBalloonInfo *)pvData, pcbDataReturned);
2597 break;
2598
2599 case VBOXGUEST_IOCTL_CHANGE_BALLOON:
2600 CHECKRET_MIN_SIZE("CHANGE_MEMORY_BALLOON", sizeof(VBoxGuestChangeBalloonInfo));
2601 rc = VBoxGuestCommonIOCtl_ChangeMemoryBalloon(pDevExt, pSession, (VBoxGuestChangeBalloonInfo *)pvData, pcbDataReturned);
2602 break;
2603
2604 case VBOXGUEST_IOCTL_WRITE_CORE_DUMP:
2605 CHECKRET_MIN_SIZE("WRITE_CORE_DUMP", sizeof(VBoxGuestWriteCoreDump));
2606 rc = VBoxGuestCommonIOCtl_WriteCoreDump(pDevExt, (VBoxGuestWriteCoreDump *)pvData);
2607 break;
2608
2609#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
2610 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
2611 rc = VBoxGuestCommonIOCtl_EnableVRDPSession(pDevExt, pSession);
2612 break;
2613
2614 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
2615 rc = VBoxGuestCommonIOCtl_DisableVRDPSession(pDevExt, pSession);
2616 break;
2617#endif /* VBOX_WITH_VRDP_SESSION_HANDLING */
2618 case VBOXGUEST_IOCTL_SET_MOUSE_STATUS:
2619 CHECKRET_SIZE("SET_MOUSE_STATUS", sizeof(uint32_t));
2620 rc = VBoxGuestCommonIOCtl_SetMouseStatus(pDevExt, pSession,
2621 *(uint32_t *)pvData);
2622 break;
2623
2624 default:
2625 {
2626 LogRel(("VBoxGuestCommonIOCtl: Unknown request iFunction=%#x Stripped size=%#x\n", iFunction,
2627 VBOXGUEST_IOCTL_STRIP_SIZE(iFunction)));
2628 rc = VERR_NOT_SUPPORTED;
2629 break;
2630 }
2631 }
2632 }
2633
2634 Log(("VBoxGuestCommonIOCtl: returns %Rrc *pcbDataReturned=%zu\n", rc, pcbDataReturned ? *pcbDataReturned : 0));
2635 return rc;
2636}
2637
2638
2639
2640/**
2641 * Common interrupt service routine.
2642 *
2643 * This deals with events and with waking up thread waiting for those events.
2644 *
2645 * @returns true if it was our interrupt, false if it wasn't.
2646 * @param pDevExt The VBoxGuest device extension.
2647 */
2648bool VBoxGuestCommonISR(PVBOXGUESTDEVEXT pDevExt)
2649{
2650#ifndef RT_OS_WINDOWS
2651 VBoxGuestMouseSetNotifyCallback MouseNotifyCallback = { NULL, NULL };
2652#endif
2653 bool fMousePositionChanged = false;
2654 VMMDevEvents volatile *pReq = pDevExt->pIrqAckEvents;
2655 int rc = 0;
2656 bool fOurIrq;
2657
2658 /*
2659 * Make sure we've initialized the device extension.
2660 */
2661 if (RT_UNLIKELY(!pReq))
2662 return false;
2663
2664 /*
2665 * Enter the spinlock, increase the ISR count and check if it's our IRQ or
2666 * not.
2667 */
2668 RTSpinlockAcquire(pDevExt->EventSpinlock);
2669 ASMAtomicIncU32(&pDevExt->cISR);
2670 fOurIrq = pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents;
2671 if (fOurIrq)
2672 {
2673 /*
2674 * Acknowlegde events.
2675 * We don't use VbglGRPerform here as it may take another spinlocks.
2676 */
2677 pReq->header.rc = VERR_INTERNAL_ERROR;
2678 pReq->events = 0;
2679 ASMCompilerBarrier();
2680 ASMOutU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST, (uint32_t)pDevExt->PhysIrqAckEvents);
2681 ASMCompilerBarrier(); /* paranoia */
2682 if (RT_SUCCESS(pReq->header.rc))
2683 {
2684 uint32_t fEvents = pReq->events;
2685 PVBOXGUESTWAIT pWait;
2686 PVBOXGUESTWAIT pSafe;
2687
2688 Log(("VBoxGuestCommonISR: acknowledge events succeeded %#RX32\n", fEvents));
2689
2690 /*
2691 * VMMDEV_EVENT_MOUSE_POSITION_CHANGED can only be polled for.
2692 */
2693 if (fEvents & VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
2694 {
2695#ifndef RT_OS_WINDOWS
2696 MouseNotifyCallback = pDevExt->MouseNotifyCallback;
2697#endif
2698 fMousePositionChanged = true;
2699 fEvents &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED;
2700 }
2701
2702#ifdef VBOX_WITH_HGCM
2703 /*
2704 * The HGCM event/list is kind of different in that we evaluate all entries.
2705 */
2706 if (fEvents & VMMDEV_EVENT_HGCM)
2707 {
2708 RTListForEachSafe(&pDevExt->HGCMWaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode)
2709 {
2710 if (pWait->pHGCMReq->fu32Flags & VBOX_HGCM_REQ_DONE)
2711 {
2712 pWait->fResEvents = VMMDEV_EVENT_HGCM;
2713 RTListNodeRemove(&pWait->ListNode);
2714# ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
2715 RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode);
2716# else
2717 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
2718 rc |= RTSemEventMultiSignal(pWait->Event);
2719# endif
2720 }
2721 }
2722 fEvents &= ~VMMDEV_EVENT_HGCM;
2723 }
2724#endif
2725
2726 /*
2727 * Normal FIFO waiter evaluation.
2728 */
2729 fEvents |= pDevExt->f32PendingEvents;
2730 RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode)
2731 {
2732 if ( (pWait->fReqEvents & fEvents)
2733 && !pWait->fResEvents)
2734 {
2735 pWait->fResEvents = pWait->fReqEvents & fEvents;
2736 fEvents &= ~pWait->fResEvents;
2737 RTListNodeRemove(&pWait->ListNode);
2738#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
2739 RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode);
2740#else
2741 RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode);
2742 rc |= RTSemEventMultiSignal(pWait->Event);
2743#endif
2744 if (!fEvents)
2745 break;
2746 }
2747 }
2748 ASMAtomicWriteU32(&pDevExt->f32PendingEvents, fEvents);
2749 }
2750 else /* something is serious wrong... */
2751 Log(("VBoxGuestCommonISR: acknowledge events failed rc=%Rrc (events=%#x)!!\n",
2752 pReq->header.rc, pReq->events));
2753 }
2754 else
2755 LogFlow(("VBoxGuestCommonISR: not ours\n"));
2756
2757 RTSpinlockReleaseNoInts(pDevExt->EventSpinlock);
2758
2759#if defined(VBOXGUEST_USE_DEFERRED_WAKE_UP) && !defined(RT_OS_WINDOWS)
2760 /*
2761 * Do wake-ups.
2762 * Note. On Windows this isn't possible at this IRQL, so a DPC will take
2763 * care of it.
2764 */
2765 VBoxGuestWaitDoWakeUps(pDevExt);
2766#endif
2767
2768 /*
2769 * Work the poll and async notification queues on OSes that implements that.
2770 * (Do this outside the spinlock to prevent some recursive spinlocking.)
2771 */
2772 if (fMousePositionChanged)
2773 {
2774 ASMAtomicIncU32(&pDevExt->u32MousePosChangedSeq);
2775 VBoxGuestNativeISRMousePollEvent(pDevExt);
2776#ifndef RT_OS_WINDOWS
2777 if (MouseNotifyCallback.pfnNotify)
2778 MouseNotifyCallback.pfnNotify(MouseNotifyCallback.pvUser);
2779#endif
2780 }
2781
2782 ASMAtomicDecU32(&pDevExt->cISR);
2783 Assert(rc == 0);
2784 return fOurIrq;
2785}
2786
Note: See TracBrowser for help on using the repository browser.

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