VirtualBox

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

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

VBoxGuest-win.cpp: style + cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.4 KB
Line 
1/* $Id: VBoxGuest-win.cpp 70104 2017-12-13 10:22:57Z 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 <iprt/nt/ntddk.h>
33
34#include "VBoxGuestInternal.h"
35#include <VBox/VBoxGuestLib.h>
36#include <VBox/log.h>
37
38#include <iprt/asm.h>
39#include <iprt/asm-amd64-x86.h>
40#include <iprt/memobj.h>
41#include <iprt/spinlock.h>
42#include <iprt/string.h>
43
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49#ifndef PCI_MAX_BUSES
50# define PCI_MAX_BUSES 256
51#endif
52
53/** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */
54#define VBOX_CM_PRE_VISTA_MASK (0x3f)
55
56
57#define VBOXGUEST_UPDATE_DEVSTATE(a_pDevExt, a_enmNewDevState) \
58 do { \
59 (a_pDevExt)->enmPrevDevState = (a_pDevExt)->enmDevState; \
60 (a_pDevExt)->enmDevState = (a_enmNewDevState); \
61 } while (0)
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67/**
68 * Possible device states for our state machine.
69 */
70typedef enum VGDRVNTDEVSTATE
71{
72 VGDRVNTDEVSTATE_STOPPED,
73 VGDRVNTDEVSTATE_WORKING,
74 VGDRVNTDEVSTATE_PENDINGSTOP,
75 VGDRVNTDEVSTATE_PENDINGREMOVE,
76 VGDRVNTDEVSTATE_SURPRISEREMOVED,
77 VGDRVNTDEVSTATE_REMOVED
78} VGDRVNTDEVSTATE;
79
80
81typedef struct VBOXGUESTWINBASEADDRESS
82{
83 /** Original device physical address. */
84 PHYSICAL_ADDRESS RangeStart;
85 /** Length of I/O or memory range. */
86 ULONG RangeLength;
87 /** Flag: Unmapped range is I/O or memory range. */
88 BOOLEAN RangeInMemory;
89 /** Mapped I/O or memory range. */
90 PVOID MappedRangeStart;
91 /** Flag: mapped range is I/O or memory range. */
92 BOOLEAN MappedRangeInMemory;
93 /** Flag: resource is mapped (i.e. MmMapIoSpace called). */
94 BOOLEAN ResourceMapped;
95} VBOXGUESTWINBASEADDRESS;
96typedef VBOXGUESTWINBASEADDRESS *PVBOXGUESTWINBASEADDRESS;
97
98
99/**
100 * Subclassing the device extension for adding windows-specific bits.
101 */
102typedef struct VBOXGUESTDEVEXTWIN
103{
104 /** The common device extension core. */
105 VBOXGUESTDEVEXT Core;
106
107 /** Our functional driver object. */
108 PDEVICE_OBJECT pDeviceObject;
109 /** Top of the stack. */
110 PDEVICE_OBJECT pNextLowerDriver;
111 /** Currently active Irp. */
112 IRP *pCurrentIrp;
113 /** Interrupt object pointer. */
114 PKINTERRUPT pInterruptObject;
115
116 /** Bus number where the device is located. */
117 ULONG uBus;
118 /** Slot number where the device is located. */
119 ULONG uSlot;
120 /** Device interrupt level. */
121 ULONG uInterruptLevel;
122 /** Device interrupt vector. */
123 ULONG uInterruptVector;
124 /** Affinity mask. */
125 KAFFINITY fInterruptAffinity;
126 /** LevelSensitive or Latched. */
127 KINTERRUPT_MODE enmInterruptMode;
128
129 /** PCI base address information. */
130 ULONG cPciAddresses;
131 VBOXGUESTWINBASEADDRESS aPciBaseAddresses[PCI_TYPE0_ADDRESSES];
132
133 /** Physical address and length of VMMDev memory. */
134 PHYSICAL_ADDRESS uVmmDevMemoryPhysAddr;
135 /** Length of VMMDev memory. */
136 ULONG cbVmmDevMemory;
137
138 /** Device state. */
139 VGDRVNTDEVSTATE enmDevState;
140 /** The previous device state. */
141 VGDRVNTDEVSTATE enmPrevDevState;
142
143 /** Last system power action set (see VBoxGuestPower). */
144 POWER_ACTION enmLastSystemPowerAction;
145 /** Preallocated generic request for shutdown. */
146 VMMDevPowerStateRequest *pPowerStateRequest;
147 /** Preallocated VMMDevEvents for IRQ handler. */
148 VMMDevEvents *pIrqAckEvents;
149
150 /** Spinlock protecting MouseNotifyCallback. Required since the consumer is
151 * in a DPC callback and not the ISR. */
152 KSPIN_LOCK MouseEventAccessSpinLock;
153} VBOXGUESTDEVEXTWIN;
154typedef VBOXGUESTDEVEXTWIN *PVBOXGUESTDEVEXTWIN;
155
156
157/** NT (windows) version identifier. */
158typedef enum VGDRVNTVER
159{
160 VGDRVNTVER_INVALID = 0,
161 VGDRVNTVER_WINNT4,
162 VGDRVNTVER_WIN2K,
163 VGDRVNTVER_WINXP,
164 VGDRVNTVER_WIN2K3,
165 VGDRVNTVER_WINVISTA,
166 VGDRVNTVER_WIN7,
167 VGDRVNTVER_WIN8,
168 VGDRVNTVER_WIN81,
169 VGDRVNTVER_WIN10
170} VGDRVNTVER;
171extern VGDRVNTVER g_enmVGDrvNtVer;
172
173
174/*********************************************************************************************************************************
175* Internal Functions *
176*********************************************************************************************************************************/
177RT_C_DECLS_BEGIN
178#ifdef TARGET_NT4
179static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber);
180static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
181#else
182static NTSTATUS vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
183static NTSTATUS vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp);
184static NTSTATUS vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp);
185static NTSTATUS vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
186#endif
187static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt);
188static NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj);
189static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
190static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
191static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
192static NTSTATUS vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
193static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
194static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
195static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
196static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
197static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
198#ifdef VBOX_STRICT
199static void vgdrvNtDoTests(void);
200#endif
201static VOID vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
202static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
203static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt);
204static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
205 void **ppvMMIOBase, uint32_t *pcbMMIO);
206RT_C_DECLS_END
207
208
209/*********************************************************************************************************************************
210* Exported Functions *
211*********************************************************************************************************************************/
212RT_C_DECLS_BEGIN
213ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
214RT_C_DECLS_END
215
216#ifdef ALLOC_PRAGMA
217/* We only do INIT allocations. PAGE is too much work and risk for little gain. */
218# pragma alloc_text(INIT, DriverEntry)
219# ifdef TARGET_NT4
220# pragma alloc_text(INIT, vgdrvNt4CreateDevice)
221# pragma alloc_text(INIT, vgdrvNt4FindPciDevice)
222# endif
223#endif
224
225
226/*********************************************************************************************************************************
227* Global Variables *
228*********************************************************************************************************************************/
229/** The detected NT (windows) version. */
230VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
231
232
233
234/**
235 * Driver entry point.
236 *
237 * @returns appropriate status code.
238 * @param pDrvObj Pointer to driver object.
239 * @param pRegPath Registry base path.
240 */
241ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
242{
243 RT_NOREF1(pRegPath);
244
245 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
246
247 /*
248 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
249 */
250 ULONG ulMajorVer;
251 ULONG ulMinorVer;
252 ULONG ulBuildNo;
253 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
254
255 /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log */
256 RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
257 if (fCheckedBuild)
258 RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
259
260#ifdef VBOX_STRICT
261 vgdrvNtDoTests();
262#endif
263 NTSTATUS rc = STATUS_SUCCESS;
264 switch (ulMajorVer)
265 {
266 case 10:
267 switch (ulMinorVer)
268 {
269 case 0:
270 /* Windows 10 Preview builds starting with 9926. */
271 default:
272 /* Also everything newer. */
273 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
274 break;
275 }
276 break;
277 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
278 switch (ulMinorVer)
279 {
280 case 0: /* Note: Also could be Windows 2008 Server! */
281 g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
282 break;
283 case 1: /* Note: Also could be Windows 2008 Server R2! */
284 g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
285 break;
286 case 2:
287 g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
288 break;
289 case 3:
290 g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
291 break;
292 case 4:
293 /* Windows 10 Preview builds. */
294 default:
295 /* Also everything newer. */
296 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
297 break;
298 }
299 break;
300 case 5:
301 switch (ulMinorVer)
302 {
303 default:
304 case 2:
305 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
306 break;
307 case 1:
308 g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
309 break;
310 case 0:
311 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
312 break;
313 }
314 break;
315 case 4:
316 g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
317 break;
318 default:
319 if (ulMajorVer > 6)
320 {
321 /* "Windows 10 mode" for Windows 8.1+. */
322 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
323 }
324 else
325 {
326 if (ulMajorVer < 4)
327 LogRelFunc(("At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer));
328 else
329 LogRelFunc(("Unknown version %u.%u!\n", ulMajorVer, ulMinorVer));
330 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
331 }
332 break;
333 }
334
335 if (NT_SUCCESS(rc))
336 {
337 /*
338 * Setup the driver entry points in pDrvObj.
339 */
340 pDrvObj->DriverUnload = vgdrvNtUnload;
341 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
342 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
343 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
344 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
345 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
346 pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
347 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
348#ifdef TARGET_NT4
349 rc = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
350#else
351 pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtNt5PlusPnP;
352 pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtNt5PlusPower;
353 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl;
354 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vgdrvNtNt5PlusAddDevice;
355#endif
356 }
357
358 LogFlowFunc(("Returning %#x\n", rc));
359 return rc;
360}
361
362
363/**
364 * Translates NT version to VBox OS.
365 *
366 * @returns VBox OS type.
367 * @param enmNtVer The NT version.
368 */
369static VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
370{
371 VBOXOSTYPE enmOsType;
372 switch (enmNtVer)
373 {
374 case VGDRVNTVER_WINNT4:
375 enmOsType = VBOXOSTYPE_WinNT4;
376 break;
377
378 case VGDRVNTVER_WIN2K:
379 enmOsType = VBOXOSTYPE_Win2k;
380 break;
381
382 case VGDRVNTVER_WINXP:
383#if ARCH_BITS == 64
384 enmOsType = VBOXOSTYPE_WinXP_x64;
385#else
386 enmOsType = VBOXOSTYPE_WinXP;
387#endif
388 break;
389
390 case VGDRVNTVER_WIN2K3:
391#if ARCH_BITS == 64
392 enmOsType = VBOXOSTYPE_Win2k3_x64;
393#else
394 enmOsType = VBOXOSTYPE_Win2k3;
395#endif
396 break;
397
398 case VGDRVNTVER_WINVISTA:
399#if ARCH_BITS == 64
400 enmOsType = VBOXOSTYPE_WinVista_x64;
401#else
402 enmOsType = VBOXOSTYPE_WinVista;
403#endif
404 break;
405
406 case VGDRVNTVER_WIN7:
407#if ARCH_BITS == 64
408 enmOsType = VBOXOSTYPE_Win7_x64;
409#else
410 enmOsType = VBOXOSTYPE_Win7;
411#endif
412 break;
413
414 case VGDRVNTVER_WIN8:
415#if ARCH_BITS == 64
416 enmOsType = VBOXOSTYPE_Win8_x64;
417#else
418 enmOsType = VBOXOSTYPE_Win8;
419#endif
420 break;
421
422 case VGDRVNTVER_WIN81:
423#if ARCH_BITS == 64
424 enmOsType = VBOXOSTYPE_Win81_x64;
425#else
426 enmOsType = VBOXOSTYPE_Win81;
427#endif
428 break;
429
430 case VGDRVNTVER_WIN10:
431#if ARCH_BITS == 64
432 enmOsType = VBOXOSTYPE_Win10_x64;
433#else
434 enmOsType = VBOXOSTYPE_Win10;
435#endif
436 break;
437
438 default:
439 /* We don't know, therefore NT family. */
440 enmOsType = VBOXOSTYPE_WinNT;
441 break;
442 }
443 return enmOsType;
444}
445
446
447#ifdef LOG_ENABLED
448/**
449 * Debug helper to dump a device resource list.
450 *
451 * @param pResourceList list of device resources.
452 */
453static void vgdrvNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
454{
455 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pResourceList->PartialDescriptors;
456 ULONG cResources = pResourceList->Count;
457
458 for (ULONG i = 0; i < cResources; ++i, ++pResource)
459 {
460 ULONG uType = pResource->Type;
461 static char const * const s_apszName[] =
462 {
463 "CmResourceTypeNull",
464 "CmResourceTypePort",
465 "CmResourceTypeInterrupt",
466 "CmResourceTypeMemory",
467 "CmResourceTypeDma",
468 "CmResourceTypeDeviceSpecific",
469 "CmResourceTypeuBusNumber",
470 "CmResourceTypeDevicePrivate",
471 "CmResourceTypeAssignedResource",
472 "CmResourceTypeSubAllocateFrom",
473 };
474
475 LogFunc(("Type=%s", uType < RT_ELEMENTS(s_apszName) ? s_apszName[uType] : "Unknown"));
476
477 switch (uType)
478 {
479 case CmResourceTypePort:
480 case CmResourceTypeMemory:
481 LogFunc(("Start %8X%8.8lX, length=%X\n",
482 pResource->u.Port.Start.HighPart, pResource->u.Port.Start.LowPart, pResource->u.Port.Length));
483 break;
484
485 case CmResourceTypeInterrupt:
486 LogFunc(("Level=%X, vector=%X, affinity=%X\n",
487 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
488 break;
489
490 case CmResourceTypeDma:
491 LogFunc(("Channel %d, Port %X\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
492 break;
493
494 default:
495 LogFunc(("\n"));
496 break;
497 }
498 }
499}
500#endif /* LOG_ENABLED */
501
502
503/**
504 * Global initialisation stuff (PnP + NT4 legacy).
505 *
506 * @param pDevObj Device object.
507 * @param pIrp Request packet.
508 */
509#ifndef TARGET_NT4
510static NTSTATUS vgdrvNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp)
511#else
512static NTSTATUS vgdrvNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath)
513#endif
514{
515 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
516#ifndef TARGET_NT4
517 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
518 LogFlowFunc(("ENTER: pDevObj=%p pIrp=%p\n", pDevObj, pIrp));
519#else
520 LogFlowFunc(("ENTER: pDrvObj=%p pDevObj=%p pRegPath=%p\n", pDrvObj, pDevObj, pRegPath));
521#endif
522
523 NTSTATUS rcNt;
524#ifdef TARGET_NT4
525 /*
526 * Let's have a look at what our PCI adapter offers.
527 */
528 LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
529
530 /* Assign the PCI resources. */
531 PCM_RESOURCE_LIST pResourceList = NULL;
532 UNICODE_STRING classNameString;
533 RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter");
534 rcNt = HalAssignSlotResources(pRegPath, &classNameString, pDrvObj, pDevObj,
535 PCIBus, pDevExt->uBus, pDevExt->uSlot, &pResourceList);
536# ifdef LOG_ENABLED
537 if (pResourceList && pResourceList->Count > 0)
538 vgdrvNtShowDeviceResources(&pResourceList->List[0].PartialResourceList);
539# endif
540 if (NT_SUCCESS(rcNt))
541 rcNt = vgdrvNtScanPCIResourceList(pResourceList, pDevExt);
542#else
543# ifdef LOG_ENABLED
544 if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0)
545 vgdrvNtShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
546# endif
547 rcNt = vgdrvNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt);
548#endif
549 if (NT_SUCCESS(rcNt))
550 {
551 /*
552 * Map physical address of VMMDev memory into MMIO region
553 * and init the common device extension bits.
554 */
555 void *pvMMIOBase = NULL;
556 uint32_t cbMMIO = 0;
557 rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
558 pDevExt->uVmmDevMemoryPhysAddr,
559 pDevExt->cbVmmDevMemory,
560 &pvMMIOBase,
561 &cbMMIO);
562 if (NT_SUCCESS(rcNt))
563 {
564 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
565
566 LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
567 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
568
569 int vrc = VGDrvCommonInitDevExt(&pDevExt->Core,
570 pDevExt->Core.IOPortBase,
571 pvMMIOBase, cbMMIO,
572 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
573 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
574 if (RT_FAILURE(vrc))
575 {
576 LogFunc(("Could not init device extension, vrc=%Rrc\n", vrc));
577 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
578 }
579 }
580 else
581 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
582 }
583
584 if (NT_SUCCESS(rcNt))
585 {
586 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
587 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
588 if (RT_FAILURE(vrc))
589 {
590 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
591 rcNt = STATUS_UNSUCCESSFUL;
592 }
593 }
594
595 if (NT_SUCCESS(rcNt))
596 {
597 /*
598 * Register DPC and ISR.
599 */
600 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
601
602 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
603#ifdef TARGET_NT4
604 ULONG uInterruptVector = UINT32_MAX;
605 KIRQL irqLevel = UINT8_MAX;
606 /* Get an interrupt vector. */
607 /* Only proceed if the device provides an interrupt. */
608 if ( pDevExt->uInterruptLevel
609 || pDevExt->uInterruptVector)
610 {
611 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
612 pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector));
613
614 uInterruptVector = HalGetInterruptVector(PCIBus,
615 pDevExt->uBus,
616 pDevExt->uInterruptLevel,
617 pDevExt->uInterruptVector,
618 &irqLevel,
619 &pDevExt->fInterruptAffinity);
620 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
621 if (uInterruptVector == 0)
622 LogFunc(("No interrupt vector found!\n"));
623 }
624 else
625 LogFunc(("Device does not provide an interrupt!\n"));
626#endif
627 if (pDevExt->uInterruptVector)
628 {
629#ifdef TARGET_NT4
630 LogFlowFunc(("Connecting interrupt (IntVector=%#u), IrqLevel=%u) ...\n", uInterruptVector, irqLevel));
631#else
632 LogFlowFunc(("Connecting interrupt (IntVector=%#u), IrqLevel=%u) ...\n", pDevExt->uInterruptVector, pDevExt->uInterruptLevel));
633#endif
634
635 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
636 (PKSERVICE_ROUTINE)vgdrvNtIsrHandler, /* Our ISR handler. */
637 pDevExt, /* Device context. */
638 NULL, /* Optional spinlock. */
639#ifdef TARGET_NT4
640 uInterruptVector, /* Interrupt vector. */
641 irqLevel, /* Interrupt level. */
642 irqLevel, /* Interrupt level. */
643#else
644 pDevExt->uInterruptVector, /* Interrupt vector. */
645 (KIRQL)pDevExt->uInterruptLevel, /* Interrupt level. */
646 (KIRQL)pDevExt->uInterruptLevel, /* Interrupt level. */
647#endif
648 pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */
649 TRUE, /* Shareable interrupt. */
650 pDevExt->fInterruptAffinity, /* CPU affinity. */
651 FALSE); /* Don't save FPU stack. */
652 if (NT_ERROR(rcNt))
653 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
654 }
655 else
656 LogFunc(("No interrupt vector found!\n"));
657 }
658
659 if (NT_SUCCESS(rcNt))
660 {
661 /*
662 * Once we've read configuration from register and host, we're finally read.
663 */
664 pDevExt->Core.fLoggingEnabled = true; /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
665 vgdrvNtReadConfiguration(pDevExt);
666
667 /* Ready to rumble! */
668 LogRelFunc(("Device is ready!\n"));
669 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_WORKING);
670 }
671 else
672 pDevExt->pInterruptObject = NULL;
673
674 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
675 * whole bunch of things... */
676
677 LogFunc(("Returned with rcNt=%#x\n", rcNt));
678 return rcNt;
679}
680
681
682
683
684#ifdef TARGET_NT4
685
686/**
687 * Legacy helper function to create the device object.
688 *
689 * @returns NT status code.
690 *
691 * @param pDrvObj The driver object.
692 * @param pRegPath The driver registry path.
693 */
694static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
695{
696 Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
697
698 /*
699 * Find our virtual PCI device
700 */
701 ULONG uuBusNumber;
702 PCI_SLOT_NUMBER uSlot;
703 NTSTATUS rc = vgdrvNt4FindPciDevice(&uuBusNumber, &uSlot);
704 if (NT_ERROR(rc))
705 {
706 Log(("vgdrvNt4CreateDevice: Device not found!\n"));
707 return rc;
708 }
709
710 /*
711 * Create device.
712 */
713 UNICODE_STRING szDevName;
714 RtlInitUnicodeString(&szDevName, VBOXGUEST_DEVICE_NAME_NT);
715 PDEVICE_OBJECT pDeviceObject = NULL;
716 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &szDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
717 if (NT_SUCCESS(rc))
718 {
719 Log(("vgdrvNt4CreateDevice: Device created\n"));
720
721 UNICODE_STRING DosName;
722 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
723 rc = IoCreateSymbolicLink(&DosName, &szDevName);
724 if (NT_SUCCESS(rc))
725 {
726 Log(("vgdrvNt4CreateDevice: Symlink created\n"));
727
728 /*
729 * Setup the device extension.
730 */
731 Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
732
733 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
734 RT_ZERO(*pDevExt);
735
736 Log(("vgdrvNt4CreateDevice: Device extension created\n"));
737
738 /* Store a reference to ourself. */
739 pDevExt->pDeviceObject = pDeviceObject;
740
741 /* Store bus and slot number we've queried before. */
742 pDevExt->uBus = uuBusNumber;
743 pDevExt->uSlot = uSlot.u.AsULONG;
744
745#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
746 rc = hlpRegisterBugCheckCallback(pDevExt);
747#endif
748
749 /* Do the actual VBox init ... */
750 if (NT_SUCCESS(rc))
751 {
752 rc = vgdrvNtInit(pDrvObj, pDeviceObject, pRegPath);
753 if (NT_SUCCESS(rc))
754 {
755 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (succcess)\n", rc));
756 return rc;
757 }
758
759 /* bail out */
760 }
761 IoDeleteSymbolicLink(&DosName);
762 }
763 else
764 Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
765 IoDeleteDevice(pDeviceObject);
766 }
767 else
768 Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
769 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
770 return rc;
771}
772
773
774/**
775 * Helper function to handle the PCI device lookup.
776 *
777 * @returns NT status code.
778 *
779 * @param puluBusNumber Where to return the bus number on success.
780 * @param puSlotNumber Where to return the slot number on success.
781 */
782static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber)
783{
784 Log(("vgdrvNt4FindPciDevice\n"));
785
786 PCI_SLOT_NUMBER uSlot;
787 uSlot.u.AsULONG = 0;
788
789 /* Scan each bus. */
790 for (ULONG uluBusNumber = 0; uluBusNumber < PCI_MAX_BUSES; uluBusNumber++)
791 {
792 /* Scan each device. */
793 for (ULONG deviceNumber = 0; deviceNumber < PCI_MAX_DEVICES; deviceNumber++)
794 {
795 uSlot.u.bits.DeviceNumber = deviceNumber;
796
797 /* Scan each function (not really required...). */
798 for (ULONG functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++)
799 {
800 uSlot.u.bits.FunctionNumber = functionNumber;
801
802 /* Have a look at what's in this slot. */
803 PCI_COMMON_CONFIG PciData;
804 if (!HalGetBusData(PCIConfiguration, uluBusNumber, uSlot.u.AsULONG, &PciData, sizeof(ULONG)))
805 {
806 /* No such bus, we're done with it. */
807 deviceNumber = PCI_MAX_DEVICES;
808 break;
809 }
810
811 if (PciData.VendorID == PCI_INVALID_VENDORID)
812 /* We have to proceed to the next function. */
813 continue;
814
815 /* Check if it's another device. */
816 if ( PciData.VendorID != VMMDEV_VENDORID
817 || PciData.DeviceID != VMMDEV_DEVICEID)
818 continue;
819
820 /* Hooray, we've found it! */
821 Log(("vgdrvNt4FindPciDevice: Device found!\n"));
822
823 *puluBusNumber = uluBusNumber;
824 *puSlotNumber = uSlot;
825 return STATUS_SUCCESS;
826 }
827 }
828 }
829
830 return STATUS_DEVICE_DOES_NOT_EXIST;
831}
832
833#else /* !TARGET_NT4 */
834
835/**
836 * Handle request from the Plug & Play subsystem.
837 *
838 * @returns NT status code
839 * @param pDrvObj Driver object
840 * @param pDevObj Device object
841 *
842 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
843 */
844static NTSTATUS vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
845{
846 NTSTATUS rc;
847 LogFlowFuncEnter();
848
849 /*
850 * Create device.
851 */
852 UNICODE_STRING DevName;
853 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
854 PDEVICE_OBJECT pDeviceObject = NULL;
855 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
856 if (NT_SUCCESS(rc))
857 {
858 /*
859 * Create symbolic link (DOS devices).
860 */
861 UNICODE_STRING DosName;
862 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
863 rc = IoCreateSymbolicLink(&DosName, &DevName);
864 if (NT_SUCCESS(rc))
865 {
866 /*
867 * Setup the device extension.
868 */
869 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
870 RT_ZERO(*pDevExt);
871
872 KeInitializeSpinLock(&pDevExt->MouseEventAccessSpinLock);
873
874 pDevExt->pDeviceObject = pDeviceObject;
875 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
876 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
877
878 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
879 if (pDevExt->pNextLowerDriver != NULL)
880 {
881 /*
882 * If we reached this point we're fine with the basic driver setup,
883 * so continue to init our own things.
884 */
885# ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
886 vgdrvNtBugCheckCallback(pDevExt); /* Ignore failure! */
887# endif
888 if (NT_SUCCESS(rc))
889 {
890 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
891 pDeviceObject->Flags |= DO_POWER_PAGABLE;
892
893 /* Driver is ready now. */
894 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
895 LogFlowFunc(("Returning with rc=%#x (success)\n", rc));
896 return rc;
897 }
898
899 IoDetachDevice(pDevExt->pNextLowerDriver);
900 }
901 else
902 {
903 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
904 rc = STATUS_DEVICE_NOT_CONNECTED;
905 }
906
907 /* bail out */
908 IoDeleteSymbolicLink(&DosName);
909 }
910 else
911 LogFunc(("IoCreateSymbolicLink failed with rc=%#x!\n", rc));
912 IoDeleteDevice(pDeviceObject);
913 }
914 else
915 LogFunc(("IoCreateDevice failed with rc=%#x!\n", rc));
916
917 LogFunc(("Returning with rc=%#x\n", rc));
918 return rc;
919}
920
921/**
922 * Irp completion routine for PnP Irps we send.
923 *
924 * @returns NT status code.
925 * @param pDevObj Device object.
926 * @param pIrp Request packet.
927 * @param pEvent Semaphore.
928 */
929static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
930{
931 RT_NOREF2(pDevObj, pIrp);
932 KeSetEvent(pEvent, 0, FALSE);
933 return STATUS_MORE_PROCESSING_REQUIRED;
934}
935
936
937/**
938 * Helper to send a PnP IRP and wait until it's done.
939 *
940 * @returns NT status code.
941 * @param pDevObj Device object.
942 * @param pIrp Request packet.
943 * @param fStrict When set, returns an error if the IRP gives an error.
944 */
945static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
946{
947 KEVENT Event;
948
949 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
950
951 IoCopyCurrentIrpStackLocationToNext(pIrp);
952 IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE);
953
954 NTSTATUS rc = IoCallDriver(pDevObj, pIrp);
955
956 if (rc == STATUS_PENDING)
957 {
958 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
959 rc = pIrp->IoStatus.Status;
960 }
961
962 if ( !fStrict
963 && (rc == STATUS_NOT_SUPPORTED || rc == STATUS_INVALID_DEVICE_REQUEST))
964 {
965 rc = STATUS_SUCCESS;
966 }
967
968 Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning 0x%x\n", rc));
969 return rc;
970}
971
972
973/**
974 * PnP Request handler.
975 *
976 * @param pDevObj Device object.
977 * @param pIrp Request packet.
978 */
979static NTSTATUS vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
980{
981 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
982 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
983
984# ifdef LOG_ENABLED
985 static char const * const s_apszFnctName[] =
986 {
987 "IRP_MN_START_DEVICE",
988 "IRP_MN_QUERY_REMOVE_DEVICE",
989 "IRP_MN_REMOVE_DEVICE",
990 "IRP_MN_CANCEL_REMOVE_DEVICE",
991 "IRP_MN_STOP_DEVICE",
992 "IRP_MN_QUERY_STOP_DEVICE",
993 "IRP_MN_CANCEL_STOP_DEVICE",
994 "IRP_MN_QUERY_DEVICE_RELATIONS",
995 "IRP_MN_QUERY_INTERFACE",
996 "IRP_MN_QUERY_CAPABILITIES",
997 "IRP_MN_QUERY_RESOURCES",
998 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
999 "IRP_MN_QUERY_DEVICE_TEXT",
1000 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
1001 "IRP_MN_0xE",
1002 "IRP_MN_READ_CONFIG",
1003 "IRP_MN_WRITE_CONFIG",
1004 "IRP_MN_EJECT",
1005 "IRP_MN_SET_LOCK",
1006 "IRP_MN_QUERY_ID",
1007 "IRP_MN_QUERY_PNP_DEVICE_STATE",
1008 "IRP_MN_QUERY_BUS_INFORMATION",
1009 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
1010 "IRP_MN_SURPRISE_REMOVAL",
1011 };
1012 Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n",
1013 pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown"));
1014# endif
1015
1016 NTSTATUS rc = STATUS_SUCCESS;
1017 switch (pStack->MinorFunction)
1018 {
1019 case IRP_MN_START_DEVICE:
1020 {
1021 Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n"));
1022
1023 /* This must be handled first by the lower driver. */
1024 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1025 if ( NT_SUCCESS(rc)
1026 && NT_SUCCESS(pIrp->IoStatus.Status))
1027 {
1028 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n",
1029 pStack->Parameters.StartDevice.AllocatedResources));
1030
1031 if (pStack->Parameters.StartDevice.AllocatedResources)
1032 rc = vgdrvNtInit(pDevObj, pIrp);
1033 else
1034 {
1035 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n",
1036 pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL));
1037 rc = STATUS_UNSUCCESSFUL;
1038 }
1039 }
1040
1041 if (NT_ERROR(rc))
1042 {
1043 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: Error: rc = 0x%x\n", rc));
1044
1045 /* Need to unmap memory in case of errors ... */
1046/** @todo r=bird: vgdrvNtInit maps it and is responsible for cleaning up its own friggin mess...
1047 * Fix it instead of kind of working around things there!! */
1048 vgdrvNtUnmapVMMDevMemory(pDevExt);
1049 }
1050 break;
1051 }
1052
1053 case IRP_MN_CANCEL_REMOVE_DEVICE:
1054 {
1055 Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n"));
1056
1057 /* This must be handled first by the lower driver. */
1058 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1059 if ( NT_SUCCESS(rc)
1060 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE)
1061 {
1062 /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */
1063 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1064 }
1065
1066 /* Complete the IRP. */
1067 break;
1068 }
1069
1070 case IRP_MN_SURPRISE_REMOVAL:
1071 {
1072 Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n"));
1073
1074 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_SURPRISEREMOVED);
1075
1076 /* Do nothing here actually. Cleanup is done in IRP_MN_REMOVE_DEVICE.
1077 * This request is not expected for VBoxGuest.
1078 */
1079 LogRel(("VBoxGuest: unexpected device removal\n"));
1080
1081 /* Pass to the lower driver. */
1082 pIrp->IoStatus.Status = STATUS_SUCCESS;
1083
1084 IoSkipCurrentIrpStackLocation(pIrp);
1085
1086 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1087
1088 /* Do not complete the IRP. */
1089 return rc;
1090 }
1091
1092 case IRP_MN_QUERY_REMOVE_DEVICE:
1093 {
1094 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n"));
1095
1096# ifdef VBOX_REBOOT_ON_UNINSTALL
1097 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n"));
1098 rc = STATUS_UNSUCCESSFUL;
1099# endif
1100
1101 if (NT_SUCCESS(rc))
1102 {
1103 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_PENDINGREMOVE);
1104
1105 /* This IRP passed down to lower driver. */
1106 pIrp->IoStatus.Status = STATUS_SUCCESS;
1107
1108 IoSkipCurrentIrpStackLocation(pIrp);
1109
1110 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1111 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1112
1113 /* we must not do anything the IRP after doing IoSkip & CallDriver
1114 * since the driver below us will complete (or already have completed) the IRP.
1115 * I.e. just return the status we got from IoCallDriver */
1116 return rc;
1117 }
1118
1119 /* Complete the IRP on failure. */
1120 break;
1121 }
1122
1123 case IRP_MN_REMOVE_DEVICE:
1124 {
1125 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n"));
1126
1127 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_REMOVED);
1128
1129 /* Free hardware resources. */
1130 /** @todo this should actually free I/O ports, interrupts, etc.
1131 * Update/bird: vgdrvNtCleanup actually does that... So, what's there to do? */
1132 rc = vgdrvNtCleanup(pDevObj);
1133 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: vgdrvNtCleanup rc = 0x%08X\n", rc));
1134
1135 /*
1136 * We need to send the remove down the stack before we detach,
1137 * but we don't need to wait for the completion of this operation
1138 * (and to register a completion routine).
1139 */
1140 pIrp->IoStatus.Status = STATUS_SUCCESS;
1141
1142 IoSkipCurrentIrpStackLocation(pIrp);
1143
1144 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1145 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1146
1147 IoDetachDevice(pDevExt->pNextLowerDriver);
1148
1149 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n"));
1150
1151 /* Destroy device extension and clean up everything else. */
1152 VGDrvCommonDeleteDevExt(&pDevExt->Core);
1153
1154 /* Remove DOS device + symbolic link. */
1155 UNICODE_STRING win32Name;
1156 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
1157 IoDeleteSymbolicLink(&win32Name);
1158
1159 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Deleting device ...\n"));
1160
1161 /* Last action: Delete our device! pDevObj is *not* failed
1162 * anymore after this call! */
1163 IoDeleteDevice(pDevObj);
1164
1165 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n"));
1166
1167 /* Propagating rc from IoCallDriver. */
1168 return rc; /* Make sure that we don't do anything below here anymore! */
1169 }
1170
1171 case IRP_MN_CANCEL_STOP_DEVICE:
1172 {
1173 Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n"));
1174
1175 /* This must be handled first by the lower driver. */
1176 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1177 if ( NT_SUCCESS(rc)
1178 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP)
1179 {
1180 /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */
1181 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1182 }
1183
1184 /* Complete the IRP. */
1185 break;
1186 }
1187
1188 case IRP_MN_QUERY_STOP_DEVICE:
1189 {
1190 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n"));
1191
1192# ifdef VBOX_REBOOT_ON_UNINSTALL
1193 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Device cannot be stopped without a reboot!\n"));
1194 pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
1195# endif
1196
1197 if (NT_SUCCESS(rc))
1198 {
1199 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_PENDINGSTOP);
1200
1201 /* This IRP passed down to lower driver. */
1202 pIrp->IoStatus.Status = STATUS_SUCCESS;
1203
1204 IoSkipCurrentIrpStackLocation(pIrp);
1205
1206 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1207 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1208
1209 /* we must not do anything with the IRP after doing IoSkip & CallDriver
1210 * since the driver below us will complete (or already have completed) the IRP.
1211 * I.e. just return the status we got from IoCallDriver */
1212 return rc;
1213 }
1214
1215 /* Complete the IRP on failure. */
1216 break;
1217 }
1218
1219 case IRP_MN_STOP_DEVICE:
1220 {
1221 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n"));
1222
1223 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_STOPPED);
1224
1225 /* Free hardware resources. */
1226 /** @todo this should actually free I/O ports, interrupts, etc.
1227 * Update/bird: vgdrvNtCleanup actually does that... So, what's there to do? */
1228 rc = vgdrvNtCleanup(pDevObj);
1229 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: cleaning up, rc = 0x%x\n", rc));
1230
1231 /* Pass to the lower driver. */
1232 pIrp->IoStatus.Status = STATUS_SUCCESS;
1233
1234 IoSkipCurrentIrpStackLocation(pIrp);
1235
1236 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1237 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1238
1239 return rc;
1240 }
1241
1242 default:
1243 {
1244 IoSkipCurrentIrpStackLocation(pIrp);
1245 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1246 return rc;
1247 }
1248 }
1249
1250 pIrp->IoStatus.Status = rc;
1251 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1252
1253 Log(("vgdrvNtNt5PlusPnP: Returning with rc = 0x%x\n", rc));
1254 return rc;
1255}
1256
1257
1258/**
1259 * Handle the power completion event.
1260 *
1261 * @returns NT status code.
1262 * @param pDevObj Targetted device object.
1263 * @param pIrp IO request packet.
1264 * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower.
1265 */
1266static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext)
1267{
1268# ifdef VBOX_STRICT
1269 RT_NOREF1(pDevObj);
1270 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext;
1271 PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
1272
1273 Assert(pDevExt);
1274
1275 if (pIrpSp)
1276 {
1277 Assert(pIrpSp->MajorFunction == IRP_MJ_POWER);
1278 if (NT_SUCCESS(pIrp->IoStatus.Status))
1279 {
1280 switch (pIrpSp->MinorFunction)
1281 {
1282 case IRP_MN_SET_POWER:
1283 switch (pIrpSp->Parameters.Power.Type)
1284 {
1285 case DevicePowerState:
1286 switch (pIrpSp->Parameters.Power.State.DeviceState)
1287 {
1288 case PowerDeviceD0:
1289 break;
1290 default: /* Shut up MSC */ break;
1291 }
1292 break;
1293 default: /* Shut up MSC */ break;
1294 }
1295 break;
1296 }
1297 }
1298 }
1299# else
1300 RT_NOREF3(pDevObj, pIrp, pContext);
1301# endif
1302
1303 return STATUS_SUCCESS;
1304}
1305
1306
1307/**
1308 * Handle the Power requests.
1309 *
1310 * @returns NT status code
1311 * @param pDevObj device object
1312 * @param pIrp IRP
1313 */
1314static NTSTATUS vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1315{
1316 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1317 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1318 POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type;
1319 POWER_STATE PowerState = pStack->Parameters.Power.State;
1320 POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType;
1321
1322 Log(("vgdrvNtNt5PlusPower:\n"));
1323
1324 switch (pStack->MinorFunction)
1325 {
1326 case IRP_MN_SET_POWER:
1327 {
1328 Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType));
1329 switch (enmPowerType)
1330 {
1331 case SystemPowerState:
1332 {
1333 Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n",
1334 enmPowerAction, PowerState.SystemState, PowerState.DeviceState));
1335
1336 switch (enmPowerAction)
1337 {
1338 case PowerActionSleep:
1339
1340 /* System now is in a working state. */
1341 if (PowerState.SystemState == PowerSystemWorking)
1342 {
1343 if ( pDevExt
1344 && pDevExt->enmLastSystemPowerAction == PowerActionHibernate)
1345 {
1346 Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n"));
1347 int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core,
1348 vgdrvNtVersionToOSType(g_enmVGDrvNtVer));
1349 if (RT_FAILURE(rc))
1350 Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc));
1351 }
1352 }
1353 break;
1354
1355 case PowerActionShutdownReset:
1356 {
1357 Log(("vgdrvNtNt5PlusPower: Power action reset!\n"));
1358
1359 /* Tell the VMM that we no longer support mouse pointer integration. */
1360 VMMDevReqMouseStatus *pReq = NULL;
1361 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus),
1362 VMMDevReq_SetMouseStatus);
1363 if (RT_SUCCESS(vrc))
1364 {
1365 pReq->mouseFeatures = 0;
1366 pReq->pointerXPos = 0;
1367 pReq->pointerYPos = 0;
1368
1369 vrc = VbglR0GRPerform(&pReq->header);
1370 if (RT_FAILURE(vrc))
1371 {
1372 Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
1373 }
1374
1375 VbglR0GRFree(&pReq->header);
1376 }
1377
1378 /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this
1379 * power action and would assert/crash when we already cleaned up all the stuff! */
1380 break;
1381 }
1382
1383 case PowerActionShutdown:
1384 case PowerActionShutdownOff:
1385 {
1386 Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n"));
1387 if (PowerState.SystemState >= PowerSystemShutdown)
1388 {
1389 Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n"));
1390
1391 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
1392 int vrc = VERR_NOT_IMPLEMENTED;
1393 if (pReq)
1394 {
1395 pReq->header.requestType = VMMDevReq_SetPowerStatus;
1396 pReq->powerState = VMMDevPowerState_PowerOff;
1397
1398 vrc = VbglR0GRPerform(&pReq->header);
1399 }
1400 if (RT_FAILURE(vrc))
1401 Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
1402
1403 /* No need to do cleanup here; at this point we should've been
1404 * turned off by VMMDev already! */
1405 }
1406 break;
1407 }
1408
1409 case PowerActionHibernate:
1410
1411 Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n"));
1412 break;
1413
1414 case PowerActionWarmEject:
1415 Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n"));
1416 break;
1417
1418 default:
1419 Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction));
1420 break;
1421 }
1422
1423 /*
1424 * Save the current system power action for later use.
1425 * This becomes handy when we return from hibernation for example.
1426 */
1427 if (pDevExt)
1428 pDevExt->enmLastSystemPowerAction = enmPowerAction;
1429
1430 break;
1431 }
1432 default:
1433 break;
1434 }
1435 break;
1436 }
1437 default:
1438 break;
1439 }
1440
1441 /*
1442 * Whether we are completing or relaying this power IRP,
1443 * we must call PoStartNextPowerIrp.
1444 */
1445 PoStartNextPowerIrp(pIrp);
1446
1447 /*
1448 * Send the IRP down the driver stack, using PoCallDriver
1449 * (not IoCallDriver, as for non-power irps).
1450 */
1451 IoCopyCurrentIrpStackLocationToNext(pIrp);
1452 IoSetCompletionRoutine(pIrp,
1453 vgdrvNtNt5PlusPowerComplete,
1454 (PVOID)pDevExt,
1455 TRUE,
1456 TRUE,
1457 TRUE);
1458 return PoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1459}
1460
1461
1462/**
1463 * IRP_MJ_SYSTEM_CONTROL handler.
1464 *
1465 * @returns NT status code
1466 * @param pDevObj Device object.
1467 * @param pIrp IRP.
1468 */
1469static NTSTATUS vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1470{
1471 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1472
1473 LogFlowFuncEnter();
1474
1475 /* Always pass it on to the next driver. */
1476 IoSkipCurrentIrpStackLocation(pIrp);
1477
1478 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1479}
1480
1481#endif /* !TARGET_NT4 */
1482
1483
1484/**
1485 * Unmaps the VMMDev I/O range from kernel space.
1486 *
1487 * @param pDevExt The device extension.
1488 */
1489static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1490{
1491 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
1492 if (pDevExt->Core.pVMMDevMemory)
1493 {
1494 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->cbVmmDevMemory);
1495 pDevExt->Core.pVMMDevMemory = NULL;
1496 }
1497
1498 pDevExt->uVmmDevMemoryPhysAddr.QuadPart = 0;
1499 pDevExt->cbVmmDevMemory = 0;
1500}
1501
1502
1503/**
1504 * Cleans up hardware resources.
1505 * Do not delete DevExt here.
1506 *
1507 * @todo r=bird: HC SVNT DRACONES!
1508 *
1509 * This code leaves clients hung when vgdrvNtInit is called afterwards.
1510 * This happens when for instance hotplugging a CPU. Problem is
1511 * vgdrvNtInit doing a full VGDrvCommonInitDevExt, orphaning all pDevExt
1512 * members, like session lists and stuff.
1513 *
1514 * @param pDevObj Device object.
1515 */
1516static NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj)
1517{
1518 LogFlowFuncEnter();
1519
1520 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1521 if (pDevExt)
1522 {
1523 if (pDevExt->pInterruptObject)
1524 {
1525 IoDisconnectInterrupt(pDevExt->pInterruptObject);
1526 pDevExt->pInterruptObject = NULL;
1527 }
1528
1529 /** @todo cleanup the rest stuff */
1530
1531
1532#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
1533 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
1534#endif
1535 /* According to MSDN we have to unmap previously mapped memory. */
1536 vgdrvNtUnmapVMMDevMemory(pDevExt);
1537 }
1538
1539 return STATUS_SUCCESS;
1540}
1541
1542
1543/**
1544 * Unload the driver.
1545 *
1546 * @param pDrvObj Driver object.
1547 */
1548static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
1549{
1550 LogFlowFuncEnter();
1551
1552#ifdef TARGET_NT4
1553 vgdrvNtCleanup(pDrvObj->DeviceObject);
1554
1555 /* Destroy device extension and clean up everything else. */
1556 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
1557 VGDrvCommonDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
1558
1559 /*
1560 * I don't think it's possible to unload a driver which processes have
1561 * opened, at least we'll blindly assume that here.
1562 */
1563 UNICODE_STRING DosName;
1564 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1565 IoDeleteSymbolicLink(&DosName);
1566
1567 IoDeleteDevice(pDrvObj->DeviceObject);
1568#else /* !TARGET_NT4 */
1569 /*
1570 * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE
1571 * where we already did the cleanup, so don't do anything here (yet).
1572 */
1573 RT_NOREF1(pDrvObj);
1574#endif /* !TARGET_NT4 */
1575
1576 LogFlowFunc(("Returning\n"));
1577}
1578
1579
1580/**
1581 * For simplifying request completion into a simple return statement, extended
1582 * version.
1583 *
1584 * @returns rcNt
1585 * @param rcNt The status code.
1586 * @param uInfo Extra info value.
1587 * @param pIrp The IRP.
1588 */
1589DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
1590{
1591 pIrp->IoStatus.Status = rcNt;
1592 pIrp->IoStatus.Information = uInfo;
1593 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1594 return rcNt;
1595}
1596
1597
1598/**
1599 * For simplifying request completion into a simple return statement.
1600 *
1601 * @returns rcNt
1602 * @param rcNt The status code.
1603 * @param pIrp The IRP.
1604 */
1605DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
1606{
1607 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
1608}
1609
1610
1611/**
1612 * Create (i.e. Open) file entry point.
1613 *
1614 * @param pDevObj Device object.
1615 * @param pIrp Request packet.
1616 */
1617static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1618{
1619 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
1620 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1621 PFILE_OBJECT pFileObj = pStack->FileObject;
1622 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1623
1624 Assert(pFileObj->FsContext == NULL);
1625
1626 /*
1627 * We are not remotely similar to a directory...
1628 * (But this is possible.)
1629 */
1630 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
1631 {
1632 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
1633 return vgdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
1634 }
1635
1636 /*
1637 * Check the device state.
1638 */
1639 if (pDevExt->enmDevState != VGDRVNTDEVSTATE_WORKING)
1640 {
1641 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", pDevExt->enmDevState));
1642 return vgdrvNtCompleteRequest(STATUS_DEVICE_NOT_READY, pIrp);
1643 }
1644
1645 /*
1646 * Create a client session.
1647 */
1648 int rc;
1649 PVBOXGUESTSESSION pSession;
1650 if (pIrp->RequestorMode == KernelMode)
1651 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
1652 else
1653 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, &pSession);
1654 if (RT_SUCCESS(rc))
1655 {
1656 pFileObj->FsContext = pSession;
1657 Log(("vgdrvNtCreate: Successfully created %s session %p\n",
1658 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession));
1659 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
1660 }
1661
1662 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
1663 /* Note. the IoStatus is completely ignored on error. */
1664 if (rc == VERR_NO_MEMORY)
1665 return vgdrvNtCompleteRequest(STATUS_NO_MEMORY, pIrp);
1666 return vgdrvNtCompleteRequest(STATUS_UNSUCCESSFUL, pIrp);
1667}
1668
1669
1670/**
1671 * Close file entry point.
1672 *
1673 * @param pDevObj Device object.
1674 * @param pIrp Request packet.
1675 */
1676static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1677{
1678 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1679 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1680 PFILE_OBJECT pFileObj = pStack->FileObject;
1681
1682 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
1683
1684#ifdef VBOX_WITH_HGCM
1685 /* Close both, R0 and R3 sessions. */
1686 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
1687 if (pSession)
1688 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
1689#endif
1690
1691 pFileObj->FsContext = NULL;
1692 pIrp->IoStatus.Information = 0;
1693 pIrp->IoStatus.Status = STATUS_SUCCESS;
1694 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1695
1696 return STATUS_SUCCESS;
1697}
1698
1699
1700/**
1701 * Device I/O Control entry point.
1702 *
1703 * @param pDevObj Device object.
1704 * @param pIrp Request packet.
1705 */
1706NTSTATUS _stdcall vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1707{
1708 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1709 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1710 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
1711
1712 if (!RT_VALID_PTR(pSession))
1713 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
1714
1715#if 0 /* No fast I/O controls defined yet. */
1716 /*
1717 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
1718 * the session and iCmd, and does not return anything.
1719 */
1720 if (pSession->fUnrestricted)
1721 {
1722 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
1723 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
1724 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
1725 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
1726 {
1727 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
1728
1729 /* Complete the I/O request. */
1730 supdrvSessionRelease(pSession);
1731 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
1732 }
1733 }
1734#endif
1735
1736 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
1737}
1738
1739
1740/**
1741 * Device I/O Control entry point.
1742 *
1743 * @param pDevExt The device extension.
1744 * @param pSession The session.
1745 * @param pIrp Request packet.
1746 * @param pStack The request stack pointer.
1747 */
1748static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1749 PIRP pIrp, PIO_STACK_LOCATION pStack)
1750{
1751 NTSTATUS rcNt;
1752 uint32_t cbOut = 0;
1753 int rc = 0;
1754 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
1755 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
1756 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
1757 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
1758
1759#if 0 /*def RT_ARCH_AMD64*/
1760 /* Don't allow 32-bit processes to do any I/O controls. */
1761 if (!IoIs32bitProcess(pIrp))
1762#endif
1763 {
1764 /* Verify that it's a buffered CTL. */
1765 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
1766 {
1767 /* Verify that the sizes in the request header are correct. */
1768 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
1769 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
1770 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
1771 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
1772 {
1773 /* Zero extra output bytes to make sure we don't leak anything. */
1774 if (pHdr->cbIn < pHdr->cbOut)
1775 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
1776
1777 /*
1778 * Do the job.
1779 */
1780 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
1781 RT_MAX(pHdr->cbIn, pHdr->cbOut));
1782 if (RT_SUCCESS(rc))
1783 {
1784 rcNt = STATUS_SUCCESS;
1785 cbOut = pHdr->cbOut;
1786 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
1787 {
1788 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1789 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
1790 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
1791 }
1792
1793 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
1794 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
1795 && RT_SUCCESS(pHdr->rc))
1796 pStack->FileObject->FsContext = NULL;
1797 }
1798 else if (rc == VERR_NOT_SUPPORTED)
1799 rcNt = STATUS_NOT_SUPPORTED;
1800 else
1801 rcNt = STATUS_INVALID_PARAMETER;
1802 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
1803 }
1804 else
1805 {
1806 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
1807 pStack->Parameters.DeviceIoControl.IoControlCode,
1808 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
1809 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
1810 pStack->Parameters.DeviceIoControl.InputBufferLength,
1811 pStack->Parameters.DeviceIoControl.OutputBufferLength));
1812 rcNt = STATUS_INVALID_PARAMETER;
1813 }
1814 }
1815 else
1816 {
1817 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
1818 pStack->Parameters.DeviceIoControl.IoControlCode));
1819 rcNt = STATUS_NOT_SUPPORTED;
1820 }
1821 }
1822#if 0 /*def RT_ARCH_AMD64*/
1823 else
1824 {
1825 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
1826 rcNt = STATUS_NOT_SUPPORTED;
1827 }
1828#endif
1829
1830 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
1831}
1832
1833
1834/**
1835 * Internal Device I/O Control entry point (for IDC).
1836 *
1837 * @param pDevObj Device object.
1838 * @param pIrp Request packet.
1839 */
1840static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1841{
1842 /* Currently no special code here. */
1843 return vgdrvNtDeviceControl(pDevObj, pIrp);
1844}
1845
1846
1847/**
1848 * IRP_MJ_SHUTDOWN handler.
1849 *
1850 * @returns NT status code
1851 * @param pDevObj Device object.
1852 * @param pIrp IRP.
1853 */
1854static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1855{
1856 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1857 LogFlowFuncEnter();
1858
1859 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
1860 if (pReq)
1861 {
1862 pReq->header.requestType = VMMDevReq_SetPowerStatus;
1863 pReq->powerState = VMMDevPowerState_PowerOff;
1864
1865 int rc = VbglR0GRPerform(&pReq->header);
1866 if (RT_FAILURE(rc))
1867 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
1868 }
1869
1870 /* just in case, since we shouldn't normally get here. */
1871 pIrp->IoStatus.Information = 0;
1872 pIrp->IoStatus.Status = STATUS_SUCCESS;
1873 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1874 return STATUS_SUCCESS;
1875}
1876
1877
1878/**
1879 * Stub function for functions we don't implemented.
1880 *
1881 * @returns STATUS_NOT_SUPPORTED
1882 * @param pDevObj Device object.
1883 * @param pIrp IRP.
1884 */
1885static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1886{
1887 RT_NOREF1(pDevObj);
1888 LogFlowFuncEnter();
1889
1890 pIrp->IoStatus.Information = 0;
1891 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1892 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1893
1894 return STATUS_NOT_SUPPORTED;
1895}
1896
1897
1898/**
1899 * Sets the mouse notification callback.
1900 *
1901 * @returns VBox status code.
1902 * @param pDevExt Pointer to the device extension.
1903 * @param pNotify Pointer to the mouse notify struct.
1904 */
1905int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1906{
1907 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
1908 /* we need a lock here to avoid concurrency with the set event functionality */
1909 KIRQL OldIrql;
1910 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
1911 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1912 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1913 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
1914 return VINF_SUCCESS;
1915}
1916
1917
1918/**
1919 * DPC handler.
1920 *
1921 * @param pDPC DPC descriptor.
1922 * @param pDevObj Device object.
1923 * @param pIrp Interrupt request packet.
1924 * @param pContext Context specific pointer.
1925 */
1926static void vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
1927{
1928 RT_NOREF3(pDPC, pIrp, pContext);
1929 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1930 Log3Func(("pDevExt=0x%p\n", pDevExt));
1931
1932 /* Test & reset the counter. */
1933 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
1934 {
1935 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
1936 * i.e. to prevent the event from destroyed while we're using it */
1937 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
1938 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
1939
1940 if (pDevExt->Core.pfnMouseNotifyCallback)
1941 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
1942
1943 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
1944 }
1945
1946 /* Process the wake-up list we were asked by the scheduling a DPC
1947 * in vgdrvNtIsrHandler(). */
1948 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
1949}
1950
1951
1952/**
1953 * ISR handler.
1954 *
1955 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
1956 * @param pInterrupt Interrupt that was triggered.
1957 * @param pServiceContext Context specific pointer.
1958 */
1959static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1960{
1961 RT_NOREF1(pInterrupt);
1962 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
1963 if (pDevExt == NULL)
1964 return FALSE;
1965
1966 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
1967
1968 /* Enter the common ISR routine and do the actual work. */
1969 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
1970
1971 /* If we need to wake up some events we do that in a DPC to make
1972 * sure we're called at the right IRQL. */
1973 if (fIRQTaken)
1974 {
1975 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
1976 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
1977 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
1978 {
1979 Log3Func(("Requesting DPC ...\n"));
1980 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
1981 }
1982 }
1983 return fIRQTaken;
1984}
1985
1986
1987/**
1988 * Overridden routine for mouse polling events.
1989 *
1990 * @param pDevExt Device extension structure.
1991 */
1992void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1993{
1994 NOREF(pDevExt);
1995 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
1996 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
1997 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
1998}
1999
2000
2001/**
2002 * Hook for handling OS specfic options from the host.
2003 *
2004 * @returns true if handled, false if not.
2005 * @param pDevExt The device extension.
2006 * @param pszName The option name.
2007 * @param pszValue The option value.
2008 */
2009bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
2010{
2011 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
2012 return false;
2013}
2014
2015
2016
2017/**
2018 * Queries (gets) a DWORD value from the registry.
2019 *
2020 * @return NTSTATUS
2021 * @param uRoot Relative path root. See RTL_REGISTRY_SERVICES or RTL_REGISTRY_ABSOLUTE.
2022 * @param pwszPath Path inside path root.
2023 * @param pwszName Actual value name to look up.
2024 * @param puValue On input this can specify the default value (if RTL_REGISTRY_OPTIONAL is
2025 * not specified in ulRoot), on output this will retrieve the looked up
2026 * registry value if found.
2027 */
2028static NTSTATUS vgdrvNtRegistryReadDWORD(ULONG uRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue)
2029{
2030 if (!pwszPath || !pwszName || !puValue)
2031 return STATUS_INVALID_PARAMETER;
2032
2033 ULONG uDefault = *puValue;
2034 RTL_QUERY_REGISTRY_TABLE aQuery[2];
2035 RT_ZERO(aQuery);
2036 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
2037 aQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
2038 aQuery[0].Name = pwszName;
2039 aQuery[0].EntryContext = puValue;
2040 aQuery[0].DefaultType = REG_DWORD;
2041 aQuery[0].DefaultData = &uDefault;
2042 aQuery[0].DefaultLength = sizeof(uDefault);
2043
2044 return RtlQueryRegistryValues(uRoot, pwszPath, &aQuery[0], NULL /* Context */, NULL /* Environment */);
2045}
2046
2047
2048/**
2049 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
2050 */
2051static NTSTATUS NTAPI vbdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
2052 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
2053{
2054 /*
2055 * Filter out general service config values.
2056 */
2057 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
2058 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
2059 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
2060 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
2061 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
2062 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
2063 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
2064 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
2065 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
2066 )
2067 {
2068 return STATUS_SUCCESS;
2069 }
2070
2071 /*
2072 * Convert the value name.
2073 */
2074 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
2075 if (cch < 64 && cch > 0)
2076 {
2077 char szValueName[72];
2078 char *pszTmp = szValueName;
2079 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
2080 if (RT_SUCCESS(rc))
2081 {
2082 /*
2083 * Convert the value.
2084 */
2085 char szValue[72];
2086 char *pszFree = NULL;
2087 char *pszValue = NULL;
2088 szValue[0] = '\0';
2089 switch (uValueType)
2090 {
2091 case REG_SZ:
2092 case REG_EXPAND_SZ:
2093 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
2094 if (RT_SUCCESS(rc) && cch < _1K)
2095 {
2096 if (cch < sizeof(szValue))
2097 {
2098 pszValue = szValue;
2099 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
2100 }
2101 else
2102 {
2103 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
2104 if (RT_SUCCESS(rc))
2105 pszFree = pszValue;
2106 }
2107 if (RT_FAILURE(rc))
2108 {
2109 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
2110 pwszValueName, rc));
2111 pszValue = NULL;
2112 }
2113 }
2114 else if (RT_SUCCESS(rc))
2115 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
2116 pwszValueName, cbValue, uValueType));
2117 else
2118 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
2119 pwszValueName, cbValue, uValueType));
2120 break;
2121
2122 case REG_DWORD:
2123 if (cbValue == sizeof(uint32_t))
2124 {
2125 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
2126 pszValue = szValue;
2127 }
2128 else
2129 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
2130 break;
2131
2132 case REG_QWORD:
2133 if (cbValue == sizeof(uint64_t))
2134 {
2135 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
2136 pszValue = szValue;
2137 }
2138 else
2139 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
2140 break;
2141
2142 default:
2143 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
2144 break;
2145 }
2146 if (pszValue)
2147 {
2148 /*
2149 * Process it.
2150 */
2151 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
2152 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
2153 if (pszFree)
2154 RTStrFree(pszFree);
2155 }
2156 }
2157 }
2158 else if (cch > 0)
2159 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
2160 else
2161 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
2162 NOREF(pvEntryCtx);
2163 return STATUS_SUCCESS;
2164}
2165
2166
2167/**
2168 * Reads configuration from the registry and guest properties.
2169 *
2170 * We ignore failures and instead preserve existing configuration values.
2171 *
2172 * Thie routine will block.
2173 *
2174 * @param pDevExt The device extension.
2175 */
2176static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
2177{
2178 /*
2179 * First the registry.
2180 */
2181 RTL_QUERY_REGISTRY_TABLE aQuery[2];
2182 RT_ZERO(aQuery);
2183 aQuery[0].QueryRoutine = vbdrvNtRegistryEnumCallback;
2184 aQuery[0].Flags = 0;
2185 aQuery[0].Name = NULL;
2186 aQuery[0].EntryContext = NULL;
2187 aQuery[0].DefaultType = REG_NONE;
2188 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /* Environment */);
2189 if (!NT_SUCCESS(rcNt))
2190 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
2191
2192 /*
2193 * Read configuration from the host.
2194 */
2195 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
2196}
2197
2198
2199/**
2200 * Helper to scan the PCI resource list and remember stuff.
2201 *
2202 * @param pResList Resource list
2203 * @param pDevExt Device extension
2204 */
2205static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
2206{
2207 /* Enumerate the resource list. */
2208 LogFlowFunc(("Found %d resources\n",
2209 pResList->List->PartialResourceList.Count));
2210
2211 NTSTATUS rc = STATUS_SUCCESS;
2212 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
2213 ULONG rangeCount = 0;
2214 ULONG cMMIORange = 0;
2215 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->aPciBaseAddresses;
2216 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
2217 {
2218 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
2219 switch (pPartialData->Type)
2220 {
2221 case CmResourceTypePort:
2222 {
2223 /* Overflow protection. */
2224 if (rangeCount < PCI_TYPE0_ADDRESSES)
2225 {
2226 LogFlowFunc(("I/O range: Base=%08x:%08x, length=%08x\n",
2227 pPartialData->u.Port.Start.HighPart,
2228 pPartialData->u.Port.Start.LowPart,
2229 pPartialData->u.Port.Length));
2230
2231 /* Save the IO port base. */
2232 /** @todo Not so good.
2233 * Update/bird: What is not so good? That we just consider the last range? */
2234 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
2235
2236 /* Save resource information. */
2237 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
2238 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
2239 pBaseAddress->RangeInMemory = FALSE;
2240 pBaseAddress->ResourceMapped = FALSE;
2241
2242 LogFunc(("I/O range for VMMDev found! Base=%08x:%08x, length=%08x\n",
2243 pPartialData->u.Port.Start.HighPart,
2244 pPartialData->u.Port.Start.LowPart,
2245 pPartialData->u.Port.Length));
2246
2247 /* Next item ... */
2248 rangeCount++; pBaseAddress++;
2249 }
2250 break;
2251 }
2252
2253 case CmResourceTypeInterrupt:
2254 {
2255 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
2256 pPartialData->u.Interrupt.Level,
2257 pPartialData->u.Interrupt.Vector,
2258 pPartialData->Flags));
2259
2260 /* Save information. */
2261 pDevExt->uInterruptLevel = pPartialData->u.Interrupt.Level;
2262 pDevExt->uInterruptVector = pPartialData->u.Interrupt.Vector;
2263 pDevExt->fInterruptAffinity = pPartialData->u.Interrupt.Affinity;
2264
2265 /* Check interrupt mode. */
2266 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
2267 pDevExt->enmInterruptMode = Latched;
2268 else
2269 pDevExt->enmInterruptMode = LevelSensitive;
2270 break;
2271 }
2272
2273 case CmResourceTypeMemory:
2274 {
2275 /* Overflow protection. */
2276 if (rangeCount < PCI_TYPE0_ADDRESSES)
2277 {
2278 LogFlowFunc(("Memory range: Base=%08x:%08x, length=%08x\n",
2279 pPartialData->u.Memory.Start.HighPart,
2280 pPartialData->u.Memory.Start.LowPart,
2281 pPartialData->u.Memory.Length));
2282
2283 /* We only care about read/write memory. */
2284 /** @todo Reconsider memory type. */
2285 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
2286 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
2287 {
2288 /* Save physical MMIO base + length for VMMDev. */
2289 pDevExt->uVmmDevMemoryPhysAddr = pPartialData->u.Memory.Start;
2290 pDevExt->cbVmmDevMemory = (ULONG)pPartialData->u.Memory.Length;
2291
2292 /* Save resource information. */
2293 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
2294 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
2295 pBaseAddress->RangeInMemory = TRUE;
2296 pBaseAddress->ResourceMapped = FALSE;
2297
2298 LogFunc(("Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
2299 pPartialData->u.Memory.Start.HighPart,
2300 pPartialData->u.Memory.Start.LowPart,
2301 pPartialData->u.Memory.Length));
2302
2303 /* Next item ... */
2304 rangeCount++; pBaseAddress++; cMMIORange++;
2305 }
2306 else
2307 LogFunc(("Ignoring memory: Flags=%08x\n", pPartialData->Flags));
2308 }
2309 break;
2310 }
2311
2312 default:
2313 {
2314 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
2315 break;
2316 }
2317 }
2318 }
2319
2320 /* Memorize the number of resources found. */
2321 pDevExt->cPciAddresses = rangeCount;
2322 return rc;
2323}
2324
2325
2326/**
2327 * Maps the I/O space from VMMDev to virtual kernel address space.
2328 *
2329 * @return NTSTATUS
2330 *
2331 * @param pDevExt The device extension.
2332 * @param PhysAddr Physical address to map.
2333 * @param cbToMap Number of bytes to map.
2334 * @param ppvMMIOBase Pointer of mapped I/O base.
2335 * @param pcbMMIO Length of mapped I/O base.
2336 */
2337static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
2338 void **ppvMMIOBase, uint32_t *pcbMMIO)
2339{
2340 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
2341 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
2342 /* pcbMMIO is optional. */
2343
2344 NTSTATUS rc = STATUS_SUCCESS;
2345 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
2346 {
2347 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
2348 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
2349 if (pVMMDevMemory)
2350 {
2351 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
2352
2353 /* Check version of the structure; do we have the right memory version? */
2354 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
2355 {
2356 /* Save results. */
2357 *ppvMMIOBase = pVMMDevMemory;
2358 if (pcbMMIO) /* Optional. */
2359 *pcbMMIO = pVMMDevMemory->u32Size;
2360
2361 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
2362 }
2363 else
2364 {
2365 /* Not our version, refuse operation and unmap the memory. */
2366 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
2367
2368 vgdrvNtUnmapVMMDevMemory(pDevExt);
2369 rc = STATUS_UNSUCCESSFUL;
2370 }
2371 }
2372 else
2373 rc = STATUS_UNSUCCESSFUL;
2374 }
2375 return rc;
2376}
2377
2378#ifdef VBOX_STRICT
2379
2380/**
2381 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
2382 */
2383static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
2384{
2385 AssertPtrReturn(pu32Bits, 0);
2386 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
2387 uint32_t u32Result = 0;
2388 uint32_t u32WorkingMask = u32Mask;
2389 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
2390
2391 while (iBitOffset > 0)
2392 {
2393 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
2394 if (fSet)
2395 u32Result |= 1 << (iBitOffset - 1);
2396 u32WorkingMask &= ~(1 << (iBitOffset - 1));
2397 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
2398 }
2399 LogFlowFunc(("Returning %#x\n", u32Result));
2400 return u32Result;
2401}
2402
2403
2404static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
2405{
2406 ULONG u32Bits2 = u32Bits;
2407 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
2408 if ( u32Result != u32Exp
2409 || (u32Bits2 & u32Mask)
2410 || (u32Bits2 & u32Result)
2411 || ((u32Bits2 | u32Result) != u32Bits)
2412 )
2413 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
2414 u32Mask, u32Bits, u32Bits2, u32Result));
2415}
2416
2417
2418static void vgdrvNtDoTests(void)
2419{
2420 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
2421 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
2422 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
2423 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
2424 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
2425 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
2426}
2427
2428#endif /* VBOX_STRICT */
2429
2430#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
2431
2432/*
2433 * DPC latency checker.
2434 */
2435
2436/**
2437 * One DPC latency sample.
2438 */
2439typedef struct DPCSAMPLE
2440{
2441 LARGE_INTEGER PerfDelta;
2442 LARGE_INTEGER PerfCounter;
2443 LARGE_INTEGER PerfFrequency;
2444 uint64_t u64TSC;
2445} DPCSAMPLE;
2446AssertCompileSize(DPCSAMPLE, 4*8);
2447
2448/**
2449 * The DPC latency measurement workset.
2450 */
2451typedef struct DPCDATA
2452{
2453 KDPC Dpc;
2454 KTIMER Timer;
2455 KSPIN_LOCK SpinLock;
2456
2457 ULONG ulTimerRes;
2458
2459 bool volatile fFinished;
2460
2461 /** The timer interval (relative). */
2462 LARGE_INTEGER DueTime;
2463
2464 LARGE_INTEGER PerfCounterPrev;
2465
2466 /** Align the sample array on a 64 byte boundrary just for the off chance
2467 * that we'll get cache line aligned memory backing this structure. */
2468 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
2469
2470 int cSamples;
2471 DPCSAMPLE aSamples[8192];
2472} DPCDATA;
2473
2474AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
2475
2476# define VBOXGUEST_DPC_TAG 'DPCS'
2477
2478
2479/**
2480 * DPC callback routine for the DPC latency measurement code.
2481 *
2482 * @param pDpc The DPC, not used.
2483 * @param pvDeferredContext Pointer to the DPCDATA.
2484 * @param SystemArgument1 System use, ignored.
2485 * @param SystemArgument2 System use, ignored.
2486 */
2487static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
2488{
2489 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
2490 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
2491
2492 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
2493
2494 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
2495 pData->fFinished = true;
2496 else
2497 {
2498 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
2499
2500 pSample->u64TSC = ASMReadTSC();
2501 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
2502 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
2503
2504 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
2505
2506 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
2507 }
2508
2509 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
2510}
2511
2512
2513/**
2514 * Handles the DPC latency checker request.
2515 *
2516 * @returns VBox status code.
2517 */
2518int VGDrvNtIOCtl_DpcLatencyChecker(void)
2519{
2520 /*
2521 * Allocate a block of non paged memory for samples and related data.
2522 */
2523 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
2524 if (!pData)
2525 {
2526 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
2527 return VERR_NO_MEMORY;
2528 }
2529
2530 /*
2531 * Initialize the data.
2532 */
2533 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
2534 KeInitializeTimer(&pData->Timer);
2535 KeInitializeSpinLock(&pData->SpinLock);
2536
2537 pData->fFinished = false;
2538 pData->cSamples = 0;
2539 pData->PerfCounterPrev.QuadPart = 0;
2540
2541 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
2542 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
2543
2544 /*
2545 * Start the DPC measurements and wait for a full set.
2546 */
2547 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
2548
2549 while (!pData->fFinished)
2550 {
2551 LARGE_INTEGER Interval;
2552 Interval.QuadPart = -100 * 1000 * 10;
2553 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
2554 }
2555
2556 ExSetTimerResolution(0, 0);
2557
2558 /*
2559 * Log everything to the host.
2560 */
2561 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
2562 for (int i = 0; i < pData->cSamples; i++)
2563 {
2564 DPCSAMPLE *pSample = &pData->aSamples[i];
2565
2566 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
2567 i,
2568 pSample->PerfDelta.QuadPart,
2569 pSample->PerfCounter.QuadPart,
2570 pSample->PerfFrequency.QuadPart,
2571 pSample->u64TSC);
2572 }
2573
2574 RTMemFree(pData);
2575 return VINF_SUCCESS;
2576}
2577
2578#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
2579
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