VirtualBox

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

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

More logging

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.5 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 dprintf(("VBoxGuest::VBoxGuestDeviceControl: vmmdevGetRequestSize failed!\n")); \
968 Status = STATUS_INVALID_PARAMETER;
969 break;
970 }
971 /* make sure the buffers suit the request */
972 CHECK_SIZE(vmmdevGetRequestSize(requestHeader->requestType));
973
974 int rc = VbglGRVerify(requestHeader, requestHeader->size);
975 if (RT_FAILURE(rc))
976 {
977 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VMMREQUEST: invalid header: size %#x, expected >= %#x (hdr); type=%#x; rc %d!!\n",
978 requestHeader->size, vmmdevGetRequestSize(requestHeader->requestType), requestHeader->requestType, rc));
979 Status = STATUS_INVALID_PARAMETER;
980 break;
981 }
982
983 /* just perform the request */
984 VMMDevRequestHeader *req = NULL;
985
986 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, requestHeader->size, requestHeader->requestType);
987
988 if (RT_SUCCESS(rc))
989 {
990 /* copy the request information */
991 memcpy((void*)req, (void*)pBuf, requestHeader->size);
992 rc = VbglGRPerform(req);
993
994 if (RT_FAILURE(rc))
995 {
996 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_VMMREQUEST: Error issuing request to VMMDev! "
997 "rc = %Rrc\n", rc));
998 Status = STATUS_UNSUCCESSFUL;
999 }
1000 else
1001 {
1002 /* copy result */
1003 memcpy((void*)pBuf, (void*)req, requestHeader->size);
1004 cbOut = requestHeader->size;
1005 }
1006
1007 VbglGRFree(req);
1008 }
1009 else
1010 {
1011 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VbglGRAlloc failed!\n")); \
1012 Status = STATUS_UNSUCCESSFUL;
1013 }
1014#undef CHECK_SIZE
1015 break;
1016 }
1017
1018 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
1019 {
1020 VBoxGuestFilterMaskInfo *maskInfo;
1021
1022 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestFilterMaskInfo)) {
1023 dprintf (("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n",
1024 pStack->Parameters.DeviceIoControl.InputBufferLength,
1025 sizeof (VBoxGuestFilterMaskInfo)));
1026 Status = STATUS_BUFFER_TOO_SMALL;
1027 break;
1028
1029 }
1030
1031 maskInfo = (VBoxGuestFilterMaskInfo *) pBuf;
1032 if (!CtlGuestFilterMask (maskInfo->u32OrMask, maskInfo->u32NotMask))
1033 {
1034 Status = STATUS_UNSUCCESSFUL;
1035 }
1036 break;
1037 }
1038
1039#ifdef VBOX_WITH_HGCM
1040 /* HGCM offers blocking IOCTLSs just like waitevent and actually
1041 * uses the same waiting code.
1042 */
1043#ifdef RT_ARCH_AMD64
1044 case VBOXGUEST_IOCTL_HGCM_CONNECT_32:
1045#endif /* RT_ARCH_AMD64 */
1046 case VBOXGUEST_IOCTL_HGCM_CONNECT:
1047 {
1048 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CONNECT\n"));
1049
1050 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMConnectInfo))
1051 {
1052 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
1053 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
1054 Status = STATUS_INVALID_PARAMETER;
1055 break;
1056 }
1057
1058 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMConnectInfo)) {
1059 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
1060 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
1061 Status = STATUS_INVALID_PARAMETER;
1062 break;
1063 }
1064
1065 VBoxGuestHGCMConnectInfo *ptr = (VBoxGuestHGCMConnectInfo *)pBuf;
1066
1067 /* If request will be processed asynchronously, execution will
1068 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
1069 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
1070 * flag is set, returns.
1071 */
1072
1073 dprintf(("a) ptr->u32ClientID = %d\n", ptr->u32ClientID));
1074
1075 int rc = VbglR0HGCMInternalConnect (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback,
1076 pDevExt, RT_INDEFINITE_WAIT);
1077
1078 dprintf(("b) ptr->u32ClientID = %d\n", ptr->u32ClientID));
1079
1080 if (RT_FAILURE(rc))
1081 {
1082 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: vbox rc = %Rrc\n", rc));
1083 Status = STATUS_UNSUCCESSFUL;
1084 }
1085 else
1086 {
1087 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1088
1089 if (RT_SUCCESS(ptr->result) && pStack->FileObject)
1090 {
1091 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: pDevExt=%p pFileObj=%p pSession=%p\n",
1092 pDevExt, pStack->FileObject, pStack->FileObject->FsContext));
1093
1094 /*
1095 * Append the client id to the client id table.
1096 * If the table has somehow become filled up, we'll disconnect the session.
1097 */
1098 unsigned i;
1099 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pStack->FileObject->FsContext;
1100 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1101
1102 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1103 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1104 if (!pSession->aHGCMClientIds[i])
1105 {
1106 pSession->aHGCMClientIds[i] = ptr->u32ClientID;
1107 break;
1108 }
1109 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1110
1111 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1112 {
1113 static unsigned s_cErrors = 0;
1114 if (s_cErrors++ < 32)
1115 dprintf(("VBoxGuestCommonIOCtl: HGCM_CONNECT: too many HGCMConnect calls for one session!\n"));
1116
1117 VBoxGuestHGCMDisconnectInfo Info;
1118 Info.result = 0;
1119 Info.u32ClientID = ptr->u32ClientID;
1120 VbglR0HGCMInternalDisconnect(&Info, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1121 Status = STATUS_UNSUCCESSFUL;
1122 break;
1123 }
1124 }
1125 else
1126 {
1127 /* @fixme, r=Leonid. I have no clue what to do in cases where
1128 * pStack->FileObject==NULL. Can't populate list of HGCM ID's...
1129 * But things worked before, so do nothing for now.
1130 */
1131 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: pDevExt=%p, pStack->FileObject=%p\n", pDevExt, pStack->FileObject));
1132 }
1133 }
1134
1135 } break;
1136
1137#ifdef RT_ARCH_AMD64
1138 case VBOXGUEST_IOCTL_HGCM_DISCONNECT_32:
1139#endif /* RT_ARCH_AMD64 */
1140 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
1141 {
1142 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_DISCONNECT\n"));
1143
1144 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo))
1145 {
1146 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
1147 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
1148 Status = STATUS_INVALID_PARAMETER;
1149 break;
1150 }
1151
1152 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo)) {
1153 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
1154 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
1155 Status = STATUS_INVALID_PARAMETER;
1156 break;
1157 }
1158
1159 VBoxGuestHGCMDisconnectInfo *ptr = (VBoxGuestHGCMDisconnectInfo *)pBuf;
1160
1161 uint32_t u32ClientId=0;
1162 unsigned i=0;
1163 PVBOXGUESTSESSION pSession=0;
1164 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1165
1166 /* See comment in VBOXGUEST_IOCTL_HGCM_CONNECT */
1167 if (pStack->FileObject)
1168 {
1169 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: pDevExt=%p pFileObj=%p pSession=%p\n",
1170 pDevExt, pStack->FileObject, pStack->FileObject->FsContext));
1171
1172 u32ClientId = ptr->u32ClientID;
1173 pSession = (PVBOXGUESTSESSION)pStack->FileObject->FsContext;
1174
1175 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1176 for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++)
1177 if (pSession->aHGCMClientIds[i] == u32ClientId)
1178 {
1179 pSession->aHGCMClientIds[i] = UINT32_MAX;
1180 break;
1181 }
1182 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1183 if (i >= RT_ELEMENTS(pSession->aHGCMClientIds))
1184 {
1185 static unsigned s_cErrors = 0;
1186 if (s_cErrors++ > 32)
1187 dprintf(("VBoxGuestCommonIOCtl: HGCM_DISCONNECT: u32Client=%RX32\n", u32ClientId));
1188 Status = STATUS_INVALID_PARAMETER;
1189 break;
1190 }
1191 }
1192
1193 /* If request will be processed asynchronously, execution will
1194 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
1195 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
1196 * flag is set, returns.
1197 */
1198
1199 int rc = VbglR0HGCMInternalDisconnect (ptr, pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback, pDevExt, RT_INDEFINITE_WAIT);
1200
1201 if (RT_FAILURE(rc))
1202 {
1203 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: vbox rc = %Rrc\n", rc));
1204 Status = STATUS_UNSUCCESSFUL;
1205 }
1206 else
1207 {
1208 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1209 }
1210
1211 if (pStack->FileObject)
1212 {
1213 RTSpinlockAcquireNoInts(pDevExt->SessionSpinlock, &Tmp);
1214 if (pSession->aHGCMClientIds[i] == UINT32_MAX)
1215 pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) && RT_SUCCESS(ptr->result) ? 0 : u32ClientId;
1216 RTSpinlockReleaseNoInts(pDevExt->SessionSpinlock, &Tmp);
1217 }
1218 } break;
1219
1220#ifdef RT_ARCH_AMD64
1221 case VBOXGUEST_IOCTL_HGCM_CALL_32(0): /* (The size isn't relevant on NT.) */
1222 {
1223 /* A 32 bit application call. */
1224 int rc;
1225
1226 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL_32\n"));
1227
1228 Status = vboxHGCMVerifyIOBuffers (pStack,
1229 sizeof (VBoxGuestHGCMCallInfo));
1230
1231 if (Status != STATUS_SUCCESS)
1232 {
1233 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
1234 break;
1235 }
1236
1237 /* @todo: Old guest OpenGL driver used the same IOCtl code for both 32 and 64 bit binaries.
1238 * This is a protection, and can be removed if there were no 64 bit driver.
1239 */
1240 if (!IoIs32bitProcess(pIrp))
1241 {
1242 Status = STATUS_UNSUCCESSFUL;
1243 break;
1244 }
1245
1246 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1247 uint32_t fFlags = pIrp->RequestorMode == KernelMode ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1248
1249 rc = VbglR0HGCMInternalCall32(ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1250 pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback,
1251 pDevExt, RT_INDEFINITE_WAIT);
1252
1253 if (RT_FAILURE(rc))
1254 {
1255 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL_32: vbox rc = %Rrc\n", rc));
1256 Status = STATUS_UNSUCCESSFUL;
1257 }
1258 else
1259 {
1260 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1261 }
1262
1263 } break;
1264#endif /* RT_ARCH_AMD64 */
1265
1266 case VBOXGUEST_IOCTL_HGCM_CALL(0): /* (The size isn't relevant on NT.) */
1267 {
1268 int rc;
1269
1270 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL\n"));
1271
1272 Status = vboxHGCMVerifyIOBuffers (pStack,
1273 sizeof (VBoxGuestHGCMCallInfo));
1274
1275 if (Status != STATUS_SUCCESS)
1276 {
1277 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
1278 break;
1279 }
1280
1281 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1282 uint32_t fFlags = pIrp->RequestorMode == KernelMode ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1283
1284 rc = VbglR0HGCMInternalCall (ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1285 pIrp->RequestorMode == KernelMode? VBoxHGCMCallbackKernelMode :VBoxHGCMCallback,
1286 pDevExt, RT_INDEFINITE_WAIT);
1287
1288 if (RT_FAILURE(rc))
1289 {
1290 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL: vbox rc = %Rrc\n", rc));
1291 Status = STATUS_UNSUCCESSFUL;
1292 }
1293 else
1294 {
1295 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1296 }
1297
1298 } break;
1299
1300 case VBOXGUEST_IOCTL_HGCM_CALL_TIMED(0): /* (The size isn't relevant on NT.) */
1301 {
1302 /* This IOCTL is not used by shared folders, so VBoxHGCMCallbackKernelMode is not used. */
1303 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL_TIMED\n"));
1304
1305 Status = vboxHGCMVerifyIOBuffers (pStack,
1306 sizeof (VBoxGuestHGCMCallInfoTimed));
1307
1308 if (Status != STATUS_SUCCESS)
1309 {
1310 dprintf(("nvalid parameter. Status: %p\n", Status));
1311 break;
1312 }
1313
1314 VBoxGuestHGCMCallInfoTimed *pInfo = (VBoxGuestHGCMCallInfoTimed *)pBuf;
1315 VBoxGuestHGCMCallInfo *ptr = &pInfo->info;
1316
1317 int rc;
1318 uint32_t fFlags = pIrp->RequestorMode == KernelMode ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER;
1319 if (pInfo->fInterruptible)
1320 {
1321 dprintf(("VBoxGuest::VBoxGuestDeviceControl: calling VBoxHGCMCall interruptible, timeout %lu ms\n",
1322 pInfo->u32Timeout));
1323 rc = VbglR0HGCMInternalCall (ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1324 VBoxHGCMCallbackInterruptible, pDevExt, pInfo->u32Timeout);
1325 }
1326 else
1327 {
1328 dprintf(("VBoxGuest::VBoxGuestDeviceControl: calling VBoxHGCMCall, timeout %lu ms\n",
1329 pInfo->u32Timeout));
1330 rc = VbglR0HGCMInternalCall (ptr, pStack->Parameters.DeviceIoControl.InputBufferLength, fFlags,
1331 VBoxHGCMCallback, pDevExt, pInfo->u32Timeout);
1332 }
1333
1334 if (RT_FAILURE(rc))
1335 {
1336 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL_TIMED: vbox rc = %Rrc\n", rc));
1337 Status = STATUS_UNSUCCESSFUL;
1338 }
1339 else
1340 {
1341 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1342 }
1343
1344 } break;
1345#endif /* VBOX_WITH_HGCM */
1346
1347#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
1348 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
1349 {
1350 LogRel(("VRDP_SESSION: Enable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1351 if (!pDevExt->fVRDPEnabled)
1352 {
1353 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1354
1355 pDevExt->fVRDPEnabled = TRUE;
1356 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1357 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
1358 pSharedUserData->ActiveConsoleId = 2;
1359 }
1360 break;
1361 }
1362
1363 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
1364 {
1365 LogRel(("VRDP_SESSION: Disable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1366 if (pDevExt->fVRDPEnabled)
1367 {
1368 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1369
1370 pDevExt->fVRDPEnabled = FALSE;
1371 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1372 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
1373 pDevExt->ulOldActiveConsoleId = 0;
1374 }
1375 break;
1376 }
1377#endif
1378
1379#ifdef VBOX_WITH_MANAGEMENT
1380 case VBOXGUEST_IOCTL_CHECK_BALLOON:
1381 {
1382 VBoxGuestCheckBalloonInfo *pInfo = (VBoxGuestCheckBalloonInfo *)pBuf;
1383
1384 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestCheckBalloonInfo))
1385 {
1386 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(ULONG) %d\n",
1387 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestCheckBalloonInfo)));
1388 Status = STATUS_INVALID_PARAMETER;
1389 break;
1390 }
1391
1392 ULONG cMemoryBalloonChunks;
1393 int rc = VBoxGuestQueryMemoryBalloon(pDevExt, &cMemoryBalloonChunks);
1394 if (RT_FAILURE(rc))
1395 {
1396 dprintf(("VBOXGUEST_IOCTL_CHECK_BALLOON: vbox rc = %Rrc\n", rc));
1397 Status = STATUS_UNSUCCESSFUL;
1398 }
1399 else
1400 {
1401 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1402 pInfo->cBalloonChunks = cMemoryBalloonChunks;
1403 pInfo->fHandleInR3 = false;
1404 }
1405 break;
1406 }
1407#endif
1408
1409 case VBOXGUEST_IOCTL_LOG(0): /* The size isn't relevant on NT. */
1410 {
1411 /* Enable this only for debugging:
1412 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_LOG %.*s\n", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1413 */
1414 LogRel(("%.*s", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1415 cbOut = 0;
1416 break;
1417 }
1418
1419 default:
1420 Status = STATUS_INVALID_PARAMETER;
1421 break;
1422 }
1423
1424 pIrp->IoStatus.Status = Status;
1425 pIrp->IoStatus.Information = cbOut;
1426
1427 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1428
1429 dprintf(("VBoxGuest::VBoxGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
1430
1431 return Status;
1432}
1433
1434
1435/**
1436 * IRP_MJ_SYSTEM_CONTROL handler
1437 *
1438 * @returns NT status code
1439 * @param pDevObj Device object.
1440 * @param pIrp IRP.
1441 */
1442NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1443{
1444 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1445
1446 dprintf(("VBoxGuest::VBoxGuestSystemControl\n"));
1447
1448 /* Always pass it on to the next driver. */
1449 IoSkipCurrentIrpStackLocation(pIrp);
1450
1451 return IoCallDriver(pDevExt->nextLowerDriver, pIrp);
1452}
1453
1454/**
1455 * IRP_MJ_SHUTDOWN handler
1456 *
1457 * @returns NT status code
1458 * @param pDevObj Device object.
1459 * @param pIrp IRP.
1460 */
1461NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1462{
1463 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1464
1465 dprintf(("VBoxGuest::VBoxGuestShutdown\n"));
1466
1467 if (pDevExt && pDevExt->powerStateRequest)
1468 {
1469 VMMDevPowerStateRequest *req = pDevExt->powerStateRequest;
1470
1471 req->header.requestType = VMMDevReq_SetPowerStatus;
1472 req->powerState = VMMDevPowerState_PowerOff;
1473
1474 int rc = VbglGRPerform (&req->header);
1475
1476 if (RT_FAILURE(rc))
1477 {
1478 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev! "
1479 "rc = %Rrc\n", rc));
1480 }
1481 }
1482
1483 return STATUS_SUCCESS;
1484}
1485
1486/**
1487 * Stub function for functions we don't implemented.
1488 *
1489 * @returns STATUS_NOT_SUPPORTED
1490 * @param pDevObj Device object.
1491 * @param pIrp IRP.
1492 */
1493NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1494{
1495 dprintf(("VBoxGuest::VBoxGuestNotSupportedStub\n"));
1496 pDevObj = pDevObj;
1497
1498 pIrp->IoStatus.Information = 0;
1499 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1500 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1501
1502 return STATUS_NOT_SUPPORTED;
1503}
1504
1505/**
1506 * DPC handler
1507 *
1508 * @param dpc DPC descriptor.
1509 * @param pDevObj Device object.
1510 * @param irp Interrupt request packet.
1511 * @param context Context specific pointer.
1512 */
1513VOID VBoxGuestDpcHandler(PKDPC dpc, PDEVICE_OBJECT pDevObj,
1514 PIRP irp, PVOID context)
1515{
1516 /* Unblock handlers waiting for arrived events.
1517 *
1518 * Events are very low things, there is one event flag (1 or more bit)
1519 * for each event. Each event is processed by exactly one handler.
1520 *
1521 * Assume that we trust additions and that other drivers will
1522 * handle its respective events without trying to fetch all events.
1523 *
1524 * Anyway design assures that wrong event processing will affect only guest.
1525 *
1526 * Event handler calls VMMDev IOCTL for waiting an event.
1527 * It supplies event mask. IOCTL blocks on EventNotification.
1528 * Here we just signal an the EventNotification to all waiting
1529 * threads, the IOCTL handler analyzes events and either
1530 * return to caller or blocks again.
1531 *
1532 * If we do not have too many events this is a simple and good
1533 * approach. Other way is to have as many Event objects as the callers
1534 * and wake up only callers waiting for the specific event.
1535 *
1536 * Now with the 'wake up all' appoach we probably do not need the DPC
1537 * handler and can signal event directly from ISR.
1538 *
1539 */
1540
1541 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1542
1543 dprintf(("VBoxGuest::VBoxGuestDpcHandler\n"));
1544
1545 KePulseEvent(&pDevExt->keventNotification, 0, FALSE);
1546
1547}
1548
1549/**
1550 * ISR handler
1551 *
1552 * @return BOOLEAN indicates whether the IRQ came from us (TRUE) or not (FALSE)
1553 * @param interrupt Interrupt that was triggered.
1554 * @param serviceContext Context specific pointer.
1555 */
1556BOOLEAN VBoxGuestIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext)
1557{
1558 NTSTATUS rc;
1559 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)serviceContext;
1560 BOOLEAN fIRQTaken = FALSE;
1561
1562 dprintf(("VBoxGuest::VBoxGuestIsrHandler haveEvents = %d\n",
1563 pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents));
1564
1565 /*
1566 * now we have to find out whether it was our IRQ. Read the event mask
1567 * from our device to see if there are any pending events
1568 */
1569 if (pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents)
1570 {
1571 /* Acknowlegde events. */
1572 VMMDevEvents *req = pDevExt->irqAckEvents;
1573
1574 rc = VbglGRPerform (&req->header);
1575 if (RT_SUCCESS(rc))
1576 {
1577 dprintf(("VBoxGuest::VBoxGuestIsrHandler: acknowledge events succeeded %#x\n",
1578 req->events));
1579
1580 ASMAtomicOrU32((uint32_t *)&pDevExt->u32Events, req->events);
1581 IoRequestDpc(pDevExt->deviceObject, pDevExt->currentIrp, NULL);
1582 }
1583 else
1584 {
1585 /* This can't be actually. This is sign of a serious problem. */
1586 dprintf(("VBoxGuest::VBoxGuestIsrHandler: "
1587 "acknowledge events failed rc = %Rrc\n", rc));
1588 }
1589
1590 /* Mark IRQ as taken, there were events for us. */
1591 fIRQTaken = TRUE;
1592 }
1593
1594 return fIRQTaken;
1595}
1596
1597/**
1598 * Worker thread to do periodic things such as notify other
1599 * drivers of events.
1600 *
1601 * @param pDevExt device extension pointer
1602 */
1603VOID vboxWorkerThread(PVOID context)
1604{
1605 PVBOXGUESTDEVEXT pDevExt;
1606
1607 pDevExt = (PVBOXGUESTDEVEXT)context;
1608 dprintf(("VBoxGuest::vboxWorkerThread entered\n"));
1609
1610 /* perform the hypervisor address space reservation */
1611 reserveHypervisorMemory(pDevExt);
1612
1613 do
1614 {
1615 /* Nothing to do here yet. */
1616
1617 /*
1618 * Go asleep unless we're supposed to terminate
1619 */
1620 if (!pDevExt->stopThread)
1621 {
1622 ULONG secWait = 60;
1623 dprintf(("VBoxGuest::vboxWorkerThread: waiting for %u seconds...\n", secWait));
1624 LARGE_INTEGER dueTime;
1625 dueTime.QuadPart = -10000 * 1000 * (int)secWait;
1626 if (KeWaitForSingleObject(&pDevExt->workerThreadRequest, Executive,
1627 KernelMode, FALSE, &dueTime) == STATUS_SUCCESS)
1628 {
1629 KeResetEvent(&pDevExt->workerThreadRequest);
1630 }
1631 }
1632 } while (!pDevExt->stopThread);
1633
1634 dprintf(("VBoxGuest::vboxWorkerThread: we've been asked to terminate!\n"));
1635
1636 if (pDevExt->workerThread)
1637 {
1638 ObDereferenceObject(pDevExt->workerThread);
1639 pDevExt->workerThread = NULL;
1640 }
1641 dprintf(("VBoxGuest::vboxWorkerThread: now really gone!\n"));
1642}
1643
1644/**
1645 * Create driver worker threads
1646 *
1647 * @returns NTSTATUS NT status code
1648 * @param pDevExt VBoxGuest device extension
1649 */
1650NTSTATUS createThreads(PVBOXGUESTDEVEXT pDevExt)
1651{
1652 NTSTATUS rc;
1653 HANDLE threadHandle;
1654 OBJECT_ATTRIBUTES objAttributes;
1655
1656 dprintf(("VBoxGuest::createThreads\n"));
1657
1658 // first setup the request semaphore
1659 KeInitializeEvent(&pDevExt->workerThreadRequest, SynchronizationEvent, FALSE);
1660
1661// the API has slightly changed after NT4
1662#ifdef TARGET_NT4
1663#ifdef OBJ_KERNEL_HANDLE
1664#undef OBJ_KERNEL_HANDLE
1665#endif
1666#define OBJ_KERNEL_HANDLE 0
1667#endif
1668
1669 /*
1670 * The worker thread
1671 */
1672 InitializeObjectAttributes(&objAttributes,
1673 NULL,
1674 OBJ_KERNEL_HANDLE,
1675 NULL,
1676 NULL);
1677
1678 rc = PsCreateSystemThread(&threadHandle,
1679 THREAD_ALL_ACCESS,
1680 &objAttributes,
1681 (HANDLE)0L,
1682 NULL,
1683 vboxWorkerThread,
1684 pDevExt);
1685 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for worker thread returned: 0x%x\n", rc));
1686 rc = ObReferenceObjectByHandle(threadHandle,
1687 THREAD_ALL_ACCESS,
1688 NULL,
1689 KernelMode,
1690 (PVOID*)&pDevExt->workerThread,
1691 NULL);
1692 ZwClose(threadHandle);
1693
1694 /*
1695 * The idle thread
1696 */
1697#if 0 /// @todo Windows "sees" that time is lost and reports 100% usage
1698 rc = PsCreateSystemThread(&threadHandle,
1699 THREAD_ALL_ACCESS,
1700 &objAttributes,
1701 (HANDLE)0L,
1702 NULL,
1703 vboxIdleThread,
1704 pDevExt);
1705 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for idle thread returned: 0x%x\n", rc));
1706 rc = ObReferenceObjectByHandle(threadHandle,
1707 THREAD_ALL_ACCESS,
1708 NULL,
1709 KernelMode,
1710 (PVOID*)&pDevExt->idleThread,
1711 NULL);
1712 ZwClose(threadHandle);
1713#endif
1714
1715 return rc;
1716}
1717
1718/**
1719 * Helper routine to reserve address space for the hypervisor
1720 * and communicate its position.
1721 *
1722 * @param pDevExt Device extension structure.
1723 */
1724VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1725{
1726 // @todo rc handling
1727 uint32_t hypervisorSize;
1728
1729 VMMDevReqHypervisorInfo *req = NULL;
1730
1731 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
1732
1733 if (RT_SUCCESS(rc))
1734 {
1735 req->hypervisorStart = 0;
1736 req->hypervisorSize = 0;
1737
1738 rc = VbglGRPerform (&req->header);
1739
1740 if (RT_SUCCESS(rc))
1741 {
1742 hypervisorSize = req->hypervisorSize;
1743
1744 if (!hypervisorSize)
1745 {
1746 dprintf(("VBoxGuest::reserveHypervisorMemory: host returned 0, not doing anything\n"));
1747 return;
1748 }
1749
1750 dprintf(("VBoxGuest::reserveHypervisorMemory: host wants %u bytes of hypervisor address space\n", hypervisorSize));
1751
1752 // Map fictive physical memory into the kernel address space to reserve virtual
1753 // address space. This API does not perform any checks but just allocate the
1754 // PTEs (which we don't really need/want but there isn't any other clean method).
1755 // The hypervisor only likes 4MB aligned virtual addresses, so we have to allocate
1756 // 4MB more than we are actually supposed to in order to guarantee that. Maybe we
1757 // can come up with a less lavish algorithm lateron.
1758 PHYSICAL_ADDRESS physAddr;
1759 physAddr.QuadPart = VBOXGUEST_HYPERVISOR_PHYSICAL_START;
1760 pDevExt->hypervisorMappingSize = hypervisorSize + 0x400000;
1761 pDevExt->hypervisorMapping = MmMapIoSpace(physAddr,
1762 pDevExt->hypervisorMappingSize,
1763 MmNonCached);
1764 if (!pDevExt->hypervisorMapping)
1765 {
1766 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned NULL!\n"));
1767 return;
1768 }
1769
1770 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned %p\n", pDevExt->hypervisorMapping));
1771 dprintf(("VBoxGuest::reserveHypervisorMemory: communicating %p to host\n",
1772 RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000)));
1773
1774 /* align at 4MB */
1775 req->hypervisorStart = (uintptr_t)RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000);
1776
1777 req->header.requestType = VMMDevReq_SetHypervisorInfo;
1778 req->header.rc = VERR_GENERAL_FAILURE;
1779
1780 /* issue request */
1781 rc = VbglGRPerform (&req->header);
1782
1783 if (RT_FAILURE(rc))
1784 {
1785 dprintf(("VBoxGuest::reserveHypervisorMemory: error communicating physical address to VMMDev! "
1786 "rc = %Rrc\n", rc));
1787 }
1788 }
1789 else
1790 {
1791 dprintf(("VBoxGuest::reserveHypervisorMemory: request failed with rc = %Rrc\n", rc));
1792 }
1793 VbglGRFree (&req->header);
1794 }
1795
1796#ifdef RT_ARCH_X86
1797 /* Allocate locked executable memory that can be used for patching guest code. */
1798 {
1799 VMMDevReqPatchMemory *req = NULL;
1800 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqPatchMemory), VMMDevReq_RegisterPatchMemory);
1801 if (RT_SUCCESS(rc))
1802 {
1803 req->cbPatchMem = VMMDEV_GUEST_DEFAULT_PATCHMEM_SIZE;
1804
1805 rc = RTR0MemObjAllocPage(&pDevExt->PatchMemObj, req->cbPatchMem, true /* executable. */);
1806 if (RT_SUCCESS(rc))
1807 {
1808 req->pPatchMem = (RTGCPTR)(uintptr_t)RTR0MemObjAddress(pDevExt->PatchMemObj);
1809
1810 rc = VbglGRPerform (&req->header);
1811 if (RT_FAILURE(rc))
1812 {
1813 dprintf(("VBoxGuest::reserveHypervisorMemory: VMMDevReq_RegisterPatchMemory error! "
1814 "rc = %Rrc\n", rc));
1815 RTR0MemObjFree(pDevExt->PatchMemObj, true);
1816 pDevExt->PatchMemObj = NULL;
1817 }
1818 }
1819 else
1820 {
1821 dprintf(("VBoxGuest::reserveHypervisorMemory: RTR0MemObjAllocPage failed with rc = %Rrc\n", rc));
1822 }
1823 VbglGRFree (&req->header);
1824 }
1825 }
1826#endif
1827 return;
1828}
1829
1830/**
1831 * Helper function to unregister a virtual address space mapping
1832 *
1833 * @param pDevExt Device extension
1834 */
1835VOID unreserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1836{
1837#ifdef RT_ARCH_X86
1838 /* Remove the locked executable memory range that can be used for patching guest code. */
1839 if (pDevExt->PatchMemObj)
1840 {
1841 VMMDevReqPatchMemory *req = NULL;
1842 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqPatchMemory), VMMDevReq_DeregisterPatchMemory);
1843 if (RT_SUCCESS(rc))
1844 {
1845 req->cbPatchMem = (uint32_t)RTR0MemObjSize(pDevExt->PatchMemObj);
1846 req->pPatchMem = (RTGCPTR)(uintptr_t)RTR0MemObjAddress(pDevExt->PatchMemObj);
1847
1848 rc = VbglGRPerform (&req->header);
1849 if (RT_FAILURE(rc))
1850 {
1851 dprintf(("VBoxGuest::reserveHypervisorMemory: VMMDevReq_DeregisterPatchMemory error! "
1852 "rc = %Rrc\n", rc));
1853 /* We intentially leak the memory object here as there still could
1854 * be references to it!!!
1855 */
1856 }
1857 else
1858 {
1859 RTR0MemObjFree(pDevExt->PatchMemObj, true);
1860 }
1861 }
1862 }
1863#endif
1864
1865 VMMDevReqHypervisorInfo *req = NULL;
1866
1867 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
1868
1869 if (RT_SUCCESS(rc))
1870 {
1871 /* tell the hypervisor that the mapping is no longer available */
1872
1873 req->hypervisorStart = 0;
1874 req->hypervisorSize = 0;
1875
1876 rc = VbglGRPerform (&req->header);
1877
1878 if (RT_FAILURE(rc))
1879 {
1880 dprintf(("VBoxGuest::unreserveHypervisorMemory: error communicating physical address to VMMDev! "
1881 "rc = %Rrc\n", rc));
1882 }
1883
1884 VbglGRFree (&req->header);
1885 }
1886
1887 if (!pDevExt->hypervisorMapping)
1888 {
1889 dprintf(("VBoxGuest::unreserveHypervisorMemory: there is no mapping, returning\n"));
1890 return;
1891 }
1892
1893 // unmap fictive IO space
1894 MmUnmapIoSpace(pDevExt->hypervisorMapping, pDevExt->hypervisorMappingSize);
1895 dprintf(("VBoxGuest::unreserveHypervisorMemmory: done\n"));
1896}
1897
1898/**
1899 * Idle thread that runs at the lowest priority possible
1900 * and whenever scheduled, makes a VMMDev call to give up
1901 * timeslices. This is so prevent Windows from thinking that
1902 * nothing is happening on the machine and doing stupid things
1903 * that would steal time from other VMs it doesn't know of.
1904 *
1905 * @param pDevExt device extension pointer
1906 */
1907VOID vboxIdleThread(PVOID context)
1908{
1909 PVBOXGUESTDEVEXT pDevExt;
1910
1911 pDevExt = (PVBOXGUESTDEVEXT)context;
1912 dprintf(("VBoxGuest::vboxIdleThread entered\n"));
1913
1914 /* set priority as low as possible */
1915 KeSetPriorityThread(KeGetCurrentThread(), LOW_PRIORITY);
1916
1917 /* allocate VMMDev request structure */
1918 VMMDevReqIdle *req;
1919 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_Idle);
1920 if (RT_FAILURE(rc))
1921 {
1922 dprintf(("VBoxGuest::vboxIdleThread: error %Rrc allocating request structure!\n"));
1923 return;
1924 }
1925
1926 do
1927 {
1928 //dprintf(("VBoxGuest: performing idle request..\n"));
1929 /* perform idle request */
1930 VbglGRPerform(&req->header);
1931
1932 } while (!pDevExt->stopThread);
1933
1934 VbglGRFree(&req->header);
1935
1936 dprintf(("VBoxGuest::vboxIdleThread leaving\n"));
1937}
1938
1939#ifdef DEBUG
1940static VOID testAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits,
1941 uint32_t u32Exp)
1942{
1943 ULONG u32Bits2 = u32Bits;
1944 uint32_t u32Result = guestAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1945 if ( u32Result != u32Exp
1946 || (u32Bits2 & u32Mask)
1947 || (u32Bits2 & u32Result)
1948 || ((u32Bits2 | u32Result) != u32Bits)
1949 )
1950 AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
1951 __PRETTY_FUNCTION__, u32Mask, u32Bits, u32Bits2,
1952 u32Result));
1953}
1954
1955static VOID testVBoxGuest(VOID)
1956{
1957 testAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1958 testAtomicTestAndClearBitsU32(0x11, 0, 0);
1959 testAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1960 testAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1961 testAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1962 testAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1963}
1964#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