VirtualBox

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

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

merging vbglioc r117700: VBoxGuest-win.cpp: Use IoCompleteRequest wrappers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.5 KB
Line 
1/* $Id: VBoxGuest-win.cpp 68554 2017-08-31 12:09:54Z 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 vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
52static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
53static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
54static NTSTATUS vgdrvNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue);
55static NTSTATUS vgdrvNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
56static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
57static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
58#ifdef VBOX_STRICT
59static void vgdrvNtDoTests(void);
60#endif
61static VOID vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
62static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
63static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt);
64static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
65 void **ppvMMIOBase, uint32_t *pcbMMIO);
66RT_C_DECLS_END
67
68
69/*********************************************************************************************************************************
70* Exported Functions *
71*********************************************************************************************************************************/
72RT_C_DECLS_BEGIN
73ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
74RT_C_DECLS_END
75
76#ifdef ALLOC_PRAGMA
77# pragma alloc_text(INIT, DriverEntry)
78# pragma alloc_text(PAGE, vgdrvNtAddDevice)
79# pragma alloc_text(PAGE, vgdrvNtUnload)
80# pragma alloc_text(PAGE, vgdrvNtCreate)
81# pragma alloc_text(PAGE, vgdrvNtClose)
82# pragma alloc_text(PAGE, vgdrvNtShutdown)
83# pragma alloc_text(PAGE, vgdrvNtNotSupportedStub)
84# pragma alloc_text(PAGE, vgdrvNtScanPCIResourceList)
85#endif
86
87
88/*********************************************************************************************************************************
89* Global Variables *
90*********************************************************************************************************************************/
91/** The detected NT (windows) version. */
92VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
93
94
95
96/**
97 * Driver entry point.
98 *
99 * @returns appropriate status code.
100 * @param pDrvObj Pointer to driver object.
101 * @param pRegPath Registry base path.
102 */
103ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
104{
105 RT_NOREF1(pRegPath);
106 NTSTATUS rc = STATUS_SUCCESS;
107
108 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
109
110 /*
111 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
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] = vgdrvNtDeviceControl;
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 if (NT_SUCCESS(rcNt))
527 {
528 ULONG uValue = 0;
529 NTSTATUS rcNt2 = vgdrvNtRegistryReadDWORD(RTL_REGISTRY_SERVICES, L"VBoxGuest", L"LoggingEnabled", &uValue);
530 if (NT_SUCCESS(rcNt2))
531 {
532 pDevExt->Core.fLoggingEnabled = uValue >= 0xFF;
533 if (pDevExt->Core.fLoggingEnabled)
534 LogRelFunc(("Logging to host log enabled (%#x)", uValue));
535 }
536
537 /* Ready to rumble! */
538 LogRelFunc(("Device is ready!\n"));
539 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_WORKING);
540 }
541 else
542 pDevExt->pInterruptObject = NULL;
543
544 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
545 * whole bunch of things... */
546
547 LogFunc(("Returned with rcNt=%#x\n", rcNt));
548 return rcNt;
549}
550
551
552/**
553 * Cleans up hardware resources.
554 * Do not delete DevExt here.
555 *
556 * @todo r=bird: HC SVNT DRACONES!
557 *
558 * This code leaves clients hung when vgdrvNtInit is called afterwards.
559 * This happens when for instance hotplugging a CPU. Problem is
560 * vgdrvNtInit doing a full VGDrvCommonInitDevExt, orphaning all pDevExt
561 * members, like session lists and stuff.
562 *
563 * @param pDevObj Device object.
564 */
565NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj)
566{
567 LogFlowFuncEnter();
568
569 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
570 if (pDevExt)
571 {
572
573#if 0 /** @todo test & enable cleaning global session data */
574#ifdef VBOX_WITH_HGCM
575 if (pDevExt->pKernelSession)
576 {
577 VGDrvCommonCloseSession(pDevExt, pDevExt->pKernelSession);
578 pDevExt->pKernelSession = NULL;
579 }
580#endif
581#endif
582
583 if (pDevExt->pInterruptObject)
584 {
585 IoDisconnectInterrupt(pDevExt->pInterruptObject);
586 pDevExt->pInterruptObject = NULL;
587 }
588
589 /** @todo cleanup the rest stuff */
590
591
592#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
593 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
594#endif
595 /* According to MSDN we have to unmap previously mapped memory. */
596 vgdrvNtUnmapVMMDevMemory(pDevExt);
597 }
598
599 return STATUS_SUCCESS;
600}
601
602
603/**
604 * Unload the driver.
605 *
606 * @param pDrvObj Driver object.
607 */
608static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
609{
610 LogFlowFuncEnter();
611
612#ifdef TARGET_NT4
613 vgdrvNtCleanup(pDrvObj->DeviceObject);
614
615 /* Destroy device extension and clean up everything else. */
616 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
617 VGDrvCommonDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
618
619 /*
620 * I don't think it's possible to unload a driver which processes have
621 * opened, at least we'll blindly assume that here.
622 */
623 UNICODE_STRING DosName;
624 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
625 IoDeleteSymbolicLink(&DosName);
626
627 IoDeleteDevice(pDrvObj->DeviceObject);
628#else /* !TARGET_NT4 */
629 /* On a PnP driver this routine will be called after
630 * IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
631 * so don't do anything here (yet). */
632 RT_NOREF1(pDrvObj);
633#endif /* !TARGET_NT4 */
634
635 LogFlowFunc(("Returning\n"));
636}
637
638
639/**
640 * For simplifying request completion into a simple return statement, extended
641 * version.
642 *
643 * @returns rcNt
644 * @param rcNt The status code.
645 * @param uInfo Extra info value.
646 * @param pIrp The IRP.
647 */
648DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
649{
650 pIrp->IoStatus.Status = rcNt;
651 pIrp->IoStatus.Information = uInfo;
652 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
653 return rcNt;
654}
655
656
657/**
658 * For simplifying request completion into a simple return statement.
659 *
660 * @returns rcNt
661 * @param rcNt The status code.
662 * @param pIrp The IRP.
663 */
664DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
665{
666 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
667}
668
669
670/**
671 * Create (i.e. Open) file entry point.
672 *
673 * @param pDevObj Device object.
674 * @param pIrp Request packet.
675 */
676static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
677{
678 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
679 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
680 PFILE_OBJECT pFileObj = pStack->FileObject;
681 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
682
683 Assert(pFileObj->FsContext == NULL);
684
685 /*
686 * We are not remotely similar to a directory...
687 * (But this is possible.)
688 */
689 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
690 {
691 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
692 return vgdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
693 }
694
695 /*
696 * Check the device state.
697 */
698 if (pDevExt->enmDevState != VGDRVNTDEVSTATE_WORKING)
699 {
700 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", pDevExt->enmDevState));
701 return vgdrvNtCompleteRequest(STATUS_DEVICE_NOT_READY, pIrp);
702 }
703
704 /*
705 * Create a client session.
706 */
707 int rc;
708 PVBOXGUESTSESSION pSession;
709 if (pIrp->RequestorMode == KernelMode)
710 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
711 else
712 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, &pSession);
713 if (RT_SUCCESS(rc))
714 {
715 pFileObj->FsContext = pSession;
716 Log(("vgdrvNtCreate: Successfully created %s session %p\n",
717 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession));
718 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
719 }
720
721 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
722 /* Note. the IoStatus is completely ignored on error. */
723 if (rc == VERR_NO_MEMORY)
724 return vgdrvNtCompleteRequest(STATUS_NO_MEMORY, pIrp);
725 return vgdrvNtCompleteRequest(STATUS_UNSUCCESSFUL, pIrp);
726}
727
728
729/**
730 * Close file entry point.
731 *
732 * @param pDevObj Device object.
733 * @param pIrp Request packet.
734 */
735static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
736{
737 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
738 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
739 PFILE_OBJECT pFileObj = pStack->FileObject;
740
741 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
742
743#ifdef VBOX_WITH_HGCM
744 /* Close both, R0 and R3 sessions. */
745 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
746 if (pSession)
747 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
748#endif
749
750 pFileObj->FsContext = NULL;
751 pIrp->IoStatus.Information = 0;
752 pIrp->IoStatus.Status = STATUS_SUCCESS;
753 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
754
755 return STATUS_SUCCESS;
756}
757
758
759/**
760 * Device I/O Control entry point.
761 *
762 * @param pDevObj Device object.
763 * @param pIrp Request packet.
764 */
765NTSTATUS _stdcall vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
766{
767 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
768 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
769 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
770
771 if (!RT_VALID_PTR(pSession))
772 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
773
774#if 0 /* No fast I/O controls defined yet. */
775 /*
776 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
777 * the session and iCmd, and does not return anything.
778 */
779 if (pSession->fUnrestricted)
780 {
781 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
782 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
783 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
784 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
785 {
786 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
787
788 /* Complete the I/O request. */
789 supdrvSessionRelease(pSession);
790 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
791 }
792 }
793#endif
794
795 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
796}
797
798
799/**
800 * Device I/O Control entry point.
801 *
802 * @param pDevExt The device extension.
803 * @param pSession The session.
804 * @param pIrp Request packet.
805 * @param pStack The request stack pointer.
806 */
807static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
808 PIRP pIrp, PIO_STACK_LOCATION pStack)
809{
810 NTSTATUS rcNt;
811 uint32_t cbOut = 0;
812 int rc = 0;
813 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
814 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
815 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
816 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
817
818#if 0 /*def RT_ARCH_AMD64*/
819 /* Don't allow 32-bit processes to do any I/O controls. */
820 if (!IoIs32bitProcess(pIrp))
821#endif
822 {
823 /* Verify that it's a buffered CTL. */
824 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
825 {
826 /* Verify that the sizes in the request header are correct. */
827 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
828 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
829 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
830 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
831 {
832 /* Zero extra output bytes to make sure we don't leak anything. */
833 if (pHdr->cbIn < pHdr->cbOut)
834 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
835
836 /*
837 * Do the job.
838 */
839 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
840 RT_MAX(pHdr->cbIn, pHdr->cbOut));
841 if (RT_SUCCESS(rc))
842 {
843 rcNt = STATUS_SUCCESS;
844 cbOut = pHdr->cbOut;
845 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
846 {
847 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
848 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
849 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
850 }
851
852 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
853 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
854 && RT_SUCCESS(pHdr->rc))
855 pStack->FileObject->FsContext = NULL;
856 }
857 else if (rc == VERR_NOT_SUPPORTED)
858 rcNt = STATUS_NOT_SUPPORTED;
859 else
860 rcNt = STATUS_INVALID_PARAMETER;
861 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
862 }
863 else
864 {
865 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
866 pStack->Parameters.DeviceIoControl.IoControlCode,
867 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
868 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
869 pStack->Parameters.DeviceIoControl.InputBufferLength,
870 pStack->Parameters.DeviceIoControl.OutputBufferLength));
871 rcNt = STATUS_INVALID_PARAMETER;
872 }
873 }
874 else
875 {
876 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
877 pStack->Parameters.DeviceIoControl.IoControlCode));
878 rcNt = STATUS_NOT_SUPPORTED;
879 }
880 }
881#if 0 /*def RT_ARCH_AMD64*/
882 else
883 {
884 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
885 rcNt = STATUS_NOT_SUPPORTED;
886 }
887#endif
888
889 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
890}
891
892
893/**
894 * Internal Device I/O Control entry point (for IDC).
895 *
896 * @param pDevObj Device object.
897 * @param pIrp Request packet.
898 */
899static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
900{
901 /* Currently no special code here. */
902 return vgdrvNtDeviceControl(pDevObj, pIrp);
903}
904
905
906/**
907 * IRP_MJ_SYSTEM_CONTROL handler.
908 *
909 * @returns NT status code
910 * @param pDevObj Device object.
911 * @param pIrp IRP.
912 */
913static NTSTATUS vgdrvNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
914{
915 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
916
917 LogFlowFuncEnter();
918
919 /* Always pass it on to the next driver. */
920 IoSkipCurrentIrpStackLocation(pIrp);
921
922 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
923}
924
925
926/**
927 * IRP_MJ_SHUTDOWN handler.
928 *
929 * @returns NT status code
930 * @param pDevObj Device object.
931 * @param pIrp IRP.
932 */
933static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
934{
935 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
936 LogFlowFuncEnter();
937
938 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
939 if (pReq)
940 {
941 pReq->header.requestType = VMMDevReq_SetPowerStatus;
942 pReq->powerState = VMMDevPowerState_PowerOff;
943
944 int rc = VbglGRPerform(&pReq->header);
945 if (RT_FAILURE(rc))
946 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
947 }
948
949 /* just in case, since we shouldn't normally get here. */
950 pIrp->IoStatus.Information = 0;
951 pIrp->IoStatus.Status = STATUS_SUCCESS;
952 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
953 return STATUS_SUCCESS;
954}
955
956
957/**
958 * Stub function for functions we don't implemented.
959 *
960 * @returns STATUS_NOT_SUPPORTED
961 * @param pDevObj Device object.
962 * @param pIrp IRP.
963 */
964static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
965{
966 RT_NOREF1(pDevObj);
967 LogFlowFuncEnter();
968
969 pIrp->IoStatus.Information = 0;
970 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
971 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
972
973 return STATUS_NOT_SUPPORTED;
974}
975
976
977/**
978 * Sets the mouse notification callback.
979 *
980 * @returns VBox status code.
981 * @param pDevExt Pointer to the device extension.
982 * @param pNotify Pointer to the mouse notify struct.
983 */
984int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
985{
986 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
987 /* we need a lock here to avoid concurrency with the set event functionality */
988 KIRQL OldIrql;
989 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessLock, &OldIrql);
990 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
991 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
992 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessLock, OldIrql);
993 return VINF_SUCCESS;
994}
995
996
997/**
998 * DPC handler.
999 *
1000 * @param pDPC DPC descriptor.
1001 * @param pDevObj Device object.
1002 * @param pIrp Interrupt request packet.
1003 * @param pContext Context specific pointer.
1004 */
1005static void vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
1006{
1007 RT_NOREF3(pDPC, pIrp, pContext);
1008 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1009 Log3Func(("pDevExt=0x%p\n", pDevExt));
1010
1011 /* Test & reset the counter. */
1012 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
1013 {
1014 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
1015 * i.e. to prevent the event from destroyed while we're using it */
1016 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
1017 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessLock);
1018
1019 if (pDevExt->Core.pfnMouseNotifyCallback)
1020 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
1021
1022 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessLock);
1023 }
1024
1025 /* Process the wake-up list we were asked by the scheduling a DPC
1026 * in vgdrvNtIsrHandler(). */
1027 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
1028}
1029
1030
1031/**
1032 * ISR handler.
1033 *
1034 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
1035 * @param pInterrupt Interrupt that was triggered.
1036 * @param pServiceContext Context specific pointer.
1037 */
1038static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1039{
1040 RT_NOREF1(pInterrupt);
1041 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
1042 if (pDevExt == NULL)
1043 return FALSE;
1044
1045 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
1046
1047 /* Enter the common ISR routine and do the actual work. */
1048 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
1049
1050 /* If we need to wake up some events we do that in a DPC to make
1051 * sure we're called at the right IRQL. */
1052 if (fIRQTaken)
1053 {
1054 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
1055 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
1056 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
1057 {
1058 Log3Func(("Requesting DPC ...\n"));
1059 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
1060 }
1061 }
1062 return fIRQTaken;
1063}
1064
1065
1066/**
1067 * Overridden routine for mouse polling events.
1068 *
1069 * @param pDevExt Device extension structure.
1070 */
1071void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1072{
1073 NOREF(pDevExt);
1074 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
1075 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
1076 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
1077}
1078
1079
1080/**
1081 * Queries (gets) a DWORD value from the registry.
1082 *
1083 * @return NTSTATUS
1084 * @param ulRoot Relative path root. See RTL_REGISTRY_SERVICES or RTL_REGISTRY_ABSOLUTE.
1085 * @param pwszPath Path inside path root.
1086 * @param pwszName Actual value name to look up.
1087 * @param puValue On input this can specify the default value (if RTL_REGISTRY_OPTIONAL is
1088 * not specified in ulRoot), on output this will retrieve the looked up
1089 * registry value if found.
1090 */
1091static NTSTATUS vgdrvNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue)
1092{
1093 if (!pwszPath || !pwszName || !puValue)
1094 return STATUS_INVALID_PARAMETER;
1095
1096 ULONG ulDefault = *puValue;
1097
1098 RTL_QUERY_REGISTRY_TABLE tblQuery[2];
1099 RtlZeroMemory(tblQuery, sizeof(tblQuery));
1100 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
1101 tblQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1102 tblQuery[0].Name = pwszName;
1103 tblQuery[0].EntryContext = puValue;
1104 tblQuery[0].DefaultType = REG_DWORD;
1105 tblQuery[0].DefaultData = &ulDefault;
1106 tblQuery[0].DefaultLength = sizeof(ULONG);
1107
1108 return RtlQueryRegistryValues(ulRoot,
1109 pwszPath,
1110 &tblQuery[0],
1111 NULL /* Context */,
1112 NULL /* Environment */);
1113}
1114
1115
1116/**
1117 * Helper to scan the PCI resource list and remember stuff.
1118 *
1119 * @param pResList Resource list
1120 * @param pDevExt Device extension
1121 */
1122static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
1123{
1124 /* Enumerate the resource list. */
1125 LogFlowFunc(("Found %d resources\n",
1126 pResList->List->PartialResourceList.Count));
1127
1128 NTSTATUS rc = STATUS_SUCCESS;
1129 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
1130 ULONG rangeCount = 0;
1131 ULONG cMMIORange = 0;
1132 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->pciBaseAddress;
1133 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
1134 {
1135 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
1136 switch (pPartialData->Type)
1137 {
1138 case CmResourceTypePort:
1139 {
1140 /* Overflow protection. */
1141 if (rangeCount < PCI_TYPE0_ADDRESSES)
1142 {
1143 LogFlowFunc(("I/O range: Base=%08x:%08x, length=%08x\n",
1144 pPartialData->u.Port.Start.HighPart,
1145 pPartialData->u.Port.Start.LowPart,
1146 pPartialData->u.Port.Length));
1147
1148 /* Save the IO port base. */
1149 /** @todo Not so good.
1150 * Update/bird: What is not so good? That we just consider the last range? */
1151 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
1152
1153 /* Save resource information. */
1154 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
1155 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
1156 pBaseAddress->RangeInMemory = FALSE;
1157 pBaseAddress->ResourceMapped = FALSE;
1158
1159 LogFunc(("I/O range for VMMDev found! Base=%08x:%08x, length=%08x\n",
1160 pPartialData->u.Port.Start.HighPart,
1161 pPartialData->u.Port.Start.LowPart,
1162 pPartialData->u.Port.Length));
1163
1164 /* Next item ... */
1165 rangeCount++; pBaseAddress++;
1166 }
1167 break;
1168 }
1169
1170 case CmResourceTypeInterrupt:
1171 {
1172 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
1173 pPartialData->u.Interrupt.Level,
1174 pPartialData->u.Interrupt.Vector,
1175 pPartialData->Flags));
1176
1177 /* Save information. */
1178 pDevExt->interruptLevel = pPartialData->u.Interrupt.Level;
1179 pDevExt->interruptVector = pPartialData->u.Interrupt.Vector;
1180 pDevExt->interruptAffinity = pPartialData->u.Interrupt.Affinity;
1181
1182 /* Check interrupt mode. */
1183 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
1184 pDevExt->interruptMode = Latched;
1185 else
1186 pDevExt->interruptMode = LevelSensitive;
1187 break;
1188 }
1189
1190 case CmResourceTypeMemory:
1191 {
1192 /* Overflow protection. */
1193 if (rangeCount < PCI_TYPE0_ADDRESSES)
1194 {
1195 LogFlowFunc(("Memory range: Base=%08x:%08x, length=%08x\n",
1196 pPartialData->u.Memory.Start.HighPart,
1197 pPartialData->u.Memory.Start.LowPart,
1198 pPartialData->u.Memory.Length));
1199
1200 /* We only care about read/write memory. */
1201 /** @todo Reconsider memory type. */
1202 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
1203 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
1204 {
1205 /* Save physical MMIO base + length for VMMDev. */
1206 pDevExt->vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start;
1207 pDevExt->vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length;
1208
1209 /* Save resource information. */
1210 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
1211 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
1212 pBaseAddress->RangeInMemory = TRUE;
1213 pBaseAddress->ResourceMapped = FALSE;
1214
1215 LogFunc(("Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1216 pPartialData->u.Memory.Start.HighPart,
1217 pPartialData->u.Memory.Start.LowPart,
1218 pPartialData->u.Memory.Length));
1219
1220 /* Next item ... */
1221 rangeCount++; pBaseAddress++; cMMIORange++;
1222 }
1223 else
1224 LogFunc(("Ignoring memory: Flags=%08x\n", pPartialData->Flags));
1225 }
1226 break;
1227 }
1228
1229 default:
1230 {
1231 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
1232 break;
1233 }
1234 }
1235 }
1236
1237 /* Memorize the number of resources found. */
1238 pDevExt->pciAddressCount = rangeCount;
1239 return rc;
1240}
1241
1242
1243/**
1244 * Maps the I/O space from VMMDev to virtual kernel address space.
1245 *
1246 * @return NTSTATUS
1247 *
1248 * @param pDevExt The device extension.
1249 * @param PhysAddr Physical address to map.
1250 * @param cbToMap Number of bytes to map.
1251 * @param ppvMMIOBase Pointer of mapped I/O base.
1252 * @param pcbMMIO Length of mapped I/O base.
1253 */
1254static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
1255 void **ppvMMIOBase, uint32_t *pcbMMIO)
1256{
1257 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
1258 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
1259 /* pcbMMIO is optional. */
1260
1261 NTSTATUS rc = STATUS_SUCCESS;
1262 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
1263 {
1264 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
1265 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
1266 if (pVMMDevMemory)
1267 {
1268 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
1269
1270 /* Check version of the structure; do we have the right memory version? */
1271 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
1272 {
1273 /* Save results. */
1274 *ppvMMIOBase = pVMMDevMemory;
1275 if (pcbMMIO) /* Optional. */
1276 *pcbMMIO = pVMMDevMemory->u32Size;
1277
1278 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
1279 }
1280 else
1281 {
1282 /* Not our version, refuse operation and unmap the memory. */
1283 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
1284
1285 vgdrvNtUnmapVMMDevMemory(pDevExt);
1286 rc = STATUS_UNSUCCESSFUL;
1287 }
1288 }
1289 else
1290 rc = STATUS_UNSUCCESSFUL;
1291 }
1292 return rc;
1293}
1294
1295
1296/**
1297 * Unmaps the VMMDev I/O range from kernel space.
1298 *
1299 * @param pDevExt The device extension.
1300 */
1301void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1302{
1303 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
1304 if (pDevExt->Core.pVMMDevMemory)
1305 {
1306 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->vmmDevPhysMemoryLength);
1307 pDevExt->Core.pVMMDevMemory = NULL;
1308 }
1309
1310 pDevExt->vmmDevPhysMemoryAddress.QuadPart = 0;
1311 pDevExt->vmmDevPhysMemoryLength = 0;
1312}
1313
1314
1315/**
1316 * Translates NT version to VBox OS.
1317 *
1318 * @returns VBox OS type.
1319 * @param enmNtVer The NT version.
1320 */
1321VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
1322{
1323 VBOXOSTYPE enmOsType;
1324 switch (enmNtVer)
1325 {
1326 case VGDRVNTVER_WINNT4:
1327 enmOsType = VBOXOSTYPE_WinNT4;
1328 break;
1329
1330 case VGDRVNTVER_WIN2K:
1331 enmOsType = VBOXOSTYPE_Win2k;
1332 break;
1333
1334 case VGDRVNTVER_WINXP:
1335#if ARCH_BITS == 64
1336 enmOsType = VBOXOSTYPE_WinXP_x64;
1337#else
1338 enmOsType = VBOXOSTYPE_WinXP;
1339#endif
1340 break;
1341
1342 case VGDRVNTVER_WIN2K3:
1343#if ARCH_BITS == 64
1344 enmOsType = VBOXOSTYPE_Win2k3_x64;
1345#else
1346 enmOsType = VBOXOSTYPE_Win2k3;
1347#endif
1348 break;
1349
1350 case VGDRVNTVER_WINVISTA:
1351#if ARCH_BITS == 64
1352 enmOsType = VBOXOSTYPE_WinVista_x64;
1353#else
1354 enmOsType = VBOXOSTYPE_WinVista;
1355#endif
1356 break;
1357
1358 case VGDRVNTVER_WIN7:
1359#if ARCH_BITS == 64
1360 enmOsType = VBOXOSTYPE_Win7_x64;
1361#else
1362 enmOsType = VBOXOSTYPE_Win7;
1363#endif
1364 break;
1365
1366 case VGDRVNTVER_WIN8:
1367#if ARCH_BITS == 64
1368 enmOsType = VBOXOSTYPE_Win8_x64;
1369#else
1370 enmOsType = VBOXOSTYPE_Win8;
1371#endif
1372 break;
1373
1374 case VGDRVNTVER_WIN81:
1375#if ARCH_BITS == 64
1376 enmOsType = VBOXOSTYPE_Win81_x64;
1377#else
1378 enmOsType = VBOXOSTYPE_Win81;
1379#endif
1380 break;
1381
1382 case VGDRVNTVER_WIN10:
1383#if ARCH_BITS == 64
1384 enmOsType = VBOXOSTYPE_Win10_x64;
1385#else
1386 enmOsType = VBOXOSTYPE_Win10;
1387#endif
1388 break;
1389
1390 default:
1391 /* We don't know, therefore NT family. */
1392 enmOsType = VBOXOSTYPE_WinNT;
1393 break;
1394 }
1395 return enmOsType;
1396}
1397
1398#ifdef VBOX_STRICT
1399
1400/**
1401 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
1402 */
1403static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
1404{
1405 AssertPtrReturn(pu32Bits, 0);
1406 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
1407 uint32_t u32Result = 0;
1408 uint32_t u32WorkingMask = u32Mask;
1409 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1410
1411 while (iBitOffset > 0)
1412 {
1413 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
1414 if (fSet)
1415 u32Result |= 1 << (iBitOffset - 1);
1416 u32WorkingMask &= ~(1 << (iBitOffset - 1));
1417 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1418 }
1419 LogFlowFunc(("Returning %#x\n", u32Result));
1420 return u32Result;
1421}
1422
1423
1424static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
1425{
1426 ULONG u32Bits2 = u32Bits;
1427 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1428 if ( u32Result != u32Exp
1429 || (u32Bits2 & u32Mask)
1430 || (u32Bits2 & u32Result)
1431 || ((u32Bits2 | u32Result) != u32Bits)
1432 )
1433 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
1434 u32Mask, u32Bits, u32Bits2, u32Result));
1435}
1436
1437
1438static void vgdrvNtDoTests(void)
1439{
1440 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1441 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
1442 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1443 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1444 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1445 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1446}
1447
1448#endif /* VBOX_STRICT */
1449
1450#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1451
1452/*
1453 * DPC latency checker.
1454 */
1455
1456/**
1457 * One DPC latency sample.
1458 */
1459typedef struct DPCSAMPLE
1460{
1461 LARGE_INTEGER PerfDelta;
1462 LARGE_INTEGER PerfCounter;
1463 LARGE_INTEGER PerfFrequency;
1464 uint64_t u64TSC;
1465} DPCSAMPLE;
1466AssertCompileSize(DPCSAMPLE, 4*8);
1467
1468/**
1469 * The DPC latency measurement workset.
1470 */
1471typedef struct DPCDATA
1472{
1473 KDPC Dpc;
1474 KTIMER Timer;
1475 KSPIN_LOCK SpinLock;
1476
1477 ULONG ulTimerRes;
1478
1479 bool volatile fFinished;
1480
1481 /** The timer interval (relative). */
1482 LARGE_INTEGER DueTime;
1483
1484 LARGE_INTEGER PerfCounterPrev;
1485
1486 /** Align the sample array on a 64 byte boundrary just for the off chance
1487 * that we'll get cache line aligned memory backing this structure. */
1488 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
1489
1490 int cSamples;
1491 DPCSAMPLE aSamples[8192];
1492} DPCDATA;
1493
1494AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
1495
1496# define VBOXGUEST_DPC_TAG 'DPCS'
1497
1498
1499/**
1500 * DPC callback routine for the DPC latency measurement code.
1501 *
1502 * @param pDpc The DPC, not used.
1503 * @param pvDeferredContext Pointer to the DPCDATA.
1504 * @param SystemArgument1 System use, ignored.
1505 * @param SystemArgument2 System use, ignored.
1506 */
1507static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
1508{
1509 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
1510 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
1511
1512 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
1513
1514 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
1515 pData->fFinished = true;
1516 else
1517 {
1518 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
1519
1520 pSample->u64TSC = ASMReadTSC();
1521 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
1522 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
1523
1524 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
1525
1526 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1527 }
1528
1529 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1530}
1531
1532
1533/**
1534 * Handles the DPC latency checker request.
1535 *
1536 * @returns VBox status code.
1537 */
1538int VGDrvNtIOCtl_DpcLatencyChecker(void)
1539{
1540 /*
1541 * Allocate a block of non paged memory for samples and related data.
1542 */
1543 DPCDATA *pData = (DPCDATA *)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCDATA), VBOXGUEST_DPC_TAG);
1544 if (!pData)
1545 {
1546 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
1547 return VERR_NO_MEMORY;
1548 }
1549
1550 /*
1551 * Initialize the data.
1552 */
1553 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
1554 KeInitializeTimer(&pData->Timer);
1555 KeInitializeSpinLock(&pData->SpinLock);
1556
1557 pData->fFinished = false;
1558 pData->cSamples = 0;
1559 pData->PerfCounterPrev.QuadPart = 0;
1560
1561 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
1562 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
1563
1564 /*
1565 * Start the DPC measurements and wait for a full set.
1566 */
1567 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1568
1569 while (!pData->fFinished)
1570 {
1571 LARGE_INTEGER Interval;
1572 Interval.QuadPart = -100 * 1000 * 10;
1573 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
1574 }
1575
1576 ExSetTimerResolution(0, 0);
1577
1578 /*
1579 * Log everything to the host.
1580 */
1581 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
1582 for (int i = 0; i < pData->cSamples; i++)
1583 {
1584 DPCSAMPLE *pSample = &pData->aSamples[i];
1585
1586 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
1587 i,
1588 pSample->PerfDelta.QuadPart,
1589 pSample->PerfCounter.QuadPart,
1590 pSample->PerfFrequency.QuadPart,
1591 pSample->u64TSC);
1592 }
1593
1594 ExFreePoolWithTag(pData, VBOXGUEST_DPC_TAG);
1595 return VINF_SUCCESS;
1596}
1597
1598#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1599
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