VirtualBox

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

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

bugref:8250: Extraneous files removed.

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