VirtualBox

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

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

Windows guest HGCM optimization: use a preallocated timeout variable.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette