VirtualBox

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

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

Wrong pointer casting

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