VirtualBox

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

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

Fixed Windows2000 guest shutdown crash in VBoxGuest.sys

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