VirtualBox

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

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

Additions: fixed a few 'the the' typos

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