VirtualBox

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

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

The Big Sun Rebranding Header Change

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