VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxGuest/VBoxGuest.cpp@ 30569

最後變更 在這個檔案從30569是 30569,由 vboxsync 提交於 15 年 前

VBoxGuestPower is pageable; ensure we are not called at elevated IRQL (Submitted by Dennis Lawler)

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 70.6 KB
 
1/** @file
2 *
3 * VBoxGuest -- VirtualBox Win32 guest support driver
4 *
5 * Copyright (C) 2006-2007 Oracle Corporation
6 *
7 * This file is part of VirtualBox Open Source Edition (OSE), as
8 * available from http://www.alldomusa.eu.org. This file is free software;
9 * you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License (GPL) as published by the Free Software
11 * Foundation, in version 2 as it comes in the "COPYING" file of the
12 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
13 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
14 */
15
16// enable backdoor logging
17//#define LOG_ENABLED
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "VBoxGuest_Internal.h"
23#ifdef TARGET_NT4
24#include "NTLegacy.h"
25#else
26#include "VBoxGuestPnP.h"
27#endif
28#include "Helper.h"
29#include <excpt.h>
30#include <VBox/err.h>
31#include <VBox/log.h>
32#include <iprt/assert.h>
33#include <iprt/asm.h>
34#include <iprt/mem.h>
35#include <stdio.h>
36#include <VBox/VBoxGuestLib.h>
37#include <VBoxGuestInternal.h>
38
39#ifdef TARGET_NT4
40/*
41 * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist
42 * on NT4, so... The same for ExAllocatePool.
43 */
44#undef ExAllocatePool
45#undef ExFreePool
46#endif
47
48/*******************************************************************************
49* Defined Constants And Macros *
50*******************************************************************************/
51
52
53/*******************************************************************************
54* Internal Functions *
55*******************************************************************************/
56extern "C"
57{
58static NTSTATUS VBoxGuestAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
59static void VBoxGuestUnload(PDRIVER_OBJECT pDrvObj);
60static NTSTATUS VBoxGuestCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
61static NTSTATUS VBoxGuestClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
62static NTSTATUS VBoxGuestDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
63static NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
64static NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
65static NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
66static VOID vboxWorkerThread(PVOID context);
67static VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt);
68static VOID vboxIdleThread(PVOID context);
69}
70
71#ifdef VBOX_WITH_HGCM
72DECLVBGL(int) VBoxHGCMCallback(VMMDevHGCMRequestHeader *pHeader, void *pvData, uint32_t u32Data);
73#endif
74
75#ifdef DEBUG
76static VOID testVBoxGuest(VOID);
77#endif
78
79/*******************************************************************************
80* Exported Functions *
81*******************************************************************************/
82RT_C_DECLS_BEGIN
83ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
84RT_C_DECLS_END
85
86#ifdef ALLOC_PRAGMA
87#pragma alloc_text (INIT, DriverEntry)
88#pragma alloc_text (PAGE, createThreads)
89#pragma alloc_text (PAGE, unreserveHypervisorMemory)
90#pragma alloc_text (PAGE, VBoxGuestAddDevice)
91#pragma alloc_text (PAGE, VBoxGuestUnload)
92#pragma alloc_text (PAGE, VBoxGuestCreate)
93#pragma alloc_text (PAGE, VBoxGuestClose)
94#pragma alloc_text (PAGE, VBoxGuestDeviceControl)
95#pragma alloc_text (PAGE, VBoxGuestShutdown)
96#pragma alloc_text (PAGE, VBoxGuestNotSupportedStub)
97/* Note: at least the isr handler should be in non-pageable memory! */
98/*#pragma alloc_text (PAGE, VBoxGuestDpcHandler)
99 #pragma alloc_text (PAGE, VBoxGuestIsrHandler) */
100#pragma alloc_text (PAGE, vboxWorkerThread)
101#pragma alloc_text (PAGE, reserveHypervisorMemory)
102#pragma alloc_text (PAGE, vboxIdleThread)
103#endif
104
105winVersion_t winVersion;
106
107/**
108 * Driver entry point.
109 *
110 * @returns appropriate status code.
111 * @param pDrvObj Pointer to driver object.
112 * @param pRegPath Registry base path.
113 */
114ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
115{
116 NTSTATUS rc = STATUS_SUCCESS;
117
118 dprintf(("VBoxGuest::DriverEntry. Driver built: %s %s\n", __DATE__, __TIME__));
119
120 ULONG majorVersion;
121 ULONG minorVersion;
122 ULONG buildNumber;
123 PsGetVersion(&majorVersion, &minorVersion, &buildNumber, NULL);
124 dprintf(("VBoxGuest::DriverEntry: Running on Windows NT version %d.%d, build %d\n", majorVersion, minorVersion, buildNumber));
125#ifdef DEBUG
126 testVBoxGuest();
127#endif
128 switch (majorVersion)
129 {
130 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
131 switch (minorVersion)
132 {
133 case 0: /* Note: Also could be Windows 2008 Server! */
134 winVersion = WINVISTA;
135 break;
136 case 1: /* Note: Also could be Windows 2008 Server R2! */
137 winVersion = WIN7;
138 break;
139 default:
140 dprintf(("VBoxGuest::DriverEntry: Unknown version of Windows, refusing!\n"));
141 return STATUS_DRIVER_UNABLE_TO_LOAD;
142 }
143 break;
144 case 5:
145 switch (minorVersion)
146 {
147 case 2:
148 winVersion = WIN2K3;
149 break;
150 case 1:
151 winVersion = WINXP;
152 break;
153 case 0:
154 winVersion = WIN2K;
155 break;
156 default:
157 dprintf(("VBoxGuest::DriverEntry: Unknown version of Windows, refusing!\n"));
158 return STATUS_DRIVER_UNABLE_TO_LOAD;
159 }
160 break;
161 case 4:
162 winVersion = WINNT4;
163 break;
164 default:
165 dprintf(("VBoxGuest::DriverEntry: At least Windows NT4 required!\n"));
166 return STATUS_DRIVER_UNABLE_TO_LOAD;
167 }
168
169 /*
170 * Setup the driver entry points in pDrvObj.
171 */
172 pDrvObj->DriverUnload = VBoxGuestUnload;
173 pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxGuestCreate;
174 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxGuestClose;
175 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxGuestDeviceControl;
176 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxGuestDeviceControl;
177 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = VBoxGuestShutdown;
178 pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxGuestNotSupportedStub;
179 pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxGuestNotSupportedStub;
180#ifdef TARGET_NT4
181 rc = ntCreateDevice(pDrvObj, NULL /* pDevObj */, pRegPath);
182#else
183 pDrvObj->MajorFunction[IRP_MJ_PNP] = VBoxGuestPnP;
184 pDrvObj->MajorFunction[IRP_MJ_POWER] = VBoxGuestPower;
185 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VBoxGuestSystemControl;
186 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)VBoxGuestAddDevice;
187#endif
188
189 dprintf(("VBoxGuest::DriverEntry returning %#x\n", rc));
190 return rc;
191}
192
193#ifndef TARGET_NT4
194/**
195 * Handle request from the Plug & Play subsystem
196 *
197 * @returns NT status code
198 * @param pDrvObj Driver object
199 * @param pDevObj Device object
200 */
201static NTSTATUS VBoxGuestAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
202{
203 NTSTATUS rc;
204 dprintf(("VBoxGuest::VBoxGuestAddDevice\n"));
205
206 /*
207 * Create device.
208 */
209 PDEVICE_OBJECT deviceObject = NULL;
210 UNICODE_STRING devName;
211 RtlInitUnicodeString(&devName, VBOXGUEST_DEVICE_NAME_NT);
212 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXT), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject);
213 if (!NT_SUCCESS(rc))
214 {
215 dprintf(("VBoxGuest::VBoxGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc));
216 return rc;
217 }
218 UNICODE_STRING win32Name;
219 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
220 rc = IoCreateSymbolicLink(&win32Name, &devName);
221 if (!NT_SUCCESS(rc))
222 {
223 dprintf(("VBoxGuest::VBoxGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc));
224 IoDeleteDevice(deviceObject);
225 return rc;
226 }
227
228 /*
229 * Setup the device extension.
230 */
231 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)deviceObject->DeviceExtension;
232 RtlZeroMemory(pDevExt, sizeof(VBOXGUESTDEVEXT));
233
234 pDevExt->deviceObject = deviceObject;
235 pDevExt->devState = STOPPED;
236
237 pDevExt->nextLowerDriver = IoAttachDeviceToDeviceStack(deviceObject, pDevObj);
238 if (pDevExt->nextLowerDriver == NULL)
239 {
240 dprintf(("VBoxGuest::VBoxGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDrive\n"));
241 IoDeleteSymbolicLink(&win32Name);
242 IoDeleteDevice(deviceObject);
243 return STATUS_DEVICE_NOT_CONNECTED;
244 }
245
246#ifdef VBOX_WITH_HGCM
247 int rc2 = RTSpinlockCreate(&pDevExt->SessionSpinlock);
248 if (RT_FAILURE(rc2))
249 {
250 dprintf(("VBoxGuest::VBoxGuestAddDevice: RTSpinlockCreate failed\n"));
251 IoDetachDevice(pDevExt->nextLowerDriver);
252 IoDeleteSymbolicLink(&win32Name);
253 IoDeleteDevice(deviceObject);
254 return STATUS_DRIVER_UNABLE_TO_LOAD;
255 }
256#endif
257
258#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
259 hlpRegisterBugCheckCallback(pDevExt); /* ignore failure! */
260#endif
261
262 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
263 deviceObject->Flags |= DO_POWER_PAGABLE;
264
265 /* Driver is ready now. */
266 deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
267
268 dprintf(("VBoxGuest::VBoxGuestAddDevice: returning with rc = 0x%x\n", rc));
269 return rc;
270}
271#endif
272
273
274/**
275 * Unload the driver.
276 *
277 * @param pDrvObj Driver object.
278 */
279void VBoxGuestUnload(PDRIVER_OBJECT pDrvObj)
280{
281 dprintf(("VBoxGuest::VBoxGuestUnload\n"));
282#ifdef TARGET_NT4
283 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension;
284 unreserveHypervisorMemory(pDevExt);
285 if (pDevExt->workerThread)
286 {
287 dprintf(("VBoxGuest::VBoxGuestUnload: waiting for the worker thread to terminate...\n"));
288 pDevExt->stopThread = TRUE;
289 KeSetEvent(&pDevExt->workerThreadRequest, 0, FALSE);
290 KeWaitForSingleObject(pDevExt->workerThread,
291 Executive, KernelMode, FALSE, NULL);
292 dprintf(("VBoxGuest::VBoxGuestUnload: returned from KeWaitForSingleObject for worker thread\n"));
293 }
294 if (pDevExt->idleThread)
295 {
296 dprintf(("VBoxGuest::VBoxGuestUnload: waiting for the idle thread to terminate...\n"));
297 pDevExt->stopThread = TRUE;
298 KeWaitForSingleObject(pDevExt->idleThread,
299 Executive, KernelMode, FALSE, NULL);
300 dprintf(("VBoxGuest::VBoxGuestUnload: returned from KeWaitForSingleObject for idle thread\n"));
301 }
302
303 hlpVBoxUnmapVMMDevMemory (pDevExt);
304
305 VBoxCleanupMemBalloon(pDevExt);
306
307#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
308 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
309#endif
310
311 /*
312 * I don't think it's possible to unload a driver which processes have
313 * opened, at least we'll blindly assume that here.
314 */
315 UNICODE_STRING win32Name;
316 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
317 NTSTATUS rc = IoDeleteSymbolicLink(&win32Name);
318
319#ifdef VBOX_WITH_HGCM
320 if (pDevExt->SessionSpinlock != NIL_RTSPINLOCK)
321 {
322 int rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock);
323 dprintf(("VBoxGuest::VBoxGuestUnload: spinlock destroyed with rc=%Rrc\n", rc2));
324 }
325#endif
326 IoDeleteDevice(pDrvObj->DeviceObject);
327#endif
328
329 dprintf(("VBoxGuest::VBoxGuestUnload: returning\n"));
330}
331
332/**
333 * Create (i.e. Open) file entry point.
334 *
335 * @param pDevObj Device object.
336 * @param pIrp Request packet.
337 */
338NTSTATUS VBoxGuestCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
339{
340 dprintf(("VBoxGuest::VBoxGuestCreate\n"));
341
342 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
343 PFILE_OBJECT pFileObj = pStack->FileObject;
344 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
345
346 /*
347 * We are not remotely similar to a directory...
348 * (But this is possible.)
349 */
350 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
351 {
352 dprintf(("VBoxGuest::VBoxGuestCreate: we're not a directory!\n"));
353 pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
354 pIrp->IoStatus.Information = 0;
355 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
356 return STATUS_NOT_A_DIRECTORY;
357 }
358
359#ifdef VBOX_WITH_HGCM
360 if (pFileObj)
361 {
362 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession));
363 if (RT_UNLIKELY(!pSession))
364 {
365 dprintf(("VBoxGuestCreate: no memory!\n"));
366 pIrp->IoStatus.Status = STATUS_NO_MEMORY;
367 pIrp->IoStatus.Information = 0;
368 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
369 return STATUS_NO_MEMORY;
370 }
371
372 pFileObj->FsContext = pSession;
373 dprintf(("VBoxGuestCreate: pDevExt=%p pFileObj=%p pSession=%p\n",
374 pDevExt, pFileObj, pFileObj->FsContext));
375 }
376#endif
377
378 NTSTATUS rcNt = pIrp->IoStatus.Status = STATUS_SUCCESS;
379 pIrp->IoStatus.Information = 0;
380 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
381
382 dprintf(("VBoxGuest::VBoxGuestCreate: returning 0x%x\n", rcNt));
383 return rcNt;
384}
385
386
387/**
388 * Close file entry point.
389 *
390 * @param pDevObj Device object.
391 * @param pIrp Request packet.
392 */
393NTSTATUS VBoxGuestClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
394{
395 dprintf(("VBoxGuest::VBoxGuestClose\n"));
396
397 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
398 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
399 PFILE_OBJECT pFileObj = pStack->FileObject;
400 dprintf(("VBoxGuest::VBoxGuestClose: pDevExt=%p pFileObj=%p pSession=%p\n",
401 pDevExt, pFileObj, pFileObj->FsContext));
402
403#ifdef VBOX_WITH_HGCM
404 if (pFileObj)
405 {
406 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
407 if (RT_UNLIKELY(!pSession))
408 {
409 dprintf(("VBoxGuestClose: no FsContext!\n"));
410 }
411 else
412 {
413 for (unsigned i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
414 if (pSession->aHGCMClientIds[i])
415 {
416 VBoxGuestHGCMDisconnectInfo Info;
417 Info.result = 0;
418 Info.u32ClientID = pSession->aHGCMClientIds[i];
419 pSession->aHGCMClientIds[i] = 0;
420 dprintf(("VBoxGuestClose: disconnecting HGCM client id %#RX32\n", Info.u32ClientID));
421 VbglR0HGCMInternalDisconnect(&Info, VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
422 }
423 RTMemFree(pSession);
424 }
425 }
426#endif
427
428 pFileObj->FsContext = NULL;
429 pIrp->IoStatus.Information = 0;
430 pIrp->IoStatus.Status = STATUS_SUCCESS;
431 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
432
433 return STATUS_SUCCESS;
434}
435
436#ifdef VBOX_WITH_HGCM
437static void VBoxHGCMCallbackWorker (VMMDevHGCMRequestHeader *pHeader, PVBOXGUESTDEVEXT pDevExt,
438 uint32_t u32Timeout, bool fInterruptible, KPROCESSOR_MODE ProcessorMode)
439{
440 /* Possible problem with request completion right between the fu32Flags check and KeWaitForSingleObject
441 * call; introduce a timeout to make sure we don't wait indefinitely.
442 */
443
444 LARGE_INTEGER timeout;
445 if (u32Timeout == RT_INDEFINITE_WAIT)
446 timeout.QuadPart = pDevExt->HGCMWaitTimeout.QuadPart;
447 else
448 {
449 timeout.QuadPart = u32Timeout;
450 timeout.QuadPart *= -10000; /* relative in 100ns units */
451 }
452 while ((pHeader->fu32Flags & VBOX_HGCM_REQ_DONE) == 0)
453 {
454 /* Specifying UserMode so killing the user process will abort the wait. */
455 NTSTATUS rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive,
456 ProcessorMode,
457 fInterruptible ? TRUE : FALSE, /* Alertable */
458 &timeout
459 );
460 dprintf(("VBoxHGCMCallback: Wait returned %d fu32Flags=%x\n", rc, pHeader->fu32Flags));
461
462 if (rc == STATUS_TIMEOUT && u32Timeout == RT_INDEFINITE_WAIT)
463 continue;
464
465 if (rc != STATUS_WAIT_0)
466 {
467 dprintf(("VBoxHGCMCallback: The external event was signalled or the wait timed out or terminated rc = 0x%08X.\n", rc));
468 break;
469 }
470
471 dprintf(("VBoxHGCMCallback: fu32Flags = %08X\n", pHeader->fu32Flags));
472 }
473 return;
474}
475
476DECLVBGL(int) VBoxHGCMCallback (VMMDevHGCMRequestHeader *pHeader, void *pvData, uint32_t u32Data)
477{
478 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvData;
479
480 dprintf(("VBoxHGCMCallback\n"));
481 VBoxHGCMCallbackWorker (pHeader, pDevExt, u32Data, false, UserMode);
482 return VINF_SUCCESS;
483}
484
485DECLVBGL(int) VBoxHGCMCallbackKernelMode (VMMDevHGCMRequestHeader *pHeader, void *pvData, uint32_t u32Data)
486{
487 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvData;
488
489 dprintf(("VBoxHGCMCallback\n"));
490 VBoxHGCMCallbackWorker (pHeader, pDevExt, u32Data, false, KernelMode);
491 return VINF_SUCCESS;
492}
493
494DECLVBGL(int) VBoxHGCMCallbackInterruptible (VMMDevHGCMRequestHeader *pHeader, void *pvData,
495 uint32_t u32Data)
496{
497 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvData;
498
499 dprintf(("VBoxHGCMCallbackInterruptible\n"));
500 VBoxHGCMCallbackWorker (pHeader, pDevExt, u32Data, true, UserMode);
501 return VINF_SUCCESS;
502}
503
504NTSTATUS vboxHGCMVerifyIOBuffers (PIO_STACK_LOCATION pStack, unsigned cb)
505{
506 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < cb)
507 {
508 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: OutputBufferLength %d < %d\n",
509 pStack->Parameters.DeviceIoControl.OutputBufferLength, cb));
510 return STATUS_INVALID_PARAMETER;
511 }
512
513 if (pStack->Parameters.DeviceIoControl.InputBufferLength < cb)
514 {
515 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: InputBufferLength %d < %d\n",
516 pStack->Parameters.DeviceIoControl.InputBufferLength, cb));
517 return STATUS_INVALID_PARAMETER;
518 }
519
520 return STATUS_SUCCESS;
521}
522
523#endif /* VBOX_WITH_HGCM */
524
525static bool IsPowerOfTwo (uint32_t val)
526{
527 return (val & (val - 1)) == 0;
528}
529
530static bool CtlGuestFilterMask (uint32_t u32OrMask, uint32_t u32NotMask)
531{
532 bool result = false;
533 VMMDevCtlGuestFilterMask *req;
534 int rc = VbglGRAlloc ((VMMDevRequestHeader **) &req, sizeof (*req),
535 VMMDevReq_CtlGuestFilterMask);
536
537 if (RT_SUCCESS (rc))
538 {
539 req->u32OrMask = u32OrMask;
540 req->u32NotMask = u32NotMask;
541
542 rc = VbglGRPerform (&req->header);
543 if (RT_FAILURE (rc))
544 {
545 dprintf (("VBoxGuest::VBoxGuestDeviceControl: error issuing request to VMMDev! "
546 "rc = %Rrc\n", rc));
547 }
548 else
549 {
550 result = true;
551 }
552 VbglGRFree (&req->header);
553 }
554
555 return result;
556}
557
558#ifdef VBOX_WITH_MANAGEMENT
559static int VBoxGuestSetBalloonSize(PVBOXGUESTDEVEXT pDevExt, uint32_t cBalloonChunks)
560{
561 VMMDevChangeMemBalloon *req = NULL;
562 int rc = VINF_SUCCESS;
563 uint32_t i;
564
565 if (cBalloonChunks > pDevExt->MemBalloon.cMaxBalloonChunks)
566 {
567 AssertMsgFailed(("VBoxGuestSetBalloonSize illegal balloon size %d (max=%d)\n", cBalloonChunks, pDevExt->MemBalloon.cMaxBalloonChunks));
568 return VERR_INVALID_PARAMETER;
569 }
570
571 if (cBalloonChunks == pDevExt->MemBalloon.cBalloonChunks)
572 return VINF_SUCCESS; /* nothing to do */
573
574 /* Allocate request packet */
575 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]), VMMDevReq_ChangeMemBalloon);
576 if (RT_FAILURE(rc))
577 return rc;
578
579 if (cBalloonChunks > pDevExt->MemBalloon.cBalloonChunks)
580 {
581 /* inflate */
582 for (i = pDevExt->MemBalloon.cBalloonChunks; i < cBalloonChunks; i++)
583 {
584#ifndef TARGET_NT4
585 /*
586 * Use MmAllocatePagesForMdl to specify the range of physical addresses we wish to use.
587 */
588 PHYSICAL_ADDRESS Zero;
589 PHYSICAL_ADDRESS HighAddr;
590 Zero.QuadPart = 0;
591 HighAddr.QuadPart = _4G - 1;
592 PMDL pMdl = MmAllocatePagesForMdl(Zero, HighAddr, Zero, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
593 if (pMdl)
594 {
595 if (MmGetMdlByteCount(pMdl) < VMMDEV_MEMORY_BALLOON_CHUNK_SIZE)
596 {
597 MmFreePagesFromMdl(pMdl);
598 ExFreePool(pMdl);
599 rc = VERR_NO_MEMORY;
600 goto end;
601 }
602 }
603#else
604 PVOID pvBalloon;
605 pvBalloon = ExAllocatePool(PagedPool, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
606 if (!pvBalloon)
607 {
608 rc = VERR_NO_MEMORY;
609 goto end;
610 }
611
612 PMDL pMdl = IoAllocateMdl (pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, FALSE, FALSE, NULL);
613 if (pMdl == NULL)
614 {
615 rc = VERR_NO_MEMORY;
616 ExFreePool(pvBalloon);
617 AssertMsgFailed(("IoAllocateMdl %p %x failed!!\n", pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE));
618 goto end;
619 }
620 else
621 {
622 __try {
623 /* Calls to MmProbeAndLockPages must be enclosed in a try/except block. */
624 MmProbeAndLockPages (pMdl, KernelMode, IoModifyAccess);
625 }
626 __except(EXCEPTION_EXECUTE_HANDLER)
627 {
628 dprintf(("MmProbeAndLockPages failed!\n"));
629 rc = VERR_NO_MEMORY;
630 IoFreeMdl (pMdl);
631 ExFreePool(pvBalloon);
632 goto end;
633 }
634 }
635#endif
636
637 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
638
639 /* Copy manually as RTGCPHYS is always 64 bits */
640 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
641 req->aPhysPage[j] = pPageDesc[j] << PAGE_SHIFT; /* PFN_NUMBER is physical page nr, so shift left by 12 to get the physical address */
642
643 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
644 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
645 req->fInflate = true;
646
647 rc = VbglGRPerform(&req->header);
648 if (RT_FAILURE(rc))
649 {
650 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev! "
651 "rc = %Rrc\n", rc));
652
653#ifndef TARGET_NT4
654 MmFreePagesFromMdl(pMdl);
655 ExFreePool(pMdl);
656#else
657 IoFreeMdl (pMdl);
658 ExFreePool(pvBalloon);
659#endif
660 goto end;
661 }
662 else
663 {
664#ifndef TARGET_NT4
665 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pMdl));
666#else
667 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pvBalloon));
668#endif
669 pDevExt->MemBalloon.paMdlMemBalloon[i] = pMdl;
670 pDevExt->MemBalloon.cBalloonChunks++;
671 }
672 }
673 }
674 else
675 {
676 /* deflate */
677 for (i = pDevExt->MemBalloon.cBalloonChunks; i > cBalloonChunks; i--)
678 {
679 uint32_t index = i - 1;
680 PMDL pMdl = pDevExt->MemBalloon.paMdlMemBalloon[index];
681
682 Assert(pMdl);
683 if (pMdl)
684 {
685#ifdef TARGET_NT4
686 PVOID pvBalloon = MmGetMdlVirtualAddress(pMdl);
687#endif
688
689 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
690
691 /* Copy manually as RTGCPHYS is always 64 bits */
692 for (uint32_t j = 0; j < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; j++)
693 req->aPhysPage[j] = pPageDesc[j] << PAGE_SHIFT; /* PFN_NUMBER is physical page nr, so shift left by 12 to get the physical address */
694
695 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
696 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
697 req->fInflate = false;
698
699 rc = VbglGRPerform(&req->header);
700 if (RT_FAILURE(rc))
701 {
702 AssertMsgFailed(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev! rc = %Rrc\n", rc));
703 break;
704 }
705
706 /* Free the ballooned memory */
707#ifndef TARGET_NT4
708 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pMdl));
709 MmFreePagesFromMdl(pMdl);
710 ExFreePool(pMdl);
711#else
712 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pvBalloon));
713 MmUnlockPages (pMdl);
714 IoFreeMdl (pMdl);
715 ExFreePool(pvBalloon);
716#endif
717
718 pDevExt->MemBalloon.paMdlMemBalloon[index] = NULL;
719 pDevExt->MemBalloon.cBalloonChunks--;
720 }
721 }
722 }
723 Assert(pDevExt->MemBalloon.cBalloonChunks <= pDevExt->MemBalloon.cMaxBalloonChunks);
724
725end:
726 VbglGRFree(&req->header);
727 return rc;
728}
729
730static int VBoxGuestQueryMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, ULONG *pMemBalloonSize)
731{
732 /* just perform the request */
733 VMMDevGetMemBalloonChangeRequest *req = NULL;
734
735 dprintf(("VBoxGuestQueryMemoryBalloon\n"));
736
737 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest);
738
739 if (RT_SUCCESS(rc))
740 {
741 req->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
742 rc = VbglGRPerform(&req->header);
743
744 if (RT_FAILURE(rc))
745 {
746 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_CHECK_BALLOON: error issuing request to VMMDev! "
747 "rc = %Rrc\n", rc));
748 }
749 else
750 {
751 if (!pDevExt->MemBalloon.paMdlMemBalloon)
752 {
753 pDevExt->MemBalloon.cMaxBalloonChunks = req->cPhysMemChunks;
754 pDevExt->MemBalloon.paMdlMemBalloon = (PMDL *)ExAllocatePool(PagedPool, req->cPhysMemChunks * sizeof(PMDL));
755 Assert(pDevExt->MemBalloon.paMdlMemBalloon);
756 if (!pDevExt->MemBalloon.paMdlMemBalloon)
757 return VERR_NO_MEMORY;
758 }
759 Assert(pDevExt->MemBalloon.cMaxBalloonChunks == req->cPhysMemChunks);
760
761 rc = VBoxGuestSetBalloonSize(pDevExt, req->cBalloonChunks);
762 /* ignore out of memory failures */
763 if (rc == VERR_NO_MEMORY)
764 rc = VINF_SUCCESS;
765
766 if (pMemBalloonSize)
767 *pMemBalloonSize = pDevExt->MemBalloon.cBalloonChunks;
768 }
769
770 VbglGRFree(&req->header);
771 }
772 return rc;
773}
774#endif
775
776void VBoxInitMemBalloon(PVBOXGUESTDEVEXT pDevExt)
777{
778#ifdef VBOX_WITH_MANAGEMENT
779 ULONG dummy;
780
781 pDevExt->MemBalloon.cBalloonChunks = 0;
782 pDevExt->MemBalloon.cMaxBalloonChunks = 0;
783 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
784
785 VBoxGuestQueryMemoryBalloon(pDevExt, &dummy);
786#endif
787}
788
789void VBoxCleanupMemBalloon(PVBOXGUESTDEVEXT pDevExt)
790{
791#ifdef VBOX_WITH_MANAGEMENT
792 if (pDevExt->MemBalloon.paMdlMemBalloon)
793 {
794 /* Clean up the memory balloon leftovers */
795 VBoxGuestSetBalloonSize(pDevExt, 0);
796 ExFreePool(pDevExt->MemBalloon.paMdlMemBalloon);
797 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
798 }
799 Assert(pDevExt->MemBalloon.cBalloonChunks == 0);
800#endif
801}
802
803/** A quick implementation of AtomicTestAndClear for uint32_t and multiple
804 * bits.
805 */
806static uint32_t guestAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
807{
808 AssertPtrReturn(pu32Bits, 0);
809 LogFlowFunc(("*pu32Bits=0x%x, u32Mask=0x%x\n", *(long *)pu32Bits,
810 u32Mask));
811 uint32_t u32Result = 0;
812 uint32_t u32WorkingMask = u32Mask;
813 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
814
815 while (iBitOffset > 0)
816 {
817 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
818 if (fSet)
819 u32Result |= 1 << (iBitOffset - 1);
820 u32WorkingMask &= ~(1 << (iBitOffset - 1));
821 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
822 }
823 LogFlowFunc(("Returning 0x%x\n", u32Result));
824 return u32Result;
825}
826
827/**
828 * Device I/O Control entry point.
829 *
830 * @param pDevObj Device object.
831 * @param pIrp Request packet.
832 */
833NTSTATUS VBoxGuestDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
834{
835 dprintf(("VBoxGuest::VBoxGuestDeviceControl\n"));
836
837 NTSTATUS Status = STATUS_SUCCESS;
838
839 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
840
841 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
842
843 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* all requests are buffered. */
844
845 unsigned cbOut = 0;
846
847 switch (pStack->Parameters.DeviceIoControl.IoControlCode)
848 {
849 case VBOXGUEST_IOCTL_GETVMMDEVPORT:
850 {
851 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_GETVMMDEVPORT\n"));
852
853 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof (VBoxGuestPortInfo))
854 {
855 Status = STATUS_BUFFER_TOO_SMALL;
856 break;
857 }
858
859 VBoxGuestPortInfo *portInfo = (VBoxGuestPortInfo*)pBuf;
860
861 portInfo->portAddress = pDevExt->startPortAddress;
862 portInfo->pVMMDevMemory = pDevExt->pVMMDevMemory;
863
864 cbOut = sizeof(VBoxGuestPortInfo);
865
866 break;
867 }
868
869 case VBOXGUEST_IOCTL_WAITEVENT:
870 {
871 /* Need to be extended to support multiple waiters for an event,
872 * array of counters for each event, event mask is computed, each
873 * time a wait event is arrived.
874 */
875 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_WAITEVENT\n"));
876
877 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VBoxGuestWaitEventInfo))
878 {
879 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
880 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
881 Status = STATUS_BUFFER_TOO_SMALL;
882 break;
883 }
884
885 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestWaitEventInfo)) {
886 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
887 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
888 Status = STATUS_BUFFER_TOO_SMALL;
889 break;
890 }
891
892 VBoxGuestWaitEventInfo *eventInfo = (VBoxGuestWaitEventInfo *)pBuf;
893
894 if (!eventInfo->u32EventMaskIn) {
895 dprintf (("VBoxGuest::VBoxGuestDeviceControl: Invalid input mask %#x\n",
896 eventInfo->u32EventMaskIn));
897 Status = STATUS_INVALID_PARAMETER;
898 break;
899 }
900
901 eventInfo->u32EventFlagsOut = 0;
902 bool fTimeout = (eventInfo->u32TimeoutIn != ~0L);
903
904 /* Possible problem with request completion right between the pending event check and KeWaitForSingleObject
905 * call; introduce a timeout (if none was specified) to make sure we don't wait indefinitely.
906 */
907 LARGE_INTEGER timeout;
908 timeout.QuadPart = (fTimeout) ? eventInfo->u32TimeoutIn : 250;
909 timeout.QuadPart *= -10000;
910
911 NTSTATUS rc = STATUS_SUCCESS;
912
913 for (;;)
914 {
915 uint32_t u32EventsPending =
916 guestAtomicBitsTestAndClear(&pDevExt->u32Events,
917 eventInfo->u32EventMaskIn);
918 dprintf (("mask = 0x%x, pending = 0x%x\n",
919 eventInfo->u32EventMaskIn, u32EventsPending));
920
921 if (u32EventsPending)
922 {
923 eventInfo->u32EventFlagsOut = u32EventsPending;
924 break;
925 }
926
927 rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive /** @todo UserRequest? */,
928 KernelMode, TRUE, &timeout);
929 dprintf(("VBOXGUEST_IOCTL_WAITEVENT: Wait returned %d -> event %x\n", rc, eventInfo->u32EventFlagsOut));
930
931 if (!fTimeout && rc == STATUS_TIMEOUT)
932 continue;
933
934 if (rc != STATUS_SUCCESS)
935 {
936 /* There was a timeout or wait was interrupted, etc. */
937 break;
938 }
939 }
940
941 dprintf (("u32EventFlagsOut = %#x\n", eventInfo->u32EventFlagsOut));
942 cbOut = sizeof(VBoxGuestWaitEventInfo);
943 break;
944 }
945
946 case VBOXGUEST_IOCTL_VMMREQUEST(0): /* (The size isn't relevant on NT.)*/
947 {
948 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_VMMREQUEST\n"));
949
950#define CHECK_SIZE(s) \
951 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < s) \
952 { \
953 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < %d\n", \
954 pStack->Parameters.DeviceIoControl.OutputBufferLength, s)); \
955 Status = STATUS_BUFFER_TOO_SMALL; \
956 break; \
957 } \
958 if (pStack->Parameters.DeviceIoControl.InputBufferLength < s) { \
959 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n", \
960 pStack->Parameters.DeviceIoControl.InputBufferLength, s)); \
961 Status = STATUS_BUFFER_TOO_SMALL; \
962 break; \
963 }
964
965 /* get the request header */
966 CHECK_SIZE(sizeof(VMMDevRequestHeader));
967 VMMDevRequestHeader *requestHeader = (VMMDevRequestHeader *)pBuf;
968 if (!vmmdevGetRequestSize(requestHeader->requestType))
969 {
970 dprintf(("VBoxGuest::VBoxGuestDeviceControl: vmmdevGetRequestSize failed!\n")); \
971 Status = STATUS_INVALID_PARAMETER;
972 break;
973 }
974 /* make sure the buffers suit the request */
975 CHECK_SIZE(vmmdevGetRequestSize(requestHeader->requestType));
976
977 int rc = VbglGRVerify(requestHeader, requestHeader->size);
978 if (RT_FAILURE(rc))
979 {
980 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VMMREQUEST: invalid header: size %#x, expected >= %#x (hdr); type=%#x; rc %d!!\n",
981 requestHeader->size, vmmdevGetRequestSize(requestHeader->requestType), requestHeader->requestType, rc));
982 Status = STATUS_INVALID_PARAMETER;
983 break;
984 }
985
986 /* just perform the request */
987 VMMDevRequestHeader *req = NULL;
988
989 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, requestHeader->size, requestHeader->requestType);
990
991 if (RT_SUCCESS(rc))
992 {
993 /* copy the request information */
994 memcpy((void*)req, (void*)pBuf, requestHeader->size);
995 rc = VbglGRPerform(req);
996
997 if (RT_FAILURE(rc))
998 {
999 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_VMMREQUEST: Error issuing request to VMMDev! "
1000 "rc = %Rrc\n", rc));
1001 Status = STATUS_UNSUCCESSFUL;
1002 }
1003 else
1004 {
1005 /* copy result */
1006 memcpy((void*)pBuf, (void*)req, requestHeader->size);
1007 cbOut = requestHeader->size;
1008 }
1009
1010 VbglGRFree(req);
1011 }
1012 else
1013 {
1014 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VbglGRAlloc failed!\n")); \
1015 Status = STATUS_UNSUCCESSFUL;
1016 }
1017#undef CHECK_SIZE
1018 break;
1019 }
1020
1021 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
1022 {
1023 VBoxGuestFilterMaskInfo *maskInfo;
1024
1025 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestFilterMaskInfo)) {
1026 dprintf (("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n",
1027 pStack->Parameters.DeviceIoControl.InputBufferLength,
1028 sizeof (VBoxGuestFilterMaskInfo)));
1029 Status = STATUS_BUFFER_TOO_SMALL;
1030 break;
1031
1032 }
1033
1034 maskInfo = (VBoxGuestFilterMaskInfo *) pBuf;
1035 if (!CtlGuestFilterMask (maskInfo->u32OrMask, maskInfo->u32NotMask))
1036 {
1037 Status = STATUS_UNSUCCESSFUL;
1038 }
1039 break;
1040 }
1041
1042#ifdef VBOX_WITH_HGCM
1043 /* HGCM offers blocking IOCTLSs just like waitevent and actually
1044 * uses the same waiting code.
1045 */
1046#ifdef RT_ARCH_AMD64
1047 case VBOXGUEST_IOCTL_HGCM_CONNECT_32:
1048#endif /* RT_ARCH_AMD64 */
1049 case VBOXGUEST_IOCTL_HGCM_CONNECT:
1050 {
1051 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CONNECT\n"));
1052
1053 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMConnectInfo))
1054 {
1055 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
1056 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
1057 Status = STATUS_INVALID_PARAMETER;
1058 break;
1059 }
1060
1061 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMConnectInfo)) {
1062 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
1063 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
1064 Status = STATUS_INVALID_PARAMETER;
1065 break;
1066 }
1067
1068 VBoxGuestHGCMConnectInfo *ptr = (VBoxGuestHGCMConnectInfo *)pBuf;
1069
1070 /* If request will be processed asynchronously, execution will
1071 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
1072 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
1073 * flag is set, returns.
1074 */
1075
1076 dprintf(("a) ptr->u32ClientID = %d\n", ptr->u32ClientID));
1077
1078 int rc = VbglR0HGCMInternalConnect (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback,
1079 pDevExt, RT_INDEFINITE_WAIT);
1080
1081 dprintf(("b) ptr->u32ClientID = %d\n", ptr->u32ClientID));
1082
1083 if (RT_FAILURE(rc))
1084 {
1085 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: vbox rc = %Rrc\n", rc));
1086 Status = STATUS_UNSUCCESSFUL;
1087 }
1088 else
1089 {
1090 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1091
1092 if (RT_SUCCESS(ptr->result) && pStack->FileObject)
1093 {
1094 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: pDevExt=%p pFileObj=%p pSession=%p\n",
1095 pDevExt, pStack->FileObject, pStack->FileObject->FsContext));
1096
1097 /*
1098 * Append the client id to the client id table.
1099 * If the table has somehow become filled up, we'll disconnect the session.
1100 */
1101 unsigned i;
1102 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pStack->FileObject->FsContext;
1103 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1104
1105 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1106 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1107 if (!pSession->aHGCMClientIds[i])
1108 {
1109 pSession->aHGCMClientIds[i] = ptr->u32ClientID;
1110 break;
1111 }
1112 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1113
1114 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1115 {
1116 static unsigned s_cErrors = 0;
1117 if (s_cErrors++ < 32)
1118 dprintf(("VBoxGuestCommonIOCtl: HGCM_CONNECT: too many HGCMConnect calls for one session!\n"));
1119
1120 VBoxGuestHGCMDisconnectInfo Info;
1121 Info.result = 0;
1122 Info.u32ClientID = ptr->u32ClientID;
1123 VbglR0HGCMInternalDisconnect(&Info, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1124 Status = STATUS_UNSUCCESSFUL;
1125 break;
1126 }
1127 }
1128 else
1129 {
1130 /* @fixme, r=Leonid. I have no clue what to do in cases where
1131 * pStack->FileObject==NULL. Can't populate list of HGCM ID's...
1132 * But things worked before, so do nothing for now.
1133 */
1134 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: pDevExt=%p, pStack->FileObject=%p\n", pDevExt, pStack->FileObject));
1135 }
1136 }
1137
1138 } break;
1139
1140#ifdef RT_ARCH_AMD64
1141 case VBOXGUEST_IOCTL_HGCM_DISCONNECT_32:
1142#endif /* RT_ARCH_AMD64 */
1143 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
1144 {
1145 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_DISCONNECT\n"));
1146
1147 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo))
1148 {
1149 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
1150 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
1151 Status = STATUS_INVALID_PARAMETER;
1152 break;
1153 }
1154
1155 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo)) {
1156 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
1157 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
1158 Status = STATUS_INVALID_PARAMETER;
1159 break;
1160 }
1161
1162 VBoxGuestHGCMDisconnectInfo *ptr = (VBoxGuestHGCMDisconnectInfo *)pBuf;
1163
1164 uint32_t u32ClientId=0;
1165 unsigned i=0;
1166 PVBOXGUESTSESSION pSession=0;
1167 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1168
1169 /* See comment in VBOXGUEST_IOCTL_HGCM_CONNECT */
1170 if (pStack->FileObject)
1171 {
1172 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: pDevExt=%p pFileObj=%p pSession=%p\n",
1173 pDevExt, pStack->FileObject, pStack->FileObject->FsContext));
1174
1175 u32ClientId = ptr->u32ClientID;
1176 pSession = (PVBOXGUESTSESSION)pStack->FileObject->FsContext;
1177
1178 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1179 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1180 if (pSession->aHGCMClientIds[i] == u32ClientId)
1181 {
1182 pSession->aHGCMClientIds[i] = UINT32_MAX;
1183 break;
1184 }
1185 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1186 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1187 {
1188 static unsigned s_cErrors = 0;
1189 if (s_cErrors++ > 32)
1190 dprintf(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", u32ClientId));
1191 Status = STATUS_INVALID_PARAMETER;
1192 break;
1193 }
1194 }
1195
1196 /* If request will be processed asynchronously, execution will
1197 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
1198 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
1199 * flag is set, returns.
1200 */
1201
1202 int rc = VbglR0HGCMInternalDisconnect (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1203
1204 if (RT_FAILURE(rc))
1205 {
1206 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: vbox rc = %Rrc\n", rc));
1207 Status = STATUS_UNSUCCESSFUL;
1208 }
1209 else
1210 {
1211 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1212 }
1213
1214 if (pStack->FileObject)
1215 {
1216 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1217 if (pSession->aHGCMClientIds[i] == UINT32_MAX)
1218 pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) && RT_SUCCESS(ptr->result) ? 0 : u32ClientId;
1219 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1220 }
1221 } break;
1222
1223#ifdef RT_ARCH_AMD64
1224 case VBOXGUEST_IOCTL_HGCM_CALL_32(0): /* (The size isn't relevant on NT.) */
1225 {
1226 /* A 32 bit application call. */
1227 int rc;
1228
1229 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL_32\n"));
1230
1231 Status = vboxHGCMVerifyIOBuffers (pStack,
1232 sizeof (VBoxGuestHGCMCallInfo));
1233
1234 if (Status != STATUS_SUCCESS)
1235 {
1236 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
1237 break;
1238 }
1239
1240 /* @todo: Old guest OpenGL driver used the same IOCtl code for both 32 and 64 bit binaries.
1241 * This is a protection, and can be removed if there were no 64 bit driver.
1242 */
1243 if (!IoIs32bitProcess(pIrp))
1244 {
1245 Status = STATUS_UNSUCCESSFUL;
1246 break;
1247 }
1248
1249 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1250 uint32_t fFlags = pIrp->RequestorMode == KernelMode ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1251
1252 rc = VbglR0HGCMInternalCall32(ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1253 pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback,
1254 pDevExt, RT_INDEFINITE_WAIT);
1255
1256 if (RT_FAILURE(rc))
1257 {
1258 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL_32: vbox rc = %Rrc\n", rc));
1259 Status = STATUS_UNSUCCESSFUL;
1260 }
1261 else
1262 {
1263 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1264 }
1265
1266 } break;
1267#endif /* RT_ARCH_AMD64 */
1268
1269 case VBOXGUEST_IOCTL_HGCM_CALL(0): /* (The size isn't relevant on NT.) */
1270 {
1271 int rc;
1272
1273 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL\n"));
1274
1275 Status = vboxHGCMVerifyIOBuffers (pStack,
1276 sizeof (VBoxGuestHGCMCallInfo));
1277
1278 if (Status != STATUS_SUCCESS)
1279 {
1280 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
1281 break;
1282 }
1283
1284 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1285 uint32_t fFlags = pIrp->RequestorMode == KernelMode ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1286
1287 rc = VbglR0HGCMInternalCall (ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1288 pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback,
1289 pDevExt, RT_INDEFINITE_WAIT);
1290
1291 if (RT_FAILURE(rc))
1292 {
1293 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL: vbox rc = %Rrc\n", rc));
1294 Status = STATUS_UNSUCCESSFUL;
1295 }
1296 else
1297 {
1298 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1299 }
1300
1301 } break;
1302
1303 case VBOXGUEST_IOCTL_HGCM_CALL_TIMED(0): /* (The size isn't relevant on NT.) */
1304 {
1305 /* This IOCTL is not used by shared folders, so VBoxHGCMCallbackKernelMode is not used. */
1306 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL_TIMED\n"));
1307
1308 Status = vboxHGCMVerifyIOBuffers (pStack,
1309 sizeof (VBoxGuestHGCMCallInfoTimed));
1310
1311 if (Status != STATUS_SUCCESS)
1312 {
1313 dprintf(("nvalid parameter. Status: %p\n", Status));
1314 break;
1315 }
1316
1317 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pBuf;
1318 VBoxGuestHGCMCallInfo *ptr = &pInfo->info;
1319
1320 int rc;
1321 uint32_t fFlags = pIrp->RequestorMode == KernelMode ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1322 if (pInfo->fInterruptible)
1323 {
1324 dprintf(("VBoxGuest::VBoxGuestDeviceControl: calling VBoxHGCMCall interruptible, timeout %lu ms\n",
1325 pInfo->u32Timeout));
1326 rc = VbglR0HGCMInternalCall (ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1327 VBoxHGCMCallbackInterruptible, pDevExt, pInfo->u32Timeout);
1328 }
1329 else
1330 {
1331 dprintf(("VBoxGuest::VBoxGuestDeviceControl: calling VBoxHGCMCall, timeout %lu ms\n",
1332 pInfo->u32Timeout));
1333 rc = VbglR0HGCMInternalCall (ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1334 VBoxHGCMCallback, pDevExt, pInfo->u32Timeout);
1335 }
1336
1337 if (RT_FAILURE(rc))
1338 {
1339 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL_TIMED: vbox rc = %Rrc\n", rc));
1340 Status = STATUS_UNSUCCESSFUL;
1341 }
1342 else
1343 {
1344 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1345 }
1346
1347 } break;
1348#endif /* VBOX_WITH_HGCM */
1349
1350#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
1351 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
1352 {
1353 LogRel(("VRDP_SESSION: Enable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1354 if (!pDevExt->fVRDPEnabled)
1355 {
1356 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1357
1358 pDevExt->fVRDPEnabled = TRUE;
1359 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1360 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
1361 pSharedUserData->ActiveConsoleId = 2;
1362 }
1363 break;
1364 }
1365
1366 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
1367 {
1368 LogRel(("VRDP_SESSION: Disable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1369 if (pDevExt->fVRDPEnabled)
1370 {
1371 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1372
1373 pDevExt->fVRDPEnabled = FALSE;
1374 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1375 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
1376 pDevExt->ulOldActiveConsoleId = 0;
1377 }
1378 break;
1379 }
1380#endif
1381
1382#ifdef VBOX_WITH_MANAGEMENT
1383 case VBOXGUEST_IOCTL_CHECK_BALLOON:
1384 {
1385 VBoxGuestCheckBalloonInfo *pInfo = (VBoxGuestCheckBalloonInfo *)pBuf;
1386
1387 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestCheckBalloonInfo))
1388 {
1389 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(ULONG) %d\n",
1390 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestCheckBalloonInfo)));
1391 Status = STATUS_INVALID_PARAMETER;
1392 break;
1393 }
1394
1395 ULONG cMemoryBalloonChunks;
1396 int rc = VBoxGuestQueryMemoryBalloon(pDevExt, &cMemoryBalloonChunks);
1397 if (RT_FAILURE(rc))
1398 {
1399 dprintf(("VBOXGUEST_IOCTL_CHECK_BALLOON: vbox rc = %Rrc\n", rc));
1400 Status = STATUS_UNSUCCESSFUL;
1401 }
1402 else
1403 {
1404 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1405 pInfo->cBalloonChunks = cMemoryBalloonChunks;
1406 pInfo->fHandleInR3 = false;
1407 }
1408 break;
1409 }
1410#endif
1411
1412 case VBOXGUEST_IOCTL_LOG(0): /* The size isn't relevant on NT. */
1413 {
1414 /* Enable this only for debugging:
1415 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_LOG %.*s\n", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1416 */
1417 LogRel(("%.*s", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1418 cbOut = 0;
1419 break;
1420 }
1421
1422 default:
1423 Status = STATUS_INVALID_PARAMETER;
1424 break;
1425 }
1426
1427 pIrp->IoStatus.Status = Status;
1428 pIrp->IoStatus.Information = cbOut;
1429
1430 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1431
1432 dprintf(("VBoxGuest::VBoxGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
1433
1434 return Status;
1435}
1436
1437
1438/**
1439 * IRP_MJ_SYSTEM_CONTROL handler
1440 *
1441 * @returns NT status code
1442 * @param pDevObj Device object.
1443 * @param pIrp IRP.
1444 */
1445NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1446{
1447 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1448
1449 dprintf(("VBoxGuest::VBoxGuestSystemControl\n"));
1450
1451 /* Always pass it on to the next driver. */
1452 IoSkipCurrentIrpStackLocation(pIrp);
1453
1454 return IoCallDriver(pDevExt->nextLowerDriver, pIrp);
1455}
1456
1457/**
1458 * IRP_MJ_SHUTDOWN handler
1459 *
1460 * @returns NT status code
1461 * @param pDevObj Device object.
1462 * @param pIrp IRP.
1463 */
1464NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1465{
1466 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1467
1468 dprintf(("VBoxGuest::VBoxGuestShutdown\n"));
1469
1470 if (pDevExt && pDevExt->powerStateRequest)
1471 {
1472 VMMDevPowerStateRequest *req = pDevExt->powerStateRequest;
1473
1474 req->header.requestType = VMMDevReq_SetPowerStatus;
1475 req->powerState = VMMDevPowerState_PowerOff;
1476
1477 int rc = VbglGRPerform (&req->header);
1478
1479 if (RT_FAILURE(rc))
1480 {
1481 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev! "
1482 "rc = %Rrc\n", rc));
1483 }
1484 }
1485
1486 return STATUS_SUCCESS;
1487}
1488
1489/**
1490 * Stub function for functions we don't implemented.
1491 *
1492 * @returns STATUS_NOT_SUPPORTED
1493 * @param pDevObj Device object.
1494 * @param pIrp IRP.
1495 */
1496NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1497{
1498 dprintf(("VBoxGuest::VBoxGuestNotSupportedStub\n"));
1499 pDevObj = pDevObj;
1500
1501 pIrp->IoStatus.Information = 0;
1502 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1503 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1504
1505 return STATUS_NOT_SUPPORTED;
1506}
1507
1508/**
1509 * DPC handler
1510 *
1511 * @param dpc DPC descriptor.
1512 * @param pDevObj Device object.
1513 * @param irp Interrupt request packet.
1514 * @param context Context specific pointer.
1515 */
1516VOID VBoxGuestDpcHandler(PKDPC dpc, PDEVICE_OBJECT pDevObj,
1517 PIRP irp, PVOID context)
1518{
1519 /* Unblock handlers waiting for arrived events.
1520 *
1521 * Events are very low things, there is one event flag (1 or more bit)
1522 * for each event. Each event is processed by exactly one handler.
1523 *
1524 * Assume that we trust additions and that other drivers will
1525 * handle its respective events without trying to fetch all events.
1526 *
1527 * Anyway design assures that wrong event processing will affect only guest.
1528 *
1529 * Event handler calls VMMDev IOCTL for waiting an event.
1530 * It supplies event mask. IOCTL blocks on EventNotification.
1531 * Here we just signal an the EventNotification to all waiting
1532 * threads, the IOCTL handler analyzes events and either
1533 * return to caller or blocks again.
1534 *
1535 * If we do not have too many events this is a simple and good
1536 * approach. Other way is to have as many Event objects as the callers
1537 * and wake up only callers waiting for the specific event.
1538 *
1539 * Now with the 'wake up all' appoach we probably do not need the DPC
1540 * handler and can signal event directly from ISR.
1541 *
1542 */
1543
1544 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1545
1546 dprintf(("VBoxGuest::VBoxGuestDpcHandler\n"));
1547
1548 KePulseEvent(&pDevExt->keventNotification, 0, FALSE);
1549
1550}
1551
1552/**
1553 * ISR handler
1554 *
1555 * @return BOOLEAN indicates whether the IRQ came from us (TRUE) or not (FALSE)
1556 * @param interrupt Interrupt that was triggered.
1557 * @param serviceContext Context specific pointer.
1558 */
1559BOOLEAN VBoxGuestIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext)
1560{
1561 NTSTATUS rc;
1562 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)serviceContext;
1563 BOOLEAN fIRQTaken = FALSE;
1564
1565 dprintf(("VBoxGuest::VBoxGuestIsrHandler haveEvents = %d\n",
1566 pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents));
1567
1568 /*
1569 * now we have to find out whether it was our IRQ. Read the event mask
1570 * from our device to see if there are any pending events
1571 */
1572 if (pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents)
1573 {
1574 /* Acknowlegde events. */
1575 VMMDevEvents *req = pDevExt->irqAckEvents;
1576
1577 rc = VbglGRPerform (&req->header);
1578 if (RT_SUCCESS(rc))
1579 {
1580 dprintf(("VBoxGuest::VBoxGuestIsrHandler: acknowledge events succeeded %#x\n",
1581 req->events));
1582
1583 ASMAtomicOrU32((uint32_t *)&pDevExt->u32Events, req->events);
1584 IoRequestDpc(pDevExt->deviceObject, pDevExt->currentIrp, NULL);
1585 }
1586 else
1587 {
1588 /* This can't be actually. This is sign of a serious problem. */
1589 dprintf(("VBoxGuest::VBoxGuestIsrHandler: "
1590 "acknowledge events failed rc = %Rrc\n", rc));
1591 }
1592
1593 /* Mark IRQ as taken, there were events for us. */
1594 fIRQTaken = TRUE;
1595 }
1596
1597 return fIRQTaken;
1598}
1599
1600/**
1601 * Worker thread to do periodic things such as notify other
1602 * drivers of events.
1603 *
1604 * @param pDevExt device extension pointer
1605 */
1606VOID vboxWorkerThread(PVOID context)
1607{
1608 PVBOXGUESTDEVEXT pDevExt;
1609
1610 pDevExt = (PVBOXGUESTDEVEXT)context;
1611 dprintf(("VBoxGuest::vboxWorkerThread entered\n"));
1612
1613 /* perform the hypervisor address space reservation */
1614 reserveHypervisorMemory(pDevExt);
1615
1616 do
1617 {
1618 /* Nothing to do here yet. */
1619
1620 /*
1621 * Go asleep unless we're supposed to terminate
1622 */
1623 if (!pDevExt->stopThread)
1624 {
1625 ULONG secWait = 60;
1626 dprintf(("VBoxGuest::vboxWorkerThread: waiting for %u seconds...\n", secWait));
1627 LARGE_INTEGER dueTime;
1628 dueTime.QuadPart = -10000 * 1000 * (int)secWait;
1629 if (KeWaitForSingleObject(&pDevExt->workerThreadRequest, Executive,
1630 KernelMode, FALSE, &dueTime) == STATUS_SUCCESS)
1631 {
1632 KeResetEvent(&pDevExt->workerThreadRequest);
1633 }
1634 }
1635 } while (!pDevExt->stopThread);
1636
1637 dprintf(("VBoxGuest::vboxWorkerThread: we've been asked to terminate!\n"));
1638
1639 if (pDevExt->workerThread)
1640 {
1641 ObDereferenceObject(pDevExt->workerThread);
1642 pDevExt->workerThread = NULL;
1643 }
1644 dprintf(("VBoxGuest::vboxWorkerThread: now really gone!\n"));
1645}
1646
1647/**
1648 * Create driver worker threads
1649 *
1650 * @returns NTSTATUS NT status code
1651 * @param pDevExt VBoxGuest device extension
1652 */
1653NTSTATUS createThreads(PVBOXGUESTDEVEXT pDevExt)
1654{
1655 NTSTATUS rc;
1656 HANDLE threadHandle;
1657 OBJECT_ATTRIBUTES objAttributes;
1658
1659 dprintf(("VBoxGuest::createThreads\n"));
1660
1661 // first setup the request semaphore
1662 KeInitializeEvent(&pDevExt->workerThreadRequest, SynchronizationEvent, FALSE);
1663
1664// the API has slightly changed after NT4
1665#ifdef TARGET_NT4
1666#ifdef OBJ_KERNEL_HANDLE
1667#undef OBJ_KERNEL_HANDLE
1668#endif
1669#define OBJ_KERNEL_HANDLE 0
1670#endif
1671
1672 /*
1673 * The worker thread
1674 */
1675 InitializeObjectAttributes(&objAttributes,
1676 NULL,
1677 OBJ_KERNEL_HANDLE,
1678 NULL,
1679 NULL);
1680
1681 rc = PsCreateSystemThread(&threadHandle,
1682 THREAD_ALL_ACCESS,
1683 &objAttributes,
1684 (HANDLE)0L,
1685 NULL,
1686 vboxWorkerThread,
1687 pDevExt);
1688 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for worker thread returned: 0x%x\n", rc));
1689 rc = ObReferenceObjectByHandle(threadHandle,
1690 THREAD_ALL_ACCESS,
1691 NULL,
1692 KernelMode,
1693 (PVOID*)&pDevExt->workerThread,
1694 NULL);
1695 ZwClose(threadHandle);
1696
1697 /*
1698 * The idle thread
1699 */
1700#if 0 /// @todo Windows "sees" that time is lost and reports 100% usage
1701 rc = PsCreateSystemThread(&threadHandle,
1702 THREAD_ALL_ACCESS,
1703 &objAttributes,
1704 (HANDLE)0L,
1705 NULL,
1706 vboxIdleThread,
1707 pDevExt);
1708 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for idle thread returned: 0x%x\n", rc));
1709 rc = ObReferenceObjectByHandle(threadHandle,
1710 THREAD_ALL_ACCESS,
1711 NULL,
1712 KernelMode,
1713 (PVOID*)&pDevExt->idleThread,
1714 NULL);
1715 ZwClose(threadHandle);
1716#endif
1717
1718 return rc;
1719}
1720
1721/**
1722 * Helper routine to reserve address space for the hypervisor
1723 * and communicate its position.
1724 *
1725 * @param pDevExt Device extension structure.
1726 */
1727VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1728{
1729 // @todo rc handling
1730 uint32_t hypervisorSize;
1731
1732 VMMDevReqHypervisorInfo *req = NULL;
1733
1734 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
1735
1736 if (RT_SUCCESS(rc))
1737 {
1738 req->hypervisorStart = 0;
1739 req->hypervisorSize = 0;
1740
1741 rc = VbglGRPerform (&req->header);
1742
1743 if (RT_SUCCESS(rc))
1744 {
1745 hypervisorSize = req->hypervisorSize;
1746
1747 if (!hypervisorSize)
1748 {
1749 dprintf(("VBoxGuest::reserveHypervisorMemory: host returned 0, not doing anything\n"));
1750 return;
1751 }
1752
1753 dprintf(("VBoxGuest::reserveHypervisorMemory: host wants %u bytes of hypervisor address space\n", hypervisorSize));
1754
1755 // Map fictive physical memory into the kernel address space to reserve virtual
1756 // address space. This API does not perform any checks but just allocate the
1757 // PTEs (which we don't really need/want but there isn't any other clean method).
1758 // The hypervisor only likes 4MB aligned virtual addresses, so we have to allocate
1759 // 4MB more than we are actually supposed to in order to guarantee that. Maybe we
1760 // can come up with a less lavish algorithm lateron.
1761 PHYSICAL_ADDRESS physAddr;
1762 physAddr.QuadPart = VBOXGUEST_HYPERVISOR_PHYSICAL_START;
1763 pDevExt->hypervisorMappingSize = hypervisorSize + 0x400000;
1764 pDevExt->hypervisorMapping = MmMapIoSpace(physAddr,
1765 pDevExt->hypervisorMappingSize,
1766 MmNonCached);
1767 if (!pDevExt->hypervisorMapping)
1768 {
1769 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned NULL!\n"));
1770 return;
1771 }
1772
1773 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned %p\n", pDevExt->hypervisorMapping));
1774 dprintf(("VBoxGuest::reserveHypervisorMemory: communicating %p to host\n",
1775 RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000)));
1776
1777 /* align at 4MB */
1778 req->hypervisorStart = (uintptr_t)RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000);
1779
1780 req->header.requestType = VMMDevReq_SetHypervisorInfo;
1781 req->header.rc = VERR_GENERAL_FAILURE;
1782
1783 /* issue request */
1784 rc = VbglGRPerform (&req->header);
1785
1786 if (RT_FAILURE(rc))
1787 {
1788 dprintf(("VBoxGuest::reserveHypervisorMemory: error communicating physical address to VMMDev! "
1789 "rc = %Rrc\n", rc));
1790 }
1791 }
1792 else
1793 {
1794 dprintf(("VBoxGuest::reserveHypervisorMemory: request failed with rc = %Rrc\n", rc));
1795 }
1796 VbglGRFree (&req->header);
1797 }
1798
1799#ifdef RT_ARCH_X86
1800 /* Allocate locked executable memory that can be used for patching guest code. */
1801 {
1802 VMMDevReqPatchMemory *req = NULL;
1803 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqPatchMemory), VMMDevReq_RegisterPatchMemory);
1804 if (RT_SUCCESS(rc))
1805 {
1806 req->cbPatchMem = VMMDEV_GUEST_DEFAULT_PATCHMEM_SIZE;
1807
1808 rc = RTR0MemObjAllocPage(&pDevExt->PatchMemObj, req->cbPatchMem, true /* executable. */);
1809 if (RT_SUCCESS(rc))
1810 {
1811 req->pPatchMem = (RTGCPTR)(uintptr_t)RTR0MemObjAddress(pDevExt->PatchMemObj);
1812
1813 rc = VbglGRPerform (&req->header);
1814 if (RT_FAILURE(rc))
1815 {
1816 dprintf(("VBoxGuest::reserveHypervisorMemory: VMMDevReq_RegisterPatchMemory error! "
1817 "rc = %Rrc\n", rc));
1818 RTR0MemObjFree(pDevExt->PatchMemObj, true);
1819 pDevExt->PatchMemObj = NULL;
1820 }
1821 }
1822 else
1823 {
1824 dprintf(("VBoxGuest::reserveHypervisorMemory: RTR0MemObjAllocPage failed with rc = %Rrc\n", rc));
1825 }
1826 VbglGRFree (&req->header);
1827 }
1828 }
1829#endif
1830 return;
1831}
1832
1833/**
1834 * Helper function to unregister a virtual address space mapping
1835 *
1836 * @param pDevExt Device extension
1837 */
1838VOID unreserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1839{
1840#ifdef RT_ARCH_X86
1841 /* Remove the locked executable memory range that can be used for patching guest code. */
1842 if (pDevExt->PatchMemObj)
1843 {
1844 VMMDevReqPatchMemory *req = NULL;
1845 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqPatchMemory), VMMDevReq_DeregisterPatchMemory);
1846 if (RT_SUCCESS(rc))
1847 {
1848 req->cbPatchMem = (uint32_t)RTR0MemObjSize(pDevExt->PatchMemObj);
1849 req->pPatchMem = (RTGCPTR)(uintptr_t)RTR0MemObjAddress(pDevExt->PatchMemObj);
1850
1851 rc = VbglGRPerform (&req->header);
1852 if (RT_FAILURE(rc))
1853 {
1854 dprintf(("VBoxGuest::reserveHypervisorMemory: VMMDevReq_DeregisterPatchMemory error! "
1855 "rc = %Rrc\n", rc));
1856 /* We intentially leak the memory object here as there still could
1857 * be references to it!!!
1858 */
1859 }
1860 else
1861 {
1862 RTR0MemObjFree(pDevExt->PatchMemObj, true);
1863 }
1864 }
1865 }
1866#endif
1867
1868 VMMDevReqHypervisorInfo *req = NULL;
1869
1870 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
1871
1872 if (RT_SUCCESS(rc))
1873 {
1874 /* tell the hypervisor that the mapping is no longer available */
1875
1876 req->hypervisorStart = 0;
1877 req->hypervisorSize = 0;
1878
1879 rc = VbglGRPerform (&req->header);
1880
1881 if (RT_FAILURE(rc))
1882 {
1883 dprintf(("VBoxGuest::unreserveHypervisorMemory: error communicating physical address to VMMDev! "
1884 "rc = %Rrc\n", rc));
1885 }
1886
1887 VbglGRFree (&req->header);
1888 }
1889
1890 if (!pDevExt->hypervisorMapping)
1891 {
1892 dprintf(("VBoxGuest::unreserveHypervisorMemory: there is no mapping, returning\n"));
1893 return;
1894 }
1895
1896 // unmap fictive IO space
1897 MmUnmapIoSpace(pDevExt->hypervisorMapping, pDevExt->hypervisorMappingSize);
1898 dprintf(("VBoxGuest::unreserveHypervisorMemmory: done\n"));
1899}
1900
1901/**
1902 * Idle thread that runs at the lowest priority possible
1903 * and whenever scheduled, makes a VMMDev call to give up
1904 * timeslices. This is so prevent Windows from thinking that
1905 * nothing is happening on the machine and doing stupid things
1906 * that would steal time from other VMs it doesn't know of.
1907 *
1908 * @param pDevExt device extension pointer
1909 */
1910VOID vboxIdleThread(PVOID context)
1911{
1912 PVBOXGUESTDEVEXT pDevExt;
1913
1914 pDevExt = (PVBOXGUESTDEVEXT)context;
1915 dprintf(("VBoxGuest::vboxIdleThread entered\n"));
1916
1917 /* set priority as low as possible */
1918 KeSetPriorityThread(KeGetCurrentThread(), LOW_PRIORITY);
1919
1920 /* allocate VMMDev request structure */
1921 VMMDevReqIdle *req;
1922 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_Idle);
1923 if (RT_FAILURE(rc))
1924 {
1925 dprintf(("VBoxGuest::vboxIdleThread: error %Rrc allocating request structure!\n"));
1926 return;
1927 }
1928
1929 do
1930 {
1931 //dprintf(("VBoxGuest: performing idle request..\n"));
1932 /* perform idle request */
1933 VbglGRPerform(&req->header);
1934
1935 } while (!pDevExt->stopThread);
1936
1937 VbglGRFree(&req->header);
1938
1939 dprintf(("VBoxGuest::vboxIdleThread leaving\n"));
1940}
1941
1942#ifdef DEBUG
1943static VOID testAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits,
1944 uint32_t u32Exp)
1945{
1946 ULONG u32Bits2 = u32Bits;
1947 uint32_t u32Result = guestAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1948 if ( u32Result != u32Exp
1949 || (u32Bits2 & u32Mask)
1950 || (u32Bits2 & u32Result)
1951 || ((u32Bits2 | u32Result) != u32Bits)
1952 )
1953 AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
1954 __PRETTY_FUNCTION__, u32Mask, u32Bits, u32Bits2,
1955 u32Result));
1956}
1957
1958static VOID testVBoxGuest(VOID)
1959{
1960 testAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1961 testAtomicTestAndClearBitsU32(0x11, 0, 0);
1962 testAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1963 testAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1964 testAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1965 testAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1966}
1967#endif
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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