summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp')
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp3503
1 files changed, 3503 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp
new file mode 100644
index 00000000..c9e180af
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp
@@ -0,0 +1,3503 @@
+/* $Id: VBoxGuest-win.cpp $ */
+/** @file
+ * VBoxGuest - Windows specifics.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include <iprt/nt/nt.h>
+
+#include "VBoxGuestInternal.h"
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/log.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/critsect.h>
+#include <iprt/dbg.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/memobj.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#ifdef TARGET_NT4
+# include <VBox/pci.h>
+# define PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS32_IPRT
+# include <iprt/formats/mz.h>
+# include <iprt/formats/pecoff.h>
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#undef ExFreePool
+
+#ifndef PCI_MAX_BUSES
+# define PCI_MAX_BUSES 256
+#endif
+
+/** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */
+#define VBOX_CM_PRE_VISTA_MASK (0x3f)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Possible device states for our state machine.
+ */
+typedef enum VGDRVNTDEVSTATE
+{
+ /** @name Stable states
+ * @{ */
+ VGDRVNTDEVSTATE_REMOVED = 0,
+ VGDRVNTDEVSTATE_STOPPED,
+ VGDRVNTDEVSTATE_OPERATIONAL,
+ /** @} */
+
+ /** @name Transitional states
+ * @{ */
+ VGDRVNTDEVSTATE_PENDINGSTOP,
+ VGDRVNTDEVSTATE_PENDINGREMOVE,
+ VGDRVNTDEVSTATE_SURPRISEREMOVED
+ /** @} */
+} VGDRVNTDEVSTATE;
+
+
+/**
+ * Subclassing the device extension for adding windows-specific bits.
+ */
+typedef struct VBOXGUESTDEVEXTWIN
+{
+ /** The common device extension core. */
+ VBOXGUESTDEVEXT Core;
+
+ /** Our functional driver object. */
+ PDEVICE_OBJECT pDeviceObject;
+ /** Top of the stack. */
+ PDEVICE_OBJECT pNextLowerDriver;
+
+ /** @name PCI bus and slot (device+function) set by for legacy NT only.
+ * @{ */
+ /** Bus number where the device is located. */
+ ULONG uBus;
+ /** Slot number where the device is located (PCI_SLOT_NUMBER). */
+ ULONG uSlot;
+ /** @} */
+
+ /** @name Interrupt stuff.
+ * @{ */
+ /** Interrupt object pointer. */
+ PKINTERRUPT pInterruptObject;
+ /** Device interrupt level. */
+ ULONG uInterruptLevel;
+ /** Device interrupt vector. */
+ ULONG uInterruptVector;
+ /** Affinity mask. */
+ KAFFINITY fInterruptAffinity;
+ /** LevelSensitive or Latched. */
+ KINTERRUPT_MODE enmInterruptMode;
+ /** @} */
+
+ /** Physical address and length of VMMDev memory. */
+ PHYSICAL_ADDRESS uVmmDevMemoryPhysAddr;
+ /** Length of VMMDev memory. */
+ ULONG cbVmmDevMemory;
+
+ /** Device state. */
+ VGDRVNTDEVSTATE volatile enmDevState;
+ /** The previous stable device state. */
+ VGDRVNTDEVSTATE enmPrevDevState;
+
+ /** Last system power action set (see VBoxGuestPower). */
+ POWER_ACTION enmLastSystemPowerAction;
+ /** Preallocated generic request for shutdown. */
+ VMMDevPowerStateRequest *pPowerStateRequest;
+
+ /** Spinlock protecting MouseNotifyCallback. Required since the consumer is
+ * in a DPC callback and not the ISR. */
+ KSPIN_LOCK MouseEventAccessSpinLock;
+
+ /** Read/write critical section for handling race between checking for idle
+ * driver (in IRP_MN_QUERY_REMOVE_DEVICE & IRP_MN_QUERY_STOP_DEVICE) and
+ * creating new sessions. The session creation code enteres the critical
+ * section in read (shared) access mode, whereas the idle checking code
+ * enteres is in write (exclusive) access mode. */
+ RTCRITSECTRW SessionCreateCritSect;
+} VBOXGUESTDEVEXTWIN;
+typedef VBOXGUESTDEVEXTWIN *PVBOXGUESTDEVEXTWIN;
+
+
+/** NT (windows) version identifier. */
+typedef enum VGDRVNTVER
+{
+ VGDRVNTVER_INVALID = 0,
+ VGDRVNTVER_WINNT310,
+ VGDRVNTVER_WINNT350,
+ VGDRVNTVER_WINNT351,
+ VGDRVNTVER_WINNT4,
+ VGDRVNTVER_WIN2K,
+ VGDRVNTVER_WINXP,
+ VGDRVNTVER_WIN2K3,
+ VGDRVNTVER_WINVISTA,
+ VGDRVNTVER_WIN7,
+ VGDRVNTVER_WIN8,
+ VGDRVNTVER_WIN81,
+ VGDRVNTVER_WIN10,
+ VGDRVNTVER_WIN11
+} VGDRVNTVER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
+static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
+static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
+ PIRP pIrp, PIO_STACK_LOCATION pStack);
+static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
+static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS NTAPI vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer);
+static VOID NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
+static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
+#ifdef VBOX_STRICT
+static void vgdrvNtDoTests(void);
+#endif
+#ifdef TARGET_NT4
+static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
+ void *pvData, ULONG offData, ULONG cbData);
+static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
+ void *pvData, ULONG offData, ULONG cbData);
+#endif
+
+/*
+ * We only do INIT allocations. PAGE is too much work and risk for little gain.
+ */
+#ifdef ALLOC_PRAGMA
+NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+# pragma alloc_text(INIT, DriverEntry)
+# ifdef TARGET_NT4
+static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+# pragma alloc_text(INIT, vgdrvNt4CreateDevice)
+static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber);
+# pragma alloc_text(INIT, vgdrvNt4FindPciDevice)
+# endif
+#endif
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The detected NT (windows) version. */
+static VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
+/** Pointer to the PoStartNextPowerIrp routine (in the NT kernel).
+ * Introduced in Windows 2000. */
+static decltype(PoStartNextPowerIrp) *g_pfnPoStartNextPowerIrp = NULL;
+/** Pointer to the PoCallDriver routine (in the NT kernel).
+ * Introduced in Windows 2000. */
+static decltype(PoCallDriver) *g_pfnPoCallDriver = NULL;
+#ifdef TARGET_NT4
+/** Pointer to the HalAssignSlotResources routine (in the HAL).
+ * Introduced in NT 3.50. */
+static decltype(HalAssignSlotResources) *g_pfnHalAssignSlotResources= NULL;
+/** Pointer to the HalGetBusDataByOffset routine (in the HAL).
+ * Introduced in NT 3.50. */
+static decltype(HalGetBusDataByOffset) *g_pfnHalGetBusDataByOffset = NULL;
+/** Pointer to the HalSetBusDataByOffset routine (in the HAL).
+ * Introduced in NT 3.50 (we provide fallback and use it only for NT 3.1). */
+static decltype(HalSetBusDataByOffset) *g_pfnHalSetBusDataByOffset = NULL;
+#endif
+/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel).
+ * Introduced in Windows 3.50. */
+static decltype(KeRegisterBugCheckCallback) *g_pfnKeRegisterBugCheckCallback = NULL;
+/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel).
+ * Introduced in Windows 3.50. */
+static decltype(KeDeregisterBugCheckCallback) *g_pfnKeDeregisterBugCheckCallback = NULL;
+/** Pointer to the KiBugCheckData array (in the NT kernel).
+ * Introduced in Windows 4. */
+static uintptr_t const *g_pauKiBugCheckData = NULL;
+/** Set if the callback was successfully registered and needs deregistering. */
+static bool g_fBugCheckCallbackRegistered = false;
+/** The bugcheck callback record. */
+static KBUGCHECK_CALLBACK_RECORD g_BugCheckCallbackRec;
+
+
+
+/**
+ * Driver entry point.
+ *
+ * @returns appropriate status code.
+ * @param pDrvObj Pointer to driver object.
+ * @param pRegPath Registry base path.
+ */
+NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ RT_NOREF1(pRegPath);
+#ifdef TARGET_NT4
+ /*
+ * Looks like NT 3.1 doesn't necessarily zero our uninitialized data segments
+ * (like ".bss"), at least not when loading at runtime, so do that.
+ */
+ PIMAGE_DOS_HEADER pMzHdr = &__ImageBase;
+ PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)((uint8_t *)pMzHdr + pMzHdr->e_lfanew);
+ if ( pNtHdrs->Signature == IMAGE_NT_SIGNATURE
+ && pNtHdrs->FileHeader.NumberOfSections > 2
+ && pNtHdrs->FileHeader.NumberOfSections < 64)
+ {
+ uint32_t iShdr = pNtHdrs->FileHeader.NumberOfSections;
+ uint32_t uRvaEnd = pNtHdrs->OptionalHeader.SizeOfImage; /* (may be changed to exclude tail sections) */
+ PIMAGE_SECTION_HEADER paShdrs;
+ paShdrs = (PIMAGE_SECTION_HEADER)&pNtHdrs->OptionalHeader.DataDirectory[pNtHdrs->OptionalHeader.NumberOfRvaAndSizes];
+ while (iShdr-- > 0)
+ {
+ if ( !(paShdrs[iShdr].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
+ && paShdrs[iShdr].VirtualAddress < uRvaEnd)
+ {
+ uint32_t const cbSection = uRvaEnd - paShdrs[iShdr].VirtualAddress;
+ uint32_t const offUninitialized = paShdrs[iShdr].SizeOfRawData;
+ //RTLogBackdoorPrintf("section #%u: rva=%#x size=%#x calcsize=%#x) rawsize=%#x\n", iShdr,
+ // paShdrs[iShdr].VirtualAddress, paShdrs[iShdr].Misc.VirtualSize, cbSection, offUninitialized);
+ if ( offUninitialized < cbSection
+ && (paShdrs[iShdr].Characteristics & IMAGE_SCN_MEM_WRITE))
+ memset((uint8_t *)pMzHdr + paShdrs[iShdr].VirtualAddress + offUninitialized, 0, cbSection - offUninitialized);
+ uRvaEnd = paShdrs[iShdr].VirtualAddress;
+ }
+ }
+ }
+ else
+ RTLogBackdoorPrintf("VBoxGuest: Bad pNtHdrs=%p: %#x\n", pNtHdrs, pNtHdrs->Signature);
+#endif
+
+ /*
+ * Start by initializing IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_FAILURE(rc))
+ {
+ RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed: %Rrc!\n", rc);
+ return STATUS_UNSUCCESSFUL;
+ }
+ VGDrvCommonInitLoggers();
+
+ LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
+
+ /*
+ * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
+ */
+ ULONG ulMajorVer;
+ ULONG ulMinorVer;
+ ULONG ulBuildNo;
+ BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
+
+ /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log on the host. */
+ RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
+ if (fCheckedBuild)
+ RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
+
+#ifdef VBOX_STRICT
+ vgdrvNtDoTests();
+#endif
+ NTSTATUS rcNt = STATUS_SUCCESS;
+ switch (ulMajorVer)
+ {
+ case 10:
+ /* Windows 10 Preview builds starting with 9926. */
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
+ /* Windows 11 Preview builds starting with 22000. */
+ if (ulBuildNo >= 22000)
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN11;
+ break;
+ case 6: /* Windows Vista or Windows 7 (based on minor ver) */
+ switch (ulMinorVer)
+ {
+ case 0: /* Note: Also could be Windows 2008 Server! */
+ g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
+ break;
+ case 1: /* Note: Also could be Windows 2008 Server R2! */
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
+ break;
+ case 2:
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
+ break;
+ case 3:
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
+ break;
+ case 4: /* Windows 10 Preview builds. */
+ default:
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
+ break;
+ }
+ break;
+ case 5:
+ switch (ulMinorVer)
+ {
+ default:
+ case 2:
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
+ break;
+ case 1:
+ g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
+ break;
+ case 0:
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
+ break;
+ }
+ break;
+ case 4:
+ g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
+ break;
+ case 3:
+ if (ulMinorVer > 50)
+ g_enmVGDrvNtVer = VGDRVNTVER_WINNT351;
+ else if (ulMinorVer >= 50)
+ g_enmVGDrvNtVer = VGDRVNTVER_WINNT350;
+ else
+ g_enmVGDrvNtVer = VGDRVNTVER_WINNT310;
+ break;
+ default:
+ /* Major versions above 6 gets classified as windows 10. */
+ if (ulMajorVer > 6)
+ g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
+ else
+ {
+ RTLogBackdoorPrintf("At least Windows NT 3.10 required! Found %u.%u!\n", ulMajorVer, ulMinorVer);
+ rcNt = STATUS_DRIVER_UNABLE_TO_LOAD;
+ }
+ break;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Dynamically resolve symbols not present in NT4.
+ */
+ RTDBGKRNLINFO hKrnlInfo;
+ rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ g_pfnKeRegisterBugCheckCallback = (decltype(KeRegisterBugCheckCallback) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeRegisterBugCheckCallback");
+ g_pfnKeDeregisterBugCheckCallback = (decltype(KeDeregisterBugCheckCallback) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeDeregisterBugCheckCallback");
+ g_pauKiBugCheckData = (uintptr_t const *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KiBugCheckData");
+ g_pfnPoCallDriver = (decltype(PoCallDriver) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoCallDriver");
+ g_pfnPoStartNextPowerIrp = (decltype(PoStartNextPowerIrp) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoStartNextPowerIrp");
+#ifdef TARGET_NT4
+ if (g_enmVGDrvNtVer > VGDRVNTVER_WINNT4)
+#endif
+ {
+ if (!g_pfnPoCallDriver) { LogRelFunc(("Missing PoCallDriver!\n")); rc = VERR_SYMBOL_NOT_FOUND; }
+ if (!g_pfnPoStartNextPowerIrp) { LogRelFunc(("Missing PoStartNextPowerIrp!\n")); rc = VERR_SYMBOL_NOT_FOUND; }
+ }
+
+#ifdef TARGET_NT4
+ g_pfnHalAssignSlotResources = (decltype(HalAssignSlotResources) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalAssignSlotResources");
+ if (!g_pfnHalAssignSlotResources && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
+ {
+ RTLogBackdoorPrintf("VBoxGuest: Missing HalAssignSlotResources!\n");
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+
+ g_pfnHalGetBusDataByOffset = (decltype(HalGetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalGetBusDataByOffset");
+ if (!g_pfnHalGetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
+ {
+ RTLogBackdoorPrintf("VBoxGuest: Missing HalGetBusDataByOffset!\n");
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+ if (!g_pfnHalGetBusDataByOffset)
+ g_pfnHalGetBusDataByOffset = vgdrvNt31GetBusDataByOffset;
+
+ g_pfnHalSetBusDataByOffset = (decltype(HalSetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalSetBusDataByOffset");
+ if (!g_pfnHalSetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
+ {
+ RTLogBackdoorPrintf("VBoxGuest: Missing HalSetBusDataByOffset!\n");
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+ if (!g_pfnHalSetBusDataByOffset)
+ g_pfnHalSetBusDataByOffset = vgdrvNt31SetBusDataByOffset;
+#endif
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Setup the driver entry points in pDrvObj.
+ */
+ pDrvObj->DriverUnload = vgdrvNtUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
+ /** @todo Need to call IoRegisterShutdownNotification or
+ * IoRegisterLastChanceShutdownNotification, possibly hooking the
+ * HalReturnToFirmware import in NTOSKRNL on older systems (<= ~NT4) and
+ * check for power off requests. */
+ pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
+ pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
+ pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
+#ifdef TARGET_NT4
+ if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
+ rcNt = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
+ else
+#endif
+ {
+ pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtNt5PlusPnP;
+ pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtNt5PlusPower;
+ pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl;
+ pDrvObj->DriverExtension->AddDevice = vgdrvNtNt5PlusAddDevice;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Try register the bugcheck callback (non-fatal).
+ */
+ if ( g_pfnKeRegisterBugCheckCallback
+ && g_pfnKeDeregisterBugCheckCallback)
+ {
+ AssertCompile(BufferEmpty == 0);
+ KeInitializeCallbackRecord(&g_BugCheckCallbackRec);
+ if (g_pfnKeRegisterBugCheckCallback(&g_BugCheckCallbackRec, vgdrvNtBugCheckCallback,
+ NULL, 0, (PUCHAR)"VBoxGuest"))
+ g_fBugCheckCallbackRegistered = true;
+ else
+ g_fBugCheckCallbackRegistered = false;
+ }
+ else
+ Assert(g_pfnKeRegisterBugCheckCallback == NULL && g_pfnKeDeregisterBugCheckCallback == NULL);
+
+ LogFlowFunc(("Returning %#x\n", rcNt));
+ return rcNt;
+ }
+ }
+ else
+ rcNt = STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ /*
+ * Failed.
+ */
+ LogRelFunc(("Failed! rcNt=%#x\n", rcNt));
+ VGDrvCommonDestroyLoggers();
+ RTR0Term();
+ return rcNt;
+}
+
+
+/**
+ * Translates our internal NT version enum to VBox OS.
+ *
+ * @returns VBox OS type.
+ * @param enmNtVer The NT version.
+ */
+static VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
+{
+ VBOXOSTYPE enmOsType;
+ switch (enmNtVer)
+ {
+ case VGDRVNTVER_WINNT310: enmOsType = VBOXOSTYPE_WinNT3x; break;
+ case VGDRVNTVER_WINNT350: enmOsType = VBOXOSTYPE_WinNT3x; break;
+ case VGDRVNTVER_WINNT351: enmOsType = VBOXOSTYPE_WinNT3x; break;
+ case VGDRVNTVER_WINNT4: enmOsType = VBOXOSTYPE_WinNT4; break;
+ case VGDRVNTVER_WIN2K: enmOsType = VBOXOSTYPE_Win2k; break;
+ case VGDRVNTVER_WINXP: enmOsType = VBOXOSTYPE_WinXP; break;
+ case VGDRVNTVER_WIN2K3: enmOsType = VBOXOSTYPE_Win2k3; break;
+ case VGDRVNTVER_WINVISTA: enmOsType = VBOXOSTYPE_WinVista; break;
+ case VGDRVNTVER_WIN7: enmOsType = VBOXOSTYPE_Win7; break;
+ case VGDRVNTVER_WIN8: enmOsType = VBOXOSTYPE_Win8; break;
+ case VGDRVNTVER_WIN81: enmOsType = VBOXOSTYPE_Win81; break;
+ case VGDRVNTVER_WIN10: enmOsType = VBOXOSTYPE_Win10; break;
+ case VGDRVNTVER_WIN11: enmOsType = VBOXOSTYPE_Win11_x64; break;
+
+ default:
+ /* We don't know, therefore NT family. */
+ enmOsType = VBOXOSTYPE_WinNT;
+ break;
+ }
+#if ARCH_BITS == 64
+ enmOsType = (VBOXOSTYPE)((int)enmOsType | VBOXOSTYPE_x64);
+#endif
+ return enmOsType;
+}
+
+
+/**
+ * Does the fundamental device extension initialization.
+ *
+ * @returns NT status.
+ * @param pDevExt The device extension.
+ * @param pDevObj The device object.
+ */
+static NTSTATUS vgdrvNtInitDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj)
+{
+ RT_ZERO(*pDevExt);
+
+ KeInitializeSpinLock(&pDevExt->MouseEventAccessSpinLock);
+ pDevExt->pDeviceObject = pDevObj;
+ pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
+
+ int rc = RTCritSectRwInit(&pDevExt->SessionCreateCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VGDrvCommonInitDevExtFundament(&pDevExt->Core);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("vgdrvNtInitDevExtFundament: returning success\n"));
+ return STATUS_SUCCESS;
+ }
+
+ RTCritSectRwDelete(&pDevExt->SessionCreateCritSect);
+ }
+ Log(("vgdrvNtInitDevExtFundament: failed: rc=%Rrc\n", rc));
+ return STATUS_UNSUCCESSFUL;
+}
+
+
+/**
+ * Counter part to vgdrvNtInitDevExtFundament.
+ *
+ * @param pDevExt The device extension.
+ */
+static void vgdrvNtDeleteDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt)
+{
+ LogFlow(("vgdrvNtDeleteDevExtFundament:\n"));
+ VGDrvCommonDeleteDevExtFundament(&pDevExt->Core);
+ RTCritSectRwDelete(&pDevExt->SessionCreateCritSect);
+}
+
+
+#ifdef LOG_ENABLED
+/**
+ * Debug helper to dump a device resource list.
+ *
+ * @param pResourceList list of device resources.
+ */
+static void vgdrvNtShowDeviceResources(PCM_RESOURCE_LIST pRsrcList)
+{
+ for (uint32_t iList = 0; iList < pRsrcList->Count; iList++)
+ {
+ PCM_FULL_RESOURCE_DESCRIPTOR pList = &pRsrcList->List[iList];
+ LogFunc(("List #%u: InterfaceType=%#x BusNumber=%#x ListCount=%u ListRev=%#x ListVer=%#x\n",
+ iList, pList->InterfaceType, pList->BusNumber, pList->PartialResourceList.Count,
+ pList->PartialResourceList.Revision, pList->PartialResourceList.Version ));
+
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pList->PartialResourceList.PartialDescriptors;
+ for (ULONG i = 0; i < pList->PartialResourceList.Count; ++i, ++pResource)
+ {
+ ULONG uType = pResource->Type;
+ static char const * const s_apszName[] =
+ {
+ "CmResourceTypeNull",
+ "CmResourceTypePort",
+ "CmResourceTypeInterrupt",
+ "CmResourceTypeMemory",
+ "CmResourceTypeDma",
+ "CmResourceTypeDeviceSpecific",
+ "CmResourceTypeuBusNumber",
+ "CmResourceTypeDevicePrivate",
+ "CmResourceTypeAssignedResource",
+ "CmResourceTypeSubAllocateFrom",
+ };
+
+ if (uType < RT_ELEMENTS(s_apszName))
+ LogFunc((" %.30s Flags=%#x Share=%#x", s_apszName[uType], pResource->Flags, pResource->ShareDisposition));
+ else
+ LogFunc((" Type=%#x Flags=%#x Share=%#x", uType, pResource->Flags, pResource->ShareDisposition));
+ switch (uType)
+ {
+ case CmResourceTypePort:
+ case CmResourceTypeMemory:
+ Log((" Start %#RX64, length=%#x\n", pResource->u.Port.Start.QuadPart, pResource->u.Port.Length));
+ break;
+
+ case CmResourceTypeInterrupt:
+ Log((" Level=%X, vector=%#x, affinity=%#x\n",
+ pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
+ break;
+
+ case CmResourceTypeDma:
+ Log((" Channel %d, Port %#x\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
+ break;
+
+ default:
+ Log(("\n"));
+ break;
+ }
+ }
+ }
+}
+#endif /* LOG_ENABLED */
+
+
+/**
+ * Helper to scan the PCI resource list and remember stuff.
+ *
+ * @param pDevExt The device extension.
+ * @param pResList Resource list
+ * @param fTranslated Whether the addresses are translated or not.
+ */
+static NTSTATUS vgdrvNtScanPCIResourceList(PVBOXGUESTDEVEXTWIN pDevExt, PCM_RESOURCE_LIST pResList, bool fTranslated)
+{
+ LogFlowFunc(("Found %d resources\n", pResList->List->PartialResourceList.Count));
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
+ bool fGotIrq = false;
+ bool fGotMmio = false;
+ bool fGotIoPorts = false;
+ NTSTATUS rc = STATUS_SUCCESS;
+ for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
+ {
+ pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
+ switch (pPartialData->Type)
+ {
+ case CmResourceTypePort:
+ LogFlowFunc(("I/O range: Base=%#RX64, length=%08x\n",
+ pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length));
+ /* Save the first I/O port base. */
+ if (!fGotIoPorts)
+ {
+ pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
+ fGotIoPorts = true;
+ LogFunc(("I/O range for VMMDev found! Base=%#RX64, length=%08x\n",
+ pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length));
+ }
+ else
+ LogRelFunc(("More than one I/O port range?!?\n"));
+ break;
+
+ case CmResourceTypeInterrupt:
+ LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
+ pPartialData->u.Interrupt.Level, pPartialData->u.Interrupt.Vector, pPartialData->Flags));
+ if (!fGotIrq)
+ {
+ /* Save information. */
+ pDevExt->uInterruptLevel = pPartialData->u.Interrupt.Level;
+ pDevExt->uInterruptVector = pPartialData->u.Interrupt.Vector;
+ pDevExt->fInterruptAffinity = pPartialData->u.Interrupt.Affinity;
+
+ /* Check interrupt mode. */
+ if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
+ pDevExt->enmInterruptMode = Latched;
+ else
+ pDevExt->enmInterruptMode = LevelSensitive;
+ fGotIrq = true;
+ LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n", pDevExt->uInterruptVector,
+ pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode));
+ }
+ else
+ LogFunc(("More than one IRQ resource!\n"));
+ break;
+
+ case CmResourceTypeMemory:
+ LogFlowFunc(("Memory range: Base=%#RX64, length=%08x\n",
+ pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length));
+ /* We only care about the first read/write memory range. */
+ if ( !fGotMmio
+ && (pPartialData->Flags & CM_RESOURCE_MEMORY_WRITEABILITY_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
+ {
+ /* Save physical MMIO base + length for VMMDev. */
+ pDevExt->uVmmDevMemoryPhysAddr = pPartialData->u.Memory.Start;
+ pDevExt->cbVmmDevMemory = (ULONG)pPartialData->u.Memory.Length;
+
+ if (!fTranslated)
+ {
+ /* Technically we need to make the HAL translate the address. since we
+ didn't used to do this and it probably just returns the input address,
+ we allow ourselves to ignore failures. */
+ ULONG uAddressSpace = 0;
+ PHYSICAL_ADDRESS PhysAddr = pPartialData->u.Memory.Start;
+ if (HalTranslateBusAddress(pResList->List->InterfaceType, pResList->List->BusNumber, PhysAddr,
+ &uAddressSpace, &PhysAddr))
+ {
+ Log(("HalTranslateBusAddress(%#RX64) -> %RX64, type %#x\n",
+ pPartialData->u.Memory.Start.QuadPart, PhysAddr.QuadPart, uAddressSpace));
+ if (pPartialData->u.Memory.Start.QuadPart != PhysAddr.QuadPart)
+ pDevExt->uVmmDevMemoryPhysAddr = PhysAddr;
+ }
+ else
+ Log(("HalTranslateBusAddress(%#RX64) -> failed!\n", pPartialData->u.Memory.Start.QuadPart));
+ }
+
+ fGotMmio = true;
+ LogFunc(("Found memory range for VMMDev! Base = %#RX64, Length = %08x\n",
+ pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length));
+ }
+ else
+ LogFunc(("Ignoring memory: Flags=%08x Base=%#RX64\n",
+ pPartialData->Flags, pPartialData->u.Memory.Start.QuadPart));
+ break;
+
+ default:
+ LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
+ break;
+ }
+ }
+ return rc;
+}
+
+
+#ifdef TARGET_NT4
+
+/**
+ * Scans the PCI resources on NT 3.1.
+ *
+ * @returns STATUS_SUCCESS or STATUS_DEVICE_CONFIGURATION_ERROR.
+ * @param pDevExt The device extension.
+ * @param uBus The bus number.
+ * @param uSlot The PCI slot to scan.
+ */
+static NTSTATUS vgdrvNt31ScanSlotResources(PVBOXGUESTDEVEXTWIN pDevExt, ULONG uBus, ULONG uSlot)
+{
+ /*
+ * Disable memory mappings so we can determin the BAR lengths
+ * without upsetting other mappings.
+ */
+ uint16_t fCmd = 0;
+ g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd));
+ if (fCmd & VBOX_PCI_COMMAND_MEMORY)
+ {
+ uint16_t fCmdTmp = fCmd & ~VBOX_PCI_COMMAND_MEMORY;
+ g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdTmp, VBOX_PCI_COMMAND, sizeof(fCmdTmp));
+ }
+
+ /*
+ * Scan the address resources first.
+ */
+ uint32_t aBars[6] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
+ g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &aBars, VBOX_PCI_BASE_ADDRESS_0, sizeof(aBars));
+
+ bool fGotMmio = false;
+ bool fGotIoPorts = false;
+ for (uint32_t i = 0; i < RT_ELEMENTS(aBars); i++)
+ {
+ uint32_t uBar = aBars[i];
+ if (uBar == UINT32_MAX)
+ continue;
+ if ((uBar & 1) == PCI_ADDRESS_SPACE_IO)
+ {
+ uint32_t uAddr = uBar & UINT32_C(0xfffffffc);
+ if (!uAddr)
+ continue;
+ if (!fGotIoPorts)
+ {
+ pDevExt->Core.IOPortBase = (uint16_t)uAddr & UINT16_C(0xfffc);
+ fGotIoPorts = true;
+ LogFunc(("I/O range for VMMDev found in BAR%u! %#x\n", i, pDevExt->Core.IOPortBase));
+ }
+ else
+ LogRelFunc(("More than one I/O port range?!? BAR%u=%#x\n", i, uBar));
+ }
+ else
+ {
+ uint32_t uAddr = uBar & UINT32_C(0xfffffff0);
+ if (!uAddr)
+ continue;
+
+ if (!fGotMmio)
+ {
+ /* Figure the length by trying to set all address bits and seeing
+ how many we're allowed to set. */
+ uint32_t iBit = 4;
+ while (!(uAddr & RT_BIT_32(iBit)))
+ iBit++;
+
+ uint32_t const offPciBar = VBOX_PCI_BASE_ADDRESS_0 + i * 4;
+ uint32_t uTmpBar = uBar | ((RT_BIT_32(iBit) - 1) & UINT32_C(0xfffffff0));
+ g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar));
+ uTmpBar = uBar;
+ g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar));
+ g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uBar, offPciBar, sizeof(uBar));
+
+ while (iBit > 4 && (uTmpBar & RT_BIT_32(iBit - 1)))
+ iBit--;
+
+ /* got it */
+ pDevExt->cbVmmDevMemory = RT_BIT_32(iBit);
+ pDevExt->uVmmDevMemoryPhysAddr.QuadPart = uAddr;
+ fGotMmio = true;
+ LogFunc(("Found memory range for VMMDev in BAR%u! %#RX64 LB %#x (raw %#x)\n",
+ i, pDevExt->uVmmDevMemoryPhysAddr.QuadPart, pDevExt->cbVmmDevMemory, uBar));
+ }
+ else
+ LogFunc(("Ignoring memory: BAR%u=%#x\n", i, uBar));
+ }
+ }
+
+ /*
+ * Get the IRQ
+ */
+ struct
+ {
+ uint8_t bInterruptLine;
+ uint8_t bInterruptPin;
+ } Buf = { 0, 0 };
+ g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &Buf, VBOX_PCI_INTERRUPT_LINE, sizeof(Buf));
+ if (Buf.bInterruptPin != 0)
+ {
+ pDevExt->uInterruptVector = Buf.bInterruptLine;
+ pDevExt->uInterruptLevel = Buf.bInterruptLine;
+ pDevExt->enmInterruptMode = LevelSensitive;
+ pDevExt->fInterruptAffinity = RT_BIT_32(RTMpGetCount()) - 1;
+ LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n",
+ pDevExt->uInterruptVector, pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode));
+ }
+
+ /*
+ * Got what we need?
+ */
+ if (fGotIoPorts && (!fGotMmio || Buf.bInterruptPin != 0))
+ {
+ /*
+ * Enable both MMIO, I/O space and busmastering so we can use the device.
+ */
+ uint16_t fCmdNew = fCmd | VBOX_PCI_COMMAND_IO | VBOX_PCI_COMMAND_MEMORY | VBOX_PCI_COMMAND_MASTER;
+ g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdNew, VBOX_PCI_COMMAND, sizeof(fCmdNew));
+
+ return STATUS_SUCCESS;
+ }
+
+ /* No. Complain, restore device command value and return failure. */
+ if (!fGotIoPorts)
+ LogRel(("VBoxGuest: Did not find I/O port range: %#x %#x %#x %#x %#x %#x\n",
+ aBars[0], aBars[1], aBars[2], aBars[3], aBars[4], aBars[5]));
+ if (!fGotMmio || Buf.bInterruptPin != 0)
+ LogRel(("VBoxGuest: Got MMIO but no interrupts!\n"));
+
+ g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd));
+ return STATUS_DEVICE_CONFIGURATION_ERROR;
+}
+
+#endif /* TARGET_NT4 */
+
+/**
+ * Unmaps the VMMDev I/O range from kernel space.
+ *
+ * @param pDevExt The device extension.
+ */
+static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
+{
+ LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
+ if (pDevExt->Core.pVMMDevMemory)
+ {
+ MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->cbVmmDevMemory);
+ pDevExt->Core.pVMMDevMemory = NULL;
+ }
+
+ pDevExt->uVmmDevMemoryPhysAddr.QuadPart = 0;
+ pDevExt->cbVmmDevMemory = 0;
+}
+
+
+/**
+ * Maps the I/O space from VMMDev to virtual kernel address space.
+ *
+ * @return NTSTATUS
+ *
+ * @param pDevExt The device extension.
+ * @param PhysAddr Physical address to map.
+ * @param cbToMap Number of bytes to map.
+ * @param ppvMMIOBase Pointer of mapped I/O base.
+ * @param pcbMMIO Length of mapped I/O base.
+ */
+static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
+ void **ppvMMIOBase, uint32_t *pcbMMIO)
+{
+ AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
+ /* pcbMMIO is optional. */
+
+ NTSTATUS rc = STATUS_SUCCESS;
+ if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
+ {
+ VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
+ LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
+ if (pVMMDevMemory)
+ {
+ LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
+
+ /* Check version of the structure; do we have the right memory version? */
+ if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
+ {
+ /* Save results. */
+ *ppvMMIOBase = pVMMDevMemory;
+ if (pcbMMIO) /* Optional. */
+ *pcbMMIO = pVMMDevMemory->u32Size;
+
+ LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
+ }
+ else
+ {
+ /* Not our version, refuse operation and unmap the memory. */
+ LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
+
+ vgdrvNtUnmapVMMDevMemory(pDevExt);
+ rc = STATUS_UNSUCCESSFUL;
+ }
+ }
+ else
+ rc = STATUS_UNSUCCESSFUL;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets up the device and its resources.
+ *
+ * @param pDevExt Our device extension data.
+ * @param pDevObj The device object.
+ * @param pIrp The request packet if NT5+, NULL for NT4 and earlier.
+ * @param pDrvObj The driver object for NT4, NULL for NT5+.
+ * @param pRegPath The registry path for NT4, NULL for NT5+.
+ */
+static NTSTATUS vgdrvNtSetupDevice(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj,
+ PIRP pIrp, PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ LogFlowFunc(("ENTER: pDevExt=%p pDevObj=%p pIrq=%p pDrvObj=%p pRegPath=%p\n", pDevExt, pDevObj, pIrp, pDrvObj, pRegPath));
+
+ NTSTATUS rcNt;
+ if (!pIrp)
+ {
+#ifdef TARGET_NT4
+ /*
+ * NT4, NT3.x: Let's have a look at what our PCI adapter offers.
+ */
+ LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
+
+ /* Assign the PCI resources. */
+ UNICODE_STRING ClassName;
+ RtlInitUnicodeString(&ClassName, L"VBoxGuestAdapter");
+ PCM_RESOURCE_LIST pResourceList = NULL;
+ if (g_pfnHalAssignSlotResources)
+ {
+ rcNt = g_pfnHalAssignSlotResources(pRegPath, &ClassName, pDrvObj, pDevObj, PCIBus, pDevExt->uBus, pDevExt->uSlot,
+ &pResourceList);
+# ifdef LOG_ENABLED
+ if (pResourceList)
+ vgdrvNtShowDeviceResources(pResourceList);
+# endif
+ if (NT_SUCCESS(rcNt))
+ {
+ rcNt = vgdrvNtScanPCIResourceList(pDevExt, pResourceList, false /*fTranslated*/);
+ ExFreePool(pResourceList);
+ }
+ }
+ else
+ rcNt = vgdrvNt31ScanSlotResources(pDevExt, pDevExt->uBus, pDevExt->uSlot);
+
+# else /* !TARGET_NT4 */
+ AssertFailed();
+ RT_NOREF(pDevObj, pDrvObj, pRegPath);
+ rcNt = STATUS_INTERNAL_ERROR;
+# endif /* !TARGET_NT4 */
+ }
+ else
+ {
+ /*
+ * NT5+: Scan the PCI resource list from the IRP.
+ */
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+# ifdef LOG_ENABLED
+ vgdrvNtShowDeviceResources(pStack->Parameters.StartDevice.AllocatedResourcesTranslated);
+# endif
+ rcNt = vgdrvNtScanPCIResourceList(pDevExt, pStack->Parameters.StartDevice.AllocatedResourcesTranslated,
+ true /*fTranslated*/);
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Map physical address of VMMDev memory into MMIO region
+ * and init the common device extension bits.
+ */
+ void *pvMMIOBase = NULL;
+ uint32_t cbMMIO = 0;
+ rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
+ pDevExt->uVmmDevMemoryPhysAddr,
+ pDevExt->cbVmmDevMemory,
+ &pvMMIOBase,
+ &cbMMIO);
+ if (NT_SUCCESS(rcNt))
+ {
+ pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
+
+ LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
+ pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
+
+ int vrc = VGDrvCommonInitDevExtResources(&pDevExt->Core,
+ pDevExt->Core.IOPortBase,
+ pvMMIOBase, cbMMIO,
+ vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
+ VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
+ if (RT_SUCCESS(vrc))
+ {
+
+ vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
+ sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Register DPC and ISR.
+ */
+ LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
+ IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
+
+ ULONG uInterruptVector = pDevExt->uInterruptVector;
+ KIRQL uHandlerIrql = (KIRQL)pDevExt->uInterruptLevel;
+#ifdef TARGET_NT4
+ if (!pIrp)
+ {
+ /* NT4: Get an interrupt vector. Only proceed if the device provides an interrupt. */
+ if ( uInterruptVector
+ || pDevExt->uInterruptLevel)
+ {
+ LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
+ pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector));
+ uInterruptVector = HalGetInterruptVector(g_enmVGDrvNtVer == VGDRVNTVER_WINNT310 ? Isa : PCIBus,
+ pDevExt->uBus,
+ pDevExt->uInterruptLevel,
+ pDevExt->uInterruptVector,
+ &uHandlerIrql,
+ &pDevExt->fInterruptAffinity);
+ LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
+ }
+ else
+ LogFunc(("Device does not provide an interrupt!\n"));
+ }
+#endif
+ if (uInterruptVector)
+ {
+ LogFlowFunc(("Connecting interrupt (IntVector=%#u), uHandlerIrql=%u) ...\n",
+ uInterruptVector, uHandlerIrql));
+
+ rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
+ vgdrvNtIsrHandler, /* Our ISR handler. */
+ pDevExt, /* Device context. */
+ NULL, /* Optional spinlock. */
+ uInterruptVector, /* Interrupt vector. */
+ uHandlerIrql, /* Irql. */
+ uHandlerIrql, /* SynchronizeIrql. */
+ pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */
+ TRUE, /* Shareable interrupt. */
+ pDevExt->fInterruptAffinity, /* CPU affinity. */
+ FALSE); /* Don't save FPU stack. */
+ if (NT_ERROR(rcNt))
+ LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
+ }
+ else
+ LogFunc(("No interrupt vector found!\n"));
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Once we've read configuration from register and host, we're finally read.
+ */
+ /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
+ pDevExt->Core.fLoggingEnabled = true;
+ vgdrvNtReadConfiguration(pDevExt);
+
+ /* Ready to rumble! */
+ LogRelFunc(("Device is ready!\n"));
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_OPERATIONAL;
+ pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_OPERATIONAL;
+ return STATUS_SUCCESS;
+ }
+
+ pDevExt->pInterruptObject = NULL;
+
+ VbglR0GRFree(&pDevExt->pPowerStateRequest->header);
+ pDevExt->pPowerStateRequest = NULL;
+ }
+ else
+ {
+ LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
+ rcNt = STATUS_UNSUCCESSFUL;
+ }
+
+ VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
+ }
+ else
+ {
+ LogFunc(("Could not init device extension resources: vrc=%Rrc\n", vrc));
+ rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
+ }
+ vgdrvNtUnmapVMMDevMemory(pDevExt);
+ }
+ else
+ LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
+ }
+
+ LogFunc(("Returned with rcNt=%#x\n", rcNt));
+ return rcNt;
+}
+
+
+
+
+#ifdef TARGET_NT4
+# define PCI_CFG_ADDR 0xcf8
+# define PCI_CFG_DATA 0xcfc
+
+/**
+ * NT 3.1 doesn't do PCI nor HalSetBusDataByOffset, this is our fallback.
+ */
+static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
+ void *pvData, ULONG offData, ULONG cbData)
+{
+ /*
+ * Validate input a little bit.
+ */
+ RT_NOREF(enmBusDataType);
+ Assert(idxBus <= 255);
+ Assert(uSlot <= 255);
+ Assert(offData <= 255);
+ Assert(cbData > 0);
+
+ PCI_SLOT_NUMBER PciSlot;
+ PciSlot.u.AsULONG = uSlot;
+ uint32_t const idxAddrTop = UINT32_C(0x80000000)
+ | (idxBus << 16)
+ | (PciSlot.u.bits.DeviceNumber << 11)
+ | (PciSlot.u.bits.FunctionNumber << 8);
+
+ /*
+ * Write the given bytes.
+ */
+ uint8_t const *pbData = (uint8_t const *)pvData;
+ uint32_t off = offData;
+ uint32_t cbRet = 0;
+
+ /* Unaligned start. */
+ if (off & 3)
+ {
+ ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
+ switch (off & 3)
+ {
+ case 1:
+ ASMOutU8(PCI_CFG_DATA + 1, pbData[cbRet++]);
+ if (cbRet >= cbData)
+ break;
+ RT_FALL_THRU();
+ case 2:
+ ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet++]);
+ if (cbRet >= cbData)
+ break;
+ RT_FALL_THRU();
+ case 3:
+ ASMOutU8(PCI_CFG_DATA + 3, pbData[cbRet++]);
+ break;
+ }
+ off = (off | 3) + 1;
+ }
+
+ /* Bulk. */
+ while (off < 256 && cbRet < cbData)
+ {
+ ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
+ switch (cbData - cbRet)
+ {
+ case 1:
+ ASMOutU8(PCI_CFG_DATA, pbData[cbRet]);
+ cbRet += 1;
+ break;
+ case 2:
+ ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
+ cbRet += 2;
+ break;
+ case 3:
+ ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
+ ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet + 2]);
+ cbRet += 3;
+ break;
+ default:
+ ASMOutU32(PCI_CFG_DATA, RT_MAKE_U32_FROM_U8(pbData[cbRet], pbData[cbRet + 1],
+ pbData[cbRet + 2], pbData[cbRet + 3]));
+ cbRet += 4;
+ break;
+ }
+ off += 4;
+ }
+
+ return cbRet;
+}
+
+
+/**
+ * NT 3.1 doesn't do PCI nor HalGetBusDataByOffset, this is our fallback.
+ */
+static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
+ void *pvData, ULONG offData, ULONG cbData)
+{
+ /*
+ * Validate input a little bit.
+ */
+ RT_NOREF(enmBusDataType);
+ Assert(idxBus <= 255);
+ Assert(uSlot <= 255);
+ Assert(offData <= 255);
+ Assert(cbData > 0);
+
+ PCI_SLOT_NUMBER PciSlot;
+ PciSlot.u.AsULONG = uSlot;
+ uint32_t const idxAddrTop = UINT32_C(0x80000000)
+ | (idxBus << 16)
+ | (PciSlot.u.bits.DeviceNumber << 11)
+ | (PciSlot.u.bits.FunctionNumber << 8);
+
+ /*
+ * Read the header type.
+ */
+ ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (VBOX_PCI_HEADER_TYPE & ~3));
+ uint8_t bHdrType = ASMInU8(PCI_CFG_DATA + (VBOX_PCI_HEADER_TYPE & 3));
+ if (bHdrType == 0xff)
+ return idxBus < 8 ? 2 : 0; /* No device here */
+ if ( offData == VBOX_PCI_HEADER_TYPE
+ && cbData == 1)
+ {
+ *(uint8_t *)pvData = bHdrType;
+ /*Log("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %02x\n", idxAddrTop, offData, bHdrType);*/
+ return 1;
+ }
+
+ /*
+ * Read the requested bytes.
+ */
+ uint8_t *pbData = (uint8_t *)pvData;
+ uint32_t off = offData;
+ uint32_t cbRet = 0;
+
+ /* Unaligned start. */
+ if (off & 3)
+ {
+ ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
+ uint32_t uValue = ASMInU32(PCI_CFG_DATA);
+ switch (off & 3)
+ {
+ case 1:
+ pbData[cbRet++] = (uint8_t)(uValue >> 8);
+ if (cbRet >= cbData)
+ break;
+ RT_FALL_THRU();
+ case 2:
+ pbData[cbRet++] = (uint8_t)(uValue >> 16);
+ if (cbRet >= cbData)
+ break;
+ RT_FALL_THRU();
+ case 3:
+ pbData[cbRet++] = (uint8_t)(uValue >> 24);
+ break;
+ }
+ off = (off | 3) + 1;
+ }
+
+ /* Bulk. */
+ while (off < 256 && cbRet < cbData)
+ {
+ ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
+ uint32_t uValue = ASMInU32(PCI_CFG_DATA);
+ switch (cbData - cbRet)
+ {
+ case 1:
+ pbData[cbRet++] = (uint8_t)uValue;
+ break;
+ case 2:
+ pbData[cbRet++] = (uint8_t)uValue;
+ pbData[cbRet++] = (uint8_t)(uValue >> 8);
+ break;
+ case 3:
+ pbData[cbRet++] = (uint8_t)uValue;
+ pbData[cbRet++] = (uint8_t)(uValue >> 8);
+ pbData[cbRet++] = (uint8_t)(uValue >> 16);
+ break;
+ default:
+ pbData[cbRet++] = (uint8_t)uValue;
+ pbData[cbRet++] = (uint8_t)(uValue >> 8);
+ pbData[cbRet++] = (uint8_t)(uValue >> 16);
+ pbData[cbRet++] = (uint8_t)(uValue >> 24);
+ break;
+ }
+ off += 4;
+ }
+
+ Log(("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %.*Rhxs\n", idxAddrTop, offData, cbRet, pvData));
+ return cbRet;
+}
+
+
+/**
+ * Helper function to handle the PCI device lookup.
+ *
+ * @returns NT status code.
+ *
+ * @param puBus Where to return the bus number on success.
+ * @param pSlot Where to return the slot number on success.
+ */
+static NTSTATUS vgdrvNt4FindPciDevice(PULONG puBus, PPCI_SLOT_NUMBER pSlot)
+{
+ Log(("vgdrvNt4FindPciDevice\n"));
+
+ PCI_SLOT_NUMBER Slot;
+ Slot.u.AsULONG = 0;
+
+ /* Scan each bus. */
+ for (ULONG uBus = 0; uBus < PCI_MAX_BUSES; uBus++)
+ {
+ /* Scan each device. */
+ for (ULONG idxDevice = 0; idxDevice < PCI_MAX_DEVICES; idxDevice++)
+ {
+ Slot.u.bits.DeviceNumber = idxDevice;
+ Slot.u.bits.FunctionNumber = 0;
+
+ /* Check the device header. */
+ uint8_t bHeaderType = 0xff;
+ ULONG cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG,
+ &bHeaderType, VBOX_PCI_HEADER_TYPE, sizeof(bHeaderType));
+ if (cbRet == 0)
+ break;
+ if (cbRet == 2 || bHeaderType == 0xff)
+ continue;
+
+ /* Scan functions. */
+ uint32_t const cFunctionStep = bHeaderType & 0x80 ? 1 : 8;
+ Log(("vgdrvNt4FindPciDevice: %#x:%#x cFunctionStep=%d bHeaderType=%#x\n", uBus, idxDevice, cFunctionStep, bHeaderType));
+ for (ULONG idxFunction = 0; idxFunction < PCI_MAX_FUNCTION; idxFunction += cFunctionStep)
+ {
+ Slot.u.bits.FunctionNumber = idxFunction;
+
+ /* Read the vendor and device IDs of this device and compare with the VMMDev. */
+ struct
+ {
+ uint16_t idVendor;
+ uint16_t idDevice;
+ } Buf = { PCI_INVALID_VENDORID, PCI_INVALID_VENDORID };
+ cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG, &Buf, VBOX_PCI_VENDOR_ID, sizeof(Buf));
+ if ( cbRet == sizeof(Buf)
+ && Buf.idVendor == VMMDEV_VENDORID
+ && Buf.idDevice == VMMDEV_DEVICEID)
+ {
+ /* Hooray, we've found it! */
+ Log(("vgdrvNt4FindPciDevice: Device found! Bus=%#x Slot=%#u (dev %#x, fun %#x, rvd %#x)\n",
+ uBus, Slot.u.AsULONG, Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber, Slot.u.bits.Reserved));
+
+ *puBus = uBus;
+ *pSlot = Slot;
+ return STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+}
+
+
+/**
+ * Legacy helper function to create the device object.
+ *
+ * @returns NT status code.
+ *
+ * @param pDrvObj The driver object.
+ * @param pRegPath The driver registry path.
+ */
+static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
+
+ /*
+ * Find our virtual PCI device
+ */
+ ULONG uBus;
+ PCI_SLOT_NUMBER uSlot;
+ NTSTATUS rc = vgdrvNt4FindPciDevice(&uBus, &uSlot);
+ if (NT_ERROR(rc))
+ {
+ Log(("vgdrvNt4CreateDevice: Device not found!\n"));
+ return rc;
+ }
+
+ /*
+ * Create device.
+ */
+ UNICODE_STRING DevName;
+ RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
+ PDEVICE_OBJECT pDeviceObject = NULL;
+ rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
+ if (NT_SUCCESS(rc))
+ {
+ Log(("vgdrvNt4CreateDevice: Device created\n"));
+
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
+ rc = IoCreateSymbolicLink(&DosName, &DevName);
+ if (NT_SUCCESS(rc))
+ {
+ Log(("vgdrvNt4CreateDevice: Symlink created\n"));
+
+ /*
+ * Setup the device extension.
+ */
+ Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
+ int vrc = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
+ if (RT_SUCCESS(vrc))
+ {
+ /* Store bus and slot number we've queried before. */
+ pDevExt->uBus = uBus;
+ pDevExt->uSlot = uSlot.u.AsULONG;
+
+ Log(("vgdrvNt4CreateDevice: Device extension created\n"));
+
+ /* Do the actual VBox init ... */
+ rc = vgdrvNtSetupDevice(pDevExt, pDeviceObject, NULL /*pIrp*/, pDrvObj, pRegPath);
+ if (NT_SUCCESS(rc))
+ {
+ Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (success)\n", rc));
+ return rc;
+ }
+
+ /* bail out */
+ vgdrvNtDeleteDevExtFundament(pDevExt);
+ }
+ IoDeleteSymbolicLink(&DosName);
+ }
+ else
+ Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
+ IoDeleteDevice(pDeviceObject);
+ }
+ else
+ Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
+ Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
+ return rc;
+}
+
+#endif /* TARGET_NT4 */
+
+/**
+ * Handle request from the Plug & Play subsystem.
+ *
+ * @returns NT status code
+ * @param pDrvObj Driver object
+ * @param pDevObj Device object
+ *
+ * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
+ */
+static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
+{
+ LogFlowFuncEnter();
+
+ /*
+ * Create device.
+ */
+ UNICODE_STRING DevName;
+ RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
+ PDEVICE_OBJECT pDeviceObject = NULL;
+ NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Create symbolic link (DOS devices).
+ */
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
+ rcNt = IoCreateSymbolicLink(&DosName, &DevName);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Setup the device extension.
+ */
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
+ rcNt = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
+ if (NT_SUCCESS(rcNt))
+ {
+ pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
+ if (pDevExt->pNextLowerDriver != NULL)
+ {
+ /* Ensure we are not called at elevated IRQL, even if our code isn't pagable any more. */
+ pDeviceObject->Flags |= DO_POWER_PAGABLE;
+
+ /* Driver is ready now. */
+ pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+ LogFlowFunc(("Returning with rcNt=%#x (success)\n", rcNt));
+ return rcNt;
+ }
+ LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
+ rcNt = STATUS_DEVICE_NOT_CONNECTED;
+ vgdrvNtDeleteDevExtFundament(pDevExt);
+ }
+
+ IoDeleteSymbolicLink(&DosName);
+ }
+ else
+ LogFunc(("IoCreateSymbolicLink failed with rcNt=%#x!\n", rcNt));
+ IoDeleteDevice(pDeviceObject);
+ }
+ else
+ LogFunc(("IoCreateDevice failed with rcNt=%#x!\n", rcNt));
+
+ LogFunc(("Returning with rcNt=%#x\n", rcNt));
+ return rcNt;
+}
+
+
+/**
+ * Irp completion routine for PnP Irps we send.
+ *
+ * @returns NT status code.
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ * @param pEvent Semaphore.
+ */
+static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
+{
+ RT_NOREF2(pDevObj, pIrp);
+ KeSetEvent(pEvent, 0, FALSE);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+/**
+ * Helper to send a PnP IRP and wait until it's done.
+ *
+ * @returns NT status code.
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ * @param fStrict When set, returns an error if the IRP gives an error.
+ */
+static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
+{
+ KEVENT Event;
+
+ KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE);
+
+ NTSTATUS rcNt = IoCallDriver(pDevObj, pIrp);
+ if (rcNt == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ rcNt = pIrp->IoStatus.Status;
+ }
+
+ if ( !fStrict
+ && (rcNt == STATUS_NOT_SUPPORTED || rcNt == STATUS_INVALID_DEVICE_REQUEST))
+ {
+ rcNt = STATUS_SUCCESS;
+ }
+
+ Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning %#x\n", rcNt));
+ return rcNt;
+}
+
+
+/**
+ * Deletes the device hardware resources.
+ *
+ * Used during removal, stopping and legacy module unloading.
+ *
+ * @param pDevExt The device extension.
+ */
+static void vgdrvNtDeleteDeviceResources(PVBOXGUESTDEVEXTWIN pDevExt)
+{
+ if (pDevExt->pInterruptObject)
+ {
+ IoDisconnectInterrupt(pDevExt->pInterruptObject);
+ pDevExt->pInterruptObject = NULL;
+ }
+ pDevExt->pPowerStateRequest = NULL; /* Will be deleted by the following call. */
+ if (pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES)
+ VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
+ vgdrvNtUnmapVMMDevMemory(pDevExt);
+}
+
+
+/**
+ * Deletes the device extension fundament and unlinks the device
+ *
+ * Used during removal and legacy module unloading. Must have called
+ * vgdrvNtDeleteDeviceResources.
+ *
+ * @param pDevObj Device object.
+ * @param pDevExt The device extension.
+ */
+static void vgdrvNtDeleteDeviceFundamentAndUnlink(PDEVICE_OBJECT pDevObj, PVBOXGUESTDEVEXTWIN pDevExt)
+{
+ /*
+ * Delete the remainder of the device extension.
+ */
+ vgdrvNtDeleteDevExtFundament(pDevExt);
+
+ /*
+ * Delete the DOS symlink to the device and finally the device itself.
+ */
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
+ IoDeleteSymbolicLink(&DosName);
+
+ Log(("vgdrvNtDeleteDeviceFundamentAndUnlink: Deleting device ...\n"));
+ IoDeleteDevice(pDevObj);
+}
+
+
+/**
+ * Checks if the device is idle.
+ * @returns STATUS_SUCCESS if idle, STATUS_UNSUCCESSFUL if busy.
+ * @param pDevExt The device extension.
+ * @param pszQueryNm The query name.
+ */
+static NTSTATUS vgdrvNtCheckIdle(PVBOXGUESTDEVEXTWIN pDevExt, const char *pszQueryNm)
+{
+ uint32_t cSessions = pDevExt->Core.cSessions;
+ if (cSessions == 0)
+ return STATUS_SUCCESS;
+ LogRel(("vgdrvNtCheckIdle/%s: cSessions=%d\n", pszQueryNm, cSessions));
+ return STATUS_UNSUCCESSFUL;
+}
+
+
+/**
+ * PnP Request handler.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+
+#ifdef LOG_ENABLED
+ static char const * const s_apszFnctName[] =
+ {
+ "IRP_MN_START_DEVICE",
+ "IRP_MN_QUERY_REMOVE_DEVICE",
+ "IRP_MN_REMOVE_DEVICE",
+ "IRP_MN_CANCEL_REMOVE_DEVICE",
+ "IRP_MN_STOP_DEVICE",
+ "IRP_MN_QUERY_STOP_DEVICE",
+ "IRP_MN_CANCEL_STOP_DEVICE",
+ "IRP_MN_QUERY_DEVICE_RELATIONS",
+ "IRP_MN_QUERY_INTERFACE",
+ "IRP_MN_QUERY_CAPABILITIES",
+ "IRP_MN_QUERY_RESOURCES",
+ "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
+ "IRP_MN_QUERY_DEVICE_TEXT",
+ "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
+ "IRP_MN_0xE",
+ "IRP_MN_READ_CONFIG",
+ "IRP_MN_WRITE_CONFIG",
+ "IRP_MN_EJECT",
+ "IRP_MN_SET_LOCK",
+ "IRP_MN_QUERY_ID",
+ "IRP_MN_QUERY_PNP_DEVICE_STATE",
+ "IRP_MN_QUERY_BUS_INFORMATION",
+ "IRP_MN_DEVICE_USAGE_NOTIFICATION",
+ "IRP_MN_SURPRISE_REMOVAL",
+ };
+ Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n",
+ pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown"));
+#endif
+
+ NTSTATUS rc = STATUS_SUCCESS;
+ uint8_t bMinorFunction = pStack->MinorFunction;
+ switch (bMinorFunction)
+ {
+ case IRP_MN_START_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n"));
+
+ /* This must be handled first by the lower driver. */
+ rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
+ if ( NT_SUCCESS(rc)
+ && NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n",
+ pStack->Parameters.StartDevice.AllocatedResources));
+ if (pStack->Parameters.StartDevice.AllocatedResources)
+ {
+ rc = vgdrvNtSetupDevice(pDevExt, pDevObj, pIrp, NULL, NULL);
+ if (NT_SUCCESS(rc))
+ Log(("vgdrvNtNt5PlusPnP: START_DEVICE: success\n"));
+ else
+ Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNtSetupDevice failed: %#x\n", rc));
+ }
+ else
+ {
+ Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n",
+ pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL));
+ rc = STATUS_UNSUCCESSFUL;
+ }
+ }
+ else
+ Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNt5PlusPnPSendIrpSynchronously failed: %#x + %#x\n",
+ rc, pIrp->IoStatus.Status));
+
+ pIrp->IoStatus.Status = rc;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rc;
+ }
+
+
+ /*
+ * Sent before removing the device and/or driver.
+ */
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n"));
+
+ RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
+#ifdef VBOX_REBOOT_ON_UNINSTALL
+ Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n"));
+ rc = STATUS_UNSUCCESSFUL;
+#endif
+ if (NT_SUCCESS(rc))
+ rc = vgdrvNtCheckIdle(pDevExt, "QUERY_REMOVE_DEVICE");
+ if (NT_SUCCESS(rc))
+ {
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGREMOVE;
+ RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
+
+ /* This IRP passed down to lower driver. */
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(pIrp);
+ rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+ Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
+
+ /* We must not do anything the IRP after doing IoSkip & CallDriver
+ since the driver below us will complete (or already have completed) the IRP.
+ I.e. just return the status we got from IoCallDriver */
+ }
+ else
+ {
+ RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
+ pIrp->IoStatus.Status = rc;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Returning with rc = 0x%x\n", rc));
+ return rc;
+ }
+
+ /*
+ * Cancels a pending remove, IRP_MN_QUERY_REMOVE_DEVICE.
+ * We only have to revert the state.
+ */
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n"));
+
+ /* This must be handled first by the lower driver. */
+ rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
+ if ( NT_SUCCESS(rc)
+ && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE)
+ {
+ /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */
+ pDevExt->enmDevState = pDevExt->enmPrevDevState;
+ }
+
+ /* Complete the IRP. */
+ pIrp->IoStatus.Status = rc;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rc;
+ }
+
+ /*
+ * We do nothing here actually, esp. since this request is not expected for VBoxGuest.
+ * The cleanup will be done in IRP_MN_REMOVE_DEVICE, which follows this call.
+ */
+ case IRP_MN_SURPRISE_REMOVAL:
+ {
+ Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n"));
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_SURPRISEREMOVED;
+ LogRel(("VBoxGuest: unexpected device removal\n"));
+
+ /* Pass to the lower driver. */
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(pIrp);
+ rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+
+ /* Do not complete the IRP. */
+ return rc;
+ }
+
+ /*
+ * Device and/or driver removal. Destroy everything.
+ */
+ case IRP_MN_REMOVE_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n"));
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_REMOVED;
+
+ /*
+ * Disconnect interrupts and delete all hardware resources.
+ * Note! This may already have been done if we're STOPPED already, if that's a possibility.
+ */
+ vgdrvNtDeleteDeviceResources(pDevExt);
+
+ /*
+ * We need to send the remove down the stack before we detach, but we don't need
+ * to wait for the completion of this operation (nor register a completion routine).
+ */
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(pIrp);
+ rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+ Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
+
+ IoDetachDevice(pDevExt->pNextLowerDriver);
+ Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n"));
+
+ /*
+ * Delete the remainder of the device extension data, unlink it from the namespace and delete it.
+ */
+ vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
+
+ pDevObj = NULL; /* invalid */
+ pDevExt = NULL; /* invalid */
+
+ Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n"));
+ return rc; /* Propagating rc from IoCallDriver. */
+ }
+
+
+ /*
+ * Sent before stopping the device/driver to check whether it is okay to do so.
+ */
+ case IRP_MN_QUERY_STOP_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n"));
+ RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
+ rc = vgdrvNtCheckIdle(pDevExt, "QUERY_STOP_DEVICE");
+ if (NT_SUCCESS(rc))
+ {
+ pDevExt->enmPrevDevState = pDevExt->enmDevState;
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGSTOP;
+ RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
+
+ /* This IRP passed down to lower driver. */
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(pIrp);
+
+ rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+ Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
+
+ /* we must not do anything with the IRP after doing IoSkip & CallDriver since the
+ driver below us will complete (or already have completed) the IRP. I.e. just
+ return the status we got from IoCallDriver. */
+ }
+ else
+ {
+ RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
+ pIrp->IoStatus.Status = rc;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Returning with rc = 0x%x\n", rc));
+ return rc;
+ }
+
+ /*
+ * Cancels a pending remove, IRP_MN_QUERY_STOP_DEVICE.
+ * We only have to revert the state.
+ */
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n"));
+
+ /* This must be handled first by the lower driver. */
+ rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
+ if ( NT_SUCCESS(rc)
+ && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP)
+ {
+ /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */
+ pDevExt->enmDevState = pDevExt->enmPrevDevState;
+ }
+
+ /* Complete the IRP. */
+ pIrp->IoStatus.Status = rc;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rc;
+ }
+
+ /*
+ * Stop the device.
+ */
+ case IRP_MN_STOP_DEVICE:
+ {
+ Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n"));
+ pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
+
+ /*
+ * Release the hardware resources.
+ */
+ vgdrvNtDeleteDeviceResources(pDevExt);
+
+ /*
+ * Pass the request to the lower driver.
+ */
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+ Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
+ return rc;
+ }
+
+ default:
+ {
+ IoSkipCurrentIrpStackLocation(pIrp);
+ rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+ Log(("vgdrvNtNt5PlusPnP: Unknown request %#x: Lower driver replied: %x\n", bMinorFunction, rc));
+ return rc;
+ }
+ }
+}
+
+
+/**
+ * Handle the power completion event.
+ *
+ * @returns NT status code.
+ * @param pDevObj Targetted device object.
+ * @param pIrp IO request packet.
+ * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower.
+ */
+static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext)
+{
+#ifdef VBOX_STRICT
+ RT_NOREF1(pDevObj);
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext;
+ PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
+
+ Assert(pDevExt);
+
+ if (pIrpSp)
+ {
+ Assert(pIrpSp->MajorFunction == IRP_MJ_POWER);
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ switch (pIrpSp->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ switch (pIrpSp->Parameters.Power.Type)
+ {
+ case DevicePowerState:
+ switch (pIrpSp->Parameters.Power.State.DeviceState)
+ {
+ case PowerDeviceD0:
+ break;
+ default: /* Shut up MSC */
+ break;
+ }
+ break;
+ default: /* Shut up MSC */
+ break;
+ }
+ break;
+ }
+ }
+ }
+#else
+ RT_NOREF3(pDevObj, pIrp, pContext);
+#endif
+
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Handle the Power requests.
+ *
+ * @returns NT status code
+ * @param pDevObj device object
+ * @param pIrp IRP
+ */
+static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type;
+ POWER_STATE PowerState = pStack->Parameters.Power.State;
+ POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType;
+
+ Log(("vgdrvNtNt5PlusPower:\n"));
+
+ switch (pStack->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ {
+ Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType));
+ switch (enmPowerType)
+ {
+ case SystemPowerState:
+ {
+ Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n",
+ enmPowerAction, PowerState.SystemState, PowerState.DeviceState));
+
+ switch (enmPowerAction)
+ {
+ case PowerActionSleep:
+
+ /* System now is in a working state. */
+ if (PowerState.SystemState == PowerSystemWorking)
+ {
+ if ( pDevExt
+ && pDevExt->enmLastSystemPowerAction == PowerActionHibernate)
+ {
+ Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n"));
+ int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core,
+ vgdrvNtVersionToOSType(g_enmVGDrvNtVer));
+ if (RT_FAILURE(rc))
+ Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc));
+ }
+ }
+ break;
+
+ case PowerActionShutdownReset:
+ {
+ Log(("vgdrvNtNt5PlusPower: Power action reset!\n"));
+
+ /* Tell the VMM that we no longer support mouse pointer integration. */
+ VMMDevReqMouseStatus *pReq = NULL;
+ int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus),
+ VMMDevReq_SetMouseStatus);
+ if (RT_SUCCESS(vrc))
+ {
+ pReq->mouseFeatures = 0;
+ pReq->pointerXPos = 0;
+ pReq->pointerYPos = 0;
+
+ vrc = VbglR0GRPerform(&pReq->header);
+ if (RT_FAILURE(vrc))
+ {
+ Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
+ }
+
+ VbglR0GRFree(&pReq->header);
+ }
+
+ /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this
+ * power action and would assert/crash when we already cleaned up all the stuff! */
+ break;
+ }
+
+ case PowerActionShutdown:
+ case PowerActionShutdownOff:
+ {
+ Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n"));
+ if (PowerState.SystemState >= PowerSystemShutdown)
+ {
+ Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n"));
+
+ VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
+ int vrc = VERR_NOT_IMPLEMENTED;
+ if (pReq)
+ {
+ pReq->header.requestType = VMMDevReq_SetPowerStatus;
+ pReq->powerState = VMMDevPowerState_PowerOff;
+
+ vrc = VbglR0GRPerform(&pReq->header);
+ }
+ if (RT_FAILURE(vrc))
+ Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
+
+ /* No need to do cleanup here; at this point we should've been
+ * turned off by VMMDev already! */
+ }
+ break;
+ }
+
+ case PowerActionHibernate:
+ Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n"));
+ break;
+
+ case PowerActionWarmEject:
+ Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n"));
+ break;
+
+ default:
+ Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction));
+ break;
+ }
+
+ /*
+ * Save the current system power action for later use.
+ * This becomes handy when we return from hibernation for example.
+ */
+ if (pDevExt)
+ pDevExt->enmLastSystemPowerAction = enmPowerAction;
+
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /*
+ * Whether we are completing or relaying this power IRP,
+ * we must call PoStartNextPowerIrp.
+ */
+ g_pfnPoStartNextPowerIrp(pIrp);
+
+ /*
+ * Send the IRP down the driver stack, using PoCallDriver
+ * (not IoCallDriver, as for non-power irps).
+ */
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp,
+ vgdrvNtNt5PlusPowerComplete,
+ (PVOID)pDevExt,
+ TRUE,
+ TRUE,
+ TRUE);
+ return g_pfnPoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+}
+
+
+/**
+ * IRP_MJ_SYSTEM_CONTROL handler.
+ *
+ * @returns NT status code
+ * @param pDevObj Device object.
+ * @param pIrp IRP.
+ */
+static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+
+ LogFlowFuncEnter();
+
+ /* Always pass it on to the next driver. */
+ IoSkipCurrentIrpStackLocation(pIrp);
+
+ return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
+}
+
+
+/**
+ * Unload the driver.
+ *
+ * @param pDrvObj Driver object.
+ */
+static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
+{
+ LogFlowFuncEnter();
+
+#ifdef TARGET_NT4
+ /*
+ * We need to destroy the device object here on NT4 and earlier.
+ */
+ PDEVICE_OBJECT pDevObj = pDrvObj->DeviceObject;
+ if (pDevObj)
+ {
+ if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
+ {
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ AssertPtr(pDevExt);
+ AssertMsg(pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES,
+ ("uInitState=%#x\n", pDevExt->Core.uInitState));
+
+ vgdrvNtDeleteDeviceResources(pDevExt);
+ vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
+ }
+ }
+#else /* !TARGET_NT4 */
+ /*
+ * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE
+ * where we already did the cleanup, so don't do anything here (yet).
+ */
+ RT_NOREF1(pDrvObj);
+#endif /* !TARGET_NT4 */
+
+ VGDrvCommonDestroyLoggers();
+ RTR0Term();
+
+ /*
+ * Finally deregister the bugcheck callback. Do it late to catch trouble in RTR0Term.
+ */
+ if (g_fBugCheckCallbackRegistered)
+ {
+ g_pfnKeDeregisterBugCheckCallback(&g_BugCheckCallbackRec);
+ g_fBugCheckCallbackRegistered = false;
+ }
+}
+
+
+/**
+ * For simplifying request completion into a simple return statement, extended
+ * version.
+ *
+ * @returns rcNt
+ * @param rcNt The status code.
+ * @param uInfo Extra info value.
+ * @param pIrp The IRP.
+ */
+DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
+{
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = uInfo;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * For simplifying request completion into a simple return statement.
+ *
+ * @returns rcNt
+ * @param rcNt The status code.
+ * @param pIrp The IRP.
+ */
+DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
+{
+ return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
+}
+
+
+/**
+ * Checks if NT authority rev 1 SID (SECURITY_NT_AUTHORITY).
+ *
+ * @returns true / false.
+ * @param pSid The SID to check.
+ */
+DECLINLINE(bool) vgdrvNtIsSidNtAuth(struct _SID const *pSid)
+{
+ return pSid != NULL
+ && pSid->Revision == 1
+ && pSid->IdentifierAuthority.Value[5] == 5
+ && pSid->IdentifierAuthority.Value[4] == 0
+ && pSid->IdentifierAuthority.Value[3] == 0
+ && pSid->IdentifierAuthority.Value[2] == 0
+ && pSid->IdentifierAuthority.Value[1] == 0
+ && pSid->IdentifierAuthority.Value[0] == 0;
+}
+
+
+/**
+ * Matches SID with local system user (S-1-5-18 / SECURITY_LOCAL_SYSTEM_RID).
+ */
+DECLINLINE(bool) vgdrvNtIsSidLocalSystemUser(SID const *pSid)
+{
+ return vgdrvNtIsSidNtAuth(pSid)
+ && pSid->SubAuthorityCount == 1
+ && pSid->SubAuthority[0] == SECURITY_LOCAL_SYSTEM_RID;
+}
+
+
+/**
+ * Matches SID with NT system admin user (S-1-5-*-500 / DOMAIN_USER_RID_ADMIN).
+ */
+DECLINLINE(bool) vgdrvNtIsSidAdminUser(SID const *pSid)
+{
+ /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
+ return vgdrvNtIsSidNtAuth(pSid)
+ && pSid->SubAuthorityCount >= 2
+ && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
+ && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_ADMIN;
+}
+
+
+/**
+ * Matches SID with NT system guest user (S-1-5-*-501 / DOMAIN_USER_RID_GUEST).
+ */
+DECLINLINE(bool) vgdrvNtIsSidGuestUser(SID const *pSid)
+{
+ /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
+ return vgdrvNtIsSidNtAuth(pSid)
+ && pSid->SubAuthorityCount >= 2
+ && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
+ && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_GUEST;
+}
+
+
+/**
+ * Matches SID with NT system admins group (S-1-5-32-544, S-1-5-*-512).
+ */
+DECLINLINE(bool) vgdrvNtIsSidAdminsGroup(SID const *pSid)
+{
+ return vgdrvNtIsSidNtAuth(pSid)
+ && ( ( pSid->SubAuthorityCount == 2
+ && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
+ && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_ADMINS)
+#if 0
+ /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
+ || ( pSid->SubAuthorityCount >= 2
+ && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
+ && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_ADMINS)
+#endif
+ );
+}
+
+
+/**
+ * Matches SID with NT system users group (S-1-5-32-545, S-1-5-32-547, S-1-5-*-512).
+ */
+DECLINLINE(bool) vgdrvNtIsSidUsersGroup(SID const *pSid)
+{
+ return vgdrvNtIsSidNtAuth(pSid)
+ && ( ( pSid->SubAuthorityCount == 2
+ && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
+ && ( pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_USERS
+ || pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_POWER_USERS) )
+#if 0
+ /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
+ || ( pSid->SubAuthorityCount >= 2
+ && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
+ && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_USERS)
+#endif
+ );
+}
+
+
+/**
+ * Matches SID with NT system guests group (S-1-5-32-546, S-1-5-*-512).
+ */
+DECLINLINE(bool) vgdrvNtIsSidGuestsGroup(SID const *pSid)
+{
+ return vgdrvNtIsSidNtAuth(pSid)
+ && ( ( pSid->SubAuthorityCount == 2
+ && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
+ && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_GUESTS)
+#if 0
+ /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
+ || ( pSid->SubAuthorityCount >= 2
+ && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
+ && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_GUESTS)
+#endif
+ );
+}
+
+
+/**
+ * Checks if local authority rev 1 SID (SECURITY_LOCAL_SID_AUTHORITY).
+ *
+ * @returns true / false.
+ * @param pSid The SID to check.
+ */
+DECLINLINE(bool) vgdrvNtIsSidLocalAuth(struct _SID const *pSid)
+{
+ return pSid != NULL
+ && pSid->Revision == 1
+ && pSid->IdentifierAuthority.Value[5] == 2
+ && pSid->IdentifierAuthority.Value[4] == 0
+ && pSid->IdentifierAuthority.Value[3] == 0
+ && pSid->IdentifierAuthority.Value[2] == 0
+ && pSid->IdentifierAuthority.Value[1] == 0
+ && pSid->IdentifierAuthority.Value[0] == 0;
+}
+
+
+/**
+ * Matches SID with console logon group (S-1-2-1 / SECURITY_LOCAL_LOGON_RID).
+ */
+DECLINLINE(bool) vgdrvNtIsSidConsoleLogonGroup(SID const *pSid)
+{
+ return vgdrvNtIsSidLocalAuth(pSid)
+ && pSid->SubAuthorityCount == 1
+ && pSid->SubAuthority[0] == SECURITY_LOCAL_LOGON_RID;
+}
+
+
+/**
+ * Checks if mandatory label authority rev 1 SID (SECURITY_MANDATORY_LABEL_AUTHORITY).
+ *
+ * @returns true / false.
+ * @param pSid The SID to check.
+ */
+DECLINLINE(bool) vgdrvNtIsSidMandatoryLabelAuth(struct _SID const *pSid)
+{
+ return pSid != NULL
+ && pSid->Revision == 1
+ && pSid->IdentifierAuthority.Value[5] == 16
+ && pSid->IdentifierAuthority.Value[4] == 0
+ && pSid->IdentifierAuthority.Value[3] == 0
+ && pSid->IdentifierAuthority.Value[2] == 0
+ && pSid->IdentifierAuthority.Value[1] == 0
+ && pSid->IdentifierAuthority.Value[0] == 0;
+}
+
+
+#ifdef LOG_ENABLED
+/** Format an SID for logging. */
+static const char *vgdrvNtFormatSid(char *pszBuf, size_t cbBuf, struct _SID const *pSid)
+{
+ uint64_t uAuth = RT_MAKE_U64_FROM_U8(pSid->IdentifierAuthority.Value[5], pSid->IdentifierAuthority.Value[4],
+ pSid->IdentifierAuthority.Value[3], pSid->IdentifierAuthority.Value[2],
+ pSid->IdentifierAuthority.Value[1], pSid->IdentifierAuthority.Value[0],
+ 0, 0);
+ ssize_t offCur = RTStrPrintf2(pszBuf, cbBuf, "S-%u-%RU64", pSid->Revision, uAuth);
+ ULONG const *puSubAuth = &pSid->SubAuthority[0];
+ unsigned cSubAuths = pSid->SubAuthorityCount;
+ while (cSubAuths > 0 && (size_t)offCur < cbBuf)
+ {
+ ssize_t cchThis = RTStrPrintf2(&pszBuf[offCur], cbBuf - (size_t)offCur, "-%u", *puSubAuth);
+ if (cchThis > 0)
+ {
+ offCur += cchThis;
+ puSubAuth++;
+ cSubAuths--;
+ }
+ else
+ {
+ Assert(cbBuf >= 5);
+ pszBuf[cbBuf - 4] = '.';
+ pszBuf[cbBuf - 3] = '.';
+ pszBuf[cbBuf - 2] = '.';
+ pszBuf[cbBuf - 1] = '\0';
+ break;
+ }
+ }
+ return pszBuf;
+}
+#endif
+
+
+/**
+ * Calculate requestor flags for the current process.
+ *
+ * ASSUMES vgdrvNtCreate is executed in the context of the process and thread
+ * doing the NtOpenFile call.
+ *
+ * @returns VMMDEV_REQUESTOR_XXX
+ */
+static uint32_t vgdrvNtCalcRequestorFlags(void)
+{
+ uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE
+ | VMMDEV_REQUESTOR_USR_NOT_GIVEN
+ | VMMDEV_REQUESTOR_CON_DONT_KNOW
+ | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN
+ | VMMDEV_REQUESTOR_NO_USER_DEVICE;
+ HANDLE hToken = NULL;
+ NTSTATUS rcNt = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken);
+ if (NT_SUCCESS(rcNt))
+ {
+ union
+ {
+ TOKEN_USER CurUser;
+ TOKEN_GROUPS CurGroups;
+ uint8_t abPadding[256];
+ } Buf;
+#ifdef LOG_ENABLED
+ char szSid[200];
+#endif
+
+ /*
+ * Get the user SID and see if it's a standard one.
+ */
+ RT_ZERO(Buf.CurUser);
+ ULONG cbReturned = 0;
+ rcNt = ZwQueryInformationToken(hToken, TokenUser, &Buf.CurUser, sizeof(Buf), &cbReturned);
+ if (NT_SUCCESS(rcNt))
+ {
+ struct _SID const *pSid = (struct _SID const *)Buf.CurUser.User.Sid;
+ Log5(("vgdrvNtCalcRequestorFlags: TokenUser: %#010x %s\n",
+ Buf.CurUser.User.Attributes, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
+
+ if (vgdrvNtIsSidLocalSystemUser(pSid))
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_SYSTEM;
+ else if (vgdrvNtIsSidAdminUser(pSid))
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_ROOT;
+ else if (vgdrvNtIsSidGuestUser(pSid))
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
+ }
+ else
+ LogRel(("vgdrvNtCalcRequestorFlags: TokenUser query failed: %#x\n", rcNt));
+
+ /*
+ * Get the groups.
+ */
+ TOKEN_GROUPS *pCurGroupsFree = NULL;
+ TOKEN_GROUPS *pCurGroups = &Buf.CurGroups;
+ uint32_t cbCurGroups = sizeof(Buf);
+ cbReturned = 0;
+ RT_ZERO(Buf);
+ rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
+ if (rcNt == STATUS_BUFFER_TOO_SMALL)
+ {
+ uint32_t cTries = 8;
+ do
+ {
+ RTMemTmpFree(pCurGroupsFree);
+ if (cbCurGroups < cbReturned)
+ cbCurGroups = RT_ALIGN_32(cbCurGroups + 32, 64);
+ else
+ cbCurGroups += 64;
+ pCurGroupsFree = pCurGroups = (TOKEN_GROUPS *)RTMemTmpAllocZ(cbCurGroups);
+ if (pCurGroupsFree)
+ rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
+ else
+ rcNt = STATUS_NO_MEMORY;
+ } while (rcNt == STATUS_BUFFER_TOO_SMALL && cTries-- > 0);
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ bool fGuestsMember = false;
+ bool fUsersMember = false;
+ if (g_enmVGDrvNtVer >= VGDRVNTVER_WIN7)
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_NO;
+
+ for (uint32_t iGrp = 0; iGrp < pCurGroups->GroupCount; iGrp++)
+ {
+ uint32_t const fAttribs = pCurGroups->Groups[iGrp].Attributes;
+ struct _SID const *pSid = (struct _SID const *)pCurGroups->Groups[iGrp].Sid;
+ Log5(("vgdrvNtCalcRequestorFlags: TokenGroups[%u]: %#10x %s\n",
+ iGrp, fAttribs, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
+
+ if ( (fAttribs & SE_GROUP_INTEGRITY_ENABLED)
+ && vgdrvNtIsSidMandatoryLabelAuth(pSid)
+ && pSid->SubAuthorityCount == 1
+ && (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)
+ {
+ fRequestor &= ~VMMDEV_REQUESTOR_TRUST_MASK;
+ if (pSid->SubAuthority[0] < SECURITY_MANDATORY_LOW_RID)
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_UNTRUSTED;
+ else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_RID)
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_LOW;
+ else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_PLUS_RID)
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM;
+ else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_HIGH_RID)
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM_PLUS;
+ else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_SYSTEM_RID)
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_HIGH;
+ else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_SYSTEM;
+ else
+ fRequestor |= VMMDEV_REQUESTOR_TRUST_PROTECTED;
+ Log5(("vgdrvNtCalcRequestorFlags: mandatory label %u: => %#x\n", pSid->SubAuthority[0], fRequestor));
+ }
+ else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
+ == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
+ && vgdrvNtIsSidConsoleLogonGroup(pSid))
+ {
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_YES;
+ Log5(("vgdrvNtCalcRequestorFlags: console: => %#x\n", fRequestor));
+ }
+ else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
+ == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
+ && vgdrvNtIsSidNtAuth(pSid))
+ {
+ if (vgdrvNtIsSidAdminsGroup(pSid))
+ {
+ fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
+ Log5(("vgdrvNtCalcRequestorFlags: admins group: => %#x\n", fRequestor));
+ }
+ else if (vgdrvNtIsSidUsersGroup(pSid))
+ {
+ Log5(("vgdrvNtCalcRequestorFlags: users group\n"));
+ fUsersMember = true;
+ }
+ else if (vgdrvNtIsSidGuestsGroup(pSid))
+ {
+ Log5(("vgdrvNtCalcRequestorFlags: guests group\n"));
+ fGuestsMember = true;
+ }
+ }
+ }
+ if ((fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_NOT_GIVEN)
+ {
+ if (fUsersMember)
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_USER;
+ else if (fGuestsMember)
+ fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
+ }
+ }
+ else
+ LogRel(("vgdrvNtCalcRequestorFlags: TokenGroups query failed: %#x\n", rcNt));
+
+ RTMemTmpFree(pCurGroupsFree);
+ ZwClose(hToken);
+
+ /*
+ * Determine whether we should set VMMDEV_REQUESTOR_USER_DEVICE or not.
+ *
+ * The purpose here is to differentiate VBoxService accesses
+ * from VBoxTray and VBoxControl, as VBoxService should be allowed to
+ * do more than the latter two. VBoxService normally runs under the
+ * system account which is easily detected, but for debugging and
+ * similar purposes we also allow an elevated admin to run it as well.
+ */
+ if ( (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_UNTRUSTED /* general paranoia wrt system account */
+ || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_LOW /* ditto */
+ || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_MEDIUM /* ditto */
+ || !( (fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_SYSTEM
+ || ( ( (fRequestor & VMMDEV_REQUESTOR_GRP_WHEEL)
+ || (fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_ROOT)
+ && ( (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) >= VMMDEV_REQUESTOR_TRUST_HIGH
+ || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)) ))
+ fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
+ }
+ else
+ {
+ LogRel(("vgdrvNtCalcRequestorFlags: NtOpenProcessToken query failed: %#x\n", rcNt));
+ fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
+ }
+
+ Log5(("vgdrvNtCalcRequestorFlags: returns %#x\n", fRequestor));
+ return fRequestor;
+}
+
+
+/**
+ * Create (i.e. Open) file entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+
+ Assert(pFileObj->FsContext == NULL);
+
+ /*
+ * We are not remotely similar to a directory...
+ */
+ NTSTATUS rcNt;
+ if (!(pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE))
+ {
+ /*
+ * Check the device state. We enter the critsect in shared mode to
+ * prevent race with PnP system requests checking whether we're idle.
+ */
+ RTCritSectRwEnterShared(&pDevExt->SessionCreateCritSect);
+ VGDRVNTDEVSTATE const enmDevState = pDevExt->enmDevState;
+ if (enmDevState == VGDRVNTDEVSTATE_OPERATIONAL)
+ {
+ /*
+ * Create a client session.
+ */
+ int rc;
+ PVBOXGUESTSESSION pSession;
+ if (pIrp->RequestorMode == KernelMode)
+ rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
+ else
+ rc = VGDrvCommonCreateUserSession(&pDevExt->Core, vgdrvNtCalcRequestorFlags(), &pSession);
+ RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pSession;
+ Log(("vgdrvNtCreate: Successfully created %s session %p (fRequestor=%#x)\n",
+ pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession, pSession->fRequestor));
+
+ return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+
+ /* Note. the IoStatus is completely ignored on error. */
+ Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
+ if (rc == VERR_NO_MEMORY)
+ rcNt = STATUS_NO_MEMORY;
+ else
+ rcNt = STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
+ LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", enmDevState));
+ rcNt = STATUS_DEVICE_NOT_READY;
+ }
+ }
+ else
+ {
+ LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
+ rcNt = STATUS_NOT_A_DIRECTORY;
+ }
+ return vgdrvNtCompleteRequest(rcNt, pIrp);
+}
+
+
+/**
+ * Close file entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+
+ LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
+
+#ifdef VBOX_WITH_HGCM
+ /* Close both, R0 and R3 sessions. */
+ PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
+ if (pSession)
+ VGDrvCommonCloseSession(&pDevExt->Core, pSession);
+#endif
+
+ pFileObj->FsContext = NULL;
+ pIrp->IoStatus.Information = 0;
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
+
+ if (!RT_VALID_PTR(pSession))
+ return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
+
+#if 0 /* No fast I/O controls defined yet. */
+ /*
+ * Deal with the 2-3 high-speed IOCtl that takes their arguments from
+ * the session and iCmd, and does not return anything.
+ */
+ if (pSession->fUnrestricted)
+ {
+ ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
+ if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
+ || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
+ || ulCmd == SUP_IOCTL_FAST_DO_NOP)
+ {
+ int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
+
+ /* Complete the I/O request. */
+ supdrvSessionRelease(pSession);
+ return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
+ }
+ }
+#endif
+
+ return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
+}
+
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The session.
+ * @param pIrp Request packet.
+ * @param pStack The request stack pointer.
+ */
+static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
+ PIRP pIrp, PIO_STACK_LOCATION pStack)
+{
+ NTSTATUS rcNt;
+ uint32_t cbOut = 0;
+ int rc = 0;
+ Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
+ pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
+
+#if 0 /*def RT_ARCH_AMD64*/
+ /* Don't allow 32-bit processes to do any I/O controls. */
+ if (!IoIs32bitProcess(pIrp))
+#endif
+ {
+ /* Verify that it's a buffered CTL. */
+ if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
+ {
+ /* Verify that the sizes in the request header are correct. */
+ PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
+ if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
+ && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
+ && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
+ {
+ /* Zero extra output bytes to make sure we don't leak anything. */
+ if (pHdr->cbIn < pHdr->cbOut)
+ RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
+
+ /*
+ * Do the job.
+ */
+ rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
+ RT_MAX(pHdr->cbIn, pHdr->cbOut));
+ if (RT_SUCCESS(rc))
+ {
+ rcNt = STATUS_SUCCESS;
+ cbOut = pHdr->cbOut;
+ if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
+ {
+ cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
+ LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
+ pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
+ }
+
+ /* If IDC successful disconnect request, we must set the context pointer to NULL. */
+ if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
+ && RT_SUCCESS(pHdr->rc))
+ pStack->FileObject->FsContext = NULL;
+ }
+ else if (rc == VERR_NOT_SUPPORTED)
+ rcNt = STATUS_NOT_SUPPORTED;
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
+ }
+ else
+ {
+ Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ }
+#if 0 /*def RT_ARCH_AMD64*/
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+#endif
+
+ return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
+}
+
+
+/**
+ * Internal Device I/O Control entry point (for IDC).
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ /* Currently no special code here. */
+ return vgdrvNtDeviceControl(pDevObj, pIrp);
+}
+
+
+/**
+ * IRP_MJ_SHUTDOWN handler.
+ *
+ * @returns NT status code
+ * @param pDevObj Device object.
+ * @param pIrp IRP.
+ */
+static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ LogFlowFuncEnter();
+
+ VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
+ if (pReq)
+ {
+ pReq->header.requestType = VMMDevReq_SetPowerStatus;
+ pReq->powerState = VMMDevPowerState_PowerOff;
+
+ int rc = VbglR0GRPerform(&pReq->header);
+ if (RT_FAILURE(rc))
+ LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
+ }
+
+ /* just in case, since we shouldn't normally get here. */
+ pIrp->IoStatus.Information = 0;
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Stub function for functions we don't implemented.
+ *
+ * @returns STATUS_NOT_SUPPORTED
+ * @param pDevObj Device object.
+ * @param pIrp IRP.
+ */
+static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ LogFlowFuncEnter();
+
+ pIrp->IoStatus.Information = 0;
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+/**
+ * Bug check callback (KBUGCHECK_CALLBACK_ROUTINE).
+ *
+ * This adds a log entry on the host, in case Hyper-V isn't active or the guest
+ * is too old for reporting it itself via the crash MSRs.
+ *
+ * @param pvBuffer Not used.
+ * @param cbBuffer Not used.
+ */
+static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer)
+{
+ if (g_pauKiBugCheckData)
+ {
+ RTLogBackdoorPrintf("VBoxGuest: BugCheck! P0=%#zx P1=%#zx P2=%#zx P3=%#zx P4=%#zx\n", g_pauKiBugCheckData[0],
+ g_pauKiBugCheckData[1], g_pauKiBugCheckData[2], g_pauKiBugCheckData[3], g_pauKiBugCheckData[4]);
+
+ VMMDevReqNtBugCheck *pReq = NULL;
+ int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_NtBugCheck);
+ if (RT_SUCCESS(rc))
+ {
+ pReq->uBugCheck = g_pauKiBugCheckData[0];
+ pReq->auParameters[0] = g_pauKiBugCheckData[1];
+ pReq->auParameters[1] = g_pauKiBugCheckData[2];
+ pReq->auParameters[2] = g_pauKiBugCheckData[3];
+ pReq->auParameters[3] = g_pauKiBugCheckData[4];
+ VbglR0GRPerform(&pReq->header);
+ VbglR0GRFree(&pReq->header);
+ }
+ }
+ else
+ {
+ RTLogBackdoorPrintf("VBoxGuest: BugCheck!\n");
+
+ VMMDevRequestHeader *pReqHdr = NULL;
+ int rc = VbglR0GRAlloc(&pReqHdr, sizeof(*pReqHdr), VMMDevReq_NtBugCheck);
+ if (RT_SUCCESS(rc))
+ {
+ VbglR0GRPerform(pReqHdr);
+ VbglR0GRFree(pReqHdr);
+ }
+ }
+
+ RT_NOREF(pvBuffer, cbBuffer);
+}
+
+
+/**
+ * Sets the mouse notification callback.
+ *
+ * @returns VBox status code.
+ * @param pDevExt Pointer to the device extension.
+ * @param pNotify Pointer to the mouse notify struct.
+ */
+int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
+{
+ PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
+ /* we need a lock here to avoid concurrency with the set event functionality */
+ KIRQL OldIrql;
+ KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
+ pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
+ pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
+ KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * DPC handler.
+ *
+ * @param pDPC DPC descriptor.
+ * @param pDevObj Device object.
+ * @param pIrp Interrupt request packet.
+ * @param pContext Context specific pointer.
+ */
+static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
+{
+ RT_NOREF3(pDPC, pIrp, pContext);
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
+ Log3Func(("pDevExt=0x%p\n", pDevExt));
+
+ /* Test & reset the counter. */
+ if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
+ {
+ /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
+ * i.e. to prevent the event from destroyed while we're using it */
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
+
+ if (pDevExt->Core.pfnMouseNotifyCallback)
+ pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
+
+ KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
+ }
+
+ /* Process the wake-up list we were asked by the scheduling a DPC
+ * in vgdrvNtIsrHandler(). */
+ VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
+}
+
+
+/**
+ * ISR handler.
+ *
+ * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
+ * @param pInterrupt Interrupt that was triggered.
+ * @param pServiceContext Context specific pointer.
+ */
+static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
+{
+ RT_NOREF1(pInterrupt);
+ PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
+ if (pDevExt == NULL)
+ return FALSE;
+
+ /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
+
+ /* Enter the common ISR routine and do the actual work. */
+ BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
+
+ /* If we need to wake up some events we do that in a DPC to make
+ * sure we're called at the right IRQL. */
+ if (fIRQTaken)
+ {
+ Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
+ if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
+ || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
+ {
+ Log3Func(("Requesting DPC...\n"));
+ IoRequestDpc(pDevExt->pDeviceObject, NULL /*pIrp*/, NULL /*pvContext*/);
+ }
+ }
+ return fIRQTaken;
+}
+
+
+void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
+{
+ NOREF(pDevExt);
+ /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
+ * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
+ * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
+}
+
+
+/**
+ * Hook for handling OS specfic options from the host.
+ *
+ * @returns true if handled, false if not.
+ * @param pDevExt The device extension.
+ * @param pszName The option name.
+ * @param pszValue The option value.
+ */
+bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
+{
+ RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
+ return false;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
+ */
+static NTSTATUS NTAPI vgdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ Log4(("vgdrvNtRegistryEnumCallback: pwszValueName=%ls uValueType=%#x Value=%.*Rhxs\n", pwszValueName, uValueType, cbValue, pvValue));
+
+ /*
+ * Filter out general service config values.
+ */
+ if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
+ || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
+ )
+ {
+ return STATUS_SUCCESS;
+ }
+
+ /*
+ * Convert the value name.
+ */
+ size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
+ if (cch < 64 && cch > 0)
+ {
+ char szValueName[72];
+ char *pszTmp = szValueName;
+ int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert the value.
+ */
+ char szValue[72];
+ char *pszFree = NULL;
+ char *pszValue = NULL;
+ szValue[0] = '\0';
+ switch (uValueType)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
+ if (RT_SUCCESS(rc) && cch < _1K)
+ {
+ if (cch < sizeof(szValue))
+ {
+ pszValue = szValue;
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
+ }
+ else
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
+ if (RT_SUCCESS(rc))
+ pszFree = pszValue;
+ }
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
+ pwszValueName, rc));
+ pszValue = NULL;
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
+ pwszValueName, cbValue, uValueType));
+ else
+ LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
+ pwszValueName, cbValue, uValueType));
+ break;
+
+ case REG_DWORD:
+ if (cbValue == sizeof(uint32_t))
+ {
+ RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
+ pszValue = szValue;
+ }
+ else
+ LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
+ break;
+
+ case REG_QWORD:
+ if (cbValue == sizeof(uint64_t))
+ {
+ RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
+ pszValue = szValue;
+ }
+ else
+ LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
+ break;
+
+ default:
+ LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
+ break;
+ }
+ if (pszValue)
+ {
+ /*
+ * Process it.
+ */
+ PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
+ VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
+ if (pszFree)
+ RTStrFree(pszFree);
+ }
+ }
+ }
+ else if (cch > 0)
+ LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
+ else
+ LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
+ NOREF(pvEntryCtx);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Reads configuration from the registry and guest properties.
+ *
+ * We ignore failures and instead preserve existing configuration values.
+ *
+ * Thie routine will block.
+ *
+ * @param pDevExt The device extension.
+ */
+static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
+{
+ /*
+ * First the registry.
+ *
+ * Note! RTL_QUERY_REGISTRY_NOEXPAND is sensible (no environment) and also necessary to
+ * avoid crash on NT 3.1 because RtlExpandEnvironmentStrings_U thinks its in ring-3
+ * and tries to get the default heap from the PEB via the TEB. No TEB in ring-0.
+ */
+ RTL_QUERY_REGISTRY_TABLE aQuery[2];
+ RT_ZERO(aQuery);
+ aQuery[0].QueryRoutine = vgdrvNtRegistryEnumCallback;
+ aQuery[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
+ aQuery[0].Name = NULL;
+ aQuery[0].EntryContext = NULL;
+ aQuery[0].DefaultType = REG_NONE;
+ NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /*pwszzEnv*/);
+ if (!NT_SUCCESS(rcNt))
+ LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
+
+ /*
+ * Read configuration from the host.
+ */
+ VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
+}
+
+#ifdef VBOX_STRICT
+
+/**
+ * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
+ */
+static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
+{
+ AssertPtrReturn(pu32Bits, 0);
+ LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
+ uint32_t u32Result = 0;
+ uint32_t u32WorkingMask = u32Mask;
+ int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
+
+ while (iBitOffset > 0)
+ {
+ bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
+ if (fSet)
+ u32Result |= 1 << (iBitOffset - 1);
+ u32WorkingMask &= ~(1 << (iBitOffset - 1));
+ iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
+ }
+ LogFlowFunc(("Returning %#x\n", u32Result));
+ return u32Result;
+}
+
+
+static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
+{
+ ULONG u32Bits2 = u32Bits;
+ uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
+ if ( u32Result != u32Exp
+ || (u32Bits2 & u32Mask)
+ || (u32Bits2 & u32Result)
+ || ((u32Bits2 | u32Result) != u32Bits)
+ )
+ AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
+ u32Mask, u32Bits, u32Bits2, u32Result));
+}
+
+
+static void vgdrvNtDoTests(void)
+{
+ vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
+ vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
+ vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
+ vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
+ vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
+ vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
+}
+
+#endif /* VBOX_STRICT */
+
+#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
+
+/*
+ * DPC latency checker.
+ */
+
+/**
+ * One DPC latency sample.
+ */
+typedef struct DPCSAMPLE
+{
+ LARGE_INTEGER PerfDelta;
+ LARGE_INTEGER PerfCounter;
+ LARGE_INTEGER PerfFrequency;
+ uint64_t u64TSC;
+} DPCSAMPLE;
+AssertCompileSize(DPCSAMPLE, 4*8);
+
+/**
+ * The DPC latency measurement workset.
+ */
+typedef struct DPCDATA
+{
+ KDPC Dpc;
+ KTIMER Timer;
+ KSPIN_LOCK SpinLock;
+
+ ULONG ulTimerRes;
+
+ bool volatile fFinished;
+
+ /** The timer interval (relative). */
+ LARGE_INTEGER DueTime;
+
+ LARGE_INTEGER PerfCounterPrev;
+
+ /** Align the sample array on a 64 byte boundrary just for the off chance
+ * that we'll get cache line aligned memory backing this structure. */
+ uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
+
+ int cSamples;
+ DPCSAMPLE aSamples[8192];
+} DPCDATA;
+
+AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
+
+/**
+ * DPC callback routine for the DPC latency measurement code.
+ *
+ * @param pDpc The DPC, not used.
+ * @param pvDeferredContext Pointer to the DPCDATA.
+ * @param SystemArgument1 System use, ignored.
+ * @param SystemArgument2 System use, ignored.
+ */
+static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
+{
+ DPCDATA *pData = (DPCDATA *)pvDeferredContext;
+ RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
+
+ KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
+
+ if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
+ pData->fFinished = true;
+ else
+ {
+ DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
+
+ pSample->u64TSC = ASMReadTSC();
+ pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
+ pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
+
+ pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
+
+ KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
+}
+
+
+/**
+ * Handles the DPC latency checker request.
+ *
+ * @returns VBox status code.
+ */
+int VGDrvNtIOCtl_DpcLatencyChecker(void)
+{
+ /*
+ * Allocate a block of non paged memory for samples and related data.
+ */
+ DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
+ if (!pData)
+ {
+ RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Initialize the data.
+ */
+ KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
+ KeInitializeTimer(&pData->Timer);
+ KeInitializeSpinLock(&pData->SpinLock);
+
+ pData->fFinished = false;
+ pData->cSamples = 0;
+ pData->PerfCounterPrev.QuadPart = 0;
+
+ pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
+ pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
+
+ /*
+ * Start the DPC measurements and wait for a full set.
+ */
+ KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
+
+ while (!pData->fFinished)
+ {
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = -100 * 1000 * 10;
+ KeDelayExecutionThread(KernelMode, TRUE, &Interval);
+ }
+
+ ExSetTimerResolution(0, 0);
+
+ /*
+ * Log everything to the host.
+ */
+ RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
+ for (int i = 0; i < pData->cSamples; i++)
+ {
+ DPCSAMPLE *pSample = &pData->aSamples[i];
+
+ RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
+ i,
+ pSample->PerfDelta.QuadPart,
+ pSample->PerfCounter.QuadPart,
+ pSample->PerfFrequency.QuadPart,
+ pSample->u64TSC);
+ }
+
+ RTMemFree(pData);
+ return VINF_SUCCESS;
+}
+
+#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
+