VirtualBox

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

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

VBoxGuest-win*: Folded VBoxGuest-win-legacy.cpp into VBoxGuest-win.cpp.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.0 KB
Line 
1/* $Id: VBoxGuest-win.cpp 70101 2017-12-13 09:37:51Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2017 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_SUP_DRV
32#include "VBoxGuest-win.h"
33#include "VBoxGuestInternal.h"
34
35#include <iprt/asm.h>
36#include <iprt/asm-amd64-x86.h>
37
38#include <VBox/log.h>
39#include <VBox/VBoxGuestLib.h>
40#include <iprt/string.h>
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46#ifndef PCI_MAX_BUSES
47# define PCI_MAX_BUSES 256
48#endif
49
50
51/*********************************************************************************************************************************
52* Internal Functions *
53*********************************************************************************************************************************/
54RT_C_DECLS_BEGIN
55#ifdef TARGET_NT4
56static NTSTATUS vgdrvNt4FindPciDevice(PULONG pulBusNumber, PPCI_SLOT_NUMBER pSlotNumber);
57static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
58#else
59static NTSTATUS vgdrvNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
60#endif
61static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
62static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
63static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
64static NTSTATUS vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
65static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
66static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
67static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
68#ifndef TARGET_NT4
69static NTSTATUS vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
70#endif
71static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
72static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
73#ifdef VBOX_STRICT
74static void vgdrvNtDoTests(void);
75#endif
76static VOID vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
77static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
78static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt);
79static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
80 void **ppvMMIOBase, uint32_t *pcbMMIO);
81RT_C_DECLS_END
82
83
84/*********************************************************************************************************************************
85* Exported Functions *
86*********************************************************************************************************************************/
87RT_C_DECLS_BEGIN
88ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
89RT_C_DECLS_END
90
91#ifdef ALLOC_PRAGMA
92/* We only do INIT allocations. PAGE is too much work and risk for little gain. */
93# pragma alloc_text(INIT, DriverEntry)
94# ifdef TARGET_NT4
95# pragma alloc_text(INIT, vgdrvNt4CreateDevice)
96# pragma alloc_text(INIT, vgdrvNt4FindPciDevice)
97# endif
98#endif
99
100
101/*********************************************************************************************************************************
102* Global Variables *
103*********************************************************************************************************************************/
104/** The detected NT (windows) version. */
105VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
106
107
108
109/**
110 * Driver entry point.
111 *
112 * @returns appropriate status code.
113 * @param pDrvObj Pointer to driver object.
114 * @param pRegPath Registry base path.
115 */
116ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
117{
118 RT_NOREF1(pRegPath);
119
120 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
121
122 /*
123 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
124 */
125 ULONG ulMajorVer;
126 ULONG ulMinorVer;
127 ULONG ulBuildNo;
128 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
129
130 /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log */
131 RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
132 if (fCheckedBuild)
133 RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
134
135#ifdef VBOX_STRICT
136 vgdrvNtDoTests();
137#endif
138 NTSTATUS rc = STATUS_SUCCESS;
139 switch (ulMajorVer)
140 {
141 case 10:
142 switch (ulMinorVer)
143 {
144 case 0:
145 /* Windows 10 Preview builds starting with 9926. */
146 default:
147 /* Also everything newer. */
148 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
149 break;
150 }
151 break;
152 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
153 switch (ulMinorVer)
154 {
155 case 0: /* Note: Also could be Windows 2008 Server! */
156 g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
157 break;
158 case 1: /* Note: Also could be Windows 2008 Server R2! */
159 g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
160 break;
161 case 2:
162 g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
163 break;
164 case 3:
165 g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
166 break;
167 case 4:
168 /* Windows 10 Preview builds. */
169 default:
170 /* Also everything newer. */
171 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
172 break;
173 }
174 break;
175 case 5:
176 switch (ulMinorVer)
177 {
178 default:
179 case 2:
180 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
181 break;
182 case 1:
183 g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
184 break;
185 case 0:
186 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
187 break;
188 }
189 break;
190 case 4:
191 g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
192 break;
193 default:
194 if (ulMajorVer > 6)
195 {
196 /* "Windows 10 mode" for Windows 8.1+. */
197 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
198 }
199 else
200 {
201 if (ulMajorVer < 4)
202 LogRelFunc(("At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer));
203 else
204 LogRelFunc(("Unknown version %u.%u!\n", ulMajorVer, ulMinorVer));
205 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
206 }
207 break;
208 }
209
210 if (NT_SUCCESS(rc))
211 {
212 /*
213 * Setup the driver entry points in pDrvObj.
214 */
215 pDrvObj->DriverUnload = vgdrvNtUnload;
216 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
217 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
218 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
219 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
220 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
221 pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
222 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
223#ifdef TARGET_NT4
224 rc = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
225#else
226 pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtPnP;
227 pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtPower;
228 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl;
229 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vgdrvNt5PlusAddDevice;
230#endif
231 }
232
233 LogFlowFunc(("Returning %#x\n", rc));
234 return rc;
235}
236
237
238#ifdef TARGET_NT4
239
240/**
241 * Legacy helper function to create the device object.
242 *
243 * @returns NT status code.
244 *
245 * @param pDrvObj The driver object.
246 * @param pRegPath The driver registry path.
247 */
248static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
249{
250 Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
251
252 /*
253 * Find our virtual PCI device
254 */
255 ULONG uBusNumber;
256 PCI_SLOT_NUMBER SlotNumber;
257 NTSTATUS rc = vgdrvNt4FindPciDevice(&uBusNumber, &SlotNumber);
258 if (NT_ERROR(rc))
259 {
260 Log(("vgdrvNt4CreateDevice: Device not found!\n"));
261 return rc;
262 }
263
264 /*
265 * Create device.
266 */
267 UNICODE_STRING szDevName;
268 RtlInitUnicodeString(&szDevName, VBOXGUEST_DEVICE_NAME_NT);
269 PDEVICE_OBJECT pDeviceObject = NULL;
270 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &szDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
271 if (NT_SUCCESS(rc))
272 {
273 Log(("vgdrvNt4CreateDevice: Device created\n"));
274
275 UNICODE_STRING DosName;
276 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
277 rc = IoCreateSymbolicLink(&DosName, &szDevName);
278 if (NT_SUCCESS(rc))
279 {
280 Log(("vgdrvNt4CreateDevice: Symlink created\n"));
281
282 /*
283 * Setup the device extension.
284 */
285 Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
286
287 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
288 RT_ZERO(*pDevExt);
289
290 Log(("vgdrvNt4CreateDevice: Device extension created\n"));
291
292 /* Store a reference to ourself. */
293 pDevExt->pDeviceObject = pDeviceObject;
294
295 /* Store bus and slot number we've queried before. */
296 pDevExt->busNumber = uBusNumber;
297 pDevExt->slotNumber = SlotNumber.u.AsULONG;
298
299#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
300 rc = hlpRegisterBugCheckCallback(pDevExt);
301#endif
302
303 /* Do the actual VBox init ... */
304 if (NT_SUCCESS(rc))
305 {
306 rc = vgdrvNtInit(pDrvObj, pDeviceObject, pRegPath);
307 if (NT_SUCCESS(rc))
308 {
309 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (succcess)\n", rc));
310 return rc;
311 }
312
313 /* bail out */
314 }
315 IoDeleteSymbolicLink(&DosName);
316 }
317 else
318 Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
319 IoDeleteDevice(pDeviceObject);
320 }
321 else
322 Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
323 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
324 return rc;
325}
326
327
328/**
329 * Helper function to handle the PCI device lookup.
330 *
331 * @returns NT status code.
332 *
333 * @param pulBusNumber Where to return the bus number on success.
334 * @param pSlotNumber Where to return the slot number on success.
335 */
336static NTSTATUS vgdrvNt4FindPciDevice(PULONG pulBusNumber, PPCI_SLOT_NUMBER pSlotNumber)
337{
338 Log(("vgdrvNt4FindPciDevice\n"));
339
340 PCI_SLOT_NUMBER SlotNumber;
341 SlotNumber.u.AsULONG = 0;
342
343 /* Scan each bus. */
344 for (ULONG ulBusNumber = 0; ulBusNumber < PCI_MAX_BUSES; ulBusNumber++)
345 {
346 /* Scan each device. */
347 for (ULONG deviceNumber = 0; deviceNumber < PCI_MAX_DEVICES; deviceNumber++)
348 {
349 SlotNumber.u.bits.DeviceNumber = deviceNumber;
350
351 /* Scan each function (not really required...). */
352 for (ULONG functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++)
353 {
354 SlotNumber.u.bits.FunctionNumber = functionNumber;
355
356 /* Have a look at what's in this slot. */
357 PCI_COMMON_CONFIG PciData;
358 if (!HalGetBusData(PCIConfiguration, ulBusNumber, SlotNumber.u.AsULONG, &PciData, sizeof(ULONG)))
359 {
360 /* No such bus, we're done with it. */
361 deviceNumber = PCI_MAX_DEVICES;
362 break;
363 }
364
365 if (PciData.VendorID == PCI_INVALID_VENDORID)
366 /* We have to proceed to the next function. */
367 continue;
368
369 /* Check if it's another device. */
370 if ( PciData.VendorID != VMMDEV_VENDORID
371 || PciData.DeviceID != VMMDEV_DEVICEID)
372 continue;
373
374 /* Hooray, we've found it! */
375 Log(("vgdrvNt4FindPciDevice: Device found!\n"));
376
377 *pulBusNumber = ulBusNumber;
378 *pSlotNumber = SlotNumber;
379 return STATUS_SUCCESS;
380 }
381 }
382 }
383
384 return STATUS_DEVICE_DOES_NOT_EXIST;
385}
386
387#else /* !TARGET_NT4 */
388
389/**
390 * Handle request from the Plug & Play subsystem.
391 *
392 * @returns NT status code
393 * @param pDrvObj Driver object
394 * @param pDevObj Device object
395 *
396 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
397 */
398static NTSTATUS vgdrvNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
399{
400 NTSTATUS rc;
401 LogFlowFuncEnter();
402
403 /*
404 * Create device.
405 */
406 UNICODE_STRING DevName;
407 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
408 PDEVICE_OBJECT pDeviceObject = NULL;
409 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
410 if (NT_SUCCESS(rc))
411 {
412 /*
413 * Create symbolic link (DOS devices).
414 */
415 UNICODE_STRING DosName;
416 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
417 rc = IoCreateSymbolicLink(&DosName, &DevName);
418 if (NT_SUCCESS(rc))
419 {
420 /*
421 * Setup the device extension.
422 */
423 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
424 RT_ZERO(*pDevExt);
425
426 KeInitializeSpinLock(&pDevExt->MouseEventAccessLock);
427
428 pDevExt->pDeviceObject = pDeviceObject;
429 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
430 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
431
432 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
433 if (pDevExt->pNextLowerDriver != NULL)
434 {
435 /*
436 * If we reached this point we're fine with the basic driver setup,
437 * so continue to init our own things.
438 */
439# ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
440 vgdrvNtBugCheckCallback(pDevExt); /* Ignore failure! */
441# endif
442 if (NT_SUCCESS(rc))
443 {
444 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
445 pDeviceObject->Flags |= DO_POWER_PAGABLE;
446
447 /* Driver is ready now. */
448 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
449 LogFlowFunc(("Returning with rc=%#x (success)\n", rc));
450 return rc;
451 }
452
453 IoDetachDevice(pDevExt->pNextLowerDriver);
454 }
455 else
456 {
457 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
458 rc = STATUS_DEVICE_NOT_CONNECTED;
459 }
460
461 /* bail out */
462 IoDeleteSymbolicLink(&DosName);
463 }
464 else
465 LogFunc(("IoCreateSymbolicLink failed with rc=%#x!\n", rc));
466 IoDeleteDevice(pDeviceObject);
467 }
468 else
469 LogFunc(("IoCreateDevice failed with rc=%#x!\n", rc));
470
471 LogFunc(("Returning with rc=%#x\n", rc));
472 return rc;
473}
474
475#endif /* !TARGET_NT4 */
476
477
478#ifdef LOG_ENABLED
479/**
480 * Debug helper to dump a device resource list.
481 *
482 * @param pResourceList list of device resources.
483 */
484static void vgdrvNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
485{
486 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pResourceList->PartialDescriptors;
487 ULONG cResources = pResourceList->Count;
488
489 for (ULONG i = 0; i < cResources; ++i, ++pResource)
490 {
491 ULONG uType = pResource->Type;
492 static char const * const s_apszName[] =
493 {
494 "CmResourceTypeNull",
495 "CmResourceTypePort",
496 "CmResourceTypeInterrupt",
497 "CmResourceTypeMemory",
498 "CmResourceTypeDma",
499 "CmResourceTypeDeviceSpecific",
500 "CmResourceTypeBusNumber",
501 "CmResourceTypeDevicePrivate",
502 "CmResourceTypeAssignedResource",
503 "CmResourceTypeSubAllocateFrom",
504 };
505
506 LogFunc(("Type=%s", uType < RT_ELEMENTS(s_apszName) ? s_apszName[uType] : "Unknown"));
507
508 switch (uType)
509 {
510 case CmResourceTypePort:
511 case CmResourceTypeMemory:
512 LogFunc(("Start %8X%8.8lX, length=%X\n",
513 pResource->u.Port.Start.HighPart, pResource->u.Port.Start.LowPart, pResource->u.Port.Length));
514 break;
515
516 case CmResourceTypeInterrupt:
517 LogFunc(("Level=%X, vector=%X, affinity=%X\n",
518 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
519 break;
520
521 case CmResourceTypeDma:
522 LogFunc(("Channel %d, Port %X\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
523 break;
524
525 default:
526 LogFunc(("\n"));
527 break;
528 }
529 }
530}
531#endif /* LOG_ENABLED */
532
533
534/**
535 * Global initialisation stuff (PnP + NT4 legacy).
536 *
537 * @param pDevObj Device object.
538 * @param pIrp Request packet.
539 */
540#ifndef TARGET_NT4
541NTSTATUS vgdrvNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp)
542#else
543NTSTATUS vgdrvNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath)
544#endif
545{
546 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
547#ifndef TARGET_NT4
548 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
549 LogFlowFunc(("ENTER: pDevObj=%p pIrp=%p\n", pDevObj, pIrp));
550#else
551 LogFlowFunc(("ENTER: pDrvObj=%p pDevObj=%p pRegPath=%p\n", pDrvObj, pDevObj, pRegPath));
552#endif
553
554 NTSTATUS rcNt;
555#ifdef TARGET_NT4
556 /*
557 * Let's have a look at what our PCI adapter offers.
558 */
559 LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
560
561 /* Assign the PCI resources. */
562 PCM_RESOURCE_LIST pResourceList = NULL;
563 UNICODE_STRING classNameString;
564 RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter");
565 rcNt = HalAssignSlotResources(pRegPath, &classNameString, pDrvObj, pDevObj,
566 PCIBus, pDevExt->busNumber, pDevExt->slotNumber, &pResourceList);
567# ifdef LOG_ENABLED
568 if (pResourceList && pResourceList->Count > 0)
569 vgdrvNtShowDeviceResources(&pResourceList->List[0].PartialResourceList);
570# endif
571 if (NT_SUCCESS(rcNt))
572 rcNt = vgdrvNtScanPCIResourceList(pResourceList, pDevExt);
573#else
574# ifdef LOG_ENABLED
575 if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0)
576 vgdrvNtShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
577# endif
578 rcNt = vgdrvNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt);
579#endif
580 if (NT_SUCCESS(rcNt))
581 {
582 /*
583 * Map physical address of VMMDev memory into MMIO region
584 * and init the common device extension bits.
585 */
586 void *pvMMIOBase = NULL;
587 uint32_t cbMMIO = 0;
588 rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
589 pDevExt->vmmDevPhysMemoryAddress,
590 pDevExt->vmmDevPhysMemoryLength,
591 &pvMMIOBase,
592 &cbMMIO);
593 if (NT_SUCCESS(rcNt))
594 {
595 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
596
597 LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
598 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
599
600 int vrc = VGDrvCommonInitDevExt(&pDevExt->Core,
601 pDevExt->Core.IOPortBase,
602 pvMMIOBase, cbMMIO,
603 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
604 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
605 if (RT_FAILURE(vrc))
606 {
607 LogFunc(("Could not init device extension, vrc=%Rrc\n", vrc));
608 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
609 }
610 }
611 else
612 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
613 }
614
615 if (NT_SUCCESS(rcNt))
616 {
617 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
618 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
619 if (RT_FAILURE(vrc))
620 {
621 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
622 rcNt = STATUS_UNSUCCESSFUL;
623 }
624 }
625
626 if (NT_SUCCESS(rcNt))
627 {
628 /*
629 * Register DPC and ISR.
630 */
631 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
632
633 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
634#ifdef TARGET_NT4
635 ULONG uInterruptVector = UINT32_MAX;
636 KIRQL irqLevel = UINT8_MAX;
637 /* Get an interrupt vector. */
638 /* Only proceed if the device provides an interrupt. */
639 if ( pDevExt->interruptLevel
640 || pDevExt->interruptVector)
641 {
642 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
643 pDevExt->busNumber, pDevExt->interruptLevel, pDevExt->interruptVector));
644
645 uInterruptVector = HalGetInterruptVector(PCIBus,
646 pDevExt->busNumber,
647 pDevExt->interruptLevel,
648 pDevExt->interruptVector,
649 &irqLevel,
650 &pDevExt->interruptAffinity);
651 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
652 if (uInterruptVector == 0)
653 LogFunc(("No interrupt vector found!\n"));
654 }
655 else
656 LogFunc(("Device does not provide an interrupt!\n"));
657#endif
658 if (pDevExt->interruptVector)
659 {
660#ifdef TARGET_NT4
661 LogFlowFunc(("Connecting interrupt (IntVector=%#u), IrqLevel=%u) ...\n", uInterruptVector, irqLevel));
662#else
663 LogFlowFunc(("Connecting interrupt (IntVector=%#u), IrqLevel=%u) ...\n", pDevExt->interruptVector, pDevExt->interruptLevel));
664#endif
665
666 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
667 (PKSERVICE_ROUTINE)vgdrvNtIsrHandler, /* Our ISR handler. */
668 pDevExt, /* Device context. */
669 NULL, /* Optional spinlock. */
670#ifdef TARGET_NT4
671 uInterruptVector, /* Interrupt vector. */
672 irqLevel, /* Interrupt level. */
673 irqLevel, /* Interrupt level. */
674#else
675 pDevExt->interruptVector, /* Interrupt vector. */
676 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
677 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
678#endif
679 pDevExt->interruptMode, /* LevelSensitive or Latched. */
680 TRUE, /* Shareable interrupt. */
681 pDevExt->interruptAffinity, /* CPU affinity. */
682 FALSE); /* Don't save FPU stack. */
683 if (NT_ERROR(rcNt))
684 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
685 }
686 else
687 LogFunc(("No interrupt vector found!\n"));
688 }
689
690 if (NT_SUCCESS(rcNt))
691 {
692 /*
693 * Once we've read configuration from register and host, we're finally read.
694 */
695 pDevExt->Core.fLoggingEnabled = true; /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
696 vgdrvNtReadConfiguration(pDevExt);
697
698 /* Ready to rumble! */
699 LogRelFunc(("Device is ready!\n"));
700 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_WORKING);
701 }
702 else
703 pDevExt->pInterruptObject = NULL;
704
705 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
706 * whole bunch of things... */
707
708 LogFunc(("Returned with rcNt=%#x\n", rcNt));
709 return rcNt;
710}
711
712
713/**
714 * Cleans up hardware resources.
715 * Do not delete DevExt here.
716 *
717 * @todo r=bird: HC SVNT DRACONES!
718 *
719 * This code leaves clients hung when vgdrvNtInit is called afterwards.
720 * This happens when for instance hotplugging a CPU. Problem is
721 * vgdrvNtInit doing a full VGDrvCommonInitDevExt, orphaning all pDevExt
722 * members, like session lists and stuff.
723 *
724 * @param pDevObj Device object.
725 */
726NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj)
727{
728 LogFlowFuncEnter();
729
730 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
731 if (pDevExt)
732 {
733
734#if 0 /** @todo test & enable cleaning global session data */
735#ifdef VBOX_WITH_HGCM
736 if (pDevExt->pKernelSession)
737 {
738 VGDrvCommonCloseSession(pDevExt, pDevExt->pKernelSession);
739 pDevExt->pKernelSession = NULL;
740 }
741#endif
742#endif
743
744 if (pDevExt->pInterruptObject)
745 {
746 IoDisconnectInterrupt(pDevExt->pInterruptObject);
747 pDevExt->pInterruptObject = NULL;
748 }
749
750 /** @todo cleanup the rest stuff */
751
752
753#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
754 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
755#endif
756 /* According to MSDN we have to unmap previously mapped memory. */
757 vgdrvNtUnmapVMMDevMemory(pDevExt);
758 }
759
760 return STATUS_SUCCESS;
761}
762
763
764/**
765 * Unload the driver.
766 *
767 * @param pDrvObj Driver object.
768 */
769static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
770{
771 LogFlowFuncEnter();
772
773#ifdef TARGET_NT4
774 vgdrvNtCleanup(pDrvObj->DeviceObject);
775
776 /* Destroy device extension and clean up everything else. */
777 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
778 VGDrvCommonDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
779
780 /*
781 * I don't think it's possible to unload a driver which processes have
782 * opened, at least we'll blindly assume that here.
783 */
784 UNICODE_STRING DosName;
785 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
786 IoDeleteSymbolicLink(&DosName);
787
788 IoDeleteDevice(pDrvObj->DeviceObject);
789#else /* !TARGET_NT4 */
790 /* On a PnP driver this routine will be called after
791 * IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
792 * so don't do anything here (yet). */
793 RT_NOREF1(pDrvObj);
794#endif /* !TARGET_NT4 */
795
796 LogFlowFunc(("Returning\n"));
797}
798
799
800/**
801 * For simplifying request completion into a simple return statement, extended
802 * version.
803 *
804 * @returns rcNt
805 * @param rcNt The status code.
806 * @param uInfo Extra info value.
807 * @param pIrp The IRP.
808 */
809DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
810{
811 pIrp->IoStatus.Status = rcNt;
812 pIrp->IoStatus.Information = uInfo;
813 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
814 return rcNt;
815}
816
817
818/**
819 * For simplifying request completion into a simple return statement.
820 *
821 * @returns rcNt
822 * @param rcNt The status code.
823 * @param pIrp The IRP.
824 */
825DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
826{
827 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
828}
829
830
831/**
832 * Create (i.e. Open) file entry point.
833 *
834 * @param pDevObj Device object.
835 * @param pIrp Request packet.
836 */
837static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
838{
839 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
840 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
841 PFILE_OBJECT pFileObj = pStack->FileObject;
842 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
843
844 Assert(pFileObj->FsContext == NULL);
845
846 /*
847 * We are not remotely similar to a directory...
848 * (But this is possible.)
849 */
850 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
851 {
852 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
853 return vgdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
854 }
855
856 /*
857 * Check the device state.
858 */
859 if (pDevExt->enmDevState != VGDRVNTDEVSTATE_WORKING)
860 {
861 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", pDevExt->enmDevState));
862 return vgdrvNtCompleteRequest(STATUS_DEVICE_NOT_READY, pIrp);
863 }
864
865 /*
866 * Create a client session.
867 */
868 int rc;
869 PVBOXGUESTSESSION pSession;
870 if (pIrp->RequestorMode == KernelMode)
871 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
872 else
873 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, &pSession);
874 if (RT_SUCCESS(rc))
875 {
876 pFileObj->FsContext = pSession;
877 Log(("vgdrvNtCreate: Successfully created %s session %p\n",
878 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession));
879 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
880 }
881
882 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
883 /* Note. the IoStatus is completely ignored on error. */
884 if (rc == VERR_NO_MEMORY)
885 return vgdrvNtCompleteRequest(STATUS_NO_MEMORY, pIrp);
886 return vgdrvNtCompleteRequest(STATUS_UNSUCCESSFUL, pIrp);
887}
888
889
890/**
891 * Close file entry point.
892 *
893 * @param pDevObj Device object.
894 * @param pIrp Request packet.
895 */
896static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
897{
898 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
899 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
900 PFILE_OBJECT pFileObj = pStack->FileObject;
901
902 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
903
904#ifdef VBOX_WITH_HGCM
905 /* Close both, R0 and R3 sessions. */
906 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
907 if (pSession)
908 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
909#endif
910
911 pFileObj->FsContext = NULL;
912 pIrp->IoStatus.Information = 0;
913 pIrp->IoStatus.Status = STATUS_SUCCESS;
914 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
915
916 return STATUS_SUCCESS;
917}
918
919
920/**
921 * Device I/O Control entry point.
922 *
923 * @param pDevObj Device object.
924 * @param pIrp Request packet.
925 */
926NTSTATUS _stdcall vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
927{
928 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
929 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
930 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
931
932 if (!RT_VALID_PTR(pSession))
933 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
934
935#if 0 /* No fast I/O controls defined yet. */
936 /*
937 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
938 * the session and iCmd, and does not return anything.
939 */
940 if (pSession->fUnrestricted)
941 {
942 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
943 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
944 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
945 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
946 {
947 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
948
949 /* Complete the I/O request. */
950 supdrvSessionRelease(pSession);
951 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
952 }
953 }
954#endif
955
956 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
957}
958
959
960/**
961 * Device I/O Control entry point.
962 *
963 * @param pDevExt The device extension.
964 * @param pSession The session.
965 * @param pIrp Request packet.
966 * @param pStack The request stack pointer.
967 */
968static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
969 PIRP pIrp, PIO_STACK_LOCATION pStack)
970{
971 NTSTATUS rcNt;
972 uint32_t cbOut = 0;
973 int rc = 0;
974 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
975 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
976 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
977 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
978
979#if 0 /*def RT_ARCH_AMD64*/
980 /* Don't allow 32-bit processes to do any I/O controls. */
981 if (!IoIs32bitProcess(pIrp))
982#endif
983 {
984 /* Verify that it's a buffered CTL. */
985 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
986 {
987 /* Verify that the sizes in the request header are correct. */
988 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
989 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
990 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
991 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
992 {
993 /* Zero extra output bytes to make sure we don't leak anything. */
994 if (pHdr->cbIn < pHdr->cbOut)
995 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
996
997 /*
998 * Do the job.
999 */
1000 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
1001 RT_MAX(pHdr->cbIn, pHdr->cbOut));
1002 if (RT_SUCCESS(rc))
1003 {
1004 rcNt = STATUS_SUCCESS;
1005 cbOut = pHdr->cbOut;
1006 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
1007 {
1008 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1009 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
1010 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
1011 }
1012
1013 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
1014 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
1015 && RT_SUCCESS(pHdr->rc))
1016 pStack->FileObject->FsContext = NULL;
1017 }
1018 else if (rc == VERR_NOT_SUPPORTED)
1019 rcNt = STATUS_NOT_SUPPORTED;
1020 else
1021 rcNt = STATUS_INVALID_PARAMETER;
1022 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
1023 }
1024 else
1025 {
1026 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
1027 pStack->Parameters.DeviceIoControl.IoControlCode,
1028 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
1029 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
1030 pStack->Parameters.DeviceIoControl.InputBufferLength,
1031 pStack->Parameters.DeviceIoControl.OutputBufferLength));
1032 rcNt = STATUS_INVALID_PARAMETER;
1033 }
1034 }
1035 else
1036 {
1037 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
1038 pStack->Parameters.DeviceIoControl.IoControlCode));
1039 rcNt = STATUS_NOT_SUPPORTED;
1040 }
1041 }
1042#if 0 /*def RT_ARCH_AMD64*/
1043 else
1044 {
1045 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
1046 rcNt = STATUS_NOT_SUPPORTED;
1047 }
1048#endif
1049
1050 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
1051}
1052
1053
1054/**
1055 * Internal Device I/O Control entry point (for IDC).
1056 *
1057 * @param pDevObj Device object.
1058 * @param pIrp Request packet.
1059 */
1060static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1061{
1062 /* Currently no special code here. */
1063 return vgdrvNtDeviceControl(pDevObj, pIrp);
1064}
1065
1066
1067#ifndef TARGET_NT4
1068/**
1069 * IRP_MJ_SYSTEM_CONTROL handler.
1070 *
1071 * @returns NT status code
1072 * @param pDevObj Device object.
1073 * @param pIrp IRP.
1074 */
1075static NTSTATUS vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1076{
1077 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1078
1079 LogFlowFuncEnter();
1080
1081 /* Always pass it on to the next driver. */
1082 IoSkipCurrentIrpStackLocation(pIrp);
1083
1084 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1085}
1086#endif /* TARGET_NT4 */
1087
1088
1089/**
1090 * IRP_MJ_SHUTDOWN handler.
1091 *
1092 * @returns NT status code
1093 * @param pDevObj Device object.
1094 * @param pIrp IRP.
1095 */
1096static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1097{
1098 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1099 LogFlowFuncEnter();
1100
1101 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
1102 if (pReq)
1103 {
1104 pReq->header.requestType = VMMDevReq_SetPowerStatus;
1105 pReq->powerState = VMMDevPowerState_PowerOff;
1106
1107 int rc = VbglR0GRPerform(&pReq->header);
1108 if (RT_FAILURE(rc))
1109 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
1110 }
1111
1112 /* just in case, since we shouldn't normally get here. */
1113 pIrp->IoStatus.Information = 0;
1114 pIrp->IoStatus.Status = STATUS_SUCCESS;
1115 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1116 return STATUS_SUCCESS;
1117}
1118
1119
1120/**
1121 * Stub function for functions we don't implemented.
1122 *
1123 * @returns STATUS_NOT_SUPPORTED
1124 * @param pDevObj Device object.
1125 * @param pIrp IRP.
1126 */
1127static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1128{
1129 RT_NOREF1(pDevObj);
1130 LogFlowFuncEnter();
1131
1132 pIrp->IoStatus.Information = 0;
1133 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1134 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1135
1136 return STATUS_NOT_SUPPORTED;
1137}
1138
1139
1140/**
1141 * Sets the mouse notification callback.
1142 *
1143 * @returns VBox status code.
1144 * @param pDevExt Pointer to the device extension.
1145 * @param pNotify Pointer to the mouse notify struct.
1146 */
1147int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1148{
1149 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
1150 /* we need a lock here to avoid concurrency with the set event functionality */
1151 KIRQL OldIrql;
1152 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessLock, &OldIrql);
1153 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1154 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1155 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessLock, OldIrql);
1156 return VINF_SUCCESS;
1157}
1158
1159
1160/**
1161 * DPC handler.
1162 *
1163 * @param pDPC DPC descriptor.
1164 * @param pDevObj Device object.
1165 * @param pIrp Interrupt request packet.
1166 * @param pContext Context specific pointer.
1167 */
1168static void vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
1169{
1170 RT_NOREF3(pDPC, pIrp, pContext);
1171 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1172 Log3Func(("pDevExt=0x%p\n", pDevExt));
1173
1174 /* Test & reset the counter. */
1175 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
1176 {
1177 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
1178 * i.e. to prevent the event from destroyed while we're using it */
1179 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
1180 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessLock);
1181
1182 if (pDevExt->Core.pfnMouseNotifyCallback)
1183 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
1184
1185 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessLock);
1186 }
1187
1188 /* Process the wake-up list we were asked by the scheduling a DPC
1189 * in vgdrvNtIsrHandler(). */
1190 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
1191}
1192
1193
1194/**
1195 * ISR handler.
1196 *
1197 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
1198 * @param pInterrupt Interrupt that was triggered.
1199 * @param pServiceContext Context specific pointer.
1200 */
1201static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1202{
1203 RT_NOREF1(pInterrupt);
1204 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
1205 if (pDevExt == NULL)
1206 return FALSE;
1207
1208 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
1209
1210 /* Enter the common ISR routine and do the actual work. */
1211 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
1212
1213 /* If we need to wake up some events we do that in a DPC to make
1214 * sure we're called at the right IRQL. */
1215 if (fIRQTaken)
1216 {
1217 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
1218 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
1219 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
1220 {
1221 Log3Func(("Requesting DPC ...\n"));
1222 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
1223 }
1224 }
1225 return fIRQTaken;
1226}
1227
1228
1229/**
1230 * Overridden routine for mouse polling events.
1231 *
1232 * @param pDevExt Device extension structure.
1233 */
1234void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1235{
1236 NOREF(pDevExt);
1237 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
1238 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
1239 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
1240}
1241
1242
1243/**
1244 * Hook for handling OS specfic options from the host.
1245 *
1246 * @returns true if handled, false if not.
1247 * @param pDevExt The device extension.
1248 * @param pszName The option name.
1249 * @param pszValue The option value.
1250 */
1251bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1252{
1253 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1254 return false;
1255}
1256
1257
1258
1259/**
1260 * Queries (gets) a DWORD value from the registry.
1261 *
1262 * @return NTSTATUS
1263 * @param uRoot Relative path root. See RTL_REGISTRY_SERVICES or RTL_REGISTRY_ABSOLUTE.
1264 * @param pwszPath Path inside path root.
1265 * @param pwszName Actual value name to look up.
1266 * @param puValue On input this can specify the default value (if RTL_REGISTRY_OPTIONAL is
1267 * not specified in ulRoot), on output this will retrieve the looked up
1268 * registry value if found.
1269 */
1270static NTSTATUS vgdrvNtRegistryReadDWORD(ULONG uRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue)
1271{
1272 if (!pwszPath || !pwszName || !puValue)
1273 return STATUS_INVALID_PARAMETER;
1274
1275 ULONG uDefault = *puValue;
1276 RTL_QUERY_REGISTRY_TABLE aQuery[2];
1277 RT_ZERO(aQuery);
1278 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
1279 aQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1280 aQuery[0].Name = pwszName;
1281 aQuery[0].EntryContext = puValue;
1282 aQuery[0].DefaultType = REG_DWORD;
1283 aQuery[0].DefaultData = &uDefault;
1284 aQuery[0].DefaultLength = sizeof(uDefault);
1285
1286 return RtlQueryRegistryValues(uRoot, pwszPath, &aQuery[0], NULL /* Context */, NULL /* Environment */);
1287}
1288
1289
1290/**
1291 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
1292 */
1293static NTSTATUS NTAPI vbdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
1294 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
1295{
1296 /*
1297 * Filter out general service config values.
1298 */
1299 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
1300 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
1301 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
1302 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
1303 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
1304 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
1305 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
1306 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
1307 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
1308 )
1309 {
1310 return STATUS_SUCCESS;
1311 }
1312
1313 /*
1314 * Convert the value name.
1315 */
1316 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
1317 if (cch < 64 && cch > 0)
1318 {
1319 char szValueName[72];
1320 char *pszTmp = szValueName;
1321 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
1322 if (RT_SUCCESS(rc))
1323 {
1324 /*
1325 * Convert the value.
1326 */
1327 char szValue[72];
1328 char *pszFree = NULL;
1329 char *pszValue = NULL;
1330 szValue[0] = '\0';
1331 switch (uValueType)
1332 {
1333 case REG_SZ:
1334 case REG_EXPAND_SZ:
1335 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
1336 if (RT_SUCCESS(rc) && cch < _1K)
1337 {
1338 if (cch < sizeof(szValue))
1339 {
1340 pszValue = szValue;
1341 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
1342 }
1343 else
1344 {
1345 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
1346 if (RT_SUCCESS(rc))
1347 pszFree = pszValue;
1348 }
1349 if (RT_FAILURE(rc))
1350 {
1351 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
1352 pwszValueName, rc));
1353 pszValue = NULL;
1354 }
1355 }
1356 else if (RT_SUCCESS(rc))
1357 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
1358 pwszValueName, cbValue, uValueType));
1359 else
1360 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
1361 pwszValueName, cbValue, uValueType));
1362 break;
1363
1364 case REG_DWORD:
1365 if (cbValue == sizeof(uint32_t))
1366 {
1367 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
1368 pszValue = szValue;
1369 }
1370 else
1371 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
1372 break;
1373
1374 case REG_QWORD:
1375 if (cbValue == sizeof(uint64_t))
1376 {
1377 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
1378 pszValue = szValue;
1379 }
1380 else
1381 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
1382 break;
1383
1384 default:
1385 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
1386 break;
1387 }
1388 if (pszValue)
1389 {
1390 /*
1391 * Process it.
1392 */
1393 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
1394 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
1395 if (pszFree)
1396 RTStrFree(pszFree);
1397 }
1398 }
1399 }
1400 else if (cch > 0)
1401 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
1402 else
1403 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
1404 NOREF(pvEntryCtx);
1405 return STATUS_SUCCESS;
1406}
1407
1408
1409/**
1410 * Reads configuration from the registry and guest properties.
1411 *
1412 * We ignore failures and instead preserve existing configuration values.
1413 *
1414 * Thie routine will block.
1415 *
1416 * @param pDevExt The device extension.
1417 */
1418static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
1419{
1420 /*
1421 * First the registry.
1422 */
1423 RTL_QUERY_REGISTRY_TABLE aQuery[2];
1424 RT_ZERO(aQuery);
1425 aQuery[0].QueryRoutine = vbdrvNtRegistryEnumCallback;
1426 aQuery[0].Flags = 0;
1427 aQuery[0].Name = NULL;
1428 aQuery[0].EntryContext = NULL;
1429 aQuery[0].DefaultType = REG_NONE;
1430 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /* Environment */);
1431 if (!NT_SUCCESS(rcNt))
1432 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
1433
1434 /*
1435 * Read configuration from the host.
1436 */
1437 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
1438}
1439
1440
1441/**
1442 * Helper to scan the PCI resource list and remember stuff.
1443 *
1444 * @param pResList Resource list
1445 * @param pDevExt Device extension
1446 */
1447static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
1448{
1449 /* Enumerate the resource list. */
1450 LogFlowFunc(("Found %d resources\n",
1451 pResList->List->PartialResourceList.Count));
1452
1453 NTSTATUS rc = STATUS_SUCCESS;
1454 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
1455 ULONG rangeCount = 0;
1456 ULONG cMMIORange = 0;
1457 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->pciBaseAddress;
1458 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
1459 {
1460 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
1461 switch (pPartialData->Type)
1462 {
1463 case CmResourceTypePort:
1464 {
1465 /* Overflow protection. */
1466 if (rangeCount < PCI_TYPE0_ADDRESSES)
1467 {
1468 LogFlowFunc(("I/O range: Base=%08x:%08x, length=%08x\n",
1469 pPartialData->u.Port.Start.HighPart,
1470 pPartialData->u.Port.Start.LowPart,
1471 pPartialData->u.Port.Length));
1472
1473 /* Save the IO port base. */
1474 /** @todo Not so good.
1475 * Update/bird: What is not so good? That we just consider the last range? */
1476 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
1477
1478 /* Save resource information. */
1479 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
1480 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
1481 pBaseAddress->RangeInMemory = FALSE;
1482 pBaseAddress->ResourceMapped = FALSE;
1483
1484 LogFunc(("I/O range for VMMDev found! Base=%08x:%08x, length=%08x\n",
1485 pPartialData->u.Port.Start.HighPart,
1486 pPartialData->u.Port.Start.LowPart,
1487 pPartialData->u.Port.Length));
1488
1489 /* Next item ... */
1490 rangeCount++; pBaseAddress++;
1491 }
1492 break;
1493 }
1494
1495 case CmResourceTypeInterrupt:
1496 {
1497 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
1498 pPartialData->u.Interrupt.Level,
1499 pPartialData->u.Interrupt.Vector,
1500 pPartialData->Flags));
1501
1502 /* Save information. */
1503 pDevExt->interruptLevel = pPartialData->u.Interrupt.Level;
1504 pDevExt->interruptVector = pPartialData->u.Interrupt.Vector;
1505 pDevExt->interruptAffinity = pPartialData->u.Interrupt.Affinity;
1506
1507 /* Check interrupt mode. */
1508 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
1509 pDevExt->interruptMode = Latched;
1510 else
1511 pDevExt->interruptMode = LevelSensitive;
1512 break;
1513 }
1514
1515 case CmResourceTypeMemory:
1516 {
1517 /* Overflow protection. */
1518 if (rangeCount < PCI_TYPE0_ADDRESSES)
1519 {
1520 LogFlowFunc(("Memory range: Base=%08x:%08x, length=%08x\n",
1521 pPartialData->u.Memory.Start.HighPart,
1522 pPartialData->u.Memory.Start.LowPart,
1523 pPartialData->u.Memory.Length));
1524
1525 /* We only care about read/write memory. */
1526 /** @todo Reconsider memory type. */
1527 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
1528 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
1529 {
1530 /* Save physical MMIO base + length for VMMDev. */
1531 pDevExt->vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start;
1532 pDevExt->vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length;
1533
1534 /* Save resource information. */
1535 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
1536 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
1537 pBaseAddress->RangeInMemory = TRUE;
1538 pBaseAddress->ResourceMapped = FALSE;
1539
1540 LogFunc(("Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1541 pPartialData->u.Memory.Start.HighPart,
1542 pPartialData->u.Memory.Start.LowPart,
1543 pPartialData->u.Memory.Length));
1544
1545 /* Next item ... */
1546 rangeCount++; pBaseAddress++; cMMIORange++;
1547 }
1548 else
1549 LogFunc(("Ignoring memory: Flags=%08x\n", pPartialData->Flags));
1550 }
1551 break;
1552 }
1553
1554 default:
1555 {
1556 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
1557 break;
1558 }
1559 }
1560 }
1561
1562 /* Memorize the number of resources found. */
1563 pDevExt->pciAddressCount = rangeCount;
1564 return rc;
1565}
1566
1567
1568/**
1569 * Maps the I/O space from VMMDev to virtual kernel address space.
1570 *
1571 * @return NTSTATUS
1572 *
1573 * @param pDevExt The device extension.
1574 * @param PhysAddr Physical address to map.
1575 * @param cbToMap Number of bytes to map.
1576 * @param ppvMMIOBase Pointer of mapped I/O base.
1577 * @param pcbMMIO Length of mapped I/O base.
1578 */
1579static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
1580 void **ppvMMIOBase, uint32_t *pcbMMIO)
1581{
1582 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
1583 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
1584 /* pcbMMIO is optional. */
1585
1586 NTSTATUS rc = STATUS_SUCCESS;
1587 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
1588 {
1589 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
1590 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
1591 if (pVMMDevMemory)
1592 {
1593 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
1594
1595 /* Check version of the structure; do we have the right memory version? */
1596 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
1597 {
1598 /* Save results. */
1599 *ppvMMIOBase = pVMMDevMemory;
1600 if (pcbMMIO) /* Optional. */
1601 *pcbMMIO = pVMMDevMemory->u32Size;
1602
1603 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
1604 }
1605 else
1606 {
1607 /* Not our version, refuse operation and unmap the memory. */
1608 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
1609
1610 vgdrvNtUnmapVMMDevMemory(pDevExt);
1611 rc = STATUS_UNSUCCESSFUL;
1612 }
1613 }
1614 else
1615 rc = STATUS_UNSUCCESSFUL;
1616 }
1617 return rc;
1618}
1619
1620
1621/**
1622 * Unmaps the VMMDev I/O range from kernel space.
1623 *
1624 * @param pDevExt The device extension.
1625 */
1626void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1627{
1628 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
1629 if (pDevExt->Core.pVMMDevMemory)
1630 {
1631 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->vmmDevPhysMemoryLength);
1632 pDevExt->Core.pVMMDevMemory = NULL;
1633 }
1634
1635 pDevExt->vmmDevPhysMemoryAddress.QuadPart = 0;
1636 pDevExt->vmmDevPhysMemoryLength = 0;
1637}
1638
1639
1640/**
1641 * Translates NT version to VBox OS.
1642 *
1643 * @returns VBox OS type.
1644 * @param enmNtVer The NT version.
1645 */
1646VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
1647{
1648 VBOXOSTYPE enmOsType;
1649 switch (enmNtVer)
1650 {
1651 case VGDRVNTVER_WINNT4:
1652 enmOsType = VBOXOSTYPE_WinNT4;
1653 break;
1654
1655 case VGDRVNTVER_WIN2K:
1656 enmOsType = VBOXOSTYPE_Win2k;
1657 break;
1658
1659 case VGDRVNTVER_WINXP:
1660#if ARCH_BITS == 64
1661 enmOsType = VBOXOSTYPE_WinXP_x64;
1662#else
1663 enmOsType = VBOXOSTYPE_WinXP;
1664#endif
1665 break;
1666
1667 case VGDRVNTVER_WIN2K3:
1668#if ARCH_BITS == 64
1669 enmOsType = VBOXOSTYPE_Win2k3_x64;
1670#else
1671 enmOsType = VBOXOSTYPE_Win2k3;
1672#endif
1673 break;
1674
1675 case VGDRVNTVER_WINVISTA:
1676#if ARCH_BITS == 64
1677 enmOsType = VBOXOSTYPE_WinVista_x64;
1678#else
1679 enmOsType = VBOXOSTYPE_WinVista;
1680#endif
1681 break;
1682
1683 case VGDRVNTVER_WIN7:
1684#if ARCH_BITS == 64
1685 enmOsType = VBOXOSTYPE_Win7_x64;
1686#else
1687 enmOsType = VBOXOSTYPE_Win7;
1688#endif
1689 break;
1690
1691 case VGDRVNTVER_WIN8:
1692#if ARCH_BITS == 64
1693 enmOsType = VBOXOSTYPE_Win8_x64;
1694#else
1695 enmOsType = VBOXOSTYPE_Win8;
1696#endif
1697 break;
1698
1699 case VGDRVNTVER_WIN81:
1700#if ARCH_BITS == 64
1701 enmOsType = VBOXOSTYPE_Win81_x64;
1702#else
1703 enmOsType = VBOXOSTYPE_Win81;
1704#endif
1705 break;
1706
1707 case VGDRVNTVER_WIN10:
1708#if ARCH_BITS == 64
1709 enmOsType = VBOXOSTYPE_Win10_x64;
1710#else
1711 enmOsType = VBOXOSTYPE_Win10;
1712#endif
1713 break;
1714
1715 default:
1716 /* We don't know, therefore NT family. */
1717 enmOsType = VBOXOSTYPE_WinNT;
1718 break;
1719 }
1720 return enmOsType;
1721}
1722
1723#ifdef VBOX_STRICT
1724
1725/**
1726 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
1727 */
1728static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
1729{
1730 AssertPtrReturn(pu32Bits, 0);
1731 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
1732 uint32_t u32Result = 0;
1733 uint32_t u32WorkingMask = u32Mask;
1734 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1735
1736 while (iBitOffset > 0)
1737 {
1738 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
1739 if (fSet)
1740 u32Result |= 1 << (iBitOffset - 1);
1741 u32WorkingMask &= ~(1 << (iBitOffset - 1));
1742 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1743 }
1744 LogFlowFunc(("Returning %#x\n", u32Result));
1745 return u32Result;
1746}
1747
1748
1749static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
1750{
1751 ULONG u32Bits2 = u32Bits;
1752 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1753 if ( u32Result != u32Exp
1754 || (u32Bits2 & u32Mask)
1755 || (u32Bits2 & u32Result)
1756 || ((u32Bits2 | u32Result) != u32Bits)
1757 )
1758 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
1759 u32Mask, u32Bits, u32Bits2, u32Result));
1760}
1761
1762
1763static void vgdrvNtDoTests(void)
1764{
1765 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1766 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
1767 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1768 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1769 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1770 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1771}
1772
1773#endif /* VBOX_STRICT */
1774
1775#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1776
1777/*
1778 * DPC latency checker.
1779 */
1780
1781/**
1782 * One DPC latency sample.
1783 */
1784typedef struct DPCSAMPLE
1785{
1786 LARGE_INTEGER PerfDelta;
1787 LARGE_INTEGER PerfCounter;
1788 LARGE_INTEGER PerfFrequency;
1789 uint64_t u64TSC;
1790} DPCSAMPLE;
1791AssertCompileSize(DPCSAMPLE, 4*8);
1792
1793/**
1794 * The DPC latency measurement workset.
1795 */
1796typedef struct DPCDATA
1797{
1798 KDPC Dpc;
1799 KTIMER Timer;
1800 KSPIN_LOCK SpinLock;
1801
1802 ULONG ulTimerRes;
1803
1804 bool volatile fFinished;
1805
1806 /** The timer interval (relative). */
1807 LARGE_INTEGER DueTime;
1808
1809 LARGE_INTEGER PerfCounterPrev;
1810
1811 /** Align the sample array on a 64 byte boundrary just for the off chance
1812 * that we'll get cache line aligned memory backing this structure. */
1813 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
1814
1815 int cSamples;
1816 DPCSAMPLE aSamples[8192];
1817} DPCDATA;
1818
1819AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
1820
1821# define VBOXGUEST_DPC_TAG 'DPCS'
1822
1823
1824/**
1825 * DPC callback routine for the DPC latency measurement code.
1826 *
1827 * @param pDpc The DPC, not used.
1828 * @param pvDeferredContext Pointer to the DPCDATA.
1829 * @param SystemArgument1 System use, ignored.
1830 * @param SystemArgument2 System use, ignored.
1831 */
1832static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
1833{
1834 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
1835 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
1836
1837 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
1838
1839 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
1840 pData->fFinished = true;
1841 else
1842 {
1843 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
1844
1845 pSample->u64TSC = ASMReadTSC();
1846 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
1847 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
1848
1849 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
1850
1851 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1852 }
1853
1854 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1855}
1856
1857
1858/**
1859 * Handles the DPC latency checker request.
1860 *
1861 * @returns VBox status code.
1862 */
1863int VGDrvNtIOCtl_DpcLatencyChecker(void)
1864{
1865 /*
1866 * Allocate a block of non paged memory for samples and related data.
1867 */
1868 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
1869 if (!pData)
1870 {
1871 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
1872 return VERR_NO_MEMORY;
1873 }
1874
1875 /*
1876 * Initialize the data.
1877 */
1878 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
1879 KeInitializeTimer(&pData->Timer);
1880 KeInitializeSpinLock(&pData->SpinLock);
1881
1882 pData->fFinished = false;
1883 pData->cSamples = 0;
1884 pData->PerfCounterPrev.QuadPart = 0;
1885
1886 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
1887 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
1888
1889 /*
1890 * Start the DPC measurements and wait for a full set.
1891 */
1892 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1893
1894 while (!pData->fFinished)
1895 {
1896 LARGE_INTEGER Interval;
1897 Interval.QuadPart = -100 * 1000 * 10;
1898 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
1899 }
1900
1901 ExSetTimerResolution(0, 0);
1902
1903 /*
1904 * Log everything to the host.
1905 */
1906 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
1907 for (int i = 0; i < pData->cSamples; i++)
1908 {
1909 DPCSAMPLE *pSample = &pData->aSamples[i];
1910
1911 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
1912 i,
1913 pSample->PerfDelta.QuadPart,
1914 pSample->PerfCounter.QuadPart,
1915 pSample->PerfFrequency.QuadPart,
1916 pSample->u64TSC);
1917 }
1918
1919 RTMemFree(pData);
1920 return VINF_SUCCESS;
1921}
1922
1923#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1924
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