VirtualBox

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

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

Windows guest additions: use KernelMode wait for HCGM calls from shared folders driver.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.5 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(void) 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 VbglHGCMDisconnect(&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(void) 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}
463
464DECLVBGL(void) VBoxHGCMCallbackKernelMode (VMMDevHGCMRequestHeader *pHeader, void *pvData, uint32_t u32Data)
465{
466 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvData;
467
468 dprintf(("VBoxHGCMCallback\n"));
469 VBoxHGCMCallbackWorker (pHeader, pDevExt, u32Data, false, KernelMode);
470}
471
472DECLVBGL(void) VBoxHGCMCallbackInterruptible (VMMDevHGCMRequestHeader *pHeader, void *pvData,
473 uint32_t u32Data)
474{
475 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvData;
476
477 dprintf(("VBoxHGCMCallbackInterruptible\n"));
478 VBoxHGCMCallbackWorker (pHeader, pDevExt, u32Data, true, UserMode);
479}
480
481NTSTATUS vboxHGCMVerifyIOBuffers (PIO_STACK_LOCATION pStack, unsigned cb)
482{
483 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < cb)
484 {
485 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: OutputBufferLength %d < %d\n",
486 pStack->Parameters.DeviceIoControl.OutputBufferLength, cb));
487 return STATUS_INVALID_PARAMETER;
488 }
489
490 if (pStack->Parameters.DeviceIoControl.InputBufferLength < cb)
491 {
492 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: InputBufferLength %d < %d\n",
493 pStack->Parameters.DeviceIoControl.InputBufferLength, cb));
494 return STATUS_INVALID_PARAMETER;
495 }
496
497 return STATUS_SUCCESS;
498}
499
500#endif /* VBOX_WITH_HGCM */
501
502static bool IsPowerOfTwo (uint32_t val)
503{
504 return (val & (val - 1)) == 0;
505}
506
507static bool CtlGuestFilterMask (uint32_t u32OrMask, uint32_t u32NotMask)
508{
509 bool result = false;
510 VMMDevCtlGuestFilterMask *req;
511 int rc = VbglGRAlloc ((VMMDevRequestHeader **) &req, sizeof (*req),
512 VMMDevReq_CtlGuestFilterMask);
513
514 if (RT_SUCCESS (rc))
515 {
516 req->u32OrMask = u32OrMask;
517 req->u32NotMask = u32NotMask;
518
519 rc = VbglGRPerform (&req->header);
520 if (RT_FAILURE (rc) || RT_FAILURE (req->header.rc))
521 {
522 dprintf (("VBoxGuest::VBoxGuestDeviceControl: error issuing request to VMMDev! "
523 "rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
524 }
525 else
526 {
527 result = true;
528 }
529 VbglGRFree (&req->header);
530 }
531
532 return result;
533}
534
535#ifdef VBOX_WITH_MANAGEMENT
536static int VBoxGuestSetBalloonSize(PVBOXGUESTDEVEXT pDevExt, uint32_t u32BalloonSize)
537{
538 VMMDevChangeMemBalloon *req = NULL;
539 int rc = VINF_SUCCESS;
540
541 if (u32BalloonSize > pDevExt->MemBalloon.cMaxBalloons)
542 {
543 AssertMsgFailed(("VBoxGuestSetBalloonSize illegal balloon size %d (max=%d)\n", u32BalloonSize, pDevExt->MemBalloon.cMaxBalloons));
544 return VERR_INVALID_PARAMETER;
545 }
546
547 if (u32BalloonSize == pDevExt->MemBalloon.cBalloons)
548 return VINF_SUCCESS; /* nothing to do */
549
550 /* Allocate request packet */
551 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]), VMMDevReq_ChangeMemBalloon);
552 if (RT_FAILURE(rc))
553 return rc;
554
555 vmmdevInitRequest(&req->header, VMMDevReq_ChangeMemBalloon);
556
557 if (u32BalloonSize > pDevExt->MemBalloon.cBalloons)
558 {
559 /* inflate */
560 for (uint32_t i=pDevExt->MemBalloon.cBalloons;i<u32BalloonSize;i++)
561 {
562#ifndef TARGET_NT4
563 /*
564 * Use MmAllocatePagesForMdl to specify the range of physical addresses we wish to use.
565 */
566 PHYSICAL_ADDRESS Zero;
567 PHYSICAL_ADDRESS HighAddr;
568 Zero.QuadPart = 0;
569 HighAddr.QuadPart = _4G - 1;
570 PMDL pMdl = MmAllocatePagesForMdl(Zero, HighAddr, Zero, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
571 if (pMdl)
572 {
573 if (MmGetMdlByteCount(pMdl) < VMMDEV_MEMORY_BALLOON_CHUNK_SIZE)
574 {
575 MmFreePagesFromMdl(pMdl);
576 ExFreePool(pMdl);
577 rc = VERR_NO_MEMORY;
578 goto end;
579 }
580 }
581#else
582 PVOID pvBalloon;
583 pvBalloon = ExAllocatePool(PagedPool, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
584 if (!pvBalloon)
585 {
586 rc = VERR_NO_MEMORY;
587 goto end;
588 }
589
590 PMDL pMdl = IoAllocateMdl (pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, FALSE, FALSE, NULL);
591 if (pMdl == NULL)
592 {
593 rc = VERR_NO_MEMORY;
594 ExFreePool(pvBalloon);
595 AssertMsgFailed(("IoAllocateMdl %p %x failed!!\n", pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE));
596 goto end;
597 }
598 else
599 {
600 __try {
601 /* Calls to MmProbeAndLockPages must be enclosed in a try/except block. */
602 MmProbeAndLockPages (pMdl, KernelMode, IoModifyAccess);
603 }
604 __except(EXCEPTION_EXECUTE_HANDLER)
605 {
606 dprintf(("MmProbeAndLockPages failed!\n"));
607 rc = VERR_NO_MEMORY;
608 IoFreeMdl (pMdl);
609 ExFreePool(pvBalloon);
610 goto end;
611 }
612 }
613#endif
614
615 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
616
617 /* Copy manually as RTGCPHYS is always 64 bits */
618 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
619 req->aPhysPage[j] = pPageDesc[j];
620
621 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
622 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
623 req->fInflate = true;
624
625 rc = VbglGRPerform(&req->header);
626 if (RT_FAILURE(rc) || RT_FAILURE(req->header.rc))
627 {
628 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev!"
629 "rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
630
631#ifndef TARGET_NT4
632 MmFreePagesFromMdl(pMdl);
633 ExFreePool(pMdl);
634#else
635 IoFreeMdl (pMdl);
636 ExFreePool(pvBalloon);
637#endif
638 goto end;
639 }
640 else
641 {
642#ifndef TARGET_NT4
643 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pMdl));
644#else
645 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pvBalloon));
646#endif
647 pDevExt->MemBalloon.paMdlMemBalloon[i] = pMdl;
648 pDevExt->MemBalloon.cBalloons++;
649 }
650 }
651 }
652 else
653 {
654 /* deflate */
655 for (uint32_t _i=pDevExt->MemBalloon.cBalloons;_i>u32BalloonSize;_i--)
656 {
657 uint32_t index = _i - 1;
658 PMDL pMdl = pDevExt->MemBalloon.paMdlMemBalloon[index];
659
660 Assert(pMdl);
661 if (pMdl)
662 {
663#ifdef TARGET_NT4
664 PVOID pvBalloon = MmGetMdlVirtualAddress(pMdl);
665#endif
666
667 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
668
669 /* Copy manually as RTGCPHYS is always 64 bits */
670 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
671 req->aPhysPage[j] = pPageDesc[j];
672
673 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
674 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
675 req->fInflate = false;
676
677 rc = VbglGRPerform(&req->header);
678 if (RT_FAILURE(rc) || RT_FAILURE(req->header.rc))
679 {
680 AssertMsgFailed(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev! rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
681 break;
682 }
683
684 /* Free the ballooned memory */
685#ifndef TARGET_NT4
686 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pMdl));
687 MmFreePagesFromMdl(pMdl);
688 ExFreePool(pMdl);
689#else
690 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pvBalloon));
691 MmUnlockPages (pMdl);
692 IoFreeMdl (pMdl);
693 ExFreePool(pvBalloon);
694#endif
695
696 pDevExt->MemBalloon.paMdlMemBalloon[index] = NULL;
697 pDevExt->MemBalloon.cBalloons--;
698 }
699 }
700 }
701 Assert(pDevExt->MemBalloon.cBalloons <= pDevExt->MemBalloon.cMaxBalloons);
702
703end:
704 VbglGRFree(&req->header);
705 return rc;
706}
707
708static int VBoxGuestQueryMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, ULONG *pMemBalloonSize)
709{
710 /* just perform the request */
711 VMMDevGetMemBalloonChangeRequest *req = NULL;
712
713 dprintf(("VBoxGuestQueryMemoryBalloon\n"));
714
715 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest);
716 vmmdevInitRequest(&req->header, VMMDevReq_GetMemBalloonChangeRequest);
717 req->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
718
719 if (RT_SUCCESS(rc))
720 {
721 rc = VbglGRPerform(&req->header);
722
723 if (RT_FAILURE(rc) || RT_FAILURE(req->header.rc))
724 {
725 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_CTL_CHECK_BALLOON: error issuing request to VMMDev!"
726 "rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
727 }
728 else
729 {
730 if (!pDevExt->MemBalloon.paMdlMemBalloon)
731 {
732 pDevExt->MemBalloon.cMaxBalloons = req->u32PhysMemSize;
733 pDevExt->MemBalloon.paMdlMemBalloon = (PMDL *)ExAllocatePool(PagedPool, req->u32PhysMemSize * sizeof(PMDL));
734 Assert(pDevExt->MemBalloon.paMdlMemBalloon);
735 if (!pDevExt->MemBalloon.paMdlMemBalloon)
736 return VERR_NO_MEMORY;
737 }
738 Assert(pDevExt->MemBalloon.cMaxBalloons == req->u32PhysMemSize);
739
740 rc = VBoxGuestSetBalloonSize(pDevExt, req->u32BalloonSize);
741 /* ignore out of memory failures */
742 if (rc == VERR_NO_MEMORY)
743 rc = VINF_SUCCESS;
744
745 if (pMemBalloonSize)
746 *pMemBalloonSize = pDevExt->MemBalloon.cBalloons;
747 }
748
749 VbglGRFree(&req->header);
750 }
751 return rc;
752}
753#endif
754
755void VBoxInitMemBalloon(PVBOXGUESTDEVEXT pDevExt)
756{
757#ifdef VBOX_WITH_MANAGEMENT
758 ULONG dummy;
759
760 pDevExt->MemBalloon.cBalloons = 0;
761 pDevExt->MemBalloon.cMaxBalloons = 0;
762 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
763
764 VBoxGuestQueryMemoryBalloon(pDevExt, &dummy);
765#endif
766}
767
768void VBoxCleanupMemBalloon(PVBOXGUESTDEVEXT pDevExt)
769{
770#ifdef VBOX_WITH_MANAGEMENT
771 if (pDevExt->MemBalloon.paMdlMemBalloon)
772 {
773 /* Clean up the memory balloon leftovers */
774 VBoxGuestSetBalloonSize(pDevExt, 0);
775 ExFreePool(pDevExt->MemBalloon.paMdlMemBalloon);
776 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
777 }
778 Assert(pDevExt->MemBalloon.cBalloons == 0);
779#endif
780}
781
782/**
783 * Device I/O Control entry point.
784 *
785 * @param pDevObj Device object.
786 * @param pIrp Request packet.
787 */
788NTSTATUS VBoxGuestDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
789{
790 dprintf(("VBoxGuest::VBoxGuestDeviceControl\n"));
791
792 NTSTATUS Status = STATUS_SUCCESS;
793
794 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
795
796 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
797
798 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* all requests are buffered. */
799
800 unsigned cbOut = 0;
801
802 switch (pStack->Parameters.DeviceIoControl.IoControlCode)
803 {
804 case VBOXGUEST_IOCTL_GETVMMDEVPORT:
805 {
806 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_GETVMMDEVPORT\n"));
807
808 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof (VBoxGuestPortInfo))
809 {
810 Status = STATUS_BUFFER_TOO_SMALL;
811 break;
812 }
813
814 VBoxGuestPortInfo *portInfo = (VBoxGuestPortInfo*)pBuf;
815
816 portInfo->portAddress = pDevExt->startPortAddress;
817 portInfo->pVMMDevMemory = pDevExt->pVMMDevMemory;
818
819 cbOut = sizeof(VBoxGuestPortInfo);
820
821 break;
822 }
823
824 case VBOXGUEST_IOCTL_WAITEVENT:
825 {
826 /* Need to be extended to support multiple waiters for an event,
827 * array of counters for each event, event mask is computed, each
828 * time a wait event is arrived.
829 */
830 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_WAITEVENT\n"));
831
832 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VBoxGuestWaitEventInfo))
833 {
834 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
835 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
836 Status = STATUS_BUFFER_TOO_SMALL;
837 break;
838 }
839
840 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestWaitEventInfo)) {
841 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
842 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
843 Status = STATUS_BUFFER_TOO_SMALL;
844 break;
845 }
846
847 VBoxGuestWaitEventInfo *eventInfo = (VBoxGuestWaitEventInfo *)pBuf;
848
849 if (!eventInfo->u32EventMaskIn || !IsPowerOfTwo (eventInfo->u32EventMaskIn)) {
850 dprintf (("VBoxGuest::VBoxGuestDeviceControl: Invalid input mask %#x\n",
851 eventInfo->u32EventMaskIn));
852 Status = STATUS_INVALID_PARAMETER;
853 break;
854 }
855
856 eventInfo->u32EventFlagsOut = 0;
857 int iBitOffset = ASMBitFirstSetU32 (eventInfo->u32EventMaskIn) - 1;
858 bool fTimeout = (eventInfo->u32TimeoutIn != ~0L);
859
860 dprintf (("mask = %d, iBitOffset = %d\n", iBitOffset, eventInfo->u32EventMaskIn));
861
862 /* Possible problem with request completion right between the pending event check and KeWaitForSingleObject
863 * call; introduce a timeout (if none was specified) to make sure we don't wait indefinitely.
864 */
865 LARGE_INTEGER timeout;
866 timeout.QuadPart = (fTimeout) ? eventInfo->u32TimeoutIn : 250;
867 timeout.QuadPart *= -10000;
868
869 NTSTATUS rc = STATUS_SUCCESS;
870
871 for (;;)
872 {
873 bool fEventPending = ASMAtomicBitTestAndClear(&pDevExt->u32Events, iBitOffset);
874 if (fEventPending)
875 {
876 eventInfo->u32EventFlagsOut = 1 << iBitOffset;
877 break;
878 }
879
880 rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive /** @todo UserRequest? */,
881 KernelMode, TRUE, &timeout);
882 dprintf(("VBOXGUEST_IOCTL_WAITEVENT: Wait returned %d -> event %x\n", rc, eventInfo->u32EventFlagsOut));
883
884 if (!fTimeout && rc == STATUS_TIMEOUT)
885 continue;
886
887 if (rc != STATUS_SUCCESS)
888 {
889 /* There was a timeout or wait was interrupted, etc. */
890 break;
891 }
892 }
893
894 dprintf (("u32EventFlagsOut = %#x\n", eventInfo->u32EventFlagsOut));
895 cbOut = sizeof(VBoxGuestWaitEventInfo);
896 break;
897 }
898
899 case VBOXGUEST_IOCTL_VMMREQUEST(0): /* (The size isn't relevant on NT.)*/
900 {
901 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_VMMREQUEST\n"));
902
903#define CHECK_SIZE(s) \
904 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < s) \
905 { \
906 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < %d\n", \
907 pStack->Parameters.DeviceIoControl.OutputBufferLength, s)); \
908 Status = STATUS_BUFFER_TOO_SMALL; \
909 break; \
910 } \
911 if (pStack->Parameters.DeviceIoControl.InputBufferLength < s) { \
912 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n", \
913 pStack->Parameters.DeviceIoControl.InputBufferLength, s)); \
914 Status = STATUS_BUFFER_TOO_SMALL; \
915 break; \
916 }
917
918 /* get the request header */
919 CHECK_SIZE(sizeof(VMMDevRequestHeader));
920 VMMDevRequestHeader *requestHeader = (VMMDevRequestHeader *)pBuf;
921 if (!vmmdevGetRequestSize(requestHeader->requestType))
922 {
923 Status = STATUS_INVALID_PARAMETER;
924 break;
925 }
926 /* make sure the buffers suit the request */
927 CHECK_SIZE(vmmdevGetRequestSize(requestHeader->requestType));
928
929 /* just perform the request */
930 VMMDevRequestHeader *req = NULL;
931
932 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, requestHeader->size, requestHeader->requestType);
933
934 if (RT_SUCCESS(rc))
935 {
936 /* copy the request information */
937 memcpy((void*)req, (void*)pBuf, requestHeader->size);
938 rc = VbglGRPerform(req);
939
940 if (RT_FAILURE(rc) || RT_FAILURE(req->rc))
941 {
942 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_VMMREQUEST: Error issuing request to VMMDev! "
943 "rc = %d, VMMDev rc = %Rrc\n", rc, req->rc));
944 Status = STATUS_UNSUCCESSFUL;
945 }
946 else
947 {
948 /* copy result */
949 memcpy((void*)pBuf, (void*)req, requestHeader->size);
950 cbOut = requestHeader->size;
951 }
952
953 VbglGRFree(req);
954 }
955 else
956 {
957 Status = STATUS_UNSUCCESSFUL;
958 }
959#undef CHECK_SIZE
960 break;
961 }
962
963 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
964 {
965 VBoxGuestFilterMaskInfo *maskInfo;
966
967 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestFilterMaskInfo)) {
968 dprintf (("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n",
969 pStack->Parameters.DeviceIoControl.InputBufferLength,
970 sizeof (VBoxGuestFilterMaskInfo)));
971 Status = STATUS_BUFFER_TOO_SMALL;
972 break;
973
974 }
975
976 maskInfo = (VBoxGuestFilterMaskInfo *) pBuf;
977 if (!CtlGuestFilterMask (maskInfo->u32OrMask, maskInfo->u32NotMask))
978 {
979 Status = STATUS_UNSUCCESSFUL;
980 }
981 break;
982 }
983
984#ifdef VBOX_WITH_HGCM
985 /* HGCM offers blocking IOCTLSs just like waitevent and actually
986 * uses the same waiting code.
987 */
988#ifdef RT_ARCH_AMD64
989 case VBOXGUEST_IOCTL_HGCM_CONNECT_32:
990#endif /* RT_ARCH_AMD64 */
991 case VBOXGUEST_IOCTL_HGCM_CONNECT:
992 {
993 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CONNECT\n"));
994
995 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMConnectInfo))
996 {
997 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
998 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
999 Status = STATUS_INVALID_PARAMETER;
1000 break;
1001 }
1002
1003 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMConnectInfo)) {
1004 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
1005 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
1006 Status = STATUS_INVALID_PARAMETER;
1007 break;
1008 }
1009
1010 VBoxGuestHGCMConnectInfo *ptr = (VBoxGuestHGCMConnectInfo *)pBuf;
1011
1012 /* If request will be processed asynchronously, execution will
1013 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
1014 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
1015 * flag is set, returns.
1016 */
1017
1018 dprintf(("a) ptr->u32ClientID = %d\n", ptr->u32ClientID));
1019
1020 int rc = VbglHGCMConnect (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1021
1022 dprintf(("b) ptr->u32ClientID = %d\n", ptr->u32ClientID));
1023
1024 if (RT_FAILURE(rc))
1025 {
1026 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: vbox rc = %Rrc\n", rc));
1027 Status = STATUS_UNSUCCESSFUL;
1028 }
1029 else
1030 {
1031 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1032
1033 if (RT_SUCCESS(ptr->result) && pStack->FileObject)
1034 {
1035 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: pDevExt=%p pFileObj=%p pSession=%p\n",
1036 pDevExt, pStack->FileObject, pStack->FileObject->FsContext));
1037
1038 /*
1039 * Append the client id to the client id table.
1040 * If the table has somehow become filled up, we'll disconnect the session.
1041 */
1042 unsigned i;
1043 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pStack->FileObject->FsContext;
1044 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1045
1046 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1047 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1048 if (!pSession->aHGCMClientIds[i])
1049 {
1050 pSession->aHGCMClientIds[i] = ptr->u32ClientID;
1051 break;
1052 }
1053 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1054
1055 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1056 {
1057 static unsigned s_cErrors = 0;
1058 if (s_cErrors++ < 32)
1059 dprintf(("VBoxGuestCommonIOCtl: HGCM_CONNECT: too many HGCMConnect calls for one session!\n"));
1060
1061 VBoxGuestHGCMDisconnectInfo Info;
1062 Info.result = 0;
1063 Info.u32ClientID = ptr->u32ClientID;
1064 VbglHGCMDisconnect(&Info, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1065 Status = STATUS_UNSUCCESSFUL;
1066 break;
1067 }
1068 }
1069 else
1070 {
1071 /* @fixme, r=Leonid. I have no clue what to do in cases where
1072 * pStack->FileObject==NULL. Can't populate list of HGCM ID's...
1073 * But things worked before, so do nothing for now.
1074 */
1075 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: pDevExt=%p, pStack->FileObject=%p\n", pDevExt, pStack->FileObject));
1076 }
1077 }
1078
1079 } break;
1080
1081#ifdef RT_ARCH_AMD64
1082 case VBOXGUEST_IOCTL_HGCM_DISCONNECT_32:
1083#endif /* RT_ARCH_AMD64 */
1084 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
1085 {
1086 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_DISCONNECT\n"));
1087
1088 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo))
1089 {
1090 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
1091 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
1092 Status = STATUS_INVALID_PARAMETER;
1093 break;
1094 }
1095
1096 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo)) {
1097 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
1098 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
1099 Status = STATUS_INVALID_PARAMETER;
1100 break;
1101 }
1102
1103 VBoxGuestHGCMDisconnectInfo *ptr = (VBoxGuestHGCMDisconnectInfo *)pBuf;
1104
1105 uint32_t u32ClientId=0;
1106 unsigned i=0;
1107 PVBOXGUESTSESSION pSession=0;
1108 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1109
1110 /* See comment in VBOXGUEST_IOCTL_HGCM_CONNECT */
1111 if (pStack->FileObject)
1112 {
1113 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: pDevExt=%p pFileObj=%p pSession=%p\n",
1114 pDevExt, pStack->FileObject, pStack->FileObject->FsContext));
1115
1116 u32ClientId = ptr->u32ClientID;
1117 pSession = (PVBOXGUESTSESSION)pStack->FileObject->FsContext;
1118
1119 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1120 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1121 if (pSession->aHGCMClientIds[i] == u32ClientId)
1122 {
1123 pSession->aHGCMClientIds[i] = UINT32_MAX;
1124 break;
1125 }
1126 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1127 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1128 {
1129 static unsigned s_cErrors = 0;
1130 if (s_cErrors++ > 32)
1131 dprintf(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", u32ClientId));
1132 Status = STATUS_INVALID_PARAMETER;
1133 break;
1134 }
1135 }
1136
1137 /* If request will be processed asynchronously, execution will
1138 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
1139 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
1140 * flag is set, returns.
1141 */
1142
1143 int rc = VbglHGCMDisconnect (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1144
1145 if (RT_FAILURE(rc))
1146 {
1147 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: vbox rc = %Rrc\n", rc));
1148 Status = STATUS_UNSUCCESSFUL;
1149 }
1150 else
1151 {
1152 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1153 }
1154
1155 if (pStack->FileObject)
1156 {
1157 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1158 if (pSession->aHGCMClientIds[i] == UINT32_MAX)
1159 pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) && RT_SUCCESS(ptr->result) ? 0 : u32ClientId;
1160 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1161 }
1162 } break;
1163
1164#ifdef RT_ARCH_AMD64
1165 case VBOXGUEST_IOCTL_HGCM_CALL_32(0): /* (The size isn't relevant on NT.) */
1166 {
1167 /* A 32 bit application call. */
1168 int rc;
1169
1170 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL_32\n"));
1171
1172 Status = vboxHGCMVerifyIOBuffers (pStack,
1173 sizeof (VBoxGuestHGCMCallInfo));
1174
1175 if (Status != STATUS_SUCCESS)
1176 {
1177 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
1178 break;
1179 }
1180
1181 /* @todo: Old guest OpenGL driver used the same IOCtl code for both 32 and 64 bit binaries.
1182 * This is a protection, and can be removed if there were no 64 bit driver.
1183 */
1184 if (!IoIs32bitProcess(pIrp))
1185 {
1186 Status = STATUS_UNSUCCESSFUL;
1187 break;
1188 }
1189
1190 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1191
1192 rc = VbglHGCMCall32(ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1193
1194 if (RT_FAILURE(rc))
1195 {
1196 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL_32: vbox rc = %Rrc\n", rc));
1197 Status = STATUS_UNSUCCESSFUL;
1198 }
1199 else
1200 {
1201 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1202 }
1203
1204 } break;
1205#endif /* RT_ARCH_AMD64 */
1206
1207 case VBOXGUEST_IOCTL_HGCM_CALL(0): /* (The size isn't relevant on NT.) */
1208 {
1209 int rc;
1210
1211 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL\n"));
1212
1213 Status = vboxHGCMVerifyIOBuffers (pStack,
1214 sizeof (VBoxGuestHGCMCallInfo));
1215
1216 if (Status != STATUS_SUCCESS)
1217 {
1218 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
1219 break;
1220 }
1221
1222 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1223
1224 rc = VbglHGCMCall (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1225
1226 if (RT_FAILURE(rc))
1227 {
1228 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL: vbox rc = %Rrc\n", rc));
1229 Status = STATUS_UNSUCCESSFUL;
1230 }
1231 else
1232 {
1233 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1234 }
1235
1236 } break;
1237
1238 case VBOXGUEST_IOCTL_HGCM_CALL_TIMED(0): /* (The size isn't relevant on NT.) */
1239 {
1240 /* This IOCTL is not used by shared folders, so VBoxHGCMCallbackKernelMode is not used. */
1241 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL_TIMED\n"));
1242
1243 Status = vboxHGCMVerifyIOBuffers (pStack,
1244 sizeof (VBoxGuestHGCMCallInfoTimed));
1245
1246 if (Status != STATUS_SUCCESS)
1247 {
1248 dprintf(("nvalid parameter. Status: %p\n", Status));
1249 break;
1250 }
1251
1252 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pBuf;
1253 VBoxGuestHGCMCallInfo *ptr = &pInfo->info;
1254
1255 int rc;
1256 if (pInfo->fInterruptible)
1257 {
1258 dprintf(("VBoxGuest::VBoxGuestDeviceControl: calling VBoxHGCMCall interruptible, timeout %lu ms\n",
1259 pInfo->u32Timeout));
1260 rc = VbglHGCMCall (ptr, VBoxHGCMCallbackInterruptible, pDevExt, pInfo->u32Timeout);
1261 }
1262 else
1263 {
1264 dprintf(("VBoxGuest::VBoxGuestDeviceControl: calling VBoxHGCMCall, timeout %lu ms\n",
1265 pInfo->u32Timeout));
1266 rc = VbglHGCMCall (ptr, VBoxHGCMCallback, pDevExt, pInfo->u32Timeout);
1267 }
1268
1269 if (RT_FAILURE(rc))
1270 {
1271 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL_TIMED: vbox rc = %Rrc\n", rc));
1272 Status = STATUS_UNSUCCESSFUL;
1273 }
1274 else
1275 {
1276 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1277 }
1278
1279 } break;
1280#endif /* VBOX_WITH_HGCM */
1281
1282#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
1283 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
1284 {
1285 LogRel(("VRDP_SESSION: Enable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1286 if (!pDevExt->fVRDPEnabled)
1287 {
1288 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1289
1290 pDevExt->fVRDPEnabled = TRUE;
1291 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1292 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
1293 pSharedUserData->ActiveConsoleId = 2;
1294 }
1295 break;
1296 }
1297
1298 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
1299 {
1300 LogRel(("VRDP_SESSION: Disable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1301 if (pDevExt->fVRDPEnabled)
1302 {
1303 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1304
1305 pDevExt->fVRDPEnabled = FALSE;
1306 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1307 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
1308 pDevExt->ulOldActiveConsoleId = 0;
1309 }
1310 break;
1311 }
1312#endif
1313
1314#ifdef VBOX_WITH_MANAGEMENT
1315 case VBOXGUEST_IOCTL_CTL_CHECK_BALLOON_MASK:
1316 {
1317 ULONG *pMemBalloonSize = (ULONG *) pBuf;
1318
1319 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG))
1320 {
1321 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(ULONG) %d\n",
1322 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(ULONG)));
1323 Status = STATUS_INVALID_PARAMETER;
1324 break;
1325 }
1326
1327 int rc = VBoxGuestQueryMemoryBalloon(pDevExt, pMemBalloonSize);
1328 if (RT_FAILURE(rc))
1329 {
1330 dprintf(("VBOXGUEST_IOCTL_CTL_CHECK_BALLOON: vbox rc = %Rrc\n", rc));
1331 Status = STATUS_UNSUCCESSFUL;
1332 }
1333 else
1334 {
1335 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1336 }
1337 break;
1338 }
1339#endif
1340
1341 case VBOXGUEST_IOCTL_LOG(0): /* The size isn't relevant on NT. */
1342 {
1343 /* Enable this only for debugging:
1344 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_LOG %.*s\n", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1345 */
1346 LogRel(("%.*s", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1347 cbOut = 0;
1348 break;
1349 }
1350
1351 default:
1352 Status = STATUS_INVALID_PARAMETER;
1353 break;
1354 }
1355
1356 pIrp->IoStatus.Status = Status;
1357 pIrp->IoStatus.Information = cbOut;
1358
1359 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1360
1361 dprintf(("VBoxGuest::VBoxGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
1362
1363 return Status;
1364}
1365
1366
1367/**
1368 * IRP_MJ_SYSTEM_CONTROL handler
1369 *
1370 * @returns NT status code
1371 * @param pDevObj Device object.
1372 * @param pIrp IRP.
1373 */
1374NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1375{
1376 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1377
1378 dprintf(("VBoxGuest::VBoxGuestSystemControl\n"));
1379
1380 /* Always pass it on to the next driver. */
1381 IoSkipCurrentIrpStackLocation(pIrp);
1382
1383 return IoCallDriver(pDevExt->nextLowerDriver, pIrp);
1384}
1385
1386/**
1387 * IRP_MJ_SHUTDOWN handler
1388 *
1389 * @returns NT status code
1390 * @param pDevObj Device object.
1391 * @param pIrp IRP.
1392 */
1393NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1394{
1395 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1396
1397 dprintf(("VBoxGuest::VBoxGuestShutdown\n"));
1398
1399 if (pDevExt && pDevExt->powerStateRequest)
1400 {
1401 VMMDevPowerStateRequest *req = pDevExt->powerStateRequest;
1402
1403 req->header.requestType = VMMDevReq_SetPowerStatus;
1404 req->powerState = VMMDevPowerState_PowerOff;
1405
1406 int rc = VbglGRPerform (&req->header);
1407
1408 if (RT_FAILURE(rc) || RT_FAILURE(req->header.rc))
1409 {
1410 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1411 "rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
1412 }
1413 }
1414
1415 return STATUS_SUCCESS;
1416}
1417
1418/**
1419 * Stub function for functions we don't implemented.
1420 *
1421 * @returns STATUS_NOT_SUPPORTED
1422 * @param pDevObj Device object.
1423 * @param pIrp IRP.
1424 */
1425NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1426{
1427 dprintf(("VBoxGuest::VBoxGuestNotSupportedStub\n"));
1428 pDevObj = pDevObj;
1429
1430 pIrp->IoStatus.Information = 0;
1431 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1432 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1433
1434 return STATUS_NOT_SUPPORTED;
1435}
1436
1437/**
1438 * DPC handler
1439 *
1440 * @param dpc DPC descriptor.
1441 * @param pDevObj Device object.
1442 * @param irp Interrupt request packet.
1443 * @param context Context specific pointer.
1444 */
1445VOID VBoxGuestDpcHandler(PKDPC dpc, PDEVICE_OBJECT pDevObj,
1446 PIRP irp, PVOID context)
1447{
1448 /* Unblock handlers waiting for arrived events.
1449 *
1450 * Events are very low things, there is one event flag (1 or more bit)
1451 * for each event. Each event is processed by exactly one handler.
1452 *
1453 * Assume that we trust additions and that other drivers will
1454 * handle its respective events without trying to fetch all events.
1455 *
1456 * Anyway design assures that wrong event processing will affect only guest.
1457 *
1458 * Event handler calls VMMDev IOCTL for waiting an event.
1459 * It supplies event mask. IOCTL blocks on EventNotification.
1460 * Here we just signal an the EventNotification to all waiting
1461 * threads, the IOCTL handler analyzes events and either
1462 * return to caller or blocks again.
1463 *
1464 * If we do not have too many events this is a simple and good
1465 * approach. Other way is to have as many Event objects as the callers
1466 * and wake up only callers waiting for the specific event.
1467 *
1468 * Now with the 'wake up all' appoach we probably do not need the DPC
1469 * handler and can signal event directly from ISR.
1470 *
1471 */
1472
1473 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1474
1475 dprintf(("VBoxGuest::VBoxGuestDpcHandler\n"));
1476
1477 KePulseEvent(&pDevExt->keventNotification, 0, FALSE);
1478
1479}
1480
1481/**
1482 * ISR handler
1483 *
1484 * @return BOOLEAN indicates whether the IRQ came from us (TRUE) or not (FALSE)
1485 * @param interrupt Interrupt that was triggered.
1486 * @param serviceContext Context specific pointer.
1487 */
1488BOOLEAN VBoxGuestIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext)
1489{
1490 NTSTATUS rc;
1491 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)serviceContext;
1492 BOOLEAN fIRQTaken = FALSE;
1493
1494 dprintf(("VBoxGuest::VBoxGuestIsrHandler haveEvents = %d\n",
1495 pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents));
1496
1497 /*
1498 * now we have to find out whether it was our IRQ. Read the event mask
1499 * from our device to see if there are any pending events
1500 */
1501 if (pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents)
1502 {
1503 /* Acknowlegde events. */
1504 VMMDevEvents *req = pDevExt->irqAckEvents;
1505
1506 rc = VbglGRPerform (&req->header);
1507 if (RT_SUCCESS(rc) && RT_SUCCESS(req->header.rc))
1508 {
1509 dprintf(("VBoxGuest::VBoxGuestIsrHandler: acknowledge events succeeded %#x\n",
1510 req->events));
1511
1512 ASMAtomicOrU32((uint32_t *)&pDevExt->u32Events, req->events);
1513 IoRequestDpc(pDevExt->deviceObject, pDevExt->currentIrp, NULL);
1514 }
1515 else
1516 {
1517 /* This can't be actually. This is sign of a serious problem. */
1518 dprintf(("VBoxGuest::VBoxGuestIsrHandler: "
1519 "acknowledge events failed rc = %d, header rc = %d\n",
1520 rc, req->header.rc));
1521 }
1522
1523 /* Mark IRQ as taken, there were events for us. */
1524 fIRQTaken = TRUE;
1525 }
1526
1527 return fIRQTaken;
1528}
1529
1530/**
1531 * Worker thread to do periodic things such as notify other
1532 * drivers of events.
1533 *
1534 * @param pDevExt device extension pointer
1535 */
1536VOID vboxWorkerThread(PVOID context)
1537{
1538 PVBOXGUESTDEVEXT pDevExt;
1539
1540 pDevExt = (PVBOXGUESTDEVEXT)context;
1541 dprintf(("VBoxGuest::vboxWorkerThread entered\n"));
1542
1543 /* perform the hypervisor address space reservation */
1544 reserveHypervisorMemory(pDevExt);
1545
1546 do
1547 {
1548 /* Nothing to do here yet. */
1549
1550 /*
1551 * Go asleep unless we're supposed to terminate
1552 */
1553 if (!pDevExt->stopThread)
1554 {
1555 ULONG secWait = 60;
1556 dprintf(("VBoxGuest::vboxWorkerThread: waiting for %u seconds...\n", secWait));
1557 LARGE_INTEGER dueTime;
1558 dueTime.QuadPart = -10000 * 1000 * (int)secWait;
1559 if (KeWaitForSingleObject(&pDevExt->workerThreadRequest, Executive,
1560 KernelMode, FALSE, &dueTime) == STATUS_SUCCESS)
1561 {
1562 KeResetEvent(&pDevExt->workerThreadRequest);
1563 }
1564 }
1565 } while (!pDevExt->stopThread);
1566
1567 dprintf(("VBoxGuest::vboxWorkerThread: we've been asked to terminate!\n"));
1568
1569 if (pDevExt->workerThread)
1570 {
1571 ObDereferenceObject(pDevExt->workerThread);
1572 pDevExt->workerThread = NULL;
1573 }
1574 dprintf(("VBoxGuest::vboxWorkerThread: now really gone!\n"));
1575}
1576
1577/**
1578 * Create driver worker threads
1579 *
1580 * @returns NTSTATUS NT status code
1581 * @param pDevExt VBoxGuest device extension
1582 */
1583NTSTATUS createThreads(PVBOXGUESTDEVEXT pDevExt)
1584{
1585 NTSTATUS rc;
1586 HANDLE threadHandle;
1587 OBJECT_ATTRIBUTES objAttributes;
1588
1589 dprintf(("VBoxGuest::createThreads\n"));
1590
1591 // first setup the request semaphore
1592 KeInitializeEvent(&pDevExt->workerThreadRequest, SynchronizationEvent, FALSE);
1593
1594// the API has slightly changed after NT4
1595#ifdef TARGET_NT4
1596#ifdef OBJ_KERNEL_HANDLE
1597#undef OBJ_KERNEL_HANDLE
1598#endif
1599#define OBJ_KERNEL_HANDLE 0
1600#endif
1601
1602 /*
1603 * The worker thread
1604 */
1605 InitializeObjectAttributes(&objAttributes,
1606 NULL,
1607 OBJ_KERNEL_HANDLE,
1608 NULL,
1609 NULL);
1610
1611 rc = PsCreateSystemThread(&threadHandle,
1612 THREAD_ALL_ACCESS,
1613 &objAttributes,
1614 (HANDLE)0L,
1615 NULL,
1616 vboxWorkerThread,
1617 pDevExt);
1618 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for worker thread returned: 0x%x\n", rc));
1619 rc = ObReferenceObjectByHandle(threadHandle,
1620 THREAD_ALL_ACCESS,
1621 NULL,
1622 KernelMode,
1623 (PVOID*)&pDevExt->workerThread,
1624 NULL);
1625 ZwClose(threadHandle);
1626
1627 /*
1628 * The idle thread
1629 */
1630#if 0 /// @todo Windows "sees" that time is lost and reports 100% usage
1631 rc = PsCreateSystemThread(&threadHandle,
1632 THREAD_ALL_ACCESS,
1633 &objAttributes,
1634 (HANDLE)0L,
1635 NULL,
1636 vboxIdleThread,
1637 pDevExt);
1638 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for idle thread returned: 0x%x\n", rc));
1639 rc = ObReferenceObjectByHandle(threadHandle,
1640 THREAD_ALL_ACCESS,
1641 NULL,
1642 KernelMode,
1643 (PVOID*)&pDevExt->idleThread,
1644 NULL);
1645 ZwClose(threadHandle);
1646#endif
1647
1648 return rc;
1649}
1650
1651/**
1652 * Helper routine to reserve address space for the hypervisor
1653 * and communicate its position.
1654 *
1655 * @param pDevExt Device extension structure.
1656 */
1657VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1658{
1659 // @todo rc handling
1660 uint32_t hypervisorSize;
1661
1662 VMMDevReqHypervisorInfo *req = NULL;
1663
1664 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
1665
1666 if (RT_SUCCESS(rc))
1667 {
1668 req->hypervisorStart = 0;
1669 req->hypervisorSize = 0;
1670
1671 rc = VbglGRPerform (&req->header);
1672
1673 if (RT_SUCCESS(rc) && RT_SUCCESS(req->header.rc))
1674 {
1675 hypervisorSize = req->hypervisorSize;
1676
1677 if (!hypervisorSize)
1678 {
1679 dprintf(("VBoxGuest::reserveHypervisorMemory: host returned 0, not doing anything\n"));
1680 return;
1681 }
1682
1683 dprintf(("VBoxGuest::reserveHypervisorMemory: host wants %u bytes of hypervisor address space\n", hypervisorSize));
1684
1685 // Map fictive physical memory into the kernel address space to reserve virtual
1686 // address space. This API does not perform any checks but just allocate the
1687 // PTEs (which we don't really need/want but there isn't any other clean method).
1688 // The hypervisor only likes 4MB aligned virtual addresses, so we have to allocate
1689 // 4MB more than we are actually supposed to in order to guarantee that. Maybe we
1690 // can come up with a less lavish algorithm lateron.
1691 PHYSICAL_ADDRESS physAddr;
1692 physAddr.QuadPart = HYPERVISOR_PHYSICAL_START;
1693 pDevExt->hypervisorMappingSize = hypervisorSize + 0x400000;
1694 pDevExt->hypervisorMapping = MmMapIoSpace(physAddr,
1695 pDevExt->hypervisorMappingSize,
1696 MmNonCached);
1697 if (!pDevExt->hypervisorMapping)
1698 {
1699 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned NULL!\n"));
1700 return;
1701 }
1702
1703 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned %p\n", pDevExt->hypervisorMapping));
1704 dprintf(("VBoxGuest::reserveHypervisorMemory: communicating %p to host\n",
1705 RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000)));
1706
1707 /* align at 4MB */
1708 req->hypervisorStart = (uintptr_t)RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000);
1709
1710 req->header.requestType = VMMDevReq_SetHypervisorInfo;
1711 req->header.rc = VERR_GENERAL_FAILURE;
1712
1713 /* issue request */
1714 rc = VbglGRPerform (&req->header);
1715
1716 if (RT_FAILURE(rc) || RT_FAILURE(req->header.rc))
1717 {
1718 dprintf(("VBoxGuest::reserveHypervisorMemory: error communicating physical address to VMMDev!"
1719 "rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
1720 }
1721 }
1722 else
1723 {
1724 dprintf(("VBoxGuest::reserveHypervisorMemory: request failed with rc %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
1725 }
1726 VbglGRFree (&req->header);
1727 }
1728
1729 return;
1730}
1731
1732/**
1733 * Helper function to unregister a virtual address space mapping
1734 *
1735 * @param pDevExt Device extension
1736 */
1737VOID unreserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1738{
1739 VMMDevReqHypervisorInfo *req = NULL;
1740
1741 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
1742
1743 if (RT_SUCCESS(rc))
1744 {
1745 /* tell the hypervisor that the mapping is no longer available */
1746
1747 req->hypervisorStart = 0;
1748 req->hypervisorSize = 0;
1749
1750 rc = VbglGRPerform (&req->header);
1751
1752 if (RT_FAILURE(rc) || RT_FAILURE(req->header.rc))
1753 {
1754 dprintf(("VBoxGuest::unreserveHypervisorMemory: error communicating physical address to VMMDev!"
1755 "rc = %d, VMMDev rc = %Rrc\n", rc, req->header.rc));
1756 }
1757
1758 VbglGRFree (&req->header);
1759 }
1760
1761 if (!pDevExt->hypervisorMapping)
1762 {
1763 dprintf(("VBoxGuest::unreserveHypervisorMemory: there is no mapping, returning\n"));
1764 return;
1765 }
1766
1767 // unmap fictive IO space
1768 MmUnmapIoSpace(pDevExt->hypervisorMapping, pDevExt->hypervisorMappingSize);
1769 dprintf(("VBoxGuest::unreserveHypervisorMemmory: done\n"));
1770}
1771
1772/**
1773 * Idle thread that runs at the lowest priority possible
1774 * and whenever scheduled, makes a VMMDev call to give up
1775 * timeslices. This is so prevent Windows from thinking that
1776 * nothing is happening on the machine and doing stupid things
1777 * that would steal time from other VMs it doesn't know of.
1778 *
1779 * @param pDevExt device extension pointer
1780 */
1781VOID vboxIdleThread(PVOID context)
1782{
1783 PVBOXGUESTDEVEXT pDevExt;
1784
1785 pDevExt = (PVBOXGUESTDEVEXT)context;
1786 dprintf(("VBoxGuest::vboxIdleThread entered\n"));
1787
1788 /* set priority as low as possible */
1789 KeSetPriorityThread(KeGetCurrentThread(), LOW_PRIORITY);
1790
1791 /* allocate VMMDev request structure */
1792 VMMDevReqIdle *req;
1793 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_Idle);
1794 if (RT_FAILURE(rc))
1795 {
1796 dprintf(("VBoxGuest::vboxIdleThread: error %Rrc allocating request structure!\n"));
1797 return;
1798 }
1799
1800 do
1801 {
1802 //dprintf(("VBoxGuest: performing idle request..\n"));
1803 /* perform idle request */
1804 VbglGRPerform(&req->header);
1805
1806 } while (!pDevExt->stopThread);
1807
1808 VbglGRFree(&req->header);
1809
1810 dprintf(("VBoxGuest::vboxIdleThread leaving\n"));
1811}
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