VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp@ 70099

Last change on this file since 70099 was 70099, checked in by vboxsync, 7 years ago

VBoxGuest-win.cpp: Two more registry value names to ignore

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.9 KB
Line 
1/* $Id: VBoxGuest-win.cpp 70099 2017-12-13 09:06:25Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_SUP_DRV
32#include "VBoxGuest-win.h"
33#include "VBoxGuestInternal.h"
34
35#include <iprt/asm.h>
36#include <iprt/asm-amd64-x86.h>
37
38#include <VBox/log.h>
39#include <VBox/VBoxGuestLib.h>
40#include <iprt/string.h>
41
42/*
43 * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist
44 * on NT4, so... The same for ExAllocatePool.
45 */
46#ifdef TARGET_NT4
47# undef ExAllocatePool
48# undef ExFreePool
49#endif
50
51
52/*********************************************************************************************************************************
53* Internal Functions *
54*********************************************************************************************************************************/
55RT_C_DECLS_BEGIN
56static NTSTATUS vgdrvNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
57static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
58static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
59static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
60static NTSTATUS vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
61static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
62static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
63static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
64static NTSTATUS vgdrvNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
65static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
66static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
67#ifdef VBOX_STRICT
68static void vgdrvNtDoTests(void);
69#endif
70static VOID vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
71static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
72static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt);
73static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
74 void **ppvMMIOBase, uint32_t *pcbMMIO);
75RT_C_DECLS_END
76
77
78/*********************************************************************************************************************************
79* Exported Functions *
80*********************************************************************************************************************************/
81RT_C_DECLS_BEGIN
82ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
83RT_C_DECLS_END
84
85#ifdef ALLOC_PRAGMA
86# pragma alloc_text(INIT, DriverEntry)
87# pragma alloc_text(PAGE, vgdrvNtAddDevice)
88# pragma alloc_text(PAGE, vgdrvNtUnload)
89# pragma alloc_text(PAGE, vgdrvNtCreate)
90# pragma alloc_text(PAGE, vgdrvNtClose)
91# pragma alloc_text(PAGE, vgdrvNtShutdown)
92# pragma alloc_text(PAGE, vgdrvNtNotSupportedStub)
93# pragma alloc_text(PAGE, vgdrvNtScanPCIResourceList)
94#endif
95
96
97/*********************************************************************************************************************************
98* Global Variables *
99*********************************************************************************************************************************/
100/** The detected NT (windows) version. */
101VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
102
103
104
105/**
106 * Driver entry point.
107 *
108 * @returns appropriate status code.
109 * @param pDrvObj Pointer to driver object.
110 * @param pRegPath Registry base path.
111 */
112ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
113{
114 RT_NOREF1(pRegPath);
115 NTSTATUS rc = STATUS_SUCCESS;
116
117 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
118
119 /*
120 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
121 */
122 ULONG ulMajorVer;
123 ULONG ulMinorVer;
124 ULONG ulBuildNo;
125 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
126
127 /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log */
128 RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
129 if (fCheckedBuild)
130 RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
131
132#ifdef VBOX_STRICT
133 vgdrvNtDoTests();
134#endif
135 switch (ulMajorVer)
136 {
137 case 10:
138 switch (ulMinorVer)
139 {
140 case 0:
141 /* Windows 10 Preview builds starting with 9926. */
142 default:
143 /* Also everything newer. */
144 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
145 break;
146 }
147 break;
148 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
149 switch (ulMinorVer)
150 {
151 case 0: /* Note: Also could be Windows 2008 Server! */
152 g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
153 break;
154 case 1: /* Note: Also could be Windows 2008 Server R2! */
155 g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
156 break;
157 case 2:
158 g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
159 break;
160 case 3:
161 g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
162 break;
163 case 4:
164 /* Windows 10 Preview builds. */
165 default:
166 /* Also everything newer. */
167 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
168 break;
169 }
170 break;
171 case 5:
172 switch (ulMinorVer)
173 {
174 default:
175 case 2:
176 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
177 break;
178 case 1:
179 g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
180 break;
181 case 0:
182 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
183 break;
184 }
185 break;
186 case 4:
187 g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
188 break;
189 default:
190 if (ulMajorVer > 6)
191 {
192 /* "Windows 10 mode" for Windows 8.1+. */
193 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
194 }
195 else
196 {
197 if (ulMajorVer < 4)
198 LogRelFunc(("At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer));
199 else
200 LogRelFunc(("Unknown version %u.%u!\n", ulMajorVer, ulMinorVer));
201 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
202 }
203 break;
204 }
205
206 if (NT_SUCCESS(rc))
207 {
208 /*
209 * Setup the driver entry points in pDrvObj.
210 */
211 pDrvObj->DriverUnload = vgdrvNtUnload;
212 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
213 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
214 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
215 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
216 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
217 pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
218 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
219#ifdef TARGET_NT4
220 rc = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
221#else
222 pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtPnP;
223 pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtPower;
224 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtSystemControl;
225 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vgdrvNtAddDevice;
226#endif
227 }
228
229 LogFlowFunc(("Returning %#x\n", rc));
230 return rc;
231}
232
233
234#ifndef TARGET_NT4
235/**
236 * Handle request from the Plug & Play subsystem.
237 *
238 * @returns NT status code
239 * @param pDrvObj Driver object
240 * @param pDevObj Device object
241 *
242 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
243 */
244static NTSTATUS vgdrvNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
245{
246 NTSTATUS rc;
247 LogFlowFuncEnter();
248
249 /*
250 * Create device.
251 */
252 UNICODE_STRING DevName;
253 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
254 PDEVICE_OBJECT pDeviceObject = NULL;
255 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
256 if (NT_SUCCESS(rc))
257 {
258 /*
259 * Create symbolic link (DOS devices).
260 */
261 UNICODE_STRING DosName;
262 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
263 rc = IoCreateSymbolicLink(&DosName, &DevName);
264 if (NT_SUCCESS(rc))
265 {
266 /*
267 * Setup the device extension.
268 */
269 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
270 RT_ZERO(*pDevExt);
271
272 KeInitializeSpinLock(&pDevExt->MouseEventAccessLock);
273
274 pDevExt->pDeviceObject = pDeviceObject;
275 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
276 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
277
278 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
279 if (pDevExt->pNextLowerDriver != NULL)
280 {
281 /*
282 * If we reached this point we're fine with the basic driver setup,
283 * so continue to init our own things.
284 */
285# ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
286 vgdrvNtBugCheckCallback(pDevExt); /* Ignore failure! */
287# endif
288 if (NT_SUCCESS(rc))
289 {
290 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
291 pDeviceObject->Flags |= DO_POWER_PAGABLE;
292
293 /* Driver is ready now. */
294 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
295 LogFlowFunc(("Returning with rc=%#x (success)\n", rc));
296 return rc;
297 }
298
299 IoDetachDevice(pDevExt->pNextLowerDriver);
300 }
301 else
302 {
303 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
304 rc = STATUS_DEVICE_NOT_CONNECTED;
305 }
306
307 /* bail out */
308 IoDeleteSymbolicLink(&DosName);
309 }
310 else
311 LogFunc(("IoCreateSymbolicLink failed with rc=%#x!\n", rc));
312 IoDeleteDevice(pDeviceObject);
313 }
314 else
315 LogFunc(("IoCreateDevice failed with rc=%#x!\n", rc));
316
317 LogFunc(("Returning with rc=%#x\n", rc));
318 return rc;
319}
320#endif /* !TARGET_NT4 */
321
322
323#ifdef LOG_ENABLED
324/**
325 * Debug helper to dump a device resource list.
326 *
327 * @param pResourceList list of device resources.
328 */
329static void vgdrvNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
330{
331 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pResourceList->PartialDescriptors;
332 ULONG cResources = pResourceList->Count;
333
334 for (ULONG i = 0; i < cResources; ++i, ++pResource)
335 {
336 ULONG uType = pResource->Type;
337 static char const * const s_apszName[] =
338 {
339 "CmResourceTypeNull",
340 "CmResourceTypePort",
341 "CmResourceTypeInterrupt",
342 "CmResourceTypeMemory",
343 "CmResourceTypeDma",
344 "CmResourceTypeDeviceSpecific",
345 "CmResourceTypeBusNumber",
346 "CmResourceTypeDevicePrivate",
347 "CmResourceTypeAssignedResource",
348 "CmResourceTypeSubAllocateFrom",
349 };
350
351 LogFunc(("Type=%s", uType < RT_ELEMENTS(s_apszName) ? s_apszName[uType] : "Unknown"));
352
353 switch (uType)
354 {
355 case CmResourceTypePort:
356 case CmResourceTypeMemory:
357 LogFunc(("Start %8X%8.8lX, length=%X\n",
358 pResource->u.Port.Start.HighPart, pResource->u.Port.Start.LowPart, pResource->u.Port.Length));
359 break;
360
361 case CmResourceTypeInterrupt:
362 LogFunc(("Level=%X, vector=%X, affinity=%X\n",
363 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
364 break;
365
366 case CmResourceTypeDma:
367 LogFunc(("Channel %d, Port %X\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
368 break;
369
370 default:
371 LogFunc(("\n"));
372 break;
373 }
374 }
375}
376#endif /* LOG_ENABLED */
377
378
379/**
380 * Global initialisation stuff (PnP + NT4 legacy).
381 *
382 * @param pDevObj Device object.
383 * @param pIrp Request packet.
384 */
385#ifndef TARGET_NT4
386NTSTATUS vgdrvNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp)
387#else
388NTSTATUS vgdrvNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath)
389#endif
390{
391 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
392#ifndef TARGET_NT4
393 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
394 LogFlowFunc(("ENTER: pDevObj=%p pIrp=%p\n", pDevObj, pIrp));
395#else
396 LogFlowFunc(("ENTER: pDrvObj=%p pDevObj=%p pRegPath=%p\n", pDrvObj, pDevObj, pRegPath));
397#endif
398
399 NTSTATUS rcNt;
400#ifdef TARGET_NT4
401 /*
402 * Let's have a look at what our PCI adapter offers.
403 */
404 LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
405
406 /* Assign the PCI resources. */
407 PCM_RESOURCE_LIST pResourceList = NULL;
408 UNICODE_STRING classNameString;
409 RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter");
410 rcNt = HalAssignSlotResources(pRegPath, &classNameString, pDrvObj, pDevObj,
411 PCIBus, pDevExt->busNumber, pDevExt->slotNumber, &pResourceList);
412# ifdef LOG_ENABLED
413 if (pResourceList && pResourceList->Count > 0)
414 vgdrvNtShowDeviceResources(&pResourceList->List[0].PartialResourceList);
415# endif
416 if (NT_SUCCESS(rcNt))
417 rcNt = vgdrvNtScanPCIResourceList(pResourceList, pDevExt);
418#else
419# ifdef LOG_ENABLED
420 if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0)
421 vgdrvNtShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
422# endif
423 rcNt = vgdrvNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt);
424#endif
425 if (NT_SUCCESS(rcNt))
426 {
427 /*
428 * Map physical address of VMMDev memory into MMIO region
429 * and init the common device extension bits.
430 */
431 void *pvMMIOBase = NULL;
432 uint32_t cbMMIO = 0;
433 rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
434 pDevExt->vmmDevPhysMemoryAddress,
435 pDevExt->vmmDevPhysMemoryLength,
436 &pvMMIOBase,
437 &cbMMIO);
438 if (NT_SUCCESS(rcNt))
439 {
440 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
441
442 LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
443 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
444
445 int vrc = VGDrvCommonInitDevExt(&pDevExt->Core,
446 pDevExt->Core.IOPortBase,
447 pvMMIOBase, cbMMIO,
448 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
449 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
450 if (RT_FAILURE(vrc))
451 {
452 LogFunc(("Could not init device extension, vrc=%Rrc\n", vrc));
453 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
454 }
455 }
456 else
457 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
458 }
459
460 if (NT_SUCCESS(rcNt))
461 {
462 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
463 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
464 if (RT_FAILURE(vrc))
465 {
466 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
467 rcNt = STATUS_UNSUCCESSFUL;
468 }
469 }
470
471 if (NT_SUCCESS(rcNt))
472 {
473 /*
474 * Register DPC and ISR.
475 */
476 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
477
478 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
479#ifdef TARGET_NT4
480 ULONG uInterruptVector = UINT32_MAX;
481 KIRQL irqLevel = UINT8_MAX;
482 /* Get an interrupt vector. */
483 /* Only proceed if the device provides an interrupt. */
484 if ( pDevExt->interruptLevel
485 || pDevExt->interruptVector)
486 {
487 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
488 pDevExt->busNumber, pDevExt->interruptLevel, pDevExt->interruptVector));
489
490 uInterruptVector = HalGetInterruptVector(PCIBus,
491 pDevExt->busNumber,
492 pDevExt->interruptLevel,
493 pDevExt->interruptVector,
494 &irqLevel,
495 &pDevExt->interruptAffinity);
496 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
497 if (uInterruptVector == 0)
498 LogFunc(("No interrupt vector found!\n"));
499 }
500 else
501 LogFunc(("Device does not provide an interrupt!\n"));
502#endif
503 if (pDevExt->interruptVector)
504 {
505#ifdef TARGET_NT4
506 LogFlowFunc(("Connecting interrupt (IntVector=%#u), IrqLevel=%u) ...\n", uInterruptVector, irqLevel));
507#else
508 LogFlowFunc(("Connecting interrupt (IntVector=%#u), IrqLevel=%u) ...\n", pDevExt->interruptVector, pDevExt->interruptLevel));
509#endif
510
511 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
512 (PKSERVICE_ROUTINE)vgdrvNtIsrHandler, /* Our ISR handler. */
513 pDevExt, /* Device context. */
514 NULL, /* Optional spinlock. */
515#ifdef TARGET_NT4
516 uInterruptVector, /* Interrupt vector. */
517 irqLevel, /* Interrupt level. */
518 irqLevel, /* Interrupt level. */
519#else
520 pDevExt->interruptVector, /* Interrupt vector. */
521 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
522 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
523#endif
524 pDevExt->interruptMode, /* LevelSensitive or Latched. */
525 TRUE, /* Shareable interrupt. */
526 pDevExt->interruptAffinity, /* CPU affinity. */
527 FALSE); /* Don't save FPU stack. */
528 if (NT_ERROR(rcNt))
529 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
530 }
531 else
532 LogFunc(("No interrupt vector found!\n"));
533 }
534
535 if (NT_SUCCESS(rcNt))
536 {
537 /*
538 * Once we've read configuration from register and host, we're finally read.
539 */
540 pDevExt->Core.fLoggingEnabled = true; /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
541 vgdrvNtReadConfiguration(pDevExt);
542
543 /* Ready to rumble! */
544 LogRelFunc(("Device is ready!\n"));
545 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_WORKING);
546 }
547 else
548 pDevExt->pInterruptObject = NULL;
549
550 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
551 * whole bunch of things... */
552
553 LogFunc(("Returned with rcNt=%#x\n", rcNt));
554 return rcNt;
555}
556
557
558/**
559 * Cleans up hardware resources.
560 * Do not delete DevExt here.
561 *
562 * @todo r=bird: HC SVNT DRACONES!
563 *
564 * This code leaves clients hung when vgdrvNtInit is called afterwards.
565 * This happens when for instance hotplugging a CPU. Problem is
566 * vgdrvNtInit doing a full VGDrvCommonInitDevExt, orphaning all pDevExt
567 * members, like session lists and stuff.
568 *
569 * @param pDevObj Device object.
570 */
571NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj)
572{
573 LogFlowFuncEnter();
574
575 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
576 if (pDevExt)
577 {
578
579#if 0 /** @todo test & enable cleaning global session data */
580#ifdef VBOX_WITH_HGCM
581 if (pDevExt->pKernelSession)
582 {
583 VGDrvCommonCloseSession(pDevExt, pDevExt->pKernelSession);
584 pDevExt->pKernelSession = NULL;
585 }
586#endif
587#endif
588
589 if (pDevExt->pInterruptObject)
590 {
591 IoDisconnectInterrupt(pDevExt->pInterruptObject);
592 pDevExt->pInterruptObject = NULL;
593 }
594
595 /** @todo cleanup the rest stuff */
596
597
598#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
599 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
600#endif
601 /* According to MSDN we have to unmap previously mapped memory. */
602 vgdrvNtUnmapVMMDevMemory(pDevExt);
603 }
604
605 return STATUS_SUCCESS;
606}
607
608
609/**
610 * Unload the driver.
611 *
612 * @param pDrvObj Driver object.
613 */
614static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
615{
616 LogFlowFuncEnter();
617
618#ifdef TARGET_NT4
619 vgdrvNtCleanup(pDrvObj->DeviceObject);
620
621 /* Destroy device extension and clean up everything else. */
622 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
623 VGDrvCommonDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
624
625 /*
626 * I don't think it's possible to unload a driver which processes have
627 * opened, at least we'll blindly assume that here.
628 */
629 UNICODE_STRING DosName;
630 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
631 IoDeleteSymbolicLink(&DosName);
632
633 IoDeleteDevice(pDrvObj->DeviceObject);
634#else /* !TARGET_NT4 */
635 /* On a PnP driver this routine will be called after
636 * IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
637 * so don't do anything here (yet). */
638 RT_NOREF1(pDrvObj);
639#endif /* !TARGET_NT4 */
640
641 LogFlowFunc(("Returning\n"));
642}
643
644
645/**
646 * For simplifying request completion into a simple return statement, extended
647 * version.
648 *
649 * @returns rcNt
650 * @param rcNt The status code.
651 * @param uInfo Extra info value.
652 * @param pIrp The IRP.
653 */
654DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
655{
656 pIrp->IoStatus.Status = rcNt;
657 pIrp->IoStatus.Information = uInfo;
658 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
659 return rcNt;
660}
661
662
663/**
664 * For simplifying request completion into a simple return statement.
665 *
666 * @returns rcNt
667 * @param rcNt The status code.
668 * @param pIrp The IRP.
669 */
670DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
671{
672 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
673}
674
675
676/**
677 * Create (i.e. Open) file entry point.
678 *
679 * @param pDevObj Device object.
680 * @param pIrp Request packet.
681 */
682static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
683{
684 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
685 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
686 PFILE_OBJECT pFileObj = pStack->FileObject;
687 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
688
689 Assert(pFileObj->FsContext == NULL);
690
691 /*
692 * We are not remotely similar to a directory...
693 * (But this is possible.)
694 */
695 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
696 {
697 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
698 return vgdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
699 }
700
701 /*
702 * Check the device state.
703 */
704 if (pDevExt->enmDevState != VGDRVNTDEVSTATE_WORKING)
705 {
706 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", pDevExt->enmDevState));
707 return vgdrvNtCompleteRequest(STATUS_DEVICE_NOT_READY, pIrp);
708 }
709
710 /*
711 * Create a client session.
712 */
713 int rc;
714 PVBOXGUESTSESSION pSession;
715 if (pIrp->RequestorMode == KernelMode)
716 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
717 else
718 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, &pSession);
719 if (RT_SUCCESS(rc))
720 {
721 pFileObj->FsContext = pSession;
722 Log(("vgdrvNtCreate: Successfully created %s session %p\n",
723 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession));
724 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
725 }
726
727 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
728 /* Note. the IoStatus is completely ignored on error. */
729 if (rc == VERR_NO_MEMORY)
730 return vgdrvNtCompleteRequest(STATUS_NO_MEMORY, pIrp);
731 return vgdrvNtCompleteRequest(STATUS_UNSUCCESSFUL, pIrp);
732}
733
734
735/**
736 * Close file entry point.
737 *
738 * @param pDevObj Device object.
739 * @param pIrp Request packet.
740 */
741static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
742{
743 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
744 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
745 PFILE_OBJECT pFileObj = pStack->FileObject;
746
747 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
748
749#ifdef VBOX_WITH_HGCM
750 /* Close both, R0 and R3 sessions. */
751 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
752 if (pSession)
753 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
754#endif
755
756 pFileObj->FsContext = NULL;
757 pIrp->IoStatus.Information = 0;
758 pIrp->IoStatus.Status = STATUS_SUCCESS;
759 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
760
761 return STATUS_SUCCESS;
762}
763
764
765/**
766 * Device I/O Control entry point.
767 *
768 * @param pDevObj Device object.
769 * @param pIrp Request packet.
770 */
771NTSTATUS _stdcall vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
772{
773 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
774 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
775 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
776
777 if (!RT_VALID_PTR(pSession))
778 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
779
780#if 0 /* No fast I/O controls defined yet. */
781 /*
782 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
783 * the session and iCmd, and does not return anything.
784 */
785 if (pSession->fUnrestricted)
786 {
787 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
788 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
789 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
790 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
791 {
792 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
793
794 /* Complete the I/O request. */
795 supdrvSessionRelease(pSession);
796 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
797 }
798 }
799#endif
800
801 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
802}
803
804
805/**
806 * Device I/O Control entry point.
807 *
808 * @param pDevExt The device extension.
809 * @param pSession The session.
810 * @param pIrp Request packet.
811 * @param pStack The request stack pointer.
812 */
813static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
814 PIRP pIrp, PIO_STACK_LOCATION pStack)
815{
816 NTSTATUS rcNt;
817 uint32_t cbOut = 0;
818 int rc = 0;
819 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
820 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
821 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
822 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
823
824#if 0 /*def RT_ARCH_AMD64*/
825 /* Don't allow 32-bit processes to do any I/O controls. */
826 if (!IoIs32bitProcess(pIrp))
827#endif
828 {
829 /* Verify that it's a buffered CTL. */
830 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
831 {
832 /* Verify that the sizes in the request header are correct. */
833 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
834 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
835 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
836 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
837 {
838 /* Zero extra output bytes to make sure we don't leak anything. */
839 if (pHdr->cbIn < pHdr->cbOut)
840 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
841
842 /*
843 * Do the job.
844 */
845 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
846 RT_MAX(pHdr->cbIn, pHdr->cbOut));
847 if (RT_SUCCESS(rc))
848 {
849 rcNt = STATUS_SUCCESS;
850 cbOut = pHdr->cbOut;
851 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
852 {
853 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
854 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
855 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
856 }
857
858 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
859 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
860 && RT_SUCCESS(pHdr->rc))
861 pStack->FileObject->FsContext = NULL;
862 }
863 else if (rc == VERR_NOT_SUPPORTED)
864 rcNt = STATUS_NOT_SUPPORTED;
865 else
866 rcNt = STATUS_INVALID_PARAMETER;
867 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
868 }
869 else
870 {
871 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
872 pStack->Parameters.DeviceIoControl.IoControlCode,
873 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
874 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
875 pStack->Parameters.DeviceIoControl.InputBufferLength,
876 pStack->Parameters.DeviceIoControl.OutputBufferLength));
877 rcNt = STATUS_INVALID_PARAMETER;
878 }
879 }
880 else
881 {
882 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
883 pStack->Parameters.DeviceIoControl.IoControlCode));
884 rcNt = STATUS_NOT_SUPPORTED;
885 }
886 }
887#if 0 /*def RT_ARCH_AMD64*/
888 else
889 {
890 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
891 rcNt = STATUS_NOT_SUPPORTED;
892 }
893#endif
894
895 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
896}
897
898
899/**
900 * Internal Device I/O Control entry point (for IDC).
901 *
902 * @param pDevObj Device object.
903 * @param pIrp Request packet.
904 */
905static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
906{
907 /* Currently no special code here. */
908 return vgdrvNtDeviceControl(pDevObj, pIrp);
909}
910
911
912/**
913 * IRP_MJ_SYSTEM_CONTROL handler.
914 *
915 * @returns NT status code
916 * @param pDevObj Device object.
917 * @param pIrp IRP.
918 */
919static NTSTATUS vgdrvNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
920{
921 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
922
923 LogFlowFuncEnter();
924
925 /* Always pass it on to the next driver. */
926 IoSkipCurrentIrpStackLocation(pIrp);
927
928 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
929}
930
931
932/**
933 * IRP_MJ_SHUTDOWN handler.
934 *
935 * @returns NT status code
936 * @param pDevObj Device object.
937 * @param pIrp IRP.
938 */
939static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
940{
941 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
942 LogFlowFuncEnter();
943
944 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
945 if (pReq)
946 {
947 pReq->header.requestType = VMMDevReq_SetPowerStatus;
948 pReq->powerState = VMMDevPowerState_PowerOff;
949
950 int rc = VbglR0GRPerform(&pReq->header);
951 if (RT_FAILURE(rc))
952 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
953 }
954
955 /* just in case, since we shouldn't normally get here. */
956 pIrp->IoStatus.Information = 0;
957 pIrp->IoStatus.Status = STATUS_SUCCESS;
958 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
959 return STATUS_SUCCESS;
960}
961
962
963/**
964 * Stub function for functions we don't implemented.
965 *
966 * @returns STATUS_NOT_SUPPORTED
967 * @param pDevObj Device object.
968 * @param pIrp IRP.
969 */
970static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
971{
972 RT_NOREF1(pDevObj);
973 LogFlowFuncEnter();
974
975 pIrp->IoStatus.Information = 0;
976 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
977 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
978
979 return STATUS_NOT_SUPPORTED;
980}
981
982
983/**
984 * Sets the mouse notification callback.
985 *
986 * @returns VBox status code.
987 * @param pDevExt Pointer to the device extension.
988 * @param pNotify Pointer to the mouse notify struct.
989 */
990int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
991{
992 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
993 /* we need a lock here to avoid concurrency with the set event functionality */
994 KIRQL OldIrql;
995 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessLock, &OldIrql);
996 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
997 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
998 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessLock, OldIrql);
999 return VINF_SUCCESS;
1000}
1001
1002
1003/**
1004 * DPC handler.
1005 *
1006 * @param pDPC DPC descriptor.
1007 * @param pDevObj Device object.
1008 * @param pIrp Interrupt request packet.
1009 * @param pContext Context specific pointer.
1010 */
1011static void vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
1012{
1013 RT_NOREF3(pDPC, pIrp, pContext);
1014 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1015 Log3Func(("pDevExt=0x%p\n", pDevExt));
1016
1017 /* Test & reset the counter. */
1018 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
1019 {
1020 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
1021 * i.e. to prevent the event from destroyed while we're using it */
1022 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
1023 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessLock);
1024
1025 if (pDevExt->Core.pfnMouseNotifyCallback)
1026 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
1027
1028 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessLock);
1029 }
1030
1031 /* Process the wake-up list we were asked by the scheduling a DPC
1032 * in vgdrvNtIsrHandler(). */
1033 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
1034}
1035
1036
1037/**
1038 * ISR handler.
1039 *
1040 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
1041 * @param pInterrupt Interrupt that was triggered.
1042 * @param pServiceContext Context specific pointer.
1043 */
1044static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1045{
1046 RT_NOREF1(pInterrupt);
1047 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
1048 if (pDevExt == NULL)
1049 return FALSE;
1050
1051 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
1052
1053 /* Enter the common ISR routine and do the actual work. */
1054 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
1055
1056 /* If we need to wake up some events we do that in a DPC to make
1057 * sure we're called at the right IRQL. */
1058 if (fIRQTaken)
1059 {
1060 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
1061 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
1062 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
1063 {
1064 Log3Func(("Requesting DPC ...\n"));
1065 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
1066 }
1067 }
1068 return fIRQTaken;
1069}
1070
1071
1072/**
1073 * Overridden routine for mouse polling events.
1074 *
1075 * @param pDevExt Device extension structure.
1076 */
1077void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1078{
1079 NOREF(pDevExt);
1080 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
1081 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
1082 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
1083}
1084
1085
1086/**
1087 * Hook for handling OS specfic options from the host.
1088 *
1089 * @returns true if handled, false if not.
1090 * @param pDevExt The device extension.
1091 * @param pszName The option name.
1092 * @param pszValue The option value.
1093 */
1094bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1095{
1096 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1097 return false;
1098}
1099
1100
1101
1102/**
1103 * Queries (gets) a DWORD value from the registry.
1104 *
1105 * @return NTSTATUS
1106 * @param uRoot Relative path root. See RTL_REGISTRY_SERVICES or RTL_REGISTRY_ABSOLUTE.
1107 * @param pwszPath Path inside path root.
1108 * @param pwszName Actual value name to look up.
1109 * @param puValue On input this can specify the default value (if RTL_REGISTRY_OPTIONAL is
1110 * not specified in ulRoot), on output this will retrieve the looked up
1111 * registry value if found.
1112 */
1113static NTSTATUS vgdrvNtRegistryReadDWORD(ULONG uRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue)
1114{
1115 if (!pwszPath || !pwszName || !puValue)
1116 return STATUS_INVALID_PARAMETER;
1117
1118 ULONG uDefault = *puValue;
1119 RTL_QUERY_REGISTRY_TABLE aQuery[2];
1120 RT_ZERO(aQuery);
1121 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
1122 aQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1123 aQuery[0].Name = pwszName;
1124 aQuery[0].EntryContext = puValue;
1125 aQuery[0].DefaultType = REG_DWORD;
1126 aQuery[0].DefaultData = &uDefault;
1127 aQuery[0].DefaultLength = sizeof(uDefault);
1128
1129 return RtlQueryRegistryValues(uRoot, pwszPath, &aQuery[0], NULL /* Context */, NULL /* Environment */);
1130}
1131
1132
1133/**
1134 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
1135 */
1136static NTSTATUS NTAPI vbdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
1137 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
1138{
1139 /*
1140 * Filter out general service config values.
1141 */
1142 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
1143 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
1144 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
1145 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
1146 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
1147 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
1148 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
1149 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
1150 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
1151 )
1152 {
1153 return STATUS_SUCCESS;
1154 }
1155
1156 /*
1157 * Convert the value name.
1158 */
1159 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
1160 if (cch < 64 && cch > 0)
1161 {
1162 char szValueName[72];
1163 char *pszTmp = szValueName;
1164 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
1165 if (RT_SUCCESS(rc))
1166 {
1167 /*
1168 * Convert the value.
1169 */
1170 char szValue[72];
1171 char *pszFree = NULL;
1172 char *pszValue = NULL;
1173 szValue[0] = '\0';
1174 switch (uValueType)
1175 {
1176 case REG_SZ:
1177 case REG_EXPAND_SZ:
1178 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
1179 if (RT_SUCCESS(rc) && cch < _1K)
1180 {
1181 if (cch < sizeof(szValue))
1182 {
1183 pszValue = szValue;
1184 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
1185 }
1186 else
1187 {
1188 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
1189 if (RT_SUCCESS(rc))
1190 pszFree = pszValue;
1191 }
1192 if (RT_FAILURE(rc))
1193 {
1194 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
1195 pwszValueName, rc));
1196 pszValue = NULL;
1197 }
1198 }
1199 else if (RT_SUCCESS(rc))
1200 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
1201 pwszValueName, cbValue, uValueType));
1202 else
1203 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
1204 pwszValueName, cbValue, uValueType));
1205 break;
1206
1207 case REG_DWORD:
1208 if (cbValue == sizeof(uint32_t))
1209 {
1210 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
1211 pszValue = szValue;
1212 }
1213 else
1214 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
1215 break;
1216
1217 case REG_QWORD:
1218 if (cbValue == sizeof(uint64_t))
1219 {
1220 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
1221 pszValue = szValue;
1222 }
1223 else
1224 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
1225 break;
1226
1227 default:
1228 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
1229 break;
1230 }
1231 if (pszValue)
1232 {
1233 /*
1234 * Process it.
1235 */
1236 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
1237 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
1238 if (pszFree)
1239 RTStrFree(pszFree);
1240 }
1241 }
1242 }
1243 else if (cch > 0)
1244 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
1245 else
1246 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
1247 NOREF(pvEntryCtx);
1248 return STATUS_SUCCESS;
1249}
1250
1251
1252/**
1253 * Reads configuration from the registry and guest properties.
1254 *
1255 * We ignore failures and instead preserve existing configuration values.
1256 *
1257 * Thie routine will block.
1258 *
1259 * @param pDevExt The device extension.
1260 */
1261static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
1262{
1263 /*
1264 * First the registry.
1265 */
1266 RTL_QUERY_REGISTRY_TABLE aQuery[2];
1267 RT_ZERO(aQuery);
1268 aQuery[0].QueryRoutine = vbdrvNtRegistryEnumCallback;
1269 aQuery[0].Flags = 0;
1270 aQuery[0].Name = NULL;
1271 aQuery[0].EntryContext = NULL;
1272 aQuery[0].DefaultType = REG_NONE;
1273 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /* Environment */);
1274 if (!NT_SUCCESS(rcNt))
1275 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
1276
1277 /*
1278 * Read configuration from the host.
1279 */
1280 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
1281}
1282
1283
1284/**
1285 * Helper to scan the PCI resource list and remember stuff.
1286 *
1287 * @param pResList Resource list
1288 * @param pDevExt Device extension
1289 */
1290static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
1291{
1292 /* Enumerate the resource list. */
1293 LogFlowFunc(("Found %d resources\n",
1294 pResList->List->PartialResourceList.Count));
1295
1296 NTSTATUS rc = STATUS_SUCCESS;
1297 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
1298 ULONG rangeCount = 0;
1299 ULONG cMMIORange = 0;
1300 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->pciBaseAddress;
1301 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
1302 {
1303 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
1304 switch (pPartialData->Type)
1305 {
1306 case CmResourceTypePort:
1307 {
1308 /* Overflow protection. */
1309 if (rangeCount < PCI_TYPE0_ADDRESSES)
1310 {
1311 LogFlowFunc(("I/O range: Base=%08x:%08x, length=%08x\n",
1312 pPartialData->u.Port.Start.HighPart,
1313 pPartialData->u.Port.Start.LowPart,
1314 pPartialData->u.Port.Length));
1315
1316 /* Save the IO port base. */
1317 /** @todo Not so good.
1318 * Update/bird: What is not so good? That we just consider the last range? */
1319 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
1320
1321 /* Save resource information. */
1322 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
1323 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
1324 pBaseAddress->RangeInMemory = FALSE;
1325 pBaseAddress->ResourceMapped = FALSE;
1326
1327 LogFunc(("I/O range for VMMDev found! Base=%08x:%08x, length=%08x\n",
1328 pPartialData->u.Port.Start.HighPart,
1329 pPartialData->u.Port.Start.LowPart,
1330 pPartialData->u.Port.Length));
1331
1332 /* Next item ... */
1333 rangeCount++; pBaseAddress++;
1334 }
1335 break;
1336 }
1337
1338 case CmResourceTypeInterrupt:
1339 {
1340 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
1341 pPartialData->u.Interrupt.Level,
1342 pPartialData->u.Interrupt.Vector,
1343 pPartialData->Flags));
1344
1345 /* Save information. */
1346 pDevExt->interruptLevel = pPartialData->u.Interrupt.Level;
1347 pDevExt->interruptVector = pPartialData->u.Interrupt.Vector;
1348 pDevExt->interruptAffinity = pPartialData->u.Interrupt.Affinity;
1349
1350 /* Check interrupt mode. */
1351 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
1352 pDevExt->interruptMode = Latched;
1353 else
1354 pDevExt->interruptMode = LevelSensitive;
1355 break;
1356 }
1357
1358 case CmResourceTypeMemory:
1359 {
1360 /* Overflow protection. */
1361 if (rangeCount < PCI_TYPE0_ADDRESSES)
1362 {
1363 LogFlowFunc(("Memory range: Base=%08x:%08x, length=%08x\n",
1364 pPartialData->u.Memory.Start.HighPart,
1365 pPartialData->u.Memory.Start.LowPart,
1366 pPartialData->u.Memory.Length));
1367
1368 /* We only care about read/write memory. */
1369 /** @todo Reconsider memory type. */
1370 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
1371 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
1372 {
1373 /* Save physical MMIO base + length for VMMDev. */
1374 pDevExt->vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start;
1375 pDevExt->vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length;
1376
1377 /* Save resource information. */
1378 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
1379 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
1380 pBaseAddress->RangeInMemory = TRUE;
1381 pBaseAddress->ResourceMapped = FALSE;
1382
1383 LogFunc(("Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1384 pPartialData->u.Memory.Start.HighPart,
1385 pPartialData->u.Memory.Start.LowPart,
1386 pPartialData->u.Memory.Length));
1387
1388 /* Next item ... */
1389 rangeCount++; pBaseAddress++; cMMIORange++;
1390 }
1391 else
1392 LogFunc(("Ignoring memory: Flags=%08x\n", pPartialData->Flags));
1393 }
1394 break;
1395 }
1396
1397 default:
1398 {
1399 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
1400 break;
1401 }
1402 }
1403 }
1404
1405 /* Memorize the number of resources found. */
1406 pDevExt->pciAddressCount = rangeCount;
1407 return rc;
1408}
1409
1410
1411/**
1412 * Maps the I/O space from VMMDev to virtual kernel address space.
1413 *
1414 * @return NTSTATUS
1415 *
1416 * @param pDevExt The device extension.
1417 * @param PhysAddr Physical address to map.
1418 * @param cbToMap Number of bytes to map.
1419 * @param ppvMMIOBase Pointer of mapped I/O base.
1420 * @param pcbMMIO Length of mapped I/O base.
1421 */
1422static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
1423 void **ppvMMIOBase, uint32_t *pcbMMIO)
1424{
1425 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
1426 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
1427 /* pcbMMIO is optional. */
1428
1429 NTSTATUS rc = STATUS_SUCCESS;
1430 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
1431 {
1432 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
1433 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
1434 if (pVMMDevMemory)
1435 {
1436 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
1437
1438 /* Check version of the structure; do we have the right memory version? */
1439 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
1440 {
1441 /* Save results. */
1442 *ppvMMIOBase = pVMMDevMemory;
1443 if (pcbMMIO) /* Optional. */
1444 *pcbMMIO = pVMMDevMemory->u32Size;
1445
1446 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
1447 }
1448 else
1449 {
1450 /* Not our version, refuse operation and unmap the memory. */
1451 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
1452
1453 vgdrvNtUnmapVMMDevMemory(pDevExt);
1454 rc = STATUS_UNSUCCESSFUL;
1455 }
1456 }
1457 else
1458 rc = STATUS_UNSUCCESSFUL;
1459 }
1460 return rc;
1461}
1462
1463
1464/**
1465 * Unmaps the VMMDev I/O range from kernel space.
1466 *
1467 * @param pDevExt The device extension.
1468 */
1469void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1470{
1471 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
1472 if (pDevExt->Core.pVMMDevMemory)
1473 {
1474 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->vmmDevPhysMemoryLength);
1475 pDevExt->Core.pVMMDevMemory = NULL;
1476 }
1477
1478 pDevExt->vmmDevPhysMemoryAddress.QuadPart = 0;
1479 pDevExt->vmmDevPhysMemoryLength = 0;
1480}
1481
1482
1483/**
1484 * Translates NT version to VBox OS.
1485 *
1486 * @returns VBox OS type.
1487 * @param enmNtVer The NT version.
1488 */
1489VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
1490{
1491 VBOXOSTYPE enmOsType;
1492 switch (enmNtVer)
1493 {
1494 case VGDRVNTVER_WINNT4:
1495 enmOsType = VBOXOSTYPE_WinNT4;
1496 break;
1497
1498 case VGDRVNTVER_WIN2K:
1499 enmOsType = VBOXOSTYPE_Win2k;
1500 break;
1501
1502 case VGDRVNTVER_WINXP:
1503#if ARCH_BITS == 64
1504 enmOsType = VBOXOSTYPE_WinXP_x64;
1505#else
1506 enmOsType = VBOXOSTYPE_WinXP;
1507#endif
1508 break;
1509
1510 case VGDRVNTVER_WIN2K3:
1511#if ARCH_BITS == 64
1512 enmOsType = VBOXOSTYPE_Win2k3_x64;
1513#else
1514 enmOsType = VBOXOSTYPE_Win2k3;
1515#endif
1516 break;
1517
1518 case VGDRVNTVER_WINVISTA:
1519#if ARCH_BITS == 64
1520 enmOsType = VBOXOSTYPE_WinVista_x64;
1521#else
1522 enmOsType = VBOXOSTYPE_WinVista;
1523#endif
1524 break;
1525
1526 case VGDRVNTVER_WIN7:
1527#if ARCH_BITS == 64
1528 enmOsType = VBOXOSTYPE_Win7_x64;
1529#else
1530 enmOsType = VBOXOSTYPE_Win7;
1531#endif
1532 break;
1533
1534 case VGDRVNTVER_WIN8:
1535#if ARCH_BITS == 64
1536 enmOsType = VBOXOSTYPE_Win8_x64;
1537#else
1538 enmOsType = VBOXOSTYPE_Win8;
1539#endif
1540 break;
1541
1542 case VGDRVNTVER_WIN81:
1543#if ARCH_BITS == 64
1544 enmOsType = VBOXOSTYPE_Win81_x64;
1545#else
1546 enmOsType = VBOXOSTYPE_Win81;
1547#endif
1548 break;
1549
1550 case VGDRVNTVER_WIN10:
1551#if ARCH_BITS == 64
1552 enmOsType = VBOXOSTYPE_Win10_x64;
1553#else
1554 enmOsType = VBOXOSTYPE_Win10;
1555#endif
1556 break;
1557
1558 default:
1559 /* We don't know, therefore NT family. */
1560 enmOsType = VBOXOSTYPE_WinNT;
1561 break;
1562 }
1563 return enmOsType;
1564}
1565
1566#ifdef VBOX_STRICT
1567
1568/**
1569 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
1570 */
1571static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
1572{
1573 AssertPtrReturn(pu32Bits, 0);
1574 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
1575 uint32_t u32Result = 0;
1576 uint32_t u32WorkingMask = u32Mask;
1577 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1578
1579 while (iBitOffset > 0)
1580 {
1581 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
1582 if (fSet)
1583 u32Result |= 1 << (iBitOffset - 1);
1584 u32WorkingMask &= ~(1 << (iBitOffset - 1));
1585 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1586 }
1587 LogFlowFunc(("Returning %#x\n", u32Result));
1588 return u32Result;
1589}
1590
1591
1592static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
1593{
1594 ULONG u32Bits2 = u32Bits;
1595 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1596 if ( u32Result != u32Exp
1597 || (u32Bits2 & u32Mask)
1598 || (u32Bits2 & u32Result)
1599 || ((u32Bits2 | u32Result) != u32Bits)
1600 )
1601 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
1602 u32Mask, u32Bits, u32Bits2, u32Result));
1603}
1604
1605
1606static void vgdrvNtDoTests(void)
1607{
1608 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1609 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
1610 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1611 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1612 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1613 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1614}
1615
1616#endif /* VBOX_STRICT */
1617
1618#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1619
1620/*
1621 * DPC latency checker.
1622 */
1623
1624/**
1625 * One DPC latency sample.
1626 */
1627typedef struct DPCSAMPLE
1628{
1629 LARGE_INTEGER PerfDelta;
1630 LARGE_INTEGER PerfCounter;
1631 LARGE_INTEGER PerfFrequency;
1632 uint64_t u64TSC;
1633} DPCSAMPLE;
1634AssertCompileSize(DPCSAMPLE, 4*8);
1635
1636/**
1637 * The DPC latency measurement workset.
1638 */
1639typedef struct DPCDATA
1640{
1641 KDPC Dpc;
1642 KTIMER Timer;
1643 KSPIN_LOCK SpinLock;
1644
1645 ULONG ulTimerRes;
1646
1647 bool volatile fFinished;
1648
1649 /** The timer interval (relative). */
1650 LARGE_INTEGER DueTime;
1651
1652 LARGE_INTEGER PerfCounterPrev;
1653
1654 /** Align the sample array on a 64 byte boundrary just for the off chance
1655 * that we'll get cache line aligned memory backing this structure. */
1656 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
1657
1658 int cSamples;
1659 DPCSAMPLE aSamples[8192];
1660} DPCDATA;
1661
1662AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
1663
1664# define VBOXGUEST_DPC_TAG 'DPCS'
1665
1666
1667/**
1668 * DPC callback routine for the DPC latency measurement code.
1669 *
1670 * @param pDpc The DPC, not used.
1671 * @param pvDeferredContext Pointer to the DPCDATA.
1672 * @param SystemArgument1 System use, ignored.
1673 * @param SystemArgument2 System use, ignored.
1674 */
1675static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
1676{
1677 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
1678 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
1679
1680 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
1681
1682 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
1683 pData->fFinished = true;
1684 else
1685 {
1686 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
1687
1688 pSample->u64TSC = ASMReadTSC();
1689 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
1690 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
1691
1692 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
1693
1694 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1695 }
1696
1697 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1698}
1699
1700
1701/**
1702 * Handles the DPC latency checker request.
1703 *
1704 * @returns VBox status code.
1705 */
1706int VGDrvNtIOCtl_DpcLatencyChecker(void)
1707{
1708 /*
1709 * Allocate a block of non paged memory for samples and related data.
1710 */
1711 DPCDATA *pData = (DPCDATA *)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCDATA), VBOXGUEST_DPC_TAG);
1712 if (!pData)
1713 {
1714 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
1715 return VERR_NO_MEMORY;
1716 }
1717
1718 /*
1719 * Initialize the data.
1720 */
1721 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
1722 KeInitializeTimer(&pData->Timer);
1723 KeInitializeSpinLock(&pData->SpinLock);
1724
1725 pData->fFinished = false;
1726 pData->cSamples = 0;
1727 pData->PerfCounterPrev.QuadPart = 0;
1728
1729 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
1730 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
1731
1732 /*
1733 * Start the DPC measurements and wait for a full set.
1734 */
1735 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1736
1737 while (!pData->fFinished)
1738 {
1739 LARGE_INTEGER Interval;
1740 Interval.QuadPart = -100 * 1000 * 10;
1741 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
1742 }
1743
1744 ExSetTimerResolution(0, 0);
1745
1746 /*
1747 * Log everything to the host.
1748 */
1749 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
1750 for (int i = 0; i < pData->cSamples; i++)
1751 {
1752 DPCSAMPLE *pSample = &pData->aSamples[i];
1753
1754 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
1755 i,
1756 pSample->PerfDelta.QuadPart,
1757 pSample->PerfCounter.QuadPart,
1758 pSample->PerfFrequency.QuadPart,
1759 pSample->u64TSC);
1760 }
1761
1762 ExFreePoolWithTag(pData, VBOXGUEST_DPC_TAG);
1763 return VINF_SUCCESS;
1764}
1765
1766#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1767
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