VirtualBox

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

Last change on this file since 12474 was 12084, checked in by vboxsync, 16 years ago

Release logging for Windows RDP session handling hack (the code is not active).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.1 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 <stdio.h>
39#include <VBox/VBoxGuestLib.h>
40#include <VBoxGuestInternal.h>
41
42#ifdef TARGET_NT4
43/* XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist on NT4, so...
44 * The same for ExAllocatePool.
45 */
46#undef ExAllocatePool
47#undef ExFreePool
48#endif
49
50/*******************************************************************************
51* Defined Constants And Macros *
52*******************************************************************************/
53
54
55/*******************************************************************************
56* Internal Functions *
57*******************************************************************************/
58extern "C"
59{
60static NTSTATUS VBoxGuestAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
61static void VBoxGuestUnload(PDRIVER_OBJECT pDrvObj);
62static NTSTATUS VBoxGuestCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
63static NTSTATUS VBoxGuestClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
64static NTSTATUS VBoxGuestDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
65static NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
66static NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
67static NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
68static VOID vboxWorkerThread(PVOID context);
69static VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt);
70static VOID vboxIdleThread(PVOID context);
71}
72
73
74/*******************************************************************************
75* Exported Functions *
76*******************************************************************************/
77__BEGIN_DECLS
78ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
79__END_DECLS
80
81#ifdef ALLOC_PRAGMA
82#pragma alloc_text (INIT, DriverEntry)
83#pragma alloc_text (PAGE, createThreads)
84#pragma alloc_text (PAGE, unreserveHypervisorMemory)
85#pragma alloc_text (PAGE, VBoxGuestAddDevice)
86#pragma alloc_text (PAGE, VBoxGuestUnload)
87#pragma alloc_text (PAGE, VBoxGuestCreate)
88#pragma alloc_text (PAGE, VBoxGuestClose)
89#pragma alloc_text (PAGE, VBoxGuestDeviceControl)
90#pragma alloc_text (PAGE, VBoxGuestShutdown)
91#pragma alloc_text (PAGE, VBoxGuestNotSupportedStub)
92/* Note: at least the isr handler should be in non-pageable memory! */
93/*#pragma alloc_text (PAGE, VBoxGuestDpcHandler)
94 #pragma alloc_text (PAGE, VBoxGuestIsrHandler) */
95#pragma alloc_text (PAGE, vboxWorkerThread)
96#pragma alloc_text (PAGE, reserveHypervisorMemory)
97#pragma alloc_text (PAGE, vboxIdleThread)
98#endif
99
100winVersion_t winVersion;
101
102/**
103 * Driver entry point.
104 *
105 * @returns appropriate status code.
106 * @param pDrvObj Pointer to driver object.
107 * @param pRegPath Registry base path.
108 */
109ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
110{
111 NTSTATUS rc = STATUS_SUCCESS;
112
113 dprintf(("VBoxGuest::DriverEntry. Driver built: %s %s\n", __DATE__, __TIME__));
114
115 ULONG majorVersion;
116 ULONG minorVersion;
117 ULONG buildNumber;
118 PsGetVersion(&majorVersion, &minorVersion, &buildNumber, NULL);
119 dprintf(("VBoxGuest::DriverEntry: running on Windows NT version %d.%d, build %d\n", majorVersion, minorVersion, buildNumber));
120 switch (majorVersion)
121 {
122 case 6:
123 winVersion = WINVISTA;
124 break;
125 case 5:
126 switch (minorVersion)
127 {
128 case 2:
129 winVersion = WIN2K3;
130 break;
131 case 1:
132 winVersion = WINXP;
133 break;
134 case 0:
135 winVersion = WIN2K;
136 break;
137 default:
138 dprintf(("VBoxGuest::DriverEntry: unknown version of Windows, refusing!\n"));
139 return STATUS_DRIVER_UNABLE_TO_LOAD;
140 }
141 break;
142 case 4:
143 winVersion = WINNT4;
144 break;
145 default:
146 dprintf(("VBoxGuest::DriverEntry: NT4 required!\n"));
147 return STATUS_DRIVER_UNABLE_TO_LOAD;
148 }
149
150 /*
151 * Setup the driver entry points in pDrvObj.
152 */
153 pDrvObj->DriverUnload = VBoxGuestUnload;
154 pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxGuestCreate;
155 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxGuestClose;
156 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxGuestDeviceControl;
157 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxGuestDeviceControl;
158 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = VBoxGuestShutdown;
159 pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxGuestNotSupportedStub;
160 pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxGuestNotSupportedStub;
161#ifdef TARGET_NT4
162 rc = ntCreateDevice(pDrvObj, NULL, pRegPath);
163#else
164 pDrvObj->MajorFunction[IRP_MJ_PNP] = VBoxGuestPnP;
165 pDrvObj->MajorFunction[IRP_MJ_POWER] = VBoxGuestPower;
166 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VBoxGuestSystemControl;
167 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)VBoxGuestAddDevice;
168#endif
169
170 dprintf(("VBoxGuest::DriverEntry returning %#x\n", rc));
171 return rc;
172}
173
174#ifndef TARGET_NT4
175/**
176 * Handle request from the Plug & Play subsystem
177 *
178 * @returns NT status code
179 * @param pDrvObj Driver object
180 * @param pDevObj Device object
181 */
182static NTSTATUS VBoxGuestAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
183{
184 NTSTATUS rc;
185 dprintf(("VBoxGuest::VBoxGuestAddDevice\n"));
186
187 /*
188 * Create device.
189 */
190 PDEVICE_OBJECT deviceObject = NULL;
191 UNICODE_STRING devName;
192 RtlInitUnicodeString(&devName, VBOXGUEST_DEVICE_NAME_NT);
193 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXT), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject);
194 if (!NT_SUCCESS(rc))
195 {
196 dprintf(("VBoxGuest::VBoxGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc));
197 return rc;
198 }
199 UNICODE_STRING win32Name;
200 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
201 rc = IoCreateSymbolicLink(&win32Name, &devName);
202 if (!NT_SUCCESS(rc))
203 {
204 dprintf(("VBoxGuest::VBoxGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc));
205 IoDeleteDevice(deviceObject);
206 return rc;
207 }
208
209 /*
210 * Setup the device extension.
211 */
212 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)deviceObject->DeviceExtension;
213 RtlZeroMemory(pDevExt, sizeof(VBOXGUESTDEVEXT));
214
215 pDevExt->deviceObject = deviceObject;
216 pDevExt->devState = STOPPED;
217
218 pDevExt->nextLowerDriver = IoAttachDeviceToDeviceStack(deviceObject, pDevObj);
219 if (pDevExt->nextLowerDriver == NULL)
220 {
221 dprintf(("VBoxGuest::VBoxGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDrive\n"));
222 IoDeleteSymbolicLink(&win32Name);
223 IoDeleteDevice(deviceObject);
224 return STATUS_DEVICE_NOT_CONNECTED;
225 }
226
227 // driver is ready now
228 deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
229
230 dprintf(("VBoxGuest::VBoxGuestAddDevice: returning with rc = 0x%x\n", rc));
231 return rc;
232}
233#endif
234
235
236/**
237 * Unload the driver.
238 *
239 * @param pDrvObj Driver object.
240 */
241void VBoxGuestUnload(PDRIVER_OBJECT pDrvObj)
242{
243 dprintf(("VBoxGuest::VBoxGuestUnload\n"));
244#ifdef TARGET_NT4
245 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension;
246 unreserveHypervisorMemory(pDevExt);
247 if (pDevExt->workerThread)
248 {
249 dprintf(("VBoxGuest::VBoxGuestUnload: waiting for the worker thread to terminate...\n"));
250 pDevExt->stopThread = TRUE;
251 KeSetEvent(&pDevExt->workerThreadRequest, 0, FALSE);
252 KeWaitForSingleObject(pDevExt->workerThread,
253 Executive, KernelMode, FALSE, NULL);
254 dprintf(("VBoxGuest::VBoxGuestUnload: returned from KeWaitForSingleObject for worker thread\n"));
255 }
256 if (pDevExt->idleThread)
257 {
258 dprintf(("VBoxGuest::VBoxGuestUnload: waiting for the idle thread to terminate...\n"));
259 pDevExt->stopThread = TRUE;
260 KeWaitForSingleObject(pDevExt->idleThread,
261 Executive, KernelMode, FALSE, NULL);
262 dprintf(("VBoxGuest::VBoxGuestUnload: returned from KeWaitForSingleObject for idle thread\n"));
263 }
264
265 hlpVBoxUnmapVMMDevMemory (pDevExt);
266
267 VBoxCleanupMemBalloon(pDevExt);
268
269 /*
270 * I don't think it's possible to unload a driver which processes have
271 * opened, at least we'll blindly assume that here.
272 */
273 UNICODE_STRING win32Name;
274 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
275 NTSTATUS rc = IoDeleteSymbolicLink(&win32Name);
276 IoDeleteDevice(pDrvObj->DeviceObject);
277#endif
278 dprintf(("VBoxGuest::VBoxGuestUnload: returning\n"));
279}
280
281
282/**
283 * Create (i.e. Open) file entry point.
284 *
285 * @param pDevObj Device object.
286 * @param pIrp Request packet.
287 */
288NTSTATUS VBoxGuestCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
289{
290 dprintf(("VBoxGuest::VBoxGuestCreate\n"));
291
292 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
293 PFILE_OBJECT pFileObj = pStack->FileObject;
294 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
295
296 /*
297 * We are not remotely similar to a directory...
298 * (But this is possible.)
299 */
300 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
301 {
302 dprintf(("VBoxGuest::VBoxGuestCreate: we're not a directory!\n"));
303 pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
304 pIrp->IoStatus.Information = 0;
305 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
306 return STATUS_NOT_A_DIRECTORY;
307 }
308
309 NTSTATUS rcNt = pIrp->IoStatus.Status = STATUS_SUCCESS;
310 pIrp->IoStatus.Information = 0;
311 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
312
313 dprintf(("VBoxGuest::VBoxGuestCreate: returning 0x%x\n", rcNt));
314 return rcNt;
315}
316
317
318/**
319 * Close file entry point.
320 *
321 * @param pDevObj Device object.
322 * @param pIrp Request packet.
323 */
324NTSTATUS VBoxGuestClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
325{
326 dprintf(("VBoxGuest::VBoxGuestClose\n"));
327
328 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
329 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
330 PFILE_OBJECT pFileObj = pStack->FileObject;
331 dprintf(("VBoxGuest::VBoxGuestClose: pDevExt=%p pFileObj=%p pSession=%p\n",
332 pDevExt, pFileObj, pFileObj->FsContext));
333
334 pFileObj->FsContext = NULL;
335 pIrp->IoStatus.Information = 0;
336 pIrp->IoStatus.Status = STATUS_SUCCESS;
337 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
338
339 return STATUS_SUCCESS;
340}
341
342#ifdef VBOX_WITH_HGCM
343DECLVBGL(void) VBoxHGCMCallback (VMMDevHGCMRequestHeader *pHeader, void *pvData, uint32_t u32Data)
344{
345 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvData;
346
347 dprintf(("VBoxHGCMCallback\n"));
348
349 /* Possible problem with request completion right between the fu32Flags check and KeWaitForSingleObject
350 * call; introduce a timeout to make sure we don't wait indefinitely.
351 */
352
353 while ((pHeader->fu32Flags & VBOX_HGCM_REQ_DONE) == 0)
354 {
355 /* Specifying UserMode so killing the user process will abort the wait.
356 * @todo Since VbglGRCancel is not yet implemented, the wait itself must
357 * be not interruptible. The wait can be interrupted only when the
358 * calling process is being killed.
359 * When alertable is TRUE, the wait sometimes ends with STATUS_USER_APC.
360 */
361 NTSTATUS rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive,
362 UserMode,
363 FALSE, /* Not Alertable */
364 &pDevExt->HGCMWaitTimeout
365 );
366 dprintf(("VBoxHGCMCallback: Wait returned %d fu32Flags=%x\n", rc, pHeader->fu32Flags));
367
368 if (rc == STATUS_TIMEOUT)
369 continue;
370
371 if (rc != STATUS_WAIT_0)
372 {
373 dprintf(("VBoxHGCMCallback: The external event was signalled or the wait timed out or terminated rc = 0x%08X.\n", rc));
374 break;
375 }
376
377 dprintf(("VBoxHGCMCallback: fu32Flags = %08X\n", pHeader->fu32Flags));
378 }
379 return;
380}
381
382NTSTATUS vboxHGCMVerifyIOBuffers (PIO_STACK_LOCATION pStack, unsigned cb)
383{
384 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < cb)
385 {
386 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: OutputBufferLength %d < %d\n",
387 pStack->Parameters.DeviceIoControl.OutputBufferLength, cb));
388 return STATUS_INVALID_PARAMETER;
389 }
390
391 if (pStack->Parameters.DeviceIoControl.InputBufferLength < cb)
392 {
393 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: InputBufferLength %d < %d\n",
394 pStack->Parameters.DeviceIoControl.InputBufferLength, cb));
395 return STATUS_INVALID_PARAMETER;
396 }
397
398 return STATUS_SUCCESS;
399}
400
401#endif /* VBOX_WITH_HGCM */
402
403static bool IsPowerOfTwo (uint32_t val)
404{
405 return (val & (val - 1)) == 0;
406}
407
408static bool CtlGuestFilterMask (uint32_t u32OrMask, uint32_t u32NotMask)
409{
410 bool result = false;
411 VMMDevCtlGuestFilterMask *req;
412 int rc = VbglGRAlloc ((VMMDevRequestHeader **) &req, sizeof (*req),
413 VMMDevReq_CtlGuestFilterMask);
414
415 if (VBOX_SUCCESS (rc))
416 {
417 req->u32OrMask = u32OrMask;
418 req->u32NotMask = u32NotMask;
419
420 rc = VbglGRPerform (&req->header);
421 if (VBOX_FAILURE (rc) || VBOX_FAILURE (req->header.rc))
422 {
423 dprintf (("VBoxGuest::VBoxGuestDeviceControl: error issuing request to VMMDev! "
424 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
425 }
426 else
427 {
428 result = true;
429 }
430 VbglGRFree (&req->header);
431 }
432
433 return result;
434}
435
436#ifdef VBOX_WITH_MANAGEMENT
437static int VBoxGuestSetBalloonSize(PVBOXGUESTDEVEXT pDevExt, uint32_t u32BalloonSize)
438{
439 VMMDevChangeMemBalloon *req = NULL;
440 int rc = VINF_SUCCESS;
441
442 if (u32BalloonSize > pDevExt->MemBalloon.cMaxBalloons)
443 {
444 AssertMsgFailed(("VBoxGuestSetBalloonSize illegal balloon size %d (max=%d)\n", u32BalloonSize, pDevExt->MemBalloon.cMaxBalloons));
445 return VERR_INVALID_PARAMETER;
446 }
447
448 if (u32BalloonSize == pDevExt->MemBalloon.cBalloons)
449 return VINF_SUCCESS; /* nothing to do */
450
451 /* Allocate request packet */
452 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]), VMMDevReq_ChangeMemBalloon);
453 if (VBOX_FAILURE(rc))
454 return rc;
455
456 vmmdevInitRequest(&req->header, VMMDevReq_ChangeMemBalloon);
457
458 if (u32BalloonSize > pDevExt->MemBalloon.cBalloons)
459 {
460 /* inflate */
461 for (uint32_t i=pDevExt->MemBalloon.cBalloons;i<u32BalloonSize;i++)
462 {
463#ifndef TARGET_NT4
464 /*
465 * Use MmAllocatePagesForMdl to specify the range of physical addresses we wish to use.
466 */
467 PHYSICAL_ADDRESS Zero;
468 PHYSICAL_ADDRESS HighAddr;
469 Zero.QuadPart = 0;
470 HighAddr.QuadPart = _4G - 1;
471 PMDL pMdl = MmAllocatePagesForMdl(Zero, HighAddr, Zero, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
472 if (pMdl)
473 {
474 if (MmGetMdlByteCount(pMdl) < VMMDEV_MEMORY_BALLOON_CHUNK_SIZE)
475 {
476 MmFreePagesFromMdl(pMdl);
477 ExFreePool(pMdl);
478 rc = VERR_NO_MEMORY;
479 goto end;
480 }
481 }
482#else
483 PVOID pvBalloon;
484 pvBalloon = ExAllocatePool(PagedPool, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
485 if (!pvBalloon)
486 {
487 rc = VERR_NO_MEMORY;
488 goto end;
489 }
490
491 PMDL pMdl = IoAllocateMdl (pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, FALSE, FALSE, NULL);
492 if (pMdl == NULL)
493 {
494 rc = VERR_NO_MEMORY;
495 ExFreePool(pvBalloon);
496 AssertMsgFailed(("IoAllocateMdl %VGv %x failed!!\n", pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE));
497 goto end;
498 }
499 else
500 {
501 __try {
502 /* Calls to MmProbeAndLockPages must be enclosed in a try/except block. */
503 MmProbeAndLockPages (pMdl, KernelMode, IoModifyAccess);
504 }
505 __except(EXCEPTION_EXECUTE_HANDLER)
506 {
507 dprintf(("MmProbeAndLockPages failed!\n"));
508 rc = VERR_NO_MEMORY;
509 IoFreeMdl (pMdl);
510 ExFreePool(pvBalloon);
511 goto end;
512 }
513 }
514#endif
515
516 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
517
518 /* Copy manually as RTGCPHYS is always 64 bits */
519 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
520 req->aPhysPage[j] = pPageDesc[j];
521
522 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
523 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
524 req->fInflate = true;
525
526 rc = VbglGRPerform(&req->header);
527 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
528 {
529 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev!"
530 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
531
532#ifndef TARGET_NT4
533 MmFreePagesFromMdl(pMdl);
534 ExFreePool(pMdl);
535#else
536 IoFreeMdl (pMdl);
537 ExFreePool(pvBalloon);
538#endif
539 goto end;
540 }
541 else
542 {
543#ifndef TARGET_NT4
544 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pMdl));
545#else
546 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pvBalloon));
547#endif
548 pDevExt->MemBalloon.paMdlMemBalloon[i] = pMdl;
549 pDevExt->MemBalloon.cBalloons++;
550 }
551 }
552 }
553 else
554 {
555 /* deflate */
556 for (uint32_t _i=pDevExt->MemBalloon.cBalloons;_i>u32BalloonSize;_i--)
557 {
558 uint32_t index = _i - 1;
559 PMDL pMdl = pDevExt->MemBalloon.paMdlMemBalloon[index];
560
561 Assert(pMdl);
562 if (pMdl)
563 {
564#ifdef TARGET_NT4
565 PVOID pvBalloon = MmGetMdlVirtualAddress(pMdl);
566#endif
567
568 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
569
570 /* Copy manually as RTGCPHYS is always 64 bits */
571 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
572 req->aPhysPage[j] = pPageDesc[j];
573
574 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
575 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
576 req->fInflate = false;
577
578 rc = VbglGRPerform(&req->header);
579 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
580 {
581 AssertMsgFailed(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev! rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
582 break;
583 }
584
585 /* Free the ballooned memory */
586#ifndef TARGET_NT4
587 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pMdl));
588 MmFreePagesFromMdl(pMdl);
589 ExFreePool(pMdl);
590#else
591 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pvBalloon));
592 MmUnlockPages (pMdl);
593 IoFreeMdl (pMdl);
594 ExFreePool(pvBalloon);
595#endif
596
597 pDevExt->MemBalloon.paMdlMemBalloon[index] = NULL;
598 pDevExt->MemBalloon.cBalloons--;
599 }
600 }
601 }
602 Assert(pDevExt->MemBalloon.cBalloons <= pDevExt->MemBalloon.cMaxBalloons);
603
604end:
605 VbglGRFree(&req->header);
606 return rc;
607}
608
609static int VBoxGuestQueryMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, ULONG *pMemBalloonSize)
610{
611 /* just perform the request */
612 VMMDevGetMemBalloonChangeRequest *req = NULL;
613
614 dprintf(("VBoxGuestQueryMemoryBalloon\n"));
615
616 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest);
617 vmmdevInitRequest(&req->header, VMMDevReq_GetMemBalloonChangeRequest);
618 req->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
619
620 if (VBOX_SUCCESS(rc))
621 {
622 rc = VbglGRPerform(&req->header);
623
624 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
625 {
626 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_CTL_CHECK_BALLOON: error issuing request to VMMDev!"
627 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
628 }
629 else
630 {
631 if (!pDevExt->MemBalloon.paMdlMemBalloon)
632 {
633 pDevExt->MemBalloon.cMaxBalloons = req->u32PhysMemSize;
634 pDevExt->MemBalloon.paMdlMemBalloon = (PMDL *)ExAllocatePool(PagedPool, req->u32PhysMemSize * sizeof(PMDL));
635 Assert(pDevExt->MemBalloon.paMdlMemBalloon);
636 if (!pDevExt->MemBalloon.paMdlMemBalloon)
637 return VERR_NO_MEMORY;
638 }
639 Assert(pDevExt->MemBalloon.cMaxBalloons == req->u32PhysMemSize);
640
641 rc = VBoxGuestSetBalloonSize(pDevExt, req->u32BalloonSize);
642 /* ignore out of memory failures */
643 if (rc == VERR_NO_MEMORY)
644 rc = VINF_SUCCESS;
645
646 if (pMemBalloonSize)
647 *pMemBalloonSize = pDevExt->MemBalloon.cBalloons;
648 }
649
650 VbglGRFree(&req->header);
651 }
652 return rc;
653}
654#endif
655
656void VBoxInitMemBalloon(PVBOXGUESTDEVEXT pDevExt)
657{
658#ifdef VBOX_WITH_MANAGEMENT
659 ULONG dummy;
660
661 pDevExt->MemBalloon.cBalloons = 0;
662 pDevExt->MemBalloon.cMaxBalloons = 0;
663 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
664
665 VBoxGuestQueryMemoryBalloon(pDevExt, &dummy);
666#endif
667}
668
669void VBoxCleanupMemBalloon(PVBOXGUESTDEVEXT pDevExt)
670{
671#ifdef VBOX_WITH_MANAGEMENT
672 if (pDevExt->MemBalloon.paMdlMemBalloon)
673 {
674 /* Clean up the memory balloon leftovers */
675 VBoxGuestSetBalloonSize(pDevExt, 0);
676 ExFreePool(pDevExt->MemBalloon.paMdlMemBalloon);
677 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
678 }
679 Assert(pDevExt->MemBalloon.cBalloons == 0);
680#endif
681}
682
683/**
684 * Device I/O Control entry point.
685 *
686 * @param pDevObj Device object.
687 * @param pIrp Request packet.
688 */
689NTSTATUS VBoxGuestDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
690{
691 dprintf(("VBoxGuest::VBoxGuestDeviceControl\n"));
692
693 NTSTATUS Status = STATUS_SUCCESS;
694
695 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
696
697 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
698
699 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* all requests are buffered. */
700
701 unsigned cbOut = 0;
702
703 switch (pStack->Parameters.DeviceIoControl.IoControlCode)
704 {
705 case VBOXGUEST_IOCTL_GETVMMDEVPORT:
706 {
707 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_GETVMMDEVPORT\n"));
708
709 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof (VBoxGuestPortInfo))
710 {
711 Status = STATUS_BUFFER_TOO_SMALL;
712 break;
713 }
714
715 VBoxGuestPortInfo *portInfo = (VBoxGuestPortInfo*)pBuf;
716
717 portInfo->portAddress = pDevExt->startPortAddress;
718 portInfo->pVMMDevMemory = pDevExt->pVMMDevMemory;
719
720 cbOut = sizeof(VBoxGuestPortInfo);
721
722 break;
723 }
724
725 case VBOXGUEST_IOCTL_WAITEVENT:
726 {
727 /* Need to be extended to support multiple waiters for an event,
728 * array of counters for each event, event mask is computed, each
729 * time a wait event is arrived.
730 */
731 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_WAITEVENT\n"));
732
733 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VBoxGuestWaitEventInfo))
734 {
735 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
736 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
737 Status = STATUS_BUFFER_TOO_SMALL;
738 break;
739 }
740
741 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestWaitEventInfo)) {
742 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
743 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
744 Status = STATUS_BUFFER_TOO_SMALL;
745 break;
746 }
747
748 VBoxGuestWaitEventInfo *eventInfo = (VBoxGuestWaitEventInfo *)pBuf;
749
750 if (!eventInfo->u32EventMaskIn || !IsPowerOfTwo (eventInfo->u32EventMaskIn)) {
751 dprintf (("VBoxGuest::VBoxGuestDeviceControl: Invalid input mask %#x\n",
752 eventInfo->u32EventMaskIn));
753 Status = STATUS_INVALID_PARAMETER;
754 break;
755 }
756
757 eventInfo->u32EventFlagsOut = 0;
758 int iBitOffset = ASMBitFirstSetU32 (eventInfo->u32EventMaskIn) - 1;
759 bool fTimeout = (eventInfo->u32TimeoutIn != ~0L);
760
761 dprintf (("mask = %d, iBitOffset = %d\n", iBitOffset, eventInfo->u32EventMaskIn));
762
763 /* Possible problem with request completion right between the pending event check and KeWaitForSingleObject
764 * call; introduce a timeout (if none was specified) to make sure we don't wait indefinitely.
765 */
766 LARGE_INTEGER timeout;
767 timeout.QuadPart = (fTimeout) ? eventInfo->u32TimeoutIn : 250;
768 timeout.QuadPart *= -10000;
769
770 NTSTATUS rc = STATUS_SUCCESS;
771
772 for (;;)
773 {
774 bool fEventPending = ASMAtomicBitTestAndClear(&pDevExt->u32Events, iBitOffset);
775 if (fEventPending)
776 {
777 eventInfo->u32EventFlagsOut = 1 << iBitOffset;
778 break;
779 }
780
781 rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive /** @todo UserRequest? */,
782 KernelMode, TRUE, &timeout);
783 dprintf(("VBOXGUEST_IOCTL_WAITEVENT: Wait returned %d -> event %x\n", rc, eventInfo->u32EventFlagsOut));
784
785 if (!fTimeout && rc == STATUS_TIMEOUT)
786 continue;
787
788 if (rc != STATUS_SUCCESS)
789 {
790 /* There was a timeout or wait was interrupted, etc. */
791 break;
792 }
793 }
794
795 dprintf (("u32EventFlagsOut = %#x\n", eventInfo->u32EventFlagsOut));
796 cbOut = sizeof(VBoxGuestWaitEventInfo);
797 break;
798 }
799
800 case VBOXGUEST_IOCTL_VMMREQUEST(0): /* (The size isn't relevant on NT.)*/
801 {
802 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_VMMREQUEST\n"));
803
804#define CHECK_SIZE(s) \
805 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < s) \
806 { \
807 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < %d\n", \
808 pStack->Parameters.DeviceIoControl.OutputBufferLength, s)); \
809 Status = STATUS_BUFFER_TOO_SMALL; \
810 break; \
811 } \
812 if (pStack->Parameters.DeviceIoControl.InputBufferLength < s) { \
813 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n", \
814 pStack->Parameters.DeviceIoControl.InputBufferLength, s)); \
815 Status = STATUS_BUFFER_TOO_SMALL; \
816 break; \
817 }
818
819 /* get the request header */
820 CHECK_SIZE(sizeof(VMMDevRequestHeader));
821 VMMDevRequestHeader *requestHeader = (VMMDevRequestHeader *)pBuf;
822 if (!vmmdevGetRequestSize(requestHeader->requestType))
823 {
824 Status = STATUS_INVALID_PARAMETER;
825 break;
826 }
827 /* make sure the buffers suit the request */
828 CHECK_SIZE(vmmdevGetRequestSize(requestHeader->requestType));
829
830 /* just perform the request */
831 VMMDevRequestHeader *req = NULL;
832
833 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, requestHeader->size, requestHeader->requestType);
834
835 if (VBOX_SUCCESS(rc))
836 {
837 /* copy the request information */
838 memcpy((void*)req, (void*)pBuf, requestHeader->size);
839 rc = VbglGRPerform(req);
840
841 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->rc))
842 {
843 dprintf(("VBoxGuest::VBoxGuestDeviceControl VBOXGUEST_IOCTL_VMMREQUEST: Error issuing request to VMMDev! "
844 "rc = %d, VMMDev rc = %Vrc\n", rc, req->rc));
845 Status = STATUS_UNSUCCESSFUL;
846 }
847 else
848 {
849 /* copy result */
850 memcpy((void*)pBuf, (void*)req, requestHeader->size);
851 cbOut = requestHeader->size;
852 }
853
854 VbglGRFree(req);
855 }
856 else
857 {
858 Status = STATUS_UNSUCCESSFUL;
859 }
860#undef CHECK_SIZE
861 break;
862 }
863
864 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
865 {
866 VBoxGuestFilterMaskInfo *maskInfo;
867
868 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestFilterMaskInfo)) {
869 dprintf (("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n",
870 pStack->Parameters.DeviceIoControl.InputBufferLength,
871 sizeof (VBoxGuestFilterMaskInfo)));
872 Status = STATUS_BUFFER_TOO_SMALL;
873 break;
874
875 }
876
877 maskInfo = (VBoxGuestFilterMaskInfo *) pBuf;
878 if (!CtlGuestFilterMask (maskInfo->u32OrMask, maskInfo->u32NotMask))
879 {
880 Status = STATUS_UNSUCCESSFUL;
881 }
882 break;
883 }
884
885#ifdef VBOX_WITH_HGCM
886 /* HGCM offers blocking IOCTLSs just like waitevent and actually
887 * uses the same waiting code.
888 */
889 case VBOXGUEST_IOCTL_HGCM_CONNECT:
890 {
891 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CONNECT\n"));
892
893 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMConnectInfo))
894 {
895 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
896 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
897 Status = STATUS_INVALID_PARAMETER;
898 break;
899 }
900
901 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMConnectInfo)) {
902 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
903 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
904 Status = STATUS_INVALID_PARAMETER;
905 break;
906 }
907
908 VBoxGuestHGCMConnectInfo *ptr = (VBoxGuestHGCMConnectInfo *)pBuf;
909
910 /* If request will be processed asynchronously, execution will
911 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
912 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
913 * flag is set, returns.
914 */
915
916 dprintf(("a) ptr->u32ClientID = %d\n", ptr->u32ClientID));
917
918 int rc = VbglHGCMConnect (ptr, VBoxHGCMCallback, pDevExt, 0);
919
920 dprintf(("b) ptr->u32ClientID = %d\n", ptr->u32ClientID));
921
922 if (VBOX_FAILURE(rc))
923 {
924 dprintf(("VBOXGUEST_IOCTL_HGCM_CONNECT: vbox rc = %Vrc\n", rc));
925 Status = STATUS_UNSUCCESSFUL;
926 }
927 else
928 {
929 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
930 }
931
932 } break;
933
934 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
935 {
936 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_DISCONNECT\n"));
937
938 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo))
939 {
940 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
941 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
942 Status = STATUS_INVALID_PARAMETER;
943 break;
944 }
945
946 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo)) {
947 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
948 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
949 Status = STATUS_INVALID_PARAMETER;
950 break;
951 }
952
953 VBoxGuestHGCMDisconnectInfo *ptr = (VBoxGuestHGCMDisconnectInfo *)pBuf;
954
955 /* If request will be processed asynchronously, execution will
956 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
957 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
958 * flag is set, returns.
959 */
960
961 int rc = VbglHGCMDisconnect (ptr, VBoxHGCMCallback, pDevExt, 0);
962
963 if (VBOX_FAILURE(rc))
964 {
965 dprintf(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: vbox rc = %Vrc\n", rc));
966 Status = STATUS_UNSUCCESSFUL;
967 }
968 else
969 {
970 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
971 }
972
973 } break;
974
975 case VBOXGUEST_IOCTL_HGCM_CALL(0): /* (The size isn't relevant on NT.) */
976 {
977 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_HGCM_CALL\n"));
978
979 Status = vboxHGCMVerifyIOBuffers (pStack,
980 sizeof (VBoxGuestHGCMCallInfo));
981
982 if (Status != STATUS_SUCCESS)
983 {
984 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
985 break;
986 }
987
988 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
989
990 int rc = VbglHGCMCall (ptr, VBoxHGCMCallback, pDevExt, 0);
991
992 if (VBOX_FAILURE(rc))
993 {
994 dprintf(("VBOXGUEST_IOCTL_HGCM_CALL: vbox rc = %Vrc\n", rc));
995 Status = STATUS_UNSUCCESSFUL;
996 }
997 else
998 {
999 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1000 }
1001
1002 } break;
1003#endif /* VBOX_WITH_HGCM */
1004
1005#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
1006 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
1007 {
1008 LogRel(("VRDP_SESSION: Enable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1009 if (!pDevExt->fVRDPEnabled)
1010 {
1011 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1012
1013 pDevExt->fVRDPEnabled = TRUE;
1014 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1015 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
1016 pSharedUserData->ActiveConsoleId = 2;
1017 }
1018 break;
1019 }
1020
1021 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
1022 {
1023 LogRel(("VRDP_SESSION: Disable. Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis"));
1024 if (pDevExt->fVRDPEnabled)
1025 {
1026 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1027
1028 pDevExt->fVRDPEnabled = FALSE;
1029 LogRel(("VRDP_SESSION: Current active console id: 0x%08X\n", pSharedUserData->ActiveConsoleId));
1030 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
1031 pDevExt->ulOldActiveConsoleId = 0;
1032 }
1033 break;
1034 }
1035#endif
1036
1037#ifdef VBOX_WITH_MANAGEMENT
1038 case VBOXGUEST_IOCTL_CTL_CHECK_BALLOON_MASK:
1039 {
1040 ULONG *pMemBalloonSize = (ULONG *) pBuf;
1041
1042 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG))
1043 {
1044 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(ULONG) %d\n",
1045 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(ULONG)));
1046 Status = STATUS_INVALID_PARAMETER;
1047 break;
1048 }
1049
1050 int rc = VBoxGuestQueryMemoryBalloon(pDevExt, pMemBalloonSize);
1051 if (VBOX_FAILURE(rc))
1052 {
1053 dprintf(("VBOXGUEST_IOCTL_CTL_CHECK_BALLOON: vbox rc = %Vrc\n", rc));
1054 Status = STATUS_UNSUCCESSFUL;
1055 }
1056 else
1057 {
1058 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1059 }
1060 break;
1061 }
1062#endif
1063
1064 case VBOXGUEST_IOCTL_LOG(0): /* The size isn't relevant on NT. */
1065 {
1066 /* Enable this only for debugging:
1067 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_LOG %.*s\n", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1068 */
1069 LogRel(("%.*s", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1070 cbOut = 0;
1071 break;
1072 }
1073
1074 default:
1075 Status = STATUS_INVALID_PARAMETER;
1076 break;
1077 }
1078
1079 pIrp->IoStatus.Status = Status;
1080 pIrp->IoStatus.Information = cbOut;
1081
1082 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1083
1084 dprintf(("VBoxGuest::VBoxGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
1085
1086 return Status;
1087}
1088
1089
1090/**
1091 * IRP_MJ_SYSTEM_CONTROL handler
1092 *
1093 * @returns NT status code
1094 * @param pDevObj Device object.
1095 * @param pIrp IRP.
1096 */
1097NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1098{
1099 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1100
1101 dprintf(("VBoxGuest::VBoxGuestSystemControl\n"));
1102
1103 /* Always pass it on to the next driver. */
1104 IoSkipCurrentIrpStackLocation(pIrp);
1105
1106 return IoCallDriver(pDevExt->nextLowerDriver, pIrp);
1107}
1108
1109/**
1110 * IRP_MJ_SHUTDOWN handler
1111 *
1112 * @returns NT status code
1113 * @param pDevObj Device object.
1114 * @param pIrp IRP.
1115 */
1116NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1117{
1118 VMMDevPowerStateRequest *req = NULL;
1119
1120 dprintf(("VBoxGuest::VBoxGuestShutdown\n"));
1121
1122 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
1123
1124 if (VBOX_SUCCESS(rc))
1125 {
1126 req->powerState = VMMDevPowerState_PowerOff;
1127
1128 rc = VbglGRPerform (&req->header);
1129
1130 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1131 {
1132 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1133 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1134 }
1135
1136 VbglGRFree (&req->header);
1137 }
1138
1139 return STATUS_SUCCESS;
1140}
1141
1142/**
1143 * Stub function for functions we don't implemented.
1144 *
1145 * @returns STATUS_NOT_SUPPORTED
1146 * @param pDevObj Device object.
1147 * @param pIrp IRP.
1148 */
1149NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1150{
1151 dprintf(("VBoxGuest::VBoxGuestNotSupportedStub\n"));
1152 pDevObj = pDevObj;
1153
1154 pIrp->IoStatus.Information = 0;
1155 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1156 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1157
1158 return STATUS_NOT_SUPPORTED;
1159}
1160
1161/**
1162 * DPC handler
1163 *
1164 * @param dpc DPC descriptor.
1165 * @param pDevObj Device object.
1166 * @param irp Interrupt request packet.
1167 * @param context Context specific pointer.
1168 */
1169VOID VBoxGuestDpcHandler(PKDPC dpc, PDEVICE_OBJECT pDevObj,
1170 PIRP irp, PVOID context)
1171{
1172 /* Unblock handlers waiting for arrived events.
1173 *
1174 * Events are very low things, there is one event flag (1 or more bit)
1175 * for each event. Each event is processed by exactly one handler.
1176 *
1177 * Assume that we trust additions and that other drivers will
1178 * handle its respective events without trying to fetch all events.
1179 *
1180 * Anyway design assures that wrong event processing will affect only guest.
1181 *
1182 * Event handler calls VMMDev IOCTL for waiting an event.
1183 * It supplies event mask. IOCTL blocks on EventNotification.
1184 * Here we just signal an the EventNotification to all waiting
1185 * threads, the IOCTL handler analyzes events and either
1186 * return to caller or blocks again.
1187 *
1188 * If we do not have too many events this is a simple and good
1189 * approach. Other way is to have as many Event objects as the callers
1190 * and wake up only callers waiting for the specific event.
1191 *
1192 * Now with the 'wake up all' appoach we probably do not need the DPC
1193 * handler and can signal event directly from ISR.
1194 *
1195 */
1196
1197 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1198
1199 dprintf(("VBoxGuest::VBoxGuestDpcHandler\n"));
1200
1201 KePulseEvent(&pDevExt->keventNotification, 0, FALSE);
1202
1203}
1204
1205/**
1206 * ISR handler
1207 *
1208 * @return BOOLEAN indicates whether the IRQ came from us (TRUE) or not (FALSE)
1209 * @param interrupt Interrupt that was triggered.
1210 * @param serviceContext Context specific pointer.
1211 */
1212BOOLEAN VBoxGuestIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext)
1213{
1214 NTSTATUS rc;
1215 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)serviceContext;
1216 BOOLEAN fIRQTaken = FALSE;
1217
1218 dprintf(("VBoxGuest::VBoxGuestIsrHandler haveEvents = %d\n",
1219 pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents));
1220
1221 /*
1222 * now we have to find out whether it was our IRQ. Read the event mask
1223 * from our device to see if there are any pending events
1224 */
1225 if (pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents)
1226 {
1227 /* Acknowlegde events. */
1228 VMMDevEvents *req = pDevExt->irqAckEvents;
1229
1230 rc = VbglGRPerform (&req->header);
1231 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1232 {
1233 dprintf(("VBoxGuest::VBoxGuestIsrHandler: acknowledge events succeeded %#x\n",
1234 req->events));
1235
1236 ASMAtomicOrU32((uint32_t *)&pDevExt->u32Events, req->events);
1237 IoRequestDpc(pDevExt->deviceObject, pDevExt->currentIrp, NULL);
1238 }
1239 else
1240 {
1241 /* This can't be actually. This is sign of a serious problem. */
1242 dprintf(("VBoxGuest::VBoxGuestIsrHandler: "
1243 "acknowledge events failed rc = %d, header rc = %d\n",
1244 rc, req->header.rc));
1245 }
1246
1247 /* Mark IRQ as taken, there were events for us. */
1248 fIRQTaken = TRUE;
1249 }
1250
1251 return fIRQTaken;
1252}
1253
1254/**
1255 * Worker thread to do periodic things such as synchronize the
1256 * system time and notify other drivers of events.
1257 *
1258 * @param pDevExt device extension pointer
1259 */
1260VOID vboxWorkerThread(PVOID context)
1261{
1262 PVBOXGUESTDEVEXT pDevExt;
1263
1264 pDevExt = (PVBOXGUESTDEVEXT)context;
1265 dprintf(("VBoxGuest::vboxWorkerThread entered\n"));
1266
1267 VMMDevReqHostTime *req = NULL;
1268
1269 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHostTime), VMMDevReq_GetHostTime);
1270
1271 if (VBOX_FAILURE(rc))
1272 {
1273 dprintf(("VBoxGuest::vboxWorkerThread: could not allocate request buffer, exiting rc = %d!\n", rc));
1274 return;
1275 }
1276
1277 /* perform the hypervisor address space reservation */
1278 reserveHypervisorMemory(pDevExt);
1279
1280 do
1281 {
1282 /*
1283 * Do the time sync
1284 */
1285 {
1286 LARGE_INTEGER systemTime;
1287 #define TICKSPERSEC 10000000
1288 #define TICKSPERMSEC 10000
1289 #define SECSPERDAY 86400
1290 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (uint64_t)SECSPERDAY)
1291 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
1292
1293
1294 req->header.rc = VERR_GENERAL_FAILURE;
1295
1296 rc = VbglGRPerform (&req->header);
1297
1298 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1299 {
1300 uint64_t hostTime = req->time;
1301
1302 // Windows was originally designed in 1601...
1303 systemTime.QuadPart = hostTime * (uint64_t)TICKSPERMSEC + (uint64_t)TICKS_1601_TO_1970;
1304 dprintf(("VBoxGuest::vboxWorkerThread: synching time with host time (msec/UTC): %llu\n", hostTime));
1305 ZwSetSystemTime(&systemTime, NULL);
1306 }
1307 else
1308 {
1309 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1310 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1311 }
1312 }
1313
1314 /*
1315 * Go asleep unless we're supposed to terminate
1316 */
1317 if (!pDevExt->stopThread)
1318 {
1319 ULONG secWait = 60;
1320 dprintf(("VBoxGuest::vboxWorkerThread: waiting for %u seconds...\n", secWait));
1321 LARGE_INTEGER dueTime;
1322 dueTime.QuadPart = -10000 * 1000 * (int)secWait;
1323 if (KeWaitForSingleObject(&pDevExt->workerThreadRequest, Executive,
1324 KernelMode, FALSE, &dueTime) == STATUS_SUCCESS)
1325 {
1326 KeResetEvent(&pDevExt->workerThreadRequest);
1327 }
1328 }
1329 } while (!pDevExt->stopThread);
1330
1331 dprintf(("VBoxGuest::vboxWorkerThread: we've been asked to terminate!\n"));
1332
1333 /* free our request buffer */
1334 VbglGRFree (&req->header);
1335
1336 if (pDevExt->workerThread)
1337 {
1338 ObDereferenceObject(pDevExt->workerThread);
1339 pDevExt->workerThread = NULL;
1340 }
1341 dprintf(("VBoxGuest::vboxWorkerThread: now really gone!\n"));
1342}
1343
1344/**
1345 * Create driver worker threads
1346 *
1347 * @returns NTSTATUS NT status code
1348 * @param pDevExt VBoxGuest device extension
1349 */
1350NTSTATUS createThreads(PVBOXGUESTDEVEXT pDevExt)
1351{
1352 NTSTATUS rc;
1353 HANDLE threadHandle;
1354 OBJECT_ATTRIBUTES objAttributes;
1355
1356 dprintf(("VBoxGuest::createThreads\n"));
1357
1358 // first setup the request semaphore
1359 KeInitializeEvent(&pDevExt->workerThreadRequest, SynchronizationEvent, FALSE);
1360
1361// the API has slightly changed after NT4
1362#ifdef TARGET_NT4
1363#ifdef OBJ_KERNEL_HANDLE
1364#undef OBJ_KERNEL_HANDLE
1365#endif
1366#define OBJ_KERNEL_HANDLE 0
1367#endif
1368
1369 /*
1370 * The worker thread
1371 */
1372 InitializeObjectAttributes(&objAttributes,
1373 NULL,
1374 OBJ_KERNEL_HANDLE,
1375 NULL,
1376 NULL);
1377
1378 rc = PsCreateSystemThread(&threadHandle,
1379 THREAD_ALL_ACCESS,
1380 &objAttributes,
1381 (HANDLE)0L,
1382 NULL,
1383 vboxWorkerThread,
1384 pDevExt);
1385 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for worker thread returned: 0x%x\n", rc));
1386 rc = ObReferenceObjectByHandle(threadHandle,
1387 THREAD_ALL_ACCESS,
1388 NULL,
1389 KernelMode,
1390 (PVOID*)&pDevExt->workerThread,
1391 NULL);
1392 ZwClose(threadHandle);
1393
1394 /*
1395 * The idle thread
1396 */
1397#if 0 /// @todo Windows "sees" that time is lost and reports 100% usage
1398 rc = PsCreateSystemThread(&threadHandle,
1399 THREAD_ALL_ACCESS,
1400 &objAttributes,
1401 (HANDLE)0L,
1402 NULL,
1403 vboxIdleThread,
1404 pDevExt);
1405 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for idle thread returned: 0x%x\n", rc));
1406 rc = ObReferenceObjectByHandle(threadHandle,
1407 THREAD_ALL_ACCESS,
1408 NULL,
1409 KernelMode,
1410 (PVOID*)&pDevExt->idleThread,
1411 NULL);
1412 ZwClose(threadHandle);
1413#endif
1414
1415 return rc;
1416}
1417
1418/**
1419 * Helper routine to reserve address space for the hypervisor
1420 * and communicate its position.
1421 *
1422 * @param pDevExt Device extension structure.
1423 */
1424VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1425{
1426 // @todo rc handling
1427 uint32_t hypervisorSize;
1428
1429 VMMDevReqHypervisorInfo *req = NULL;
1430
1431 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
1432
1433 if (VBOX_SUCCESS(rc))
1434 {
1435 req->hypervisorStart = 0;
1436 req->hypervisorSize = 0;
1437
1438 rc = VbglGRPerform (&req->header);
1439
1440 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1441 {
1442 hypervisorSize = req->hypervisorSize;
1443
1444 if (!hypervisorSize)
1445 {
1446 dprintf(("VBoxGuest::reserveHypervisorMemory: host returned 0, not doing anything\n"));
1447 return;
1448 }
1449
1450 dprintf(("VBoxGuest::reserveHypervisorMemory: host wants %u bytes of hypervisor address space\n", hypervisorSize));
1451
1452 // Map fictive physical memory into the kernel address space to reserve virtual
1453 // address space. This API does not perform any checks but just allocate the
1454 // PTEs (which we don't really need/want but there isn't any other clean method).
1455 // The hypervisor only likes 4MB aligned virtual addresses, so we have to allocate
1456 // 4MB more than we are actually supposed to in order to guarantee that. Maybe we
1457 // can come up with a less lavish algorithm lateron.
1458 PHYSICAL_ADDRESS physAddr;
1459 physAddr.QuadPart = HYPERVISOR_PHYSICAL_START;
1460 pDevExt->hypervisorMappingSize = hypervisorSize + 0x400000;
1461 pDevExt->hypervisorMapping = MmMapIoSpace(physAddr,
1462 pDevExt->hypervisorMappingSize,
1463 MmNonCached);
1464 if (!pDevExt->hypervisorMapping)
1465 {
1466 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned NULL!\n"));
1467 return;
1468 }
1469
1470 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned %p\n", pDevExt->hypervisorMapping));
1471 dprintf(("VBoxGuest::reserveHypervisorMemory: communicating %p to host\n",
1472 RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000)));
1473
1474 /* align at 4MB */
1475 req->hypervisorStart = (RTGCPTR)RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000);
1476
1477 req->header.requestType = VMMDevReq_SetHypervisorInfo;
1478 req->header.rc = VERR_GENERAL_FAILURE;
1479
1480 /* issue request */
1481 rc = VbglGRPerform (&req->header);
1482
1483 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1484 {
1485 dprintf(("VBoxGuest::reserveHypervisorMemory: error communicating physical address to VMMDev!"
1486 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1487 }
1488 }
1489 else
1490 {
1491 dprintf(("VBoxGuest::reserveHypervisorMemory: request failed with rc %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1492 }
1493 VbglGRFree (&req->header);
1494 }
1495
1496 return;
1497}
1498
1499/**
1500 * Helper function to unregister a virtual address space mapping
1501 *
1502 * @param pDevExt Device extension
1503 */
1504VOID unreserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1505{
1506 VMMDevReqHypervisorInfo *req = NULL;
1507
1508 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
1509
1510 if (VBOX_SUCCESS(rc))
1511 {
1512 /* tell the hypervisor that the mapping is no longer available */
1513
1514 req->hypervisorStart = 0;
1515 req->hypervisorSize = 0;
1516
1517 rc = VbglGRPerform (&req->header);
1518
1519 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1520 {
1521 dprintf(("VBoxGuest::unreserveHypervisorMemory: error communicating physical address to VMMDev!"
1522 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1523 }
1524
1525 VbglGRFree (&req->header);
1526 }
1527
1528 if (!pDevExt->hypervisorMapping)
1529 {
1530 dprintf(("VBoxGuest::unreserveHypervisorMemory: there is no mapping, returning\n"));
1531 return;
1532 }
1533
1534 // unmap fictive IO space
1535 MmUnmapIoSpace(pDevExt->hypervisorMapping, pDevExt->hypervisorMappingSize);
1536 dprintf(("VBoxGuest::unreserveHypervisorMemmory: done\n"));
1537}
1538
1539/**
1540 * Idle thread that runs at the lowest priority possible
1541 * and whenever scheduled, makes a VMMDev call to give up
1542 * timeslices. This is so prevent Windows from thinking that
1543 * nothing is happening on the machine and doing stupid things
1544 * that would steal time from other VMs it doesn't know of.
1545 *
1546 * @param pDevExt device extension pointer
1547 */
1548VOID vboxIdleThread(PVOID context)
1549{
1550 PVBOXGUESTDEVEXT pDevExt;
1551
1552 pDevExt = (PVBOXGUESTDEVEXT)context;
1553 dprintf(("VBoxGuest::vboxIdleThread entered\n"));
1554
1555 /* set priority as low as possible */
1556 KeSetPriorityThread(KeGetCurrentThread(), LOW_PRIORITY);
1557
1558 /* allocate VMMDev request structure */
1559 VMMDevReqIdle *req;
1560 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_Idle);
1561 if (VBOX_FAILURE(rc))
1562 {
1563 dprintf(("VBoxGuest::vboxIdleThread: error %Vrc allocating request structure!\n"));
1564 return;
1565 }
1566
1567 do
1568 {
1569 //dprintf(("VBoxGuest: performing idle request..\n"));
1570 /* perform idle request */
1571 VbglGRPerform(&req->header);
1572
1573 } while (!pDevExt->stopThread);
1574
1575 VbglGRFree(&req->header);
1576
1577 dprintf(("VBoxGuest::vboxIdleThread leaving\n"));
1578}
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