VirtualBox

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

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

ALIGNP SIZEOFMEMB

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.9 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 as published by the Free Software Foundation,
11 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
12 * distribution. VirtualBox OSE is distributed in the hope that it will
13 * 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 LARGE_INTEGER timeout;
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 timeout.QuadPart = 250;
349 timeout.QuadPart *= -10000; /* relative in 100ns units */
350
351 while ((pHeader->fu32Flags & VBOX_HGCM_REQ_DONE) == 0)
352 {
353 NTSTATUS rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive,
354 KernelMode, TRUE, &timeout
355 );
356 dprintf(("VBoxHGCMCallback: Wait returned %d fu32Flags=%x\n", rc, pHeader->fu32Flags));
357
358 if (rc == STATUS_TIMEOUT)
359 continue;
360
361 if (rc != STATUS_WAIT_0)
362 {
363 dprintf(("VBoxHGCMCallback: The external event was signalled or the wait timed out or terminated.\n"));
364 break;
365 }
366
367 dprintf(("VBoxHGCMCallback: fu32Flags = %08X\n", pHeader->fu32Flags));
368 }
369
370 return;
371}
372
373NTSTATUS vboxHGCMVerifyIOBuffers (PIO_STACK_LOCATION pStack, unsigned cb)
374{
375 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < cb)
376 {
377 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: OutputBufferLength %d < %d\n",
378 pStack->Parameters.DeviceIoControl.OutputBufferLength, cb));
379 return STATUS_INVALID_PARAMETER;
380 }
381
382 if (pStack->Parameters.DeviceIoControl.InputBufferLength < cb)
383 {
384 dprintf(("VBoxGuest::vboxHGCMVerifyIOBuffers: InputBufferLength %d < %d\n",
385 pStack->Parameters.DeviceIoControl.InputBufferLength, cb));
386 return STATUS_INVALID_PARAMETER;
387 }
388
389 return STATUS_SUCCESS;
390}
391
392#endif /* VBOX_HGCM */
393
394static bool
395__declspec (naked) __fastcall
396TestAndClearEvent (PVBOXGUESTDEVEXT pDevExt, int iBitOffset)
397{
398 _asm {
399 lock btr PVBOXGUESTDEVEXT[ecx].u32Events, edx;
400 setc al;
401 movzx eax, al;
402 ret;
403 }
404}
405
406static bool IsPowerOfTwo (uint32_t val)
407{
408 return (val & (val - 1)) == 0;
409}
410
411static int __declspec (naked) __fastcall GetMsb32 (uint32_t val)
412{
413 _asm {
414 bsf eax, ecx;
415 ret;
416 }
417}
418
419static bool CtlGuestFilterMask (uint32_t u32OrMask, uint32_t u32NotMask)
420{
421 bool result = false;
422 VMMDevCtlGuestFilterMask *req;
423 int rc = VbglGRAlloc ((VMMDevRequestHeader **) &req, sizeof (*req),
424 VMMDevReq_CtlGuestFilterMask);
425
426 if (VBOX_SUCCESS (rc))
427 {
428 req->u32OrMask = u32OrMask;
429 req->u32NotMask = u32NotMask;
430
431 rc = VbglGRPerform (&req->header);
432 if (VBOX_FAILURE (rc) || VBOX_FAILURE (req->header.rc))
433 {
434 dprintf (("VBoxGuest::VBoxGuestDeviceControl: error issuing request to VMMDev! "
435 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
436 }
437 else
438 {
439 result = true;
440 }
441 VbglGRFree (&req->header);
442 }
443
444 return result;
445}
446
447#ifdef VBOX_WITH_MANAGEMENT
448static int VBoxGuestSetBalloonSize(PVBOXGUESTDEVEXT pDevExt, uint32_t u32BalloonSize)
449{
450 VMMDevChangeMemBalloon *req = NULL;
451 int rc = VINF_SUCCESS;
452
453 if (u32BalloonSize > pDevExt->MemBalloon.cMaxBalloons)
454 {
455 AssertMsgFailed(("VBoxGuestSetBalloonSize illegal balloon size %d (max=%d)\n", u32BalloonSize, pDevExt->MemBalloon.cMaxBalloons));
456 return VERR_INVALID_PARAMETER;
457 }
458
459 if (u32BalloonSize == pDevExt->MemBalloon.cBalloons)
460 return VINF_SUCCESS; /* nothing to do */
461
462 /* Allocate request packet */
463 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]), VMMDevReq_ChangeMemBalloon);
464 if (VBOX_FAILURE(rc))
465 return rc;
466
467 vmmdevInitRequest(&req->header, VMMDevReq_ChangeMemBalloon);
468
469 if (u32BalloonSize > pDevExt->MemBalloon.cBalloons)
470 {
471 /* inflate */
472 for (uint32_t i=pDevExt->MemBalloon.cBalloons;i<u32BalloonSize;i++)
473 {
474#ifndef TARGET_NT4
475 /*
476 * Use MmAllocatePagesForMdl to specify the range of physical addresses we wish to use.
477 */
478 PHYSICAL_ADDRESS Zero;
479 PHYSICAL_ADDRESS HighAddr;
480 Zero.QuadPart = 0;
481 HighAddr.QuadPart = _4G - 1;
482 PMDL pMdl = MmAllocatePagesForMdl(Zero, HighAddr, Zero, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
483 if (pMdl)
484 {
485 if (MmGetMdlByteCount(pMdl) < VMMDEV_MEMORY_BALLOON_CHUNK_SIZE)
486 {
487 MmFreePagesFromMdl(pMdl);
488 ExFreePool(pMdl);
489 rc = VERR_NO_MEMORY;
490 goto end;
491 }
492 }
493#else
494 PVOID pvBalloon;
495 pvBalloon = ExAllocatePool(PagedPool, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
496 if (!pvBalloon)
497 {
498 rc = VERR_NO_MEMORY;
499 goto end;
500 }
501
502 PMDL pMdl = IoAllocateMdl (pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, FALSE, FALSE, NULL);
503 if (pMdl == NULL)
504 {
505 rc = VERR_NO_MEMORY;
506 ExFreePool(pvBalloon);
507 AssertMsgFailed(("IoAllocateMdl %VGv %x failed!!\n", pvBalloon, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE));
508 goto end;
509 }
510 else
511 {
512 __try {
513 /* Calls to MmProbeAndLockPages must be enclosed in a try/except block. */
514 MmProbeAndLockPages (pMdl, KernelMode, IoModifyAccess);
515 }
516 __except(EXCEPTION_EXECUTE_HANDLER)
517 {
518 dprintf(("MmProbeAndLockPages failed!\n"));
519 rc = VERR_NO_MEMORY;
520 IoFreeMdl (pMdl);
521 ExFreePool(pvBalloon);
522 goto end;
523 }
524 }
525#endif
526
527 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
528
529 /* Copy manually as RTGCPHYS is always 64 bits */
530 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
531 req->aPhysPage[j] = pPageDesc[j];
532
533 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
534 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
535 req->fInflate = true;
536
537 rc = VbglGRPerform(&req->header);
538 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
539 {
540 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev!"
541 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
542
543#ifndef TARGET_NT4
544 MmFreePagesFromMdl(pMdl);
545 ExFreePool(pMdl);
546#else
547 IoFreeMdl (pMdl);
548 ExFreePool(pvBalloon);
549#endif
550 goto end;
551 }
552 else
553 {
554#ifndef TARGET_NT4
555 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pMdl));
556#else
557 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB added chunk at %x\n", i, pvBalloon));
558#endif
559 pDevExt->MemBalloon.paMdlMemBalloon[i] = pMdl;
560 pDevExt->MemBalloon.cBalloons++;
561 }
562 }
563 }
564 else
565 {
566 /* deflate */
567 for (uint32_t _i=pDevExt->MemBalloon.cBalloons;_i>u32BalloonSize;_i--)
568 {
569 uint32_t index = _i - 1;
570 PMDL pMdl = pDevExt->MemBalloon.paMdlMemBalloon[index];
571
572 Assert(pMdl);
573 if (pMdl)
574 {
575#ifdef TARGET_NT4
576 PVOID pvBalloon = MmGetMdlVirtualAddress(pMdl);
577#endif
578
579 PPFN_NUMBER pPageDesc = MmGetMdlPfnArray(pMdl);
580
581 /* Copy manually as RTGCPHYS is always 64 bits */
582 for (uint32_t j=0;j<VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;j++)
583 req->aPhysPage[j] = pPageDesc[j];
584
585 req->header.size = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]);
586 req->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES;
587 req->fInflate = false;
588
589 rc = VbglGRPerform(&req->header);
590 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
591 {
592 AssertMsgFailed(("VBoxGuest::VBoxGuestSetBalloonSize: error issuing request to VMMDev! rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
593 break;
594 }
595
596 /* Free the ballooned memory */
597#ifndef TARGET_NT4
598 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pMdl));
599 MmFreePagesFromMdl(pMdl);
600 ExFreePool(pMdl);
601#else
602 dprintf(("VBoxGuest::VBoxGuestSetBalloonSize %d MB free chunk at %x\n", index, pvBalloon));
603 MmUnlockPages (pMdl);
604 IoFreeMdl (pMdl);
605 ExFreePool(pvBalloon);
606#endif
607
608 pDevExt->MemBalloon.paMdlMemBalloon[index] = NULL;
609 pDevExt->MemBalloon.cBalloons--;
610 }
611 }
612 }
613 Assert(pDevExt->MemBalloon.cBalloons <= pDevExt->MemBalloon.cMaxBalloons);
614
615end:
616 VbglGRFree(&req->header);
617 return rc;
618}
619
620static int VBoxGuestQueryMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, ULONG *pMemBalloonSize)
621{
622 /* just perform the request */
623 VMMDevGetMemBalloonChangeRequest *req = NULL;
624
625 dprintf(("VBoxGuestQueryMemoryBalloon\n"));
626
627 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest);
628 vmmdevInitRequest(&req->header, VMMDevReq_GetMemBalloonChangeRequest);
629 req->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
630
631 if (VBOX_SUCCESS(rc))
632 {
633 rc = VbglGRPerform(&req->header);
634
635 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
636 {
637 dprintf(("VBoxGuest::VBoxGuestDeviceControl IOCTL_VBOXGUEST_CTL_CHECK_BALLOON: error issuing request to VMMDev!"
638 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
639 }
640 else
641 {
642 if (!pDevExt->MemBalloon.paMdlMemBalloon)
643 {
644 pDevExt->MemBalloon.cMaxBalloons = req->u32PhysMemSize;
645 pDevExt->MemBalloon.paMdlMemBalloon = (PMDL *)ExAllocatePool(PagedPool, req->u32PhysMemSize * sizeof(PMDL));
646 Assert(pDevExt->MemBalloon.paMdlMemBalloon);
647 if (!pDevExt->MemBalloon.paMdlMemBalloon)
648 return VERR_NO_MEMORY;
649 }
650 Assert(pDevExt->MemBalloon.cMaxBalloons == req->u32PhysMemSize);
651
652 rc = VBoxGuestSetBalloonSize(pDevExt, req->u32BalloonSize);
653 /* ignore out of memory failures */
654 if (rc == VERR_NO_MEMORY)
655 rc = VINF_SUCCESS;
656
657 if (pMemBalloonSize)
658 *pMemBalloonSize = pDevExt->MemBalloon.cBalloons;
659 }
660
661 VbglGRFree(&req->header);
662 }
663 return rc;
664}
665#endif
666
667void VBoxInitMemBalloon(PVBOXGUESTDEVEXT pDevExt)
668{
669#ifdef VBOX_WITH_MANAGEMENT
670 ULONG dummy;
671
672 pDevExt->MemBalloon.cBalloons = 0;
673 pDevExt->MemBalloon.cMaxBalloons = 0;
674 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
675
676 VBoxGuestQueryMemoryBalloon(pDevExt, &dummy);
677#endif
678}
679
680void VBoxCleanupMemBalloon(PVBOXGUESTDEVEXT pDevExt)
681{
682#ifdef VBOX_WITH_MANAGEMENT
683 if (pDevExt->MemBalloon.paMdlMemBalloon)
684 {
685 /* Clean up the memory balloon leftovers */
686 VBoxGuestSetBalloonSize(pDevExt, 0);
687 ExFreePool(pDevExt->MemBalloon.paMdlMemBalloon);
688 pDevExt->MemBalloon.paMdlMemBalloon = NULL;
689 }
690 Assert(pDevExt->MemBalloon.cBalloons == 0);
691#endif
692}
693
694/**
695 * Device I/O Control entry point.
696 *
697 * @param pDevObj Device object.
698 * @param pIrp Request packet.
699 */
700NTSTATUS VBoxGuestDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
701{
702 dprintf(("VBoxGuest::VBoxGuestDeviceControl\n"));
703
704 NTSTATUS Status = STATUS_SUCCESS;
705
706 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
707
708 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
709
710 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* all requests are buffered. */
711
712 unsigned cbOut = 0;
713
714 switch (pStack->Parameters.DeviceIoControl.IoControlCode)
715 {
716 case IOCTL_VBOXGUEST_GETVMMDEVPORT:
717 {
718 dprintf(("VBoxGuest::VBoxGuestDeviceControl: IOCTL_VBOXGUEST_GETVMMDEVPORT\n"));
719
720 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof (VBoxGuestPortInfo))
721 {
722 Status = STATUS_BUFFER_TOO_SMALL;
723 break;
724 }
725
726 VBoxGuestPortInfo *portInfo = (VBoxGuestPortInfo*)pBuf;
727
728 portInfo->portAddress = pDevExt->startPortAddress;
729 portInfo->pVMMDevMemory = pDevExt->pVMMDevMemory;
730
731 cbOut = sizeof(VBoxGuestPortInfo);
732
733 break;
734 }
735
736 case IOCTL_VBOXGUEST_WAITEVENT:
737 {
738 /* Need to be extended to support multiple waiters for an event,
739 * array of counters for each event, event mask is computed, each
740 * time a wait event is arrived.
741 */
742 dprintf(("VBoxGuest::VBoxGuestDeviceControl: IOCTL_VBOXGUEST_WAITEVENT\n"));
743
744 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VBoxGuestWaitEventInfo))
745 {
746 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
747 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
748 Status = STATUS_BUFFER_TOO_SMALL;
749 break;
750 }
751
752 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestWaitEventInfo)) {
753 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < sizeof(VBoxGuestWaitEventInfo)\n",
754 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestWaitEventInfo)));
755 Status = STATUS_BUFFER_TOO_SMALL;
756 break;
757 }
758
759 VBoxGuestWaitEventInfo *eventInfo = (VBoxGuestWaitEventInfo *)pBuf;
760
761 if (!eventInfo->u32EventMaskIn || !IsPowerOfTwo (eventInfo->u32EventMaskIn)) {
762 dprintf (("VBoxGuest::VBoxGuestDeviceControl: Invalid input mask %#x\n",
763 eventInfo->u32EventMaskIn));
764 Status = STATUS_INVALID_PARAMETER;
765 break;
766 }
767
768 eventInfo->u32EventFlagsOut = 0;
769 int iBitOffset = GetMsb32 (eventInfo->u32EventMaskIn);
770 bool fTimeout = (eventInfo->u32TimeoutIn != ~0L);
771
772 dprintf (("mask = %d, iBitOffset = %d\n", iBitOffset, eventInfo->u32EventMaskIn));
773
774 /* Possible problem with request completion right between the pending event check and KeWaitForSingleObject
775 * call; introduce a timeout (if none was specified) to make sure we don't wait indefinitely.
776 */
777 LARGE_INTEGER timeout;
778 timeout.QuadPart = (fTimeout) ? eventInfo->u32TimeoutIn : 250;
779 timeout.QuadPart *= -10000;
780
781 NTSTATUS rc = STATUS_SUCCESS;
782
783 for (;;)
784 {
785 bool fEventPending = TestAndClearEvent (pDevExt, iBitOffset);
786 if (fEventPending)
787 {
788 eventInfo->u32EventFlagsOut = 1 << iBitOffset;
789 break;
790 }
791
792 rc = KeWaitForSingleObject (&pDevExt->keventNotification, Executive /** @todo UserRequest? */,
793 KernelMode, TRUE, &timeout);
794 dprintf(("IOCTL_VBOXGUEST_WAITEVENT: Wait returned %d -> event %x\n", rc, eventInfo->u32EventFlagsOut));
795
796 if (!fTimeout && rc == STATUS_TIMEOUT)
797 continue;
798
799 if (rc != STATUS_SUCCESS)
800 {
801 /* There was a timeout or wait was interrupted, etc. */
802 break;
803 }
804 }
805
806 dprintf (("u32EventFlagsOut = %#x\n", eventInfo->u32EventFlagsOut));
807 cbOut = sizeof(VBoxGuestWaitEventInfo);
808 break;
809 }
810
811 case IOCTL_VBOXGUEST_VMMREQUEST:
812 {
813 dprintf(("VBoxGuest::VBoxGuestDeviceControl: IOCTL_VBOXGUEST_VMMREQUEST\n"));
814
815#define CHECK_SIZE(s) \
816 if (pStack->Parameters.DeviceIoControl.OutputBufferLength < s) \
817 { \
818 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d < %d\n", \
819 pStack->Parameters.DeviceIoControl.OutputBufferLength, s)); \
820 Status = STATUS_BUFFER_TOO_SMALL; \
821 break; \
822 } \
823 if (pStack->Parameters.DeviceIoControl.InputBufferLength < s) { \
824 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n", \
825 pStack->Parameters.DeviceIoControl.InputBufferLength, s)); \
826 Status = STATUS_BUFFER_TOO_SMALL; \
827 break; \
828 }
829
830 /* get the request header */
831 CHECK_SIZE(sizeof(VMMDevRequestHeader));
832 VMMDevRequestHeader *requestHeader = (VMMDevRequestHeader *)pBuf;
833 if (!vmmdevGetRequestSize(requestHeader->requestType))
834 {
835 Status = STATUS_INVALID_PARAMETER;
836 break;
837 }
838 /* make sure the buffers suit the request */
839 CHECK_SIZE(vmmdevGetRequestSize(requestHeader->requestType));
840
841 /* just perform the request */
842 VMMDevRequestHeader *req = NULL;
843
844 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, requestHeader->size, requestHeader->requestType);
845
846 if (VBOX_SUCCESS(rc))
847 {
848 /* copy the request information */
849 memcpy((void*)req, (void*)pBuf, requestHeader->size);
850 rc = VbglGRPerform(req);
851
852 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->rc))
853 {
854 dprintf(("VBoxGuest::VBoxGuestDeviceControl IOCTL_VBOXGUEST_VMMREQUEST: error issuing request to VMMDev!"
855 "rc = %d, VMMDev rc = %Vrc\n", rc, req->rc));
856 Status = STATUS_UNSUCCESSFUL;
857 }
858 else
859 {
860 /* copy result */
861 memcpy((void*)pBuf, (void*)req, requestHeader->size);
862 cbOut = requestHeader->size;
863 }
864
865 VbglGRFree(req);
866 }
867 else
868 {
869 Status = STATUS_UNSUCCESSFUL;
870 }
871#undef CHECK_SIZE
872 break;
873 }
874
875 case IOCTL_VBOXGUEST_CTL_FILTER_MASK:
876 {
877 VBoxGuestFilterMaskInfo *maskInfo;
878
879 if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VBoxGuestFilterMaskInfo)) {
880 dprintf (("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d < %d\n",
881 pStack->Parameters.DeviceIoControl.InputBufferLength,
882 sizeof (VBoxGuestFilterMaskInfo)));
883 Status = STATUS_BUFFER_TOO_SMALL;
884 break;
885
886 }
887
888 maskInfo = (VBoxGuestFilterMaskInfo *) pBuf;
889 if (!CtlGuestFilterMask (maskInfo->u32OrMask, maskInfo->u32NotMask))
890 {
891 Status = STATUS_UNSUCCESSFUL;
892 }
893 break;
894 }
895
896#ifdef VBOX_HGCM
897 /* HGCM offers blocking IOCTLSs just like waitevent and actually
898 * uses the same waiting code.
899 */
900 case IOCTL_VBOXGUEST_HGCM_CONNECT:
901 {
902 dprintf(("VBoxGuest::VBoxGuestDeviceControl: IOCTL_VBOXGUEST_HGCM_CONNECT\n"));
903
904 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMConnectInfo))
905 {
906 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
907 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
908 Status = STATUS_INVALID_PARAMETER;
909 break;
910 }
911
912 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMConnectInfo)) {
913 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMConnectInfo) %d\n",
914 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMConnectInfo)));
915 Status = STATUS_INVALID_PARAMETER;
916 break;
917 }
918
919 VBoxGuestHGCMConnectInfo *ptr = (VBoxGuestHGCMConnectInfo *)pBuf;
920
921 /* If request will be processed asynchronously, execution will
922 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
923 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
924 * flag is set, returns.
925 */
926
927 dprintf(("a) ptr->u32ClientID = %d\n", ptr->u32ClientID));
928
929 int rc = VbglHGCMConnect (ptr, VBoxHGCMCallback, pDevExt, 0);
930
931 dprintf(("b) ptr->u32ClientID = %d\n", ptr->u32ClientID));
932
933 if (VBOX_FAILURE(rc))
934 {
935 dprintf(("IOCTL_VBOXGUEST_HGCM_CONNECT: vbox rc = %Vrc\n", rc));
936 Status = STATUS_UNSUCCESSFUL;
937 }
938 else
939 {
940 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
941 }
942
943 } break;
944
945 case IOCTL_VBOXGUEST_HGCM_DISCONNECT:
946 {
947 dprintf(("VBoxGuest::VBoxGuestDeviceControl: IOCTL_VBOXGUEST_HGCM_DISCONNECT\n"));
948
949 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo))
950 {
951 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
952 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
953 Status = STATUS_INVALID_PARAMETER;
954 break;
955 }
956
957 if (pStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(VBoxGuestHGCMDisconnectInfo)) {
958 dprintf(("VBoxGuest::VBoxGuestDeviceControl: InputBufferLength %d != sizeof(VBoxGuestHGCMDisconnectInfo) %d\n",
959 pStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(VBoxGuestHGCMDisconnectInfo)));
960 Status = STATUS_INVALID_PARAMETER;
961 break;
962 }
963
964 VBoxGuestHGCMDisconnectInfo *ptr = (VBoxGuestHGCMDisconnectInfo *)pBuf;
965
966 /* If request will be processed asynchronously, execution will
967 * go to VBoxHGCMCallback. There it will wait for the request event, signalled from IRQ.
968 * On IRQ arrival, the VBoxHGCMCallback(s) will check the request memory and, if completion
969 * flag is set, returns.
970 */
971
972 int rc = VbglHGCMDisconnect (ptr, VBoxHGCMCallback, pDevExt, 0);
973
974 if (VBOX_FAILURE(rc))
975 {
976 dprintf(("IOCTL_VBOXGUEST_HGCM_DISCONNECT: vbox rc = %Vrc\n", rc));
977 Status = STATUS_UNSUCCESSFUL;
978 }
979 else
980 {
981 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
982 }
983
984 } break;
985
986 case IOCTL_VBOXGUEST_HGCM_CALL:
987 {
988 dprintf(("VBoxGuest::VBoxGuestDeviceControl: IOCTL_VBOXGUEST_HGCM_CALL\n"));
989
990 Status = vboxHGCMVerifyIOBuffers (pStack,
991 sizeof (VBoxGuestHGCMCallInfo));
992
993 if (Status != STATUS_SUCCESS)
994 {
995 dprintf(("VBoxGuest::VBoxGuestDeviceControl: invalid parameter. Status: %p\n", Status));
996 break;
997 }
998
999 VBoxGuestHGCMCallInfo *ptr = (VBoxGuestHGCMCallInfo *)pBuf;
1000
1001 int rc = VbglHGCMCall (ptr, VBoxHGCMCallback, pDevExt, 0);
1002
1003 if (VBOX_FAILURE(rc))
1004 {
1005 dprintf(("IOCTL_VBOXGUEST_HGCM_CALL: vbox rc = %Vrc\n", rc));
1006 Status = STATUS_UNSUCCESSFUL;
1007 }
1008 else
1009 {
1010 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1011 }
1012
1013 } break;
1014#endif /* VBOX_HGCM */
1015
1016#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
1017 case IOCTL_VBOXGUEST_ENABLE_VRDP_SESSION:
1018 {
1019 if (!pDevExt->fVRDPEnabled)
1020 {
1021 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1022
1023 pDevExt->fVRDPEnabled = TRUE;
1024 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
1025 pSharedUserData->ActiveConsoleId = 2;
1026 }
1027 break;
1028 }
1029
1030 case IOCTL_VBOXGUEST_DISABLE_VRDP_SESSION:
1031 {
1032 if (pDevExt->fVRDPEnabled)
1033 {
1034 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
1035
1036 pDevExt->fVRDPEnabled = FALSE;
1037 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
1038 pDevExt->ulOldActiveConsoleId = 0;
1039 }
1040 break;
1041 }
1042#endif
1043
1044#ifdef VBOX_WITH_MANAGEMENT
1045 case IOCTL_VBOXGUEST_CTL_CHECK_BALLOON:
1046 {
1047 ULONG *pMemBalloonSize = (ULONG *) pBuf;
1048
1049 if (pStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG))
1050 {
1051 dprintf(("VBoxGuest::VBoxGuestDeviceControl: OutputBufferLength %d != sizeof(ULONG) %d\n",
1052 pStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(ULONG)));
1053 Status = STATUS_INVALID_PARAMETER;
1054 break;
1055 }
1056
1057 int rc = VBoxGuestQueryMemoryBalloon(pDevExt, pMemBalloonSize);
1058 if (VBOX_FAILURE(rc))
1059 {
1060 dprintf(("IOCTL_VBOXGUEST_CTL_CHECK_BALLOON: vbox rc = %Vrc\n", rc));
1061 Status = STATUS_UNSUCCESSFUL;
1062 }
1063 else
1064 {
1065 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1066 }
1067 break;
1068 }
1069#endif
1070
1071 default:
1072 Status = STATUS_INVALID_PARAMETER;
1073 break;
1074 }
1075
1076 pIrp->IoStatus.Status = Status;
1077 pIrp->IoStatus.Information = cbOut;
1078
1079 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1080
1081 dprintf(("VBoxGuest::VBoxGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
1082
1083 return Status;
1084}
1085
1086
1087/**
1088 * IRP_MJ_SYSTEM_CONTROL handler
1089 *
1090 * @returns NT status code
1091 * @param pDevObj Device object.
1092 * @param pIrp IRP.
1093 */
1094NTSTATUS VBoxGuestSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1095{
1096 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1097
1098 dprintf(("VBoxGuest::VBoxGuestSystemControl\n"));
1099
1100 /* Always pass it on to the next driver. */
1101 IoSkipCurrentIrpStackLocation(pIrp);
1102
1103 return IoCallDriver(pDevExt->nextLowerDriver, pIrp);
1104}
1105
1106/**
1107 * IRP_MJ_SHUTDOWN handler
1108 *
1109 * @returns NT status code
1110 * @param pDevObj Device object.
1111 * @param pIrp IRP.
1112 */
1113NTSTATUS VBoxGuestShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1114{
1115 VMMDevPowerStateRequest *req = NULL;
1116
1117 dprintf(("VBoxGuest::VBoxGuestShutdown\n"));
1118
1119 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
1120
1121 if (VBOX_SUCCESS(rc))
1122 {
1123 req->powerState = VMMDevPowerState_PowerOff;
1124
1125 rc = VbglGRPerform (&req->header);
1126
1127 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1128 {
1129 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1130 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1131 }
1132
1133 VbglGRFree (&req->header);
1134 }
1135
1136 return STATUS_SUCCESS;
1137}
1138
1139/**
1140 * Stub function for functions we don't implemented.
1141 *
1142 * @returns STATUS_NOT_SUPPORTED
1143 * @param pDevObj Device object.
1144 * @param pIrp IRP.
1145 */
1146NTSTATUS VBoxGuestNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1147{
1148 dprintf(("VBoxGuest::VBoxGuestNotSupportedStub\n"));
1149 pDevObj = pDevObj;
1150
1151 pIrp->IoStatus.Information = 0;
1152 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1153 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1154
1155 return STATUS_NOT_SUPPORTED;
1156}
1157
1158/**
1159 * DPC handler
1160 *
1161 * @param dpc DPC descriptor.
1162 * @param pDevObj Device object.
1163 * @param irp Interrupt request packet.
1164 * @param context Context specific pointer.
1165 */
1166VOID VBoxGuestDpcHandler(PKDPC dpc, PDEVICE_OBJECT pDevObj,
1167 PIRP irp, PVOID context)
1168{
1169 /* Unblock handlers waiting for arrived events.
1170 *
1171 * Events are very low things, there is one event flag (1 or more bit)
1172 * for each event. Each event is processed by exactly one handler.
1173 *
1174 * Assume that we trust additions and that other drivers will
1175 * handle its respective events without trying to fetch all events.
1176 *
1177 * Anyway design assures that wrong event processing will affect only guest.
1178 *
1179 * Event handler calls VMMDev IOCTL for waiting an event.
1180 * It supplies event mask. IOCTL blocks on EventNotification.
1181 * Here we just signal an the EventNotification to all waiting
1182 * threads, the IOCTL handler analyzes events and either
1183 * return to caller or blocks again.
1184 *
1185 * If we do not have too many events this is a simple and good
1186 * approach. Other way is to have as many Event objects as the callers
1187 * and wake up only callers waiting for the specific event.
1188 *
1189 * Now with the 'wake up all' appoach we probably do not need the DPC
1190 * handler and can signal event directly from ISR.
1191 *
1192 */
1193
1194 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
1195
1196 dprintf(("VBoxGuest::VBoxGuestDpcHandler\n"));
1197
1198 KePulseEvent(&pDevExt->keventNotification, 0, FALSE);
1199
1200}
1201
1202/**
1203 * ISR handler
1204 *
1205 * @return BOOLEAN indicates whether the IRQ came from us (TRUE) or not (FALSE)
1206 * @param interrupt Interrupt that was triggered.
1207 * @param serviceContext Context specific pointer.
1208 */
1209BOOLEAN VBoxGuestIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext)
1210{
1211 NTSTATUS rc;
1212 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)serviceContext;
1213 BOOLEAN fIRQTaken = FALSE;
1214
1215 dprintf(("VBoxGuest::VBoxGuestIsrHandler haveEvents = %d\n",
1216 pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents));
1217
1218 /*
1219 * now we have to find out whether it was our IRQ. Read the event mask
1220 * from our device to see if there are any pending events
1221 */
1222 if (pDevExt->pVMMDevMemory->V.V1_04.fHaveEvents)
1223 {
1224 /* Acknowlegde events. */
1225 VMMDevEvents *req = pDevExt->irqAckEvents;
1226
1227 rc = VbglGRPerform (&req->header);
1228 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1229 {
1230 dprintf(("VBoxGuest::VBoxGuestIsrHandler: acknowledge events succeeded %#x\n",
1231 req->events));
1232
1233 ASMAtomicOrU32((uint32_t *)&pDevExt->u32Events, req->events);
1234 IoRequestDpc(pDevExt->deviceObject, pDevExt->currentIrp, NULL);
1235 }
1236 else
1237 {
1238 /* This can't be actually. This is sign of a serious problem. */
1239 dprintf(("VBoxGuest::VBoxGuestIsrHandler: "
1240 "acknowledge events failed rc = %d, header rc = %d\n",
1241 rc, req->header.rc));
1242 }
1243
1244 /* Mark IRQ as taken, there were events for us. */
1245 fIRQTaken = TRUE;
1246 }
1247
1248 return fIRQTaken;
1249}
1250
1251/**
1252 * Worker thread to do periodic things such as synchronize the
1253 * system time and notify other drivers of events.
1254 *
1255 * @param pDevExt device extension pointer
1256 */
1257VOID vboxWorkerThread(PVOID context)
1258{
1259 PVBOXGUESTDEVEXT pDevExt;
1260
1261 pDevExt = (PVBOXGUESTDEVEXT)context;
1262 dprintf(("VBoxGuest::vboxWorkerThread entered\n"));
1263
1264 VMMDevReqHostTime *req = NULL;
1265
1266 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHostTime), VMMDevReq_GetHostTime);
1267
1268 if (VBOX_FAILURE(rc))
1269 {
1270 dprintf(("VBoxGuest::vboxWorkerThread: could not allocate request buffer, exiting rc = %d!\n", rc));
1271 return;
1272 }
1273
1274 /* perform the hypervisor address space reservation */
1275 reserveHypervisorMemory(pDevExt);
1276
1277 do
1278 {
1279 /*
1280 * Do the time sync
1281 */
1282 {
1283 LARGE_INTEGER systemTime;
1284 #define TICKSPERSEC 10000000
1285 #define TICKSPERMSEC 10000
1286 #define SECSPERDAY 86400
1287 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (uint64_t)SECSPERDAY)
1288 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
1289
1290
1291 req->header.rc = VERR_GENERAL_FAILURE;
1292
1293 rc = VbglGRPerform (&req->header);
1294
1295 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1296 {
1297 uint64_t hostTime = req->time;
1298
1299 // Windows was originally designed in 1601...
1300 systemTime.QuadPart = hostTime * (uint64_t)TICKSPERMSEC + (uint64_t)TICKS_1601_TO_1970;
1301 dprintf(("VBoxGuest::vboxWorkerThread: synching time with host time (msec/UTC): %llu\n", hostTime));
1302 ZwSetSystemTime(&systemTime, NULL);
1303 }
1304 else
1305 {
1306 dprintf(("VBoxGuest::PowerStateRequest: error performing request to VMMDev."
1307 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1308 }
1309 }
1310
1311 /*
1312 * Go asleep unless we're supposed to terminate
1313 */
1314 if (!pDevExt->stopThread)
1315 {
1316 ULONG secWait = 60;
1317 dprintf(("VBoxGuest::vboxWorkerThread: waiting for %u seconds...\n", secWait));
1318 LARGE_INTEGER dueTime;
1319 dueTime.QuadPart = -10000 * 1000 * (int)secWait;
1320 if (KeWaitForSingleObject(&pDevExt->workerThreadRequest, Executive,
1321 KernelMode, FALSE, &dueTime) == STATUS_SUCCESS)
1322 {
1323 KeResetEvent(&pDevExt->workerThreadRequest);
1324 }
1325 }
1326 } while (!pDevExt->stopThread);
1327
1328 dprintf(("VBoxGuest::vboxWorkerThread: we've been asked to terminate!\n"));
1329
1330 /* free our request buffer */
1331 VbglGRFree (&req->header);
1332
1333 if (pDevExt->workerThread)
1334 {
1335 ObDereferenceObject(pDevExt->workerThread);
1336 pDevExt->workerThread = NULL;
1337 }
1338 dprintf(("VBoxGuest::vboxWorkerThread: now really gone!\n"));
1339}
1340
1341/**
1342 * Create driver worker threads
1343 *
1344 * @returns NTSTATUS NT status code
1345 * @param pDevExt VBoxGuest device extension
1346 */
1347NTSTATUS createThreads(PVBOXGUESTDEVEXT pDevExt)
1348{
1349 NTSTATUS rc;
1350 HANDLE threadHandle;
1351 OBJECT_ATTRIBUTES objAttributes;
1352
1353 dprintf(("VBoxGuest::createThreads\n"));
1354
1355 // first setup the request semaphore
1356 KeInitializeEvent(&pDevExt->workerThreadRequest, SynchronizationEvent, FALSE);
1357
1358// the API has slightly changed after NT4
1359#ifdef TARGET_NT4
1360#ifdef OBJ_KERNEL_HANDLE
1361#undef OBJ_KERNEL_HANDLE
1362#endif
1363#define OBJ_KERNEL_HANDLE 0
1364#endif
1365
1366 /*
1367 * The worker thread
1368 */
1369 InitializeObjectAttributes(&objAttributes,
1370 NULL,
1371 OBJ_KERNEL_HANDLE,
1372 NULL,
1373 NULL);
1374
1375 rc = PsCreateSystemThread(&threadHandle,
1376 THREAD_ALL_ACCESS,
1377 &objAttributes,
1378 (HANDLE)0L,
1379 NULL,
1380 vboxWorkerThread,
1381 pDevExt);
1382 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for worker thread returned: 0x%x\n", rc));
1383 rc = ObReferenceObjectByHandle(threadHandle,
1384 THREAD_ALL_ACCESS,
1385 NULL,
1386 KernelMode,
1387 (PVOID*)&pDevExt->workerThread,
1388 NULL);
1389 ZwClose(threadHandle);
1390
1391 /*
1392 * The idle thread
1393 */
1394#if 0 /// @todo Windows "sees" that time is lost and reports 100% usage
1395 rc = PsCreateSystemThread(&threadHandle,
1396 THREAD_ALL_ACCESS,
1397 &objAttributes,
1398 (HANDLE)0L,
1399 NULL,
1400 vboxIdleThread,
1401 pDevExt);
1402 dprintf(("VBoxGuest::createThreads: PsCreateSystemThread for idle thread returned: 0x%x\n", rc));
1403 rc = ObReferenceObjectByHandle(threadHandle,
1404 THREAD_ALL_ACCESS,
1405 NULL,
1406 KernelMode,
1407 (PVOID*)&pDevExt->idleThread,
1408 NULL);
1409 ZwClose(threadHandle);
1410#endif
1411
1412 return rc;
1413}
1414
1415/**
1416 * Helper routine to reserve address space for the hypervisor
1417 * and communicate its position.
1418 *
1419 * @param pDevExt Device extension structure.
1420 */
1421VOID reserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1422{
1423 // @todo rc handling
1424 uint32_t hypervisorSize;
1425
1426 VMMDevReqHypervisorInfo *req = NULL;
1427
1428 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo);
1429
1430 if (VBOX_SUCCESS(rc))
1431 {
1432 req->hypervisorStart = 0;
1433 req->hypervisorSize = 0;
1434
1435 rc = VbglGRPerform (&req->header);
1436
1437 if (VBOX_SUCCESS(rc) && VBOX_SUCCESS(req->header.rc))
1438 {
1439 hypervisorSize = req->hypervisorSize;
1440
1441 if (!hypervisorSize)
1442 {
1443 dprintf(("VBoxGuest::reserveHypervisorMemory: host returned 0, not doing anything\n"));
1444 return;
1445 }
1446
1447 dprintf(("VBoxGuest::reserveHypervisorMemory: host wants %u bytes of hypervisor address space\n", hypervisorSize));
1448
1449 // Map fictive physical memory into the kernel address space to reserve virtual
1450 // address space. This API does not perform any checks but just allocate the
1451 // PTEs (which we don't really need/want but there isn't any other clean method).
1452 // The hypervisor only likes 4MB aligned virtual addresses, so we have to allocate
1453 // 4MB more than we are actually supposed to in order to guarantee that. Maybe we
1454 // can come up with a less lavish algorithm lateron.
1455 PHYSICAL_ADDRESS physAddr;
1456 physAddr.QuadPart = HYPERVISOR_PHYSICAL_START;
1457 pDevExt->hypervisorMappingSize = hypervisorSize + 0x400000;
1458 pDevExt->hypervisorMapping = MmMapIoSpace(physAddr,
1459 pDevExt->hypervisorMappingSize,
1460 MmNonCached);
1461 if (!pDevExt->hypervisorMapping)
1462 {
1463 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned NULL!\n"));
1464 return;
1465 }
1466
1467 dprintf(("VBoxGuest::reserveHypervisorMemory: MmMapIoSpace returned %p\n", pDevExt->hypervisorMapping));
1468 dprintf(("VBoxGuest::reserveHypervisorMemory: communicating %p to host\n",
1469 RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000)));
1470
1471 /* align at 4MB */
1472 req->hypervisorStart = (RTGCPTR)RT_ALIGN_P(pDevExt->hypervisorMapping, 0x400000);
1473
1474 req->header.requestType = VMMDevReq_SetHypervisorInfo;
1475 req->header.rc = VERR_GENERAL_FAILURE;
1476
1477 /* issue request */
1478 rc = VbglGRPerform (&req->header);
1479
1480 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1481 {
1482 dprintf(("VBoxGuest::reserveHypervisorMemory: error communicating physical address to VMMDev!"
1483 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1484 }
1485 }
1486 else
1487 {
1488 dprintf(("VBoxGuest::reserveHypervisorMemory: request failed with rc %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1489 }
1490 VbglGRFree (&req->header);
1491 }
1492
1493 return;
1494}
1495
1496/**
1497 * Helper function to unregister a virtual address space mapping
1498 *
1499 * @param pDevExt Device extension
1500 */
1501VOID unreserveHypervisorMemory(PVBOXGUESTDEVEXT pDevExt)
1502{
1503 VMMDevReqHypervisorInfo *req = NULL;
1504
1505 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo);
1506
1507 if (VBOX_SUCCESS(rc))
1508 {
1509 /* tell the hypervisor that the mapping is no longer available */
1510
1511 req->hypervisorStart = 0;
1512 req->hypervisorSize = 0;
1513
1514 rc = VbglGRPerform (&req->header);
1515
1516 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
1517 {
1518 dprintf(("VBoxGuest::unreserveHypervisorMemory: error communicating physical address to VMMDev!"
1519 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
1520 }
1521
1522 VbglGRFree (&req->header);
1523 }
1524
1525 if (!pDevExt->hypervisorMapping)
1526 {
1527 dprintf(("VBoxGuest::unreserveHypervisorMemory: there is no mapping, returning\n"));
1528 return;
1529 }
1530
1531 // unmap fictive IO space
1532 MmUnmapIoSpace(pDevExt->hypervisorMapping, pDevExt->hypervisorMappingSize);
1533 dprintf(("VBoxGuest::unreserveHypervisorMemmory: done\n"));
1534}
1535
1536/**
1537 * Idle thread that runs at the lowest priority possible
1538 * and whenever scheduled, makes a VMMDev call to give up
1539 * timeslices. This is so prevent Windows from thinking that
1540 * nothing is happening on the machine and doing stupid things
1541 * that would steal time from other VMs it doesn't know of.
1542 *
1543 * @param pDevExt device extension pointer
1544 */
1545VOID vboxIdleThread(PVOID context)
1546{
1547 PVBOXGUESTDEVEXT pDevExt;
1548
1549 pDevExt = (PVBOXGUESTDEVEXT)context;
1550 dprintf(("VBoxGuest::vboxIdleThread entered\n"));
1551
1552 /* set priority as low as possible */
1553 KeSetPriorityThread(KeGetCurrentThread(), LOW_PRIORITY);
1554
1555 /* allocate VMMDev request structure */
1556 VMMDevReqIdle *req;
1557 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof (VMMDevReqHypervisorInfo), VMMDevReq_Idle);
1558 if (VBOX_FAILURE(rc))
1559 {
1560 dprintf(("VBoxGuest::vboxIdleThread: error %Vrc allocating request structure!\n"));
1561 return;
1562 }
1563
1564 do
1565 {
1566 //dprintf(("VBoxGuest: performing idle request..\n"));
1567 /* perform idle request */
1568 VbglGRPerform(&req->header);
1569
1570 } while (!pDevExt->stopThread);
1571
1572 VbglGRFree(&req->header);
1573
1574 dprintf(("VBoxGuest::vboxIdleThread leaving\n"));
1575}
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