VirtualBox

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

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

Verify VMMREQUEST (xTracker #4336).

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

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