VirtualBox

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

Last change on this file since 64306 was 64306, checked in by vboxsync, 8 years ago

vgdrvNtCleanup: Added @todo about nasty misbehavior if the driver is restarted like when CPUs are hotplugged.

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