/* $Id: VBoxGuest-win.cpp 100267 2023-06-23 14:57:53Z vboxsync $ */ /** @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 . * * 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 #include "VBoxGuestInternal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TARGET_NT4 # include # define PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS32_IPRT # include # include 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, NULL /*pvMmioReq*/, 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 */