VirtualBox

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

Last change on this file since 55771 was 54608, checked in by vboxsync, 10 years ago

VBoxGuest: cleanups, mainly fixing and shortening function name prefixes.

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