VirtualBox

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

Last change on this file since 8061 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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