VirtualBox

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

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

Fixed wraparound

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