VirtualBox

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

Last change on this file since 7587 was 7520, checked in by vboxsync, 17 years ago

Added a todo for clearing caps on unload.

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