VirtualBox

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

Last change on this file since 21170 was 21170, checked in by vboxsync, 16 years ago

Additions: Mouse polling support in Solaris kernel module and minor common code changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 51.6 KB
Line 
1/* $Id: VBoxGuest.cpp 21170 2009-07-02 14:45:00Z vboxsync $ */
2/** @file
3 * VBoxGuest - Guest Additions Driver, Common Code.
4 */
5
6/*
7 * Copyright (C) 2007-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DEFAULT
28#include "VBoxGuestInternal.h"
29#include <VBox/VBoxDev.h> /* for VMMDEV_RAM_SIZE */
30#include <VBox/log.h>
31#include <iprt/mem.h>
32#include <iprt/time.h>
33#include <iprt/memobj.h>
34#include <iprt/asm.h>
35#include <iprt/string.h>
36#include <iprt/process.h>
37#include <iprt/assert.h>
38#include <iprt/param.h>
39#ifdef VBOX_WITH_HGCM
40# include <iprt/thread.h>
41#endif
42
43
44/*******************************************************************************
45* Internal Functions *
46*******************************************************************************/
47#ifdef VBOX_WITH_HGCM
48static DECLCALLBACK(void) VBoxGuestHGCMAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdrNonVolatile, void *pvUser, uint32_t u32User);
49#endif
50
51
52
53/**
54 * Reserves memory in which the VMM can relocate any guest mappings
55 * that are floating around.
56 *
57 * This operation is a little bit tricky since the VMM might not accept
58 * just any address because of address clashes between the three contexts
59 * it operates in, so use a small stack to perform this operation.
60 *
61 * @returns VBox status code (ignored).
62 * @param pDevExt The device extension.
63 */
64static int vboxGuestInitFixateGuestMappings(PVBOXGUESTDEVEXT pDevExt)
65{
66 /** @todo implement this using RTR0MemObjReserveKernel() (it needs to be implemented everywhere too). */
67 return VINF_SUCCESS;
68}
69
70
71/**
72 * Initializes the interrupt filter mask.
73 *
74 * This will ASSUME that we're the ones in carge over the mask, so
75 * we'll simply clear all bits we don't set.
76 *
77 * @returns VBox status code (ignored).
78 * @param pDevExt The device extension.
79 * @param fMask The new mask.
80 */
81static int vboxGuestInitFilterMask(PVBOXGUESTDEVEXT pDevExt, uint32_t fMask)
82{
83 VMMDevCtlGuestFilterMask *pReq;
84 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
85 if (RT_SUCCESS(rc))
86 {
87 pReq->u32OrMask = fMask;
88 pReq->u32NotMask = ~fMask; /* It's an AND mask. */
89 rc = VbglGRPerform(&pReq->header);
90 if ( RT_FAILURE(rc)
91 || RT_FAILURE(pReq->header.rc))
92 LogRel(("vboxGuestInitCtlFilterMask: failed with rc=%Rrc and VMMDev rc=%Rrc\n",
93 rc, pReq->header.rc));
94 VbglGRFree(&pReq->header);
95 }
96 return rc;
97}
98
99
100/**
101 * Report guest information to the VMMDev.
102 *
103 * @returns VBox status code.
104 * @param pDevExt The device extension.
105 * @param enmOSType The OS type to report.
106 */
107static int vboxGuestInitReportGuestInfo(PVBOXGUESTDEVEXT pDevExt, VBOXOSTYPE enmOSType)
108{
109 VMMDevReportGuestInfo *pReq;
110 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_ReportGuestInfo);
111 if (RT_SUCCESS(rc))
112 {
113 pReq->guestInfo.additionsVersion = VMMDEV_VERSION;
114 pReq->guestInfo.osType = enmOSType;
115 rc = VbglGRPerform(&pReq->header);
116 if ( RT_FAILURE(rc)
117 || RT_FAILURE(pReq->header.rc))
118 LogRel(("vboxGuestInitReportGuestInfo: failed with rc=%Rrc and VMMDev rc=%Rrc\n",
119 rc, pReq->header.rc));
120 VbglGRFree(&pReq->header);
121 }
122 return rc;
123}
124
125
126/**
127 * Initializes the VBoxGuest device extension when the
128 * device driver is loaded.
129 *
130 * The native code locates the VMMDev on the PCI bus and retrieve
131 * the MMIO and I/O port ranges, this function will take care of
132 * mapping the MMIO memory (if present). Upon successful return
133 * the native code should set up the interrupt handler.
134 *
135 * @returns VBox status code.
136 *
137 * @param pDevExt The device extension. Allocated by the native code.
138 * @param IOPortBase The base of the I/O port range.
139 * @param pvMMIOBase The base of the MMIO memory mapping.
140 * This is optional, pass NULL if not present.
141 * @param cbMMIO The size of the MMIO memory mapping.
142 * This is optional, pass 0 if not present.
143 * @param enmOSType The guest OS type to report to the VMMDev.
144 * @param fEvents Additional requested events (like Mouse events).
145 */
146int VBoxGuestInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase,
147 void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fEvents)
148{
149 int rc, rc2;
150
151 /*
152 * Initalize the data.
153 */
154 pDevExt->IOPortBase = IOPortBase;
155 pDevExt->pVMMDevMemory = NULL;
156 pDevExt->pIrqAckEvents = NULL;
157 pDevExt->WaitList.pHead = NULL;
158 pDevExt->WaitList.pTail = NULL;
159#ifdef VBOX_WITH_HGCM
160 pDevExt->HGCMWaitList.pHead = NULL;
161 pDevExt->HGCMWaitList.pTail = NULL;
162#endif
163 pDevExt->FreeList.pHead = NULL;
164 pDevExt->FreeList.pTail = NULL;
165 pDevExt->f32PendingEvents = 0;
166 pDevExt->u32ClipboardClientId = 0;
167 pDevExt->u32MousePosChangedSeq = 0;
168
169 /*
170 * If there is an MMIO region validate the version and size.
171 */
172 if (pvMMIOBase)
173 {
174 VMMDevMemory *pVMMDev = (VMMDevMemory *)pvMMIOBase;
175 Assert(cbMMIO);
176 if ( pVMMDev->u32Version == VMMDEV_MEMORY_VERSION
177 && pVMMDev->u32Size >= 32
178 && pVMMDev->u32Size <= cbMMIO)
179 {
180 pDevExt->pVMMDevMemory = pVMMDev;
181 Log(("VBoxGuestInitDevExt: VMMDevMemory: mapping=%p size=%#RX32 (%#RX32) version=%#RX32\n",
182 pVMMDev, pVMMDev->u32Size, cbMMIO, pVMMDev->u32Version));
183 }
184 else /* try live without it. */
185 LogRel(("VBoxGuestInitDevExt: Bogus VMMDev memory; u32Version=%RX32 (expected %RX32) u32Size=%RX32 (expected <= %RX32)\n",
186 pVMMDev->u32Version, VMMDEV_MEMORY_VERSION, pVMMDev->u32Size, cbMMIO));
187 }
188
189 /*
190 * Create the wait and seesion spinlocks.
191 */
192 rc = RTSpinlockCreate(&pDevExt->WaitSpinlock);
193 if (RT_SUCCESS(rc))
194 rc = RTSpinlockCreate(&pDevExt->SessionSpinlock);
195 if (RT_FAILURE(rc))
196 {
197 Log(("VBoxGuestInitDevExt: failed to spinlock, rc=%d!\n", rc));
198 if (pDevExt->WaitSpinlock != NIL_RTSPINLOCK)
199 RTSpinlockDestroy(pDevExt->WaitSpinlock);
200 return rc;
201 }
202
203 /*
204 * Initialize the guest library and report the guest info back to VMMDev,
205 * set the interrupt control filter mask, and fixate the guest mappings
206 * made by the VMM.
207 */
208 rc = VbglInit(pDevExt->IOPortBase, (VMMDevMemory *)pDevExt->pVMMDevMemory);
209 if (RT_SUCCESS(rc))
210 {
211 rc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->pIrqAckEvents, sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents);
212 if (RT_SUCCESS(rc))
213 {
214 rc = vboxGuestInitReportGuestInfo(pDevExt, enmOSType);
215 if (RT_SUCCESS(rc))
216 {
217#ifdef VBOX_WITH_HGCM
218 rc = vboxGuestInitFilterMask(pDevExt, VMMDEV_EVENT_HGCM | fEvents);
219#else
220 rc = vboxGuestInitFilterMask(pDevExt, fEvents);
221#endif
222 if (RT_SUCCESS(rc))
223 {
224 /*
225 * Disable guest graphics capability by default. The guest specific
226 * graphics driver will re-enable this when it is necessary.
227 */
228 rc = VBoxGuestSetGuestCapabilities(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS);
229 if (RT_SUCCESS(rc))
230 {
231 vboxGuestInitFixateGuestMappings(pDevExt);
232 Log(("VBoxGuestInitDevExt: returns success\n"));
233 return VINF_SUCCESS;
234 }
235 }
236 }
237
238 /* failure cleanup */
239 }
240 else
241 Log(("VBoxGuestInitDevExt: VBoxGRAlloc failed, rc=%Rrc\n", rc));
242
243 VbglTerminate();
244 }
245 else
246 Log(("VBoxGuestInitDevExt: VbglInit failed, rc=%Rrc\n", rc));
247
248 rc2 = RTSpinlockDestroy(pDevExt->WaitSpinlock); AssertRC(rc2);
249 rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2);
250 return rc; /* (failed) */
251}
252
253
254/**
255 * Deletes all the items in a wait chain.
256 * @param pWait The head of the chain.
257 */
258static void VBoxGuestDeleteWaitList(PVBOXGUESTWAITLIST pList)
259{
260 while (pList->pHead)
261 {
262 int rc2;
263 PVBOXGUESTWAIT pWait = pList->pHead;
264 pList->pHead = pWait->pNext;
265
266 pWait->pNext = NULL;
267 pWait->pPrev = NULL;
268 rc2 = RTSemEventMultiDestroy(pWait->Event); AssertRC(rc2);
269 pWait->Event = NIL_RTSEMEVENTMULTI;
270 RTMemFree(pWait);
271 }
272 pList->pHead = NULL;
273 pList->pTail = NULL;
274}
275
276
277/**
278 * Destroys the VBoxGuest device extension.
279 *
280 * The native code should call this before the driver is loaded,
281 * but don't call this on shutdown.
282 *
283 * @param pDevExt The device extension.
284 */
285void VBoxGuestDeleteDevExt(PVBOXGUESTDEVEXT pDevExt)
286{
287 int rc2;
288 Log(("VBoxGuestDeleteDevExt:\n"));
289
290/** @todo tell VMMDev that the guest additions are no longer running (clear all capability masks).
291 * Like calling VBoxGuestSetGuestCapabilities. This wasn't done initially since it was not
292 * relevant for OS/2. On solaris modules can be unloaded, so we should implement it.
293 */
294
295 rc2 = RTSpinlockDestroy(pDevExt->WaitSpinlock); AssertRC(rc2);
296 rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2);
297
298 VBoxGuestDeleteWaitList(&pDevExt->WaitList);
299#ifdef VBOX_WITH_HGCM
300 VBoxGuestDeleteWaitList(&pDevExt->HGCMWaitList);
301#endif
302 VBoxGuestDeleteWaitList(&pDevExt->FreeList);
303
304 VbglTerminate();
305
306 pDevExt->pVMMDevMemory = NULL;
307
308 pDevExt->IOPortBase = 0;
309 pDevExt->pIrqAckEvents = NULL;
310}
311
312
313/**
314 * Creates a VBoxGuest user session.
315 *
316 * The native code calls this when a ring-3 client opens the device.
317 * Use VBoxGuestCreateKernelSession when a ring-0 client connects.
318 *
319 * @returns VBox status code.
320 * @param pDevExt The device extension.
321 * @param ppSession Where to store the session on success.
322 */
323int VBoxGuestCreateUserSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession)
324{
325 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
326 if (RT_UNLIKELY(!pSession))
327 {
328 LogRel(("VBoxGuestCreateUserSession: no memory!\n"));
329 return VERR_NO_MEMORY;
330 }
331
332 pSession->Process = RTProcSelf();
333 pSession->R0Process = RTR0ProcHandleSelf();
334 pSession->pDevExt = pDevExt;
335
336 *ppSession = pSession;
337 LogFlow(("VBoxGuestCreateUserSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
338 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
339 return VINF_SUCCESS;
340}
341
342
343/**
344 * Creates a VBoxGuest kernel session.
345 *
346 * The native code calls this when a ring-0 client connects to the device.
347 * Use VBoxGuestCreateUserSession when a ring-3 client opens the device.
348 *
349 * @returns VBox status code.
350 * @param pDevExt The device extension.
351 * @param ppSession Where to store the session on success.
352 */
353int VBoxGuestCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession)
354{
355 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
356 if (RT_UNLIKELY(!pSession))
357 {
358 LogRel(("VBoxGuestCreateKernelSession: no memory!\n"));
359 return VERR_NO_MEMORY;
360 }
361
362 pSession->Process = NIL_RTPROCESS;
363 pSession->R0Process = NIL_RTR0PROCESS;
364 pSession->pDevExt = pDevExt;
365
366 *ppSession = pSession;
367 LogFlow(("VBoxGuestCreateKernelSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
368 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
369 return VINF_SUCCESS;
370}
371
372
373
374/**
375 * Closes a VBoxGuest session.
376 *
377 * @param pDevExt The device extension.
378 * @param pSession The session to close (and free).
379 */
380void VBoxGuestCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
381{
382 unsigned i; NOREF(i);
383 Log(("VBoxGuestCloseSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n",
384 pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */
385
386#ifdef VBOX_WITH_HGCM
387 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
388 if (pSession->aHGCMClientIds[i])
389 {
390 VBoxGuestHGCMDisconnectInfo Info;
391 Info.result = 0;
392 Info.u32ClientID = pSession->aHGCMClientIds[i];
393 pSession->aHGCMClientIds[i] = 0;
394 Log(("VBoxGuestCloseSession: disconnecting client id %#RX32\n", Info.u32ClientID));
395 VbglHGCMDisconnect(&Info, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
396 }
397#endif
398
399 pSession->pDevExt = NULL;
400 pSession->Process = NIL_RTPROCESS;
401 pSession->R0Process = NIL_RTR0PROCESS;
402 RTMemFree(pSession);
403}
404
405
406/**
407 * Links the wait-for-event entry into the tail of the given list.
408 *
409 * @param pList The list to link it into.
410 * @param pWait The wait for event entry to append.
411 */
412DECLINLINE(void) VBoxGuestWaitAppend(PVBOXGUESTWAITLIST pList, PVBOXGUESTWAIT pWait)
413{
414 const PVBOXGUESTWAIT pTail = pList->pTail;
415 pWait->pNext = NULL;
416 pWait->pPrev = pTail;
417 if (pTail)
418 pTail->pNext = pWait;
419 else
420 pList->pHead = pWait;
421 pList->pTail = pWait;
422}
423
424
425/**
426 * Unlinks the wait-for-event entry.
427 *
428 * @param pList The list to unlink it from.
429 * @param pWait The wait for event entry to unlink.
430 */
431DECLINLINE(void) VBoxGuestWaitUnlink(PVBOXGUESTWAITLIST pList, PVBOXGUESTWAIT pWait)
432{
433 const PVBOXGUESTWAIT pPrev = pWait->pPrev;
434 const PVBOXGUESTWAIT pNext = pWait->pNext;
435 if (pNext)
436 pNext->pPrev = pPrev;
437 else
438 pList->pTail = pPrev;
439 if (pPrev)
440 pPrev->pNext = pNext;
441 else
442 pList->pHead = pNext;
443}
444
445
446/**
447 * Allocates a wiat-for-event entry.
448 *
449 * @returns The wait-for-event entry.
450 * @param pDevExt The device extension.
451 */
452static PVBOXGUESTWAIT VBoxGuestWaitAlloc(PVBOXGUESTDEVEXT pDevExt)
453{
454 /*
455 * Allocate it one way or the other.
456 */
457 PVBOXGUESTWAIT pWait = pDevExt->FreeList.pTail;
458 if (pWait)
459 {
460 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
461 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
462
463 pWait = pDevExt->FreeList.pTail;
464 if (pWait)
465 VBoxGuestWaitUnlink(&pDevExt->FreeList, pWait);
466
467 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
468 }
469 if (!pWait)
470 {
471 static unsigned s_cErrors = 0;
472 int rc;
473
474 pWait = (PVBOXGUESTWAIT)RTMemAlloc(sizeof(*pWait));
475 if (!pWait)
476 {
477 if (s_cErrors++ < 32)
478 LogRel(("VBoxGuestWaitAlloc: out-of-memory!\n"));
479 return NULL;
480 }
481
482 rc = RTSemEventMultiCreate(&pWait->Event);
483 if (RT_FAILURE(rc))
484 {
485 if (s_cErrors++ < 32)
486 LogRel(("VBoxGuestCommonIOCtl: RTSemEventMultiCreate failed with rc=%Rrc!\n", rc));
487 RTMemFree(pWait);
488 return NULL;
489 }
490 }
491
492 /*
493 * Zero members just as an precaution.
494 */
495 pWait->pNext = NULL;
496 pWait->pPrev = NULL;
497 pWait->fReqEvents = 0;
498 pWait->fResEvents = 0;
499#ifdef VBOX_WITH_HGCM
500 pWait->pHGCMReq = NULL;
501#endif
502 RTSemEventMultiReset(pWait->Event);
503 return pWait;
504}
505
506
507/**
508 * Frees the wait-for-event entry.
509 * The caller must own the wait spinlock!
510 *
511 * @param pDevExt The device extension.
512 * @param pWait The wait-for-event entry to free.
513 */
514static void VBoxGuestWaitFreeLocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait)
515{
516 pWait->fReqEvents = 0;
517 pWait->fResEvents = 0;
518#ifdef VBOX_WITH_HGCM
519 pWait->pHGCMReq = NULL;
520#endif
521 VBoxGuestWaitAppend(&pDevExt->FreeList, pWait);
522}
523
524
525/**
526 * Frees the wait-for-event entry.
527 *
528 * @param pDevExt The device extension.
529 * @param pWait The wait-for-event entry to free.
530 */
531static void VBoxGuestWaitFreeUnlocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait)
532{
533 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
534 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
535 VBoxGuestWaitFreeLocked(pDevExt, pWait);
536 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
537}
538
539
540/**
541 * Modifies the guest capabilities.
542 *
543 * Should be called during driver init and termination.
544 *
545 * @returns VBox status code.
546 * @param fOr The Or mask (what to enable).
547 * @param fNot The Not mask (what to disable).
548 */
549int VBoxGuestSetGuestCapabilities(uint32_t fOr, uint32_t fNot)
550{
551 VMMDevReqGuestCapabilities2 *pReq;
552 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities);
553 if (RT_FAILURE(rc))
554 {
555 Log(("VBoxGuestSetGuestCapabilities: failed to allocate %u (%#x) bytes to cache the request. rc=%d!!\n",
556 sizeof(*pReq), sizeof(*pReq), rc));
557 return rc;
558 }
559
560 pReq->u32OrMask = fOr;
561 pReq->u32NotMask = fNot;
562
563 rc = VbglGRPerform(&pReq->header);
564 if (RT_FAILURE(rc))
565 Log(("VBoxGuestSetGuestCapabilities:VbglGRPerform failed, rc=%Rrc!\n", rc));
566 else if (RT_FAILURE(pReq->header.rc))
567 {
568 Log(("VBoxGuestSetGuestCapabilities: The request failed; VMMDev rc=%Rrc!\n", pReq->header.rc));
569 rc = pReq->header.rc;
570 }
571
572 VbglGRFree(&pReq->header);
573 return rc;
574}
575
576
577/**
578 * Implements the fast (no input or output) type of IOCtls.
579 *
580 * This is currently just a placeholder stub inherited from the support driver code.
581 *
582 * @returns VBox status code.
583 * @param iFunction The IOCtl function number.
584 * @param pDevExt The device extension.
585 * @param pSession The session.
586 */
587int VBoxGuestCommonIOCtlFast(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession)
588{
589 Log(("VBoxGuestCommonIOCtlFast: iFunction=%#x pDevExt=%p pSession=%p\n", iFunction, pDevExt, pSession));
590
591 return VERR_NOT_SUPPORTED;
592}
593
594
595
596static int VBoxGuestCommonIOCtl_GetVMMDevPort(PVBOXGUESTDEVEXT pDevExt, VBoxGuestPortInfo *pInfo, size_t *pcbDataReturned)
597{
598 Log(("VBoxGuestCommonIOCtl: GETVMMDEVPORT\n"));
599 pInfo->portAddress = pDevExt->IOPortBase;
600 pInfo->pVMMDevMemory = (VMMDevMemory *)pDevExt->pVMMDevMemory;
601 if (pcbDataReturned)
602 *pcbDataReturned = sizeof(*pInfo);
603 return VINF_SUCCESS;
604}
605
606
607/**
608 * Worker VBoxGuestCommonIOCtl_WaitEvent.
609 * The caller enters the spinlock, we may or may not leave it.
610 *
611 * @returns VINF_SUCCESS if we've left the spinlock and can return immediately.
612 */
613DECLINLINE(int) WaitEventCheckCondition(PVBOXGUESTDEVEXT pDevExt, VBoxGuestWaitEventInfo *pInfo,
614 int iEvent, const uint32_t fReqEvents, PRTSPINLOCKTMP pTmp)
615{
616 uint32_t fMatches = pDevExt->f32PendingEvents & fReqEvents;
617 if (fMatches)
618 {
619 ASMAtomicAndU32(&pDevExt->f32PendingEvents, ~fMatches);
620 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, pTmp);
621
622 pInfo->u32EventFlagsOut = fMatches;
623 pInfo->u32Result = VBOXGUEST_WAITEVENT_OK;
624 if (fReqEvents & ~((uint32_t)1 << iEvent))
625 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x\n", pInfo->u32EventFlagsOut));
626 else
627 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x/%d\n", pInfo->u32EventFlagsOut, iEvent));
628 return VINF_SUCCESS;
629 }
630 return VERR_TIMEOUT;
631}
632
633
634static int VBoxGuestCommonIOCtl_WaitEvent(PVBOXGUESTDEVEXT pDevExt, VBoxGuestWaitEventInfo *pInfo, size_t *pcbDataReturned,
635 bool fInterruptible)
636{
637 pInfo->u32EventFlagsOut = 0;
638 pInfo->u32Result = VBOXGUEST_WAITEVENT_ERROR;
639 if (pcbDataReturned)
640 *pcbDataReturned = sizeof(*pInfo);
641
642 /*
643 * Copy and verify the input mask.
644 */
645 const uint32_t fReqEvents = pInfo->u32EventMaskIn;
646 int iEvent = ASMBitFirstSetU32(fReqEvents) - 1;
647 if (RT_UNLIKELY(iEvent < 0))
648 {
649 Log(("VBoxGuestCommonIOCtl: WAITEVENT: Invalid input mask %#x!!\n", fReqEvents));
650 return VERR_INVALID_PARAMETER;
651 }
652
653 /*
654 * Check the condition up front, before doing the wait-for-event allocations.
655 */
656 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
657 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
658 int rc = WaitEventCheckCondition(pDevExt, pInfo, iEvent, fReqEvents, &Tmp);
659 if (rc == VINF_SUCCESS)
660 return rc;
661 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
662
663 if (!pInfo->u32TimeoutIn)
664 {
665 pInfo->u32Result = VBOXGUEST_WAITEVENT_TIMEOUT;
666 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns VERR_TIMEOUT\n"));
667 return VERR_TIMEOUT;
668 }
669
670 PVBOXGUESTWAIT pWait = VBoxGuestWaitAlloc(pDevExt);
671 if (!pWait)
672 return VERR_NO_MEMORY;
673 pWait->fReqEvents = fReqEvents;
674
675 /*
676 * We've got the wait entry now, re-enter the spinlock and check for the condition.
677 * If the wait condition is met, return.
678 * Otherwise enter into the list and go to sleep waiting for the ISR to signal us.
679 */
680 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
681 rc = WaitEventCheckCondition(pDevExt, pInfo, iEvent, fReqEvents, &Tmp);
682 if (rc == VINF_SUCCESS)
683 {
684 VBoxGuestWaitFreeUnlocked(pDevExt, pWait);
685 return rc;
686 }
687 VBoxGuestWaitAppend(&pDevExt->WaitList, pWait);
688 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
689
690 if (fInterruptible)
691 rc = RTSemEventMultiWaitNoResume(pWait->Event,
692 pInfo->u32TimeoutIn == UINT32_MAX ? RT_INDEFINITE_WAIT : pInfo->u32TimeoutIn);
693 else
694 rc = RTSemEventMultiWait(pWait->Event,
695 pInfo->u32TimeoutIn == UINT32_MAX ? RT_INDEFINITE_WAIT : pInfo->u32TimeoutIn);
696
697 /*
698 * There is one special case here and that's when the semaphore is
699 * destroyed upon device driver unload. This shouldn't happen of course,
700 * but in case it does, just get out of here ASAP.
701 */
702 if (rc == VERR_SEM_DESTROYED)
703 return rc;
704
705 /*
706 * Unlink the wait item and dispose of it.
707 */
708 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
709 VBoxGuestWaitUnlink(&pDevExt->WaitList, pWait);
710 const uint32_t fResEvents = pWait->fResEvents;
711 VBoxGuestWaitFreeLocked(pDevExt, pWait);
712 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
713
714 /*
715 * Now deal with the return code.
716 */
717 if (fResEvents)
718 {
719 pInfo->u32EventFlagsOut = fResEvents;
720 pInfo->u32Result = VBOXGUEST_WAITEVENT_OK;
721 if (fReqEvents & ~((uint32_t)1 << iEvent))
722 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x\n", pInfo->u32EventFlagsOut));
723 else
724 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %#x/%d\n", pInfo->u32EventFlagsOut, iEvent));
725 rc = VINF_SUCCESS;
726 }
727 else if (rc == VERR_TIMEOUT)
728 {
729 pInfo->u32Result = VBOXGUEST_WAITEVENT_TIMEOUT;
730 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns VERR_TIMEOUT\n"));
731 }
732 else if (rc == VERR_INTERRUPTED)
733 {
734 pInfo->u32Result = VBOXGUEST_WAITEVENT_INTERRUPTED;
735 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns VERR_INTERRUPTED\n"));
736 }
737 else
738 {
739 if (RT_SUCCESS(rc))
740 {
741 static unsigned s_cErrors = 0;
742 if (s_cErrors++ < 32)
743 LogRel(("VBoxGuestCommonIOCtl: WAITEVENT: returns %Rrc but no events!\n", rc));
744 rc = VERR_INTERNAL_ERROR;
745 }
746 pInfo->u32Result = VBOXGUEST_WAITEVENT_ERROR;
747 Log(("VBoxGuestCommonIOCtl: WAITEVENT: returns %Rrc\n", rc));
748 }
749
750 return rc;
751}
752
753
754static int VBoxGuestCommonIOCtl_VMMRequest(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
755 VMMDevRequestHeader *pReqHdr, size_t cbData, size_t *pcbDataReturned)
756{
757 Log(("VBoxGuestCommonIOCtl: VMMREQUEST type %d\n", pReqHdr->requestType));
758
759 /*
760 * Validate the header and request size.
761 */
762 const VMMDevRequestType enmType = pReqHdr->requestType;
763 const uint32_t cbReq = pReqHdr->size;
764 const uint32_t cbMinSize = vmmdevGetRequestSize(enmType);
765 if (cbReq < cbMinSize)
766 {
767 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid hdr size %#x, expected >= %#x; type=%#x!!\n",
768 cbReq, cbMinSize, enmType));
769 return VERR_INVALID_PARAMETER;
770 }
771 if (cbReq > cbData)
772 {
773 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid size %#x, expected >= %#x (hdr); type=%#x!!\n",
774 cbData, cbReq, enmType));
775 return VERR_INVALID_PARAMETER;
776 }
777
778 /*
779 * Make a copy of the request in the physical memory heap so
780 * the VBoxGuestLibrary can more easily deal with the request.
781 * (This is really a waste of time since the OS or the OS specific
782 * code has already buffered or locked the input/output buffer, but
783 * it does makes things a bit simpler wrt to phys address.)
784 */
785 VMMDevRequestHeader *pReqCopy;
786 int rc = VbglGRAlloc(&pReqCopy, cbReq, enmType);
787 if (RT_FAILURE(rc))
788 {
789 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: failed to allocate %u (%#x) bytes to cache the request. rc=%d!!\n",
790 cbReq, cbReq, rc));
791 return rc;
792 }
793 memcpy(pReqCopy, pReqHdr, cbReq);
794
795 if (enmType == VMMDevReq_GetMouseStatus) /* clear poll condition. */
796 pSession->u32MousePosChangedSeq = ASMAtomicUoReadU32(&pDevExt->u32MousePosChangedSeq);
797
798 rc = VbglGRPerform(pReqCopy);
799 if ( RT_SUCCESS(rc)
800 && RT_SUCCESS(pReqCopy->rc))
801 {
802 Assert(rc != VINF_HGCM_ASYNC_EXECUTE);
803 Assert(pReqCopy->rc != VINF_HGCM_ASYNC_EXECUTE);
804
805 memcpy(pReqHdr, pReqCopy, cbReq);
806 if (pcbDataReturned)
807 *pcbDataReturned = cbReq;
808 }
809 else if (RT_FAILURE(rc))
810 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: VbglGRPerform - rc=%Rrc!\n", rc));
811 else
812 {
813 Log(("VBoxGuestCommonIOCtl: VMMREQUEST: request execution failed; VMMDev rc=%Rrc!\n", pReqCopy->rc));
814 rc = pReqCopy->rc;
815 }
816
817 VbglGRFree(pReqCopy);
818 return rc;
819}
820
821
822static int VBoxGuestCommonIOCtl_CtlFilterMask(PVBOXGUESTDEVEXT pDevExt, VBoxGuestFilterMaskInfo *pInfo)
823{
824 VMMDevCtlGuestFilterMask *pReq;
825 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
826 if (RT_FAILURE(rc))
827 {
828 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: failed to allocate %u (%#x) bytes to cache the request. rc=%d!!\n",
829 sizeof(*pReq), sizeof(*pReq), rc));
830 return rc;
831 }
832
833 pReq->u32OrMask = pInfo->u32OrMask;
834 pReq->u32NotMask = pInfo->u32NotMask;
835
836 rc = VbglGRPerform(&pReq->header);
837 if (RT_FAILURE(rc))
838 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: VbglGRPerform failed, rc=%Rrc!\n", rc));
839 else if (RT_FAILURE(pReq->header.rc))
840 {
841 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: The request failed; VMMDev rc=%Rrc!\n", pReq->header.rc));
842 rc = pReq->header.rc;
843 }
844
845 VbglGRFree(&pReq->header);
846 return rc;
847}
848
849#ifdef VBOX_WITH_HGCM
850
851AssertCompile(RT_INDEFINITE_WAIT == (uint32_t)RT_INDEFINITE_WAIT); /* assumed by code below */
852
853/** Worker for VBoxGuestHGCMAsyncWaitCallback*. */
854static void VBoxGuestHGCMAsyncWaitCallbackWorker(VMMDevHGCMRequestHeader volatile *pHdr, PVBOXGUESTDEVEXT pDevExt,
855 bool fInterruptible, uint32_t cMillies)
856{
857
858 /*
859 * Check to see if the condition was met by the time we got here.
860 *
861 * We create a simple poll loop here for dealing with out-of-memory
862 * conditions since the caller isn't necessarily able to deal with
863 * us returning too early.
864 */
865 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
866 PVBOXGUESTWAIT pWait;
867 for (;;)
868 {
869 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
870 if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0)
871 {
872 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
873 return;
874 }
875 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
876
877 pWait = VBoxGuestWaitAlloc(pDevExt);
878 if (pWait)
879 break;
880 if (fInterruptible)
881 return;
882 RTThreadSleep(1);
883 }
884 pWait->fReqEvents = VMMDEV_EVENT_HGCM;
885 pWait->pHGCMReq = pHdr;
886
887 /*
888 * Re-enter the spinlock and re-check for the condition.
889 * If the condition is met, return.
890 * Otherwise link us into the HGCM wait list and go to sleep.
891 */
892 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
893 if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0)
894 {
895 VBoxGuestWaitFreeLocked(pDevExt, pWait);
896 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
897 return;
898 }
899 VBoxGuestWaitAppend(&pDevExt->HGCMWaitList, pWait);
900 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
901
902 int rc;
903 if (fInterruptible)
904 rc = RTSemEventMultiWaitNoResume(pWait->Event, cMillies);
905 else
906 rc = RTSemEventMultiWait(pWait->Event, cMillies);
907
908 /*
909 * This shouldn't ever return failure...
910 * Unlink, free and return.
911 */
912 if (rc == VERR_SEM_DESTROYED)
913 return;
914 if (RT_FAILURE(rc))
915 LogRel(("VBoxGuestHGCMAsyncWaitCallback: wait failed! %Rrc\n", rc));
916
917 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
918 VBoxGuestWaitUnlink(&pDevExt->HGCMWaitList, pWait);
919 VBoxGuestWaitFreeLocked(pDevExt, pWait);
920 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
921}
922
923
924/**
925 * This is a callback for dealing with async waits.
926 *
927 * It operates in a manner similar to VBoxGuestCommonIOCtl_WaitEvent.
928 */
929static DECLCALLBACK(void) VBoxGuestHGCMAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdr, void *pvUser, uint32_t u32User)
930{
931 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
932 LogFunc(("requestType=%d\n", pHdr->header.requestType));
933 VBoxGuestHGCMAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr,
934 pDevExt,
935 false /* fInterruptible */,
936 u32User /* cMillies */);
937}
938
939
940/**
941 * This is a callback for dealing with async waits with a timeout.
942 *
943 * It operates in a manner similar to VBoxGuestCommonIOCtl_WaitEvent.
944 */
945static DECLCALLBACK(void) VBoxGuestHGCMAsyncWaitCallbackInterruptible(VMMDevHGCMRequestHeader *pHdr,
946 void *pvUser, uint32_t u32User)
947{
948 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
949 LogFunc(("requestType=%d\n", pHdr->header.requestType));
950 VBoxGuestHGCMAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr,
951 pDevExt,
952 true /* fInterruptible */,
953 u32User /* cMillies */ );
954}
955
956
957static int VBoxGuestCommonIOCtl_HGCMConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VBoxGuestHGCMConnectInfo *pInfo,
958 size_t *pcbDataReturned)
959{
960 /*
961 * The VbglHGCMConnect call will invoke the callback if the HGCM
962 * call is performed in an ASYNC fashion. The function is not able
963 * to deal with cancelled requests.
964 */
965 Log(("VBoxGuestCommonIOCtl: HGCM_CONNECT: %.128s\n",
966 pInfo->Loc.type == VMMDevHGCMLoc_LocalHost || pInfo->Loc.type == VMMDevHGCMLoc_LocalHost_Existing
967 ? pInfo->Loc.u.host.achName : "<not local host>"));
968
969 int rc = VbglHGCMConnect(pInfo, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
970 if (RT_SUCCESS(rc))
971 {
972 Log(("VBoxGuestCommonIOCtl: HGCM_CONNECT: u32Client=%RX32 result=%Rrc (rc=%Rrc)\n",
973 pInfo->u32ClientID, pInfo->result, rc));
974 if (RT_SUCCESS(pInfo->result))
975 {
976 /*
977 * Append the client id to the client id table.
978 * If the table has somehow become filled up, we'll disconnect the session.
979 */
980 unsigned i;
981 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
982 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
983 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
984 if (!pSession->aHGCMClientIds[i])
985 {
986 pSession->aHGCMClientIds[i] = pInfo->u32ClientID;
987 break;
988 }
989 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
990 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
991 {
992 static unsigned s_cErrors = 0;
993 if (s_cErrors++ < 32)
994 LogRel(("VBoxGuestCommonIOCtl: HGCM_CONNECT: too many HGCMConnect calls for one session!\n"));
995
996 VBoxGuestHGCMDisconnectInfo Info;
997 Info.result = 0;
998 Info.u32ClientID = pInfo->u32ClientID;
999 VbglHGCMDisconnect(&Info, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1000 return VERR_TOO_MANY_OPEN_FILES;
1001 }
1002 }
1003 if (pcbDataReturned)
1004 *pcbDataReturned = sizeof(*pInfo);
1005 }
1006 return rc;
1007}
1008
1009
1010static int VBoxGuestCommonIOCtl_HGCMDisconnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VBoxGuestHGCMDisconnectInfo *pInfo,
1011 size_t *pcbDataReturned)
1012{
1013 /*
1014 * Validate the client id and invalidate its entry while we're in the call.
1015 */
1016 const uint32_t u32ClientId = pInfo->u32ClientID;
1017 unsigned i;
1018 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1019 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1020 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1021 if (pSession->aHGCMClientIds[i] == u32ClientId)
1022 {
1023 pSession->aHGCMClientIds[i] = UINT32_MAX;
1024 break;
1025 }
1026 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1027 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1028 {
1029 static unsigned s_cErrors = 0;
1030 if (s_cErrors++ > 32)
1031 LogRel(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", u32ClientId));
1032 return VERR_INVALID_HANDLE;
1033 }
1034
1035 /*
1036 * The VbglHGCMConnect call will invoke the callback if the HGCM
1037 * call is performed in an ASYNC fashion. The function is not able
1038 * to deal with cancelled requests.
1039 */
1040 Log(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", pInfo->u32ClientID));
1041 int rc = VbglHGCMDisconnect(pInfo, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1042 if (RT_SUCCESS(rc))
1043 {
1044 Log(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: result=%Rrc\n", pInfo->result));
1045 if (pcbDataReturned)
1046 *pcbDataReturned = sizeof(*pInfo);
1047 }
1048
1049 /* Update the client id array according to the result. */
1050 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1051 if (pSession->aHGCMClientIds[i] == UINT32_MAX)
1052 pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) && RT_SUCCESS(pInfo->result) ? 0 : u32ClientId;
1053 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1054
1055 return rc;
1056}
1057
1058
1059static int VBoxGuestCommonIOCtl_HGCMCall(PVBOXGUESTDEVEXT pDevExt,
1060 PVBOXGUESTSESSION pSession,
1061 VBoxGuestHGCMCallInfo *pInfo,
1062 uint32_t cMillies, bool fInterruptible, bool f32bit,
1063 size_t cbExtra, size_t cbData, size_t *pcbDataReturned)
1064{
1065 /*
1066 * Some more validations.
1067 */
1068 if (pInfo->cParms > 4096) /* (Just make sure it doesn't overflow the next check.) */
1069 {
1070 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: cParm=%RX32 is not sane\n", pInfo->cParms));
1071 return VERR_INVALID_PARAMETER;
1072 }
1073 size_t cbActual = cbExtra + sizeof(*pInfo);
1074#ifdef RT_ARCH_AMD64
1075 if (f32bit)
1076 cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter32);
1077 else
1078#endif
1079 cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter);
1080 if (cbData < cbActual)
1081 {
1082 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: cbData=%#zx (%zu) required size is %#zx (%zu)\n",
1083 cbData, cbActual));
1084 return VERR_INVALID_PARAMETER;
1085 }
1086
1087 /*
1088 * Validate the client id.
1089 */
1090 const uint32_t u32ClientId = pInfo->u32ClientID;
1091 unsigned i;
1092 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1093 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1094 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1095 if (pSession->aHGCMClientIds[i] == u32ClientId)
1096 break;
1097 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1098 if (RT_UNLIKELY(i >= RT_ELEMENTS(pSession->aHGCMClientIds)))
1099 {
1100 static unsigned s_cErrors = 0;
1101 if (s_cErrors++ > 32)
1102 LogRel(("VBoxGuestCommonIOCtl: HGCM_CALL: Invalid handle. u32Client=%RX32\n", u32ClientId));
1103 return VERR_INVALID_HANDLE;
1104 }
1105
1106 /*
1107 * The VbglHGCMCall call will invoke the callback if the HGCM
1108 * call is performed in an ASYNC fashion. This function can
1109 * deal with cancelled requests, so we let user more requests
1110 * be interruptible (should add a flag for this later I guess).
1111 */
1112 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: u32Client=%RX32\n", pInfo->u32ClientID));
1113 int rc;
1114#ifdef RT_ARCH_AMD64
1115 if (f32bit)
1116 {
1117 if (fInterruptible)
1118 rc = VbglHGCMCall32(pInfo, VBoxGuestHGCMAsyncWaitCallbackInterruptible, pDevExt, cMillies);
1119 else
1120 rc = VbglHGCMCall32(pInfo, VBoxGuestHGCMAsyncWaitCallback, pDevExt, cMillies);
1121 }
1122 else
1123#endif
1124 {
1125 if (fInterruptible)
1126 rc = VbglHGCMCall(pInfo, VBoxGuestHGCMAsyncWaitCallbackInterruptible, pDevExt, cMillies);
1127 else
1128 rc = VbglHGCMCall(pInfo, VBoxGuestHGCMAsyncWaitCallback, pDevExt, cMillies);
1129 }
1130 if (RT_SUCCESS(rc))
1131 {
1132 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: result=%Rrc\n", pInfo->result));
1133 if (pcbDataReturned)
1134 *pcbDataReturned = cbActual;
1135 }
1136 Log(("VBoxGuestCommonIOCtl: HGCM_CALL: Failed. rc=%Rrc.\n", rc));
1137 return rc;
1138}
1139
1140
1141/**
1142 * @returns VBox status code. Unlike the other HGCM IOCtls this will combine
1143 * the VbglHGCMConnect/Disconnect return code with the Info.result.
1144 */
1145static int VBoxGuestCommonIOCtl_HGCMClipboardReConnect(PVBOXGUESTDEVEXT pDevExt, uint32_t *pu32ClientId, size_t *pcbDataReturned)
1146{
1147 int rc;
1148 Log(("VBoxGuestCommonIOCtl: CLIPBOARD_CONNECT: Current u32ClientId=%RX32\n", pDevExt->u32ClipboardClientId));
1149
1150
1151 /*
1152 * If there is an old client, try disconnect it first.
1153 */
1154 if (pDevExt->u32ClipboardClientId != 0)
1155 {
1156 VBoxGuestHGCMDisconnectInfo Info;
1157 Info.result = VERR_WRONG_ORDER;
1158 Info.u32ClientID = pDevExt->u32ClipboardClientId;
1159 rc = VbglHGCMDisconnect(&Info, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1160 if (RT_SUCCESS(rc))
1161 {
1162 LogRel(("VBoxGuestCommonIOCtl: CLIPBOARD_CONNECT: failed to disconnect old client. VbglHGCMDisconnect -> rc=%Rrc\n", rc));
1163 return rc;
1164 }
1165 if (RT_FAILURE((int32_t)Info.result))
1166 {
1167 Log(("VBoxGuestCommonIOCtl: CLIPBOARD_CONNECT: failed to disconnect old client. Info.result=%Rrc\n", rc));
1168 return Info.result;
1169 }
1170 pDevExt->u32ClipboardClientId = 0;
1171 }
1172
1173 /*
1174 * Try connect.
1175 */
1176 VBoxGuestHGCMConnectInfo Info;
1177 Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
1178 strcpy(Info.Loc.u.host.achName, "VBoxSharedClipboard");
1179 Info.u32ClientID = 0;
1180 Info.result = VERR_WRONG_ORDER;
1181
1182 rc = VbglHGCMConnect(&Info, VBoxGuestHGCMAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT);
1183 if (RT_FAILURE(rc))
1184 {
1185 LogRel(("VBoxGuestCommonIOCtl: CLIPBOARD_CONNECT: VbglHGCMConnected -> rc=%Rrc\n", rc));
1186 return rc;
1187 }
1188 if (RT_FAILURE(Info.result))
1189 {
1190 LogRel(("VBoxGuestCommonIOCtl: CLIPBOARD_CONNECT: VbglHGCMConnected -> rc=%Rrc\n", rc));
1191 return rc;
1192 }
1193
1194 Log(("VBoxGuestCommonIOCtl: CLIPBOARD_CONNECT: connected successfully u32ClientId=%RX32\n", Info.u32ClientID));
1195
1196 pDevExt->u32ClipboardClientId = Info.u32ClientID;
1197 *pu32ClientId = Info.u32ClientID;
1198 if (pcbDataReturned)
1199 *pcbDataReturned = sizeof(uint32_t);
1200
1201 return VINF_SUCCESS;
1202}
1203
1204#endif /* VBOX_WITH_HGCM */
1205
1206/**
1207 * Guest backdoor logging.
1208 *
1209 * @returns VBox status code.
1210 *
1211 * @param pch The log message (need not be NULL terminated).
1212 * @param cbData Size of the buffer.
1213 * @param pcbDataReturned Where to store the amount of returned data. Can be NULL.
1214 */
1215static int VBoxGuestCommonIOCtl_Log(const char *pch, size_t cbData, size_t *pcbDataReturned)
1216{
1217 Log(("%.*s", cbData, pch));
1218 if (pcbDataReturned)
1219 *pcbDataReturned = 0;
1220 return VINF_SUCCESS;
1221}
1222
1223
1224/**
1225 * Common IOCtl for user to kernel and kernel to kernel communcation.
1226 *
1227 * This function only does the basic validation and then invokes
1228 * worker functions that takes care of each specific function.
1229 *
1230 * @returns VBox status code.
1231 *
1232 * @param iFunction The requested function.
1233 * @param pDevExt The device extension.
1234 * @param pSession The client session.
1235 * @param pvData The input/output data buffer. Can be NULL depending on the function.
1236 * @param cbData The max size of the data buffer.
1237 * @param pcbDataReturned Where to store the amount of returned data. Can be NULL.
1238 */
1239int VBoxGuestCommonIOCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1240 void *pvData, size_t cbData, size_t *pcbDataReturned)
1241{
1242 Log(("VBoxGuestCommonIOCtl: iFunction=%#x pDevExt=%p pSession=%p pvData=%p cbData=%zu\n",
1243 iFunction, pDevExt, pSession, pvData, cbData));
1244
1245 /*
1246 * Define some helper macros to simplify validation.
1247 */
1248#define CHECKRET_RING0(mnemonic) \
1249 do { \
1250 if (pSession->R0Process != NIL_RTR0PROCESS) \
1251 { \
1252 Log(("VBoxGuestCommonIOCtl: " mnemonic ": Ring-0 only, caller is %RTproc/%p\n", \
1253 pSession->Process, (uintptr_t)pSession->R0Process)); \
1254 return VERR_PERMISSION_DENIED; \
1255 } \
1256 } while (0)
1257#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
1258 do { \
1259 if (cbData < (cbMin)) \
1260 { \
1261 Log(("VBoxGuestCommonIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
1262 cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
1263 return VERR_BUFFER_OVERFLOW; \
1264 } \
1265 if ((cbMin) != 0 && !VALID_PTR(pvData)) \
1266 { \
1267 Log(("VBoxGuestCommonIOCtl: " mnemonic ": Invalid pointer %p\n", pvData)); \
1268 return VERR_INVALID_POINTER; \
1269 } \
1270 } while (0)
1271
1272
1273 /*
1274 * Deal with variably sized requests first.
1275 */
1276 int rc = VINF_SUCCESS;
1277 if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_VMMREQUEST(0)))
1278 {
1279 CHECKRET_MIN_SIZE("VMMREQUEST", sizeof(VMMDevRequestHeader));
1280 rc = VBoxGuestCommonIOCtl_VMMRequest(pDevExt, pSession, (VMMDevRequestHeader *)pvData, cbData, pcbDataReturned);
1281 }
1282#ifdef VBOX_WITH_HGCM
1283 /*
1284 * These ones are a bit tricky.
1285 */
1286 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL(0)))
1287 {
1288 CHECKRET_MIN_SIZE("HGCM_CALL", sizeof(VBoxGuestHGCMCallInfo));
1289 bool fInterruptible = pSession->R0Process != NIL_RTR0PROCESS;
1290 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, (VBoxGuestHGCMCallInfo *)pvData, RT_INDEFINITE_WAIT,
1291 fInterruptible, false /*f32bit*/,
1292 0, cbData, pcbDataReturned);
1293 }
1294 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_TIMED(0)))
1295 {
1296 CHECKRET_MIN_SIZE("HGCM_CALL_TIMED", sizeof(VBoxGuestHGCMCallInfoTimed));
1297 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pvData;
1298 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, &pInfo->info, pInfo->u32Timeout,
1299 !!pInfo->fInterruptible || pSession->R0Process != NIL_RTR0PROCESS,
1300 false /*f32bit*/,
1301 RT_OFFSETOF(VBoxGuestHGCMCallInfoTimed, info), cbData, pcbDataReturned);
1302 }
1303# ifdef RT_ARCH_AMD64
1304 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_32(0)))
1305 {
1306 CHECKRET_MIN_SIZE("HGCM_CALL", sizeof(VBoxGuestHGCMCallInfo));
1307 bool fInterruptible = pSession->R0Process != NIL_RTR0PROCESS;
1308 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, (VBoxGuestHGCMCallInfo *)pvData, RT_INDEFINITE_WAIT,
1309 fInterruptible, true /*f32bit*/,
1310 0, cbData, pcbDataReturned);
1311 }
1312 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL_TIMED_32(0)))
1313 {
1314 CHECKRET_MIN_SIZE("HGCM_CALL_TIMED", sizeof(VBoxGuestHGCMCallInfoTimed));
1315 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pvData;
1316 rc = VBoxGuestCommonIOCtl_HGCMCall(pDevExt, pSession, &pInfo->info, pInfo->u32Timeout,
1317 !!pInfo->fInterruptible || pSession->R0Process != NIL_RTR0PROCESS,
1318 true /*f32bit*/,
1319 RT_OFFSETOF(VBoxGuestHGCMCallInfoTimed, info), cbData, pcbDataReturned);
1320 }
1321# endif
1322#endif /* VBOX_WITH_HGCM */
1323 else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_LOG(0)))
1324 {
1325 CHECKRET_MIN_SIZE("LOG", 1);
1326 rc = VBoxGuestCommonIOCtl_Log((char *)pvData, cbData, pcbDataReturned);
1327 }
1328 else
1329 {
1330 switch (iFunction)
1331 {
1332 case VBOXGUEST_IOCTL_GETVMMDEVPORT:
1333 CHECKRET_RING0("GETVMMDEVPORT");
1334 CHECKRET_MIN_SIZE("GETVMMDEVPORT", sizeof(VBoxGuestPortInfo));
1335 rc = VBoxGuestCommonIOCtl_GetVMMDevPort(pDevExt, (VBoxGuestPortInfo *)pvData, pcbDataReturned);
1336 break;
1337
1338 case VBOXGUEST_IOCTL_WAITEVENT:
1339 CHECKRET_MIN_SIZE("WAITEVENT", sizeof(VBoxGuestWaitEventInfo));
1340 rc = VBoxGuestCommonIOCtl_WaitEvent(pDevExt, (VBoxGuestWaitEventInfo *)pvData, pcbDataReturned,
1341 pSession->R0Process != NIL_RTR0PROCESS);
1342 break;
1343
1344 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
1345 CHECKRET_MIN_SIZE("CTL_FILTER_MASK", sizeof(VBoxGuestFilterMaskInfo));
1346 rc = VBoxGuestCommonIOCtl_CtlFilterMask(pDevExt, (VBoxGuestFilterMaskInfo *)pvData);
1347 break;
1348
1349#ifdef VBOX_WITH_HGCM
1350 case VBOXGUEST_IOCTL_HGCM_CONNECT:
1351# ifdef RT_ARCH_AMD64
1352 case VBOXGUEST_IOCTL_HGCM_CONNECT_32:
1353# endif
1354 CHECKRET_MIN_SIZE("HGCM_CONNECT", sizeof(VBoxGuestHGCMConnectInfo));
1355 rc = VBoxGuestCommonIOCtl_HGCMConnect(pDevExt, pSession, (VBoxGuestHGCMConnectInfo *)pvData, pcbDataReturned);
1356 break;
1357
1358 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
1359# ifdef RT_ARCH_AMD64
1360 case VBOXGUEST_IOCTL_HGCM_DISCONNECT_32:
1361# endif
1362 CHECKRET_MIN_SIZE("HGCM_DISCONNECT", sizeof(VBoxGuestHGCMDisconnectInfo));
1363 rc = VBoxGuestCommonIOCtl_HGCMDisconnect(pDevExt, pSession, (VBoxGuestHGCMDisconnectInfo *)pvData, pcbDataReturned);
1364 break;
1365
1366 case VBOXGUEST_IOCTL_CLIPBOARD_CONNECT:
1367 CHECKRET_MIN_SIZE("CLIPBOARD_CONNECT", sizeof(uint32_t));
1368 rc = VBoxGuestCommonIOCtl_HGCMClipboardReConnect(pDevExt, (uint32_t *)pvData, pcbDataReturned);
1369 break;
1370#endif /* VBOX_WITH_HGCM */
1371
1372 default:
1373 {
1374 Log(("VBoxGuestCommonIOCtl: Unknown request iFunction=%#x Stripped size=%#x\n", iFunction,
1375 VBOXGUEST_IOCTL_STRIP_SIZE(iFunction)));
1376 rc = VERR_NOT_SUPPORTED;
1377 break;
1378 }
1379 }
1380 }
1381
1382 Log(("VBoxGuestCommonIOCtl: returns %Rrc *pcbDataReturned=%zu\n", rc, pcbDataReturned ? *pcbDataReturned : 0));
1383 return rc;
1384}
1385
1386
1387
1388/**
1389 * Common interrupt service routine.
1390 *
1391 * This deals with events and with waking up thread waiting for those events.
1392 *
1393 * @returns true if it was our interrupt, false if it wasn't.
1394 * @param pDevExt The VBoxGuest device extension.
1395 */
1396bool VBoxGuestCommonISR(PVBOXGUESTDEVEXT pDevExt)
1397{
1398 /*
1399 * Now we have to find out whether it was our IRQ. Read the event mask
1400 * from our device to see if there are any pending events.
1401 */
1402 bool fOurIrq = pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents;
1403 if (fOurIrq)
1404 {
1405 /* Acknowlegde events. */
1406 VMMDevEvents *pReq = pDevExt->pIrqAckEvents;
1407 int rc = VbglGRPerform(&pReq->header);
1408 if ( RT_SUCCESS(rc)
1409 && RT_SUCCESS(pReq->header.rc))
1410 {
1411 uint32_t fEvents = pReq->events;
1412 Log(("VBoxGuestCommonISR: acknowledge events succeeded %#RX32\n", fEvents));
1413
1414 /*
1415 * Enter the spinlock and examin the waiting threads.
1416 */
1417 int rc2 = 0;
1418 PVBOXGUESTWAIT pWait;
1419 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1420 RTSpinlockAcquireNoInts(pDevExt->WaitSpinlock, &Tmp);
1421
1422 /** @todo This looks wrong: Seems like VMMDEV_EVENT_HGCM will always be set in
1423 * f32PendingEvents... */
1424#ifdef VBOX_WITH_HGCM
1425 /* The HGCM event/list is kind of different in that we evaluate all entries. */
1426 if (fEvents & VMMDEV_EVENT_HGCM)
1427 for (pWait = pDevExt->HGCMWaitList.pHead; pWait; pWait = pWait->pNext)
1428 if ( !pWait->fResEvents
1429 && (pWait->pHGCMReq->fu32Flags & VBOX_HGCM_REQ_DONE))
1430 {
1431 pWait->fResEvents = VMMDEV_EVENT_HGCM;
1432 rc2 |= RTSemEventMultiSignal(pWait->Event);
1433 }
1434#endif
1435
1436 /* VMMDEV_EVENT_MOUSE_POSITION_CHANGED can only be polled for. */
1437#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1438 if (fEvents & VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
1439 {
1440 pDevExt->u32MousePosChangedSeq++;
1441 VBoxGuestNativeISRMousePollEvent(pDevExt);
1442 }
1443#endif
1444 fEvents &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED;
1445
1446 /* Normal FIFO evaluation. */
1447 fEvents |= pDevExt->f32PendingEvents;
1448 for (pWait = pDevExt->WaitList.pHead; pWait; pWait = pWait->pNext)
1449 if (!pWait->fResEvents)
1450 {
1451 pWait->fResEvents = pWait->fReqEvents & fEvents;
1452 fEvents &= ~pWait->fResEvents;
1453 rc2 |= RTSemEventMultiSignal(pWait->Event);
1454 if (!fEvents)
1455 break;
1456 }
1457
1458 ASMAtomicXchgU32(&pDevExt->f32PendingEvents, fEvents);
1459 RTSpinlockReleaseNoInts(pDevExt->WaitSpinlock, &Tmp);
1460 Assert(rc2 == 0);
1461 }
1462 else /* something is serious wrong... */
1463 Log(("VBoxGuestCommonISR: acknowledge events failed rc=%d, header rc=%d (events=%#x)!!\n",
1464 rc, pReq->header.rc, pReq->events));
1465 }
1466 else
1467 LogFlow(("VBoxGuestCommonISR: not ours\n"));
1468
1469 return fOurIrq;
1470}
1471
Note: See TracBrowser for help on using the repository browser.

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