VirtualBox

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

Last change on this file since 27023 was 27023, checked in by vboxsync, 15 years ago

Guest Additions: memory ballooning for guests without support for RTR0MemObjAllocPhysNC()

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

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