VirtualBox

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

Last change on this file since 58053 was 58053, checked in by vboxsync, 9 years ago

VBoxGuest: s/vbgd/VGDrv/ + started on page.

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