VirtualBox

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

Last change on this file since 11698 was 10636, checked in by vboxsync, 17 years ago

build fix and log adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.6 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_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_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_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_HGCM */
1004
1005#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
1006 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
1007 {
1008 if (!pDevExt->fVRDPEnabled)
1009 {
1010 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1011
1012 pDevExt->fVRDPEnabled = TRUE;
1013 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
1014 pSharedUserData->ActiveConsoleId = 2;
1015 }
1016 break;
1017 }
1018
1019 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
1020 {
1021 if (pDevExt->fVRDPEnabled)
1022 {
1023 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1024
1025 pDevExt->fVRDPEnabled = FALSE;
1026 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
1027 pDevExt->ulOldActiveConsoleId = 0;
1028 }
1029 break;
1030 }
1031#endif
1032
1033#ifdef VBOX_WITH_MANAGEMENT
1034 case VBOXGUEST_IOCTL_CTL_CHECK_BALLOON_MASK:
1035 {
1036 ULONG *pMemBalloonSize = (ULONG *) pBuf;
1037
1038 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG))
1039 {
1040 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(ULONG) %d\n",
1041 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(ULONG)));
1042 Status = STATUS_INVALID_PARAMETER;
1043 break;
1044 }
1045
1046 int rc = VBoxGuestQueryMemoryBalloon(pDevExt, pMemBalloonSize);
1047 if (VBOX_FAILURE(rc))
1048 {
1049 dprintf(("VBOXGUEST_IOCTL_CTL_CHECK_BALLOON: vbox rc = %Vrc\n", rc));
1050 Status = STATUS_UNSUCCESSFUL;
1051 }
1052 else
1053 {
1054 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1055 }
1056 break;
1057 }
1058#endif
1059
1060 case VBOXGUEST_IOCTL_LOG(0): /* The size isn't relevant on NT. */
1061 {
1062 /* Enable this only for debugging:
1063 dprintf(("VBoxGuest::VBoxGuestDeviceControl: VBOXGUEST_IOCTL_LOG %.*s\n", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1064 */
1065 LogRel(("%.*s", (int)pStack->Parameters.DeviceIoControl.InputBufferLength, pBuf));
1066 cbOut = 0;
1067 break;
1068 }
1069
1070 default:
1071 Status = STATUS_INVALID_PARAMETER;
1072 break;
1073 }
1074
1075 pIrp->IoStatus.Status = Status;
1076 pIrp->IoStatus.Information = cbOut;
1077
1078 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1079
1080 dprintf(("VBoxGuest::VBoxGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
1081
1082 return Status;
1083}
1084
1085
1086/**
1087 * IRP_MJ_SYSTEM_CONTROL handler
1088 *
1089 * @returns NT status code
1090 * @param pDevObj Device object.
1091 * @param pIrp IRP.
1092 */
1093NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1094{
1095 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1096
1097 dprintf(("VBoxGuest::VBoxGuestSystemControl\n"));
1098
1099 /* Always pass it on to the next driver. */
1100 IoSkipCurrentIrpStackLocation(pIrp);
1101
1102 return IoCallDriver(pDevExt->nextLowerDriver, pIrp);
1103}
1104
1105/**
1106 * IRP_MJ_SHUTDOWN handler
1107 *
1108 * @returns NT status code
1109 * @param pDevObj Device object.
1110 * @param pIrp IRP.
1111 */
1112NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1113{
1114 VMMDevPowerStateRequest *req = NULL;
1115
1116 dprintf(("VBoxGuest::VBoxGuestShutdown\n"));
1117
1118 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
1119
1120 if (VBOX_SUCCESS(rc))
1121 {
1122 req->powerState = VMMDevPowerState_PowerOff;
1123
1124 rc = VbglGRPerform (&req->header);
1125
1126 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1127 {
1128 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1129 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1130 }
1131
1132 VbglGRFree (&req->header);
1133 }
1134
1135 return STATUS_SUCCESS;
1136}
1137
1138/**
1139 * Stub function for functions we don't implemented.
1140 *
1141 * @returns STATUS_NOT_SUPPORTED
1142 * @param pDevObj Device object.
1143 * @param pIrp IRP.
1144 */
1145NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1146{
1147 dprintf(("VBoxGuest::VBoxGuestNotSupportedStub\n"));
1148 pDevObj = pDevObj;
1149
1150 pIrp->IoStatus.Information = 0;
1151 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1152 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1153
1154 return STATUS_NOT_SUPPORTED;
1155}
1156
1157/**
1158 * DPC handler
1159 *
1160 * @param dpc DPC descriptor.
1161 * @param pDevObj Device object.
1162 * @param irp Interrupt request packet.
1163 * @param context Context specific pointer.
1164 */
1165VOID VBoxGuestDpcHandler(PKDPC dpc, PDEVICE_OBJECT pDevObj,
1166 PIRP irp, PVOID context)
1167{
1168 /* Unblock handlers waiting for arrived events.
1169 *
1170 * Events are very low things, there is one event flag (1 or more bit)
1171 * for each event. Each event is processed by exactly one handler.
1172 *
1173 * Assume that we trust additions and that other drivers will
1174 * handle its respective events without trying to fetch all events.
1175 *
1176 * Anyway design assures that wrong event processing will affect only guest.
1177 *
1178 * Event handler calls VMMDev IOCTL for waiting an event.
1179 * It supplies event mask. IOCTL blocks on EventNotification.
1180 * Here we just signal an the EventNotification to all waiting
1181 * threads, the IOCTL handler analyzes events and either
1182 * return to caller or blocks again.
1183 *
1184 * If we do not have too many events this is a simple and good
1185 * approach. Other way is to have as many Event objects as the callers
1186 * and wake up only callers waiting for the specific event.
1187 *
1188 * Now with the 'wake up all' appoach we probably do not need the DPC
1189 * handler and can signal event directly from ISR.
1190 *
1191 */
1192
1193 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1194
1195 dprintf(("VBoxGuest::VBoxGuestDpcHandler\n"));
1196
1197 KePulseEvent(&pDevExt->keventNotification, 0, FALSE);
1198
1199}
1200
1201/**
1202 * ISR handler
1203 *
1204 * @return BOOLEAN indicates whether the IRQ came from us (TRUE) or not (FALSE)
1205 * @param interrupt Interrupt that was triggered.
1206 * @param serviceContext Context specific pointer.
1207 */
1208BOOLEAN VBoxGuestIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext)
1209{
1210 NTSTATUS rc;
1211 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)serviceContext;
1212 BOOLEAN fIRQTaken = FALSE;
1213
1214 dprintf(("VBoxGuest::VBoxGuestIsrHandler haveEvents = %d\n",
1215 pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents));
1216
1217 /*
1218 * now we have to find out whether it was our IRQ. Read the event mask
1219 * from our device to see if there are any pending events
1220 */
1221 if (pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents)
1222 {
1223 /* Acknowlegde events. */
1224 VMMDevEvents *req = pDevExt->irqAckEvents;
1225
1226 rc = VbglGRPerform (&req->header);
1227 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1228 {
1229 dprintf(("VBoxGuest::VBoxGuestIsrHandler: acknowledge events succeeded %#x\n",
1230 req->events));
1231
1232 ASMAtomicOrU32((uint32_t *)&pDevExt->u32Events, req->events);
1233 IoRequestDpc(pDevExt->deviceObject, pDevExt->currentIrp, NULL);
1234 }
1235 else
1236 {
1237 /* This can't be actually. This is sign of a serious problem. */
1238 dprintf(("VBoxGuest::VBoxGuestIsrHandler: "
1239 "acknowledge events failed rc = %d, header rc = %d\n",
1240 rc, req->header.rc));
1241 }
1242
1243 /* Mark IRQ as taken, there were events for us. */
1244 fIRQTaken = TRUE;
1245 }
1246
1247 return fIRQTaken;
1248}
1249
1250/**
1251 * Worker thread to do periodic things such as synchronize the
1252 * system time and notify other drivers of events.
1253 *
1254 * @param pDevExt device extension pointer
1255 */
1256VOID vboxWorkerThread(PVOID context)
1257{
1258 PVBOXGUESTDEVEXT pDevExt;
1259
1260 pDevExt = (PVBOXGUESTDEVEXT)context;
1261 dprintf(("VBoxGuest::vboxWorkerThread entered\n"));
1262
1263 VMMDevReqHostTime *req = NULL;
1264
1265 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHostTime), VMMDevReq_GetHostTime);
1266
1267 if (VBOX_FAILURE(rc))
1268 {
1269 dprintf(("VBoxGuest::vboxWorkerThread: could not allocate request buffer, exiting rc = %d!\n", rc));
1270 return;
1271 }
1272
1273 /* perform the hypervisor address space reservation */
1274 reserveHypervisorMemory(pDevExt);
1275
1276 do
1277 {
1278 /*
1279 * Do the time sync
1280 */
1281 {
1282 LARGE_INTEGER systemTime;
1283 #define TICKSPERSEC 10000000
1284 #define TICKSPERMSEC 10000
1285 #define SECSPERDAY 86400
1286 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (uint64_t)SECSPERDAY)
1287 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
1288
1289
1290 req->header.rc = VERR_GENERAL_FAILURE;
1291
1292 rc = VbglGRPerform (&req->header);
1293
1294 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1295 {
1296 uint64_t hostTime = req->time;
1297
1298 // Windows was originally designed in 1601...
1299 systemTime.QuadPart = hostTime * (uint64_t)TICKSPERMSEC + (uint64_t)TICKS_1601_TO_1970;
1300 dprintf(("VBoxGuest::vboxWorkerThread: synching time with host time (msec/UTC): %llu\n", hostTime));
1301 ZwSetSystemTime(&systemTime, NULL);
1302 }
1303 else
1304 {
1305 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1306 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1307 }
1308 }
1309
1310 /*
1311 * Go asleep unless we're supposed to terminate
1312 */
1313 if (!pDevExt->stopThread)
1314 {
1315 ULONG secWait = 60;
1316 dprintf(("VBoxGuest::vboxWorkerThread: waiting for %u seconds...\n", secWait));
1317 LARGE_INTEGER dueTime;
1318 dueTime.QuadPart = -10000 * 1000 * (int)secWait;
1319 if (KeWaitForSingleObject(&pDevExt->workerThreadRequest, Executive,
1320 KernelMode, FALSE, &dueTime) == STATUS_SUCCESS)
1321 {
1322 KeResetEvent(&pDevExt->workerThreadRequest);
1323 }
1324 }
1325 } while (!pDevExt->stopThread);
1326
1327 dprintf(("VBoxGuest::vboxWorkerThread: we've been asked to terminate!\n"));
1328
1329 /* free our request buffer */
1330 VbglGRFree (&req->header);
1331
1332 if (pDevExt->workerThread)
1333 {
1334 ObDereferenceObject(pDevExt->workerThread);
1335 pDevExt->workerThread = NULL;
1336 }
1337 dprintf(("VBoxGuest::vboxWorkerThread: now really gone!\n"));
1338}
1339
1340/**
1341 * Create driver worker threads
1342 *
1343 * @returns NTSTATUS NT status code
1344 * @param pDevExt VBoxGuest device extension
1345 */
1346NTSTATUS createThreads(PVBOXGUESTDEVEXT pDevExt)
1347{
1348 NTSTATUS rc;
1349 HANDLE threadHandle;
1350 OBJECT_ATTRIBUTES objAttributes;
1351
1352 dprintf(("VBoxGuest::createThreads\n"));
1353
1354 // first setup the request semaphore
1355 KeInitializeEvent(&pDevExt->workerThreadRequest, SynchronizationEvent, FALSE);
1356
1357// the API has slightly changed after NT4
1358#ifdef TARGET_NT4
1359#ifdef OBJ_KERNEL_HANDLE
1360#undef OBJ_KERNEL_HANDLE
1361#endif
1362#define OBJ_KERNEL_HANDLE 0
1363#endif
1364
1365 /*
1366 * The worker thread
1367 */
1368 InitializeObjectAttributes(&objAttributes,
1369 NULL,
1370 OBJ_KERNEL_HANDLE,
1371 NULL,
1372 NULL);
1373
1374 rc = PsCreateSystemThread(&threadHandle,
1375 THREAD_ALL_ACCESS,
1376 &objAttributes,
1377 (HANDLE)0L,
1378 NULL,
1379 vboxWorkerThread,
1380 pDevExt);
1381 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for worker thread returned: 0x%x\n", rc));
1382 rc = ObReferenceObjectByHandle(threadHandle,
1383 THREAD_ALL_ACCESS,
1384 NULL,
1385 KernelMode,
1386 (PVOID*)&pDevExt->workerThread,
1387 NULL);
1388 ZwClose(threadHandle);
1389
1390 /*
1391 * The idle thread
1392 */
1393#if 0 /// @todo Windows "sees" that time is lost and reports 100% usage
1394 rc = PsCreateSystemThread(&threadHandle,
1395 THREAD_ALL_ACCESS,
1396 &objAttributes,
1397 (HANDLE)0L,
1398 NULL,
1399 vboxIdleThread,
1400 pDevExt);
1401 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for idle thread returned: 0x%x\n", rc));
1402 rc = ObReferenceObjectByHandle(threadHandle,
1403 THREAD_ALL_ACCESS,
1404 NULL,
1405 KernelMode,
1406 (PVOID*)&pDevExt->idleThread,
1407 NULL);
1408 ZwClose(threadHandle);
1409#endif
1410
1411 return rc;
1412}
1413
1414/**
1415 * Helper routine to reserve address space for the hypervisor
1416 * and communicate its position.
1417 *
1418 * @param pDevExt Device extension structure.
1419 */
1420VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1421{
1422 // @todo rc handling
1423 uint32_t hypervisorSize;
1424
1425 VMMDevReqHypervisorInfo *req = NULL;
1426
1427 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
1428
1429 if (VBOX_SUCCESS(rc))
1430 {
1431 req->hypervisorStart = 0;
1432 req->hypervisorSize = 0;
1433
1434 rc = VbglGRPerform (&req->header);
1435
1436 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1437 {
1438 hypervisorSize = req->hypervisorSize;
1439
1440 if (!hypervisorSize)
1441 {
1442 dprintf(("VBoxGuest::reserveHypervisorMemory: host returned 0, not doing anything\n"));
1443 return;
1444 }
1445
1446 dprintf(("VBoxGuest::reserveHypervisorMemory: host wants %u bytes of hypervisor address space\n", hypervisorSize));
1447
1448 // Map fictive physical memory into the kernel address space to reserve virtual
1449 // address space. This API does not perform any checks but just allocate the
1450 // PTEs (which we don't really need/want but there isn't any other clean method).
1451 // The hypervisor only likes 4MB aligned virtual addresses, so we have to allocate
1452 // 4MB more than we are actually supposed to in order to guarantee that. Maybe we
1453 // can come up with a less lavish algorithm lateron.
1454 PHYSICAL_ADDRESS physAddr;
1455 physAddr.QuadPart = HYPERVISOR_PHYSICAL_START;
1456 pDevExt->hypervisorMappingSize = hypervisorSize + 0x400000;
1457 pDevExt->hypervisorMapping = MmMapIoSpace(physAddr,
1458 pDevExt->hypervisorMappingSize,
1459 MmNonCached);
1460 if (!pDevExt->hypervisorMapping)
1461 {
1462 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned NULL!\n"));
1463 return;
1464 }
1465
1466 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned %p\n", pDevExt->hypervisorMapping));
1467 dprintf(("VBoxGuest::reserveHypervisorMemory: communicating %p to host\n",
1468 RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000)));
1469
1470 /* align at 4MB */
1471 req->hypervisorStart = (RTGCPTR)RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000);
1472
1473 req->header.requestType = VMMDevReq_SetHypervisorInfo;
1474 req->header.rc = VERR_GENERAL_FAILURE;
1475
1476 /* issue request */
1477 rc = VbglGRPerform (&req->header);
1478
1479 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1480 {
1481 dprintf(("VBoxGuest::reserveHypervisorMemory: error communicating physical address to VMMDev!"
1482 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1483 }
1484 }
1485 else
1486 {
1487 dprintf(("VBoxGuest::reserveHypervisorMemory: request failed with rc %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1488 }
1489 VbglGRFree (&req->header);
1490 }
1491
1492 return;
1493}
1494
1495/**
1496 * Helper function to unregister a virtual address space mapping
1497 *
1498 * @param pDevExt Device extension
1499 */
1500VOID unreserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1501{
1502 VMMDevReqHypervisorInfo *req = NULL;
1503
1504 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
1505
1506 if (VBOX_SUCCESS(rc))
1507 {
1508 /* tell the hypervisor that the mapping is no longer available */
1509
1510 req->hypervisorStart = 0;
1511 req->hypervisorSize = 0;
1512
1513 rc = VbglGRPerform (&req->header);
1514
1515 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1516 {
1517 dprintf(("VBoxGuest::unreserveHypervisorMemory: error communicating physical address to VMMDev!"
1518 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1519 }
1520
1521 VbglGRFree (&req->header);
1522 }
1523
1524 if (!pDevExt->hypervisorMapping)
1525 {
1526 dprintf(("VBoxGuest::unreserveHypervisorMemory: there is no mapping, returning\n"));
1527 return;
1528 }
1529
1530 // unmap fictive IO space
1531 MmUnmapIoSpace(pDevExt->hypervisorMapping, pDevExt->hypervisorMappingSize);
1532 dprintf(("VBoxGuest::unreserveHypervisorMemmory: done\n"));
1533}
1534
1535/**
1536 * Idle thread that runs at the lowest priority possible
1537 * and whenever scheduled, makes a VMMDev call to give up
1538 * timeslices. This is so prevent Windows from thinking that
1539 * nothing is happening on the machine and doing stupid things
1540 * that would steal time from other VMs it doesn't know of.
1541 *
1542 * @param pDevExt device extension pointer
1543 */
1544VOID vboxIdleThread(PVOID context)
1545{
1546 PVBOXGUESTDEVEXT pDevExt;
1547
1548 pDevExt = (PVBOXGUESTDEVEXT)context;
1549 dprintf(("VBoxGuest::vboxIdleThread entered\n"));
1550
1551 /* set priority as low as possible */
1552 KeSetPriorityThread(KeGetCurrentThread(), LOW_PRIORITY);
1553
1554 /* allocate VMMDev request structure */
1555 VMMDevReqIdle *req;
1556 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_Idle);
1557 if (VBOX_FAILURE(rc))
1558 {
1559 dprintf(("VBoxGuest::vboxIdleThread: error %Vrc allocating request structure!\n"));
1560 return;
1561 }
1562
1563 do
1564 {
1565 //dprintf(("VBoxGuest: performing idle request..\n"));
1566 /* perform idle request */
1567 VbglGRPerform(&req->header);
1568
1569 } while (!pDevExt->stopThread);
1570
1571 VbglGRFree(&req->header);
1572
1573 dprintf(("VBoxGuest::vboxIdleThread leaving\n"));
1574}
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