VirtualBox

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

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

WINNT/VBoxGuest.cpp: disabled bugcheck registration code, see review comments.

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

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