VirtualBox

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

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

Additions/win: be safe here

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

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