VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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