From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp | 534 ++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp (limited to 'src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp') diff --git a/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp new file mode 100644 index 00000000..2d9e9a52 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp @@ -0,0 +1,534 @@ +/* $Id: initterm-r0drv-nt.cpp $ */ +/** @file + * IPRT - Initialization & Termination, R0 Driver, NT. + */ + +/* + * Copyright (C) 2006-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 * +*********************************************************************************************************************************/ +#include "the-nt-kernel.h" +#include +#include +#include +#include +#include "internal/initterm.h" +#include "internal-r0drv-nt.h" +#include "symdb.h" +#include "symdbdata.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** ExAllocatePoolWithTag, introduced in W2K. */ +decltype(ExAllocatePoolWithTag) *g_pfnrtExAllocatePoolWithTag; +/** ExFreePoolWithTag, introduced in W2K. */ +decltype(ExFreePoolWithTag) *g_pfnrtExFreePoolWithTag; +/** ExSetTimerResolution, introduced in W2K. */ +PFNMYEXSETTIMERRESOLUTION g_pfnrtNtExSetTimerResolution; +/** ExAllocateTimer, introduced in Windows 8.1 */ +PFNEXALLOCATETIMER g_pfnrtExAllocateTimer; +/** ExDeleteTimer, introduced in Windows 8.1 */ +PFNEXDELETETIMER g_pfnrtExDeleteTimer; +/** ExSetTimer, introduced in Windows 8.1 */ +PFNEXSETTIMER g_pfnrtExSetTimer; +/** ExCancelTimer, introduced in Windows 8.1 */ +PFNEXCANCELTIMER g_pfnrtExCancelTimer; +/** KeFlushQueuedDpcs, introduced in XP. */ +PFNMYKEFLUSHQUEUEDDPCS g_pfnrtNtKeFlushQueuedDpcs; +/** HalRequestIpi, version introduced with windows 7. */ +PFNHALREQUESTIPI_W7PLUS g_pfnrtHalRequestIpiW7Plus; +/** HalRequestIpi, version valid up to windows vista?? */ +PFNHALREQUESTIPI_PRE_W7 g_pfnrtHalRequestIpiPreW7; +/** Worker for RTMpPokeCpu. */ +PFNRTSENDIPI g_pfnrtMpPokeCpuWorker; +/** KeIpiGenericCall - Introduced in Windows Server 2003. */ +PFNRTKEIPIGENERICCALL g_pfnrtKeIpiGenericCall; +/** KeSetTargetProcessorDpcEx - Introduced in Windows 7. */ +PFNKESETTARGETPROCESSORDPCEX g_pfnrtKeSetTargetProcessorDpcEx; +/** KeInitializeAffinityEx - Introducted in Windows 7. */ +PFNKEINITIALIZEAFFINITYEX g_pfnrtKeInitializeAffinityEx; +/** KeAddProcessorAffinityEx - Introducted in Windows 7. */ +PFNKEADDPROCESSORAFFINITYEX g_pfnrtKeAddProcessorAffinityEx; +/** KeGetProcessorIndexFromNumber - Introducted in Windows 7. */ +PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnrtKeGetProcessorIndexFromNumber; +/** KeGetProcessorNumberFromIndex - Introducted in Windows 7. */ +PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnrtKeGetProcessorNumberFromIndex; +/** KeGetCurrentProcessorNumberEx - Introducted in Windows 7. */ +PFNKEGETCURRENTPROCESSORNUMBEREX g_pfnrtKeGetCurrentProcessorNumberEx; +/** KeQueryActiveProcessors - Introducted in Windows 2000. */ +PFNKEQUERYACTIVEPROCESSORS g_pfnrtKeQueryActiveProcessors; +/** KeQueryMaximumProcessorCount - Introducted in Vista and obsoleted W7. */ +PFNKEQUERYMAXIMUMPROCESSORCOUNT g_pfnrtKeQueryMaximumProcessorCount; +/** KeQueryMaximumProcessorCountEx - Introducted in Windows 7. */ +PFNKEQUERYMAXIMUMPROCESSORCOUNTEX g_pfnrtKeQueryMaximumProcessorCountEx; +/** KeQueryMaximumGroupCount - Introducted in Windows 7. */ +PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnrtKeQueryMaximumGroupCount; +/** KeQueryActiveProcessorCount - Introducted in Vista and obsoleted W7. */ +PFNKEQUERYACTIVEPROCESSORCOUNT g_pfnrtKeQueryActiveProcessorCount; +/** KeQueryActiveProcessorCountEx - Introducted in Windows 7. */ +PFNKEQUERYACTIVEPROCESSORCOUNTEX g_pfnrtKeQueryActiveProcessorCountEx; +/** KeQueryLogicalProcessorRelationship - Introducted in Windows 7. */ +PFNKEQUERYLOGICALPROCESSORRELATIONSHIP g_pfnrtKeQueryLogicalProcessorRelationship; +/** KeRegisterProcessorChangeCallback - Introducted in Windows 7. */ +PFNKEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeRegisterProcessorChangeCallback; +/** KeDeregisterProcessorChangeCallback - Introducted in Windows 7. */ +PFNKEDEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeDeregisterProcessorChangeCallback; +/** KeSetImportanceDpc - Introducted in NT 3.51. */ +decltype(KeSetImportanceDpc) *g_pfnrtKeSetImportanceDpc; +/** KeSetTargetProcessorDpc - Introducted in NT 3.51. */ +decltype(KeSetTargetProcessorDpc) *g_pfnrtKeSetTargetProcessorDpc; +/** KeInitializeTimerEx - Introduced in NT 4. */ +decltype(KeInitializeTimerEx) *g_pfnrtKeInitializeTimerEx; +/** KeShouldYieldProcessor - Introduced in Windows 10. */ +PFNKESHOULDYIELDPROCESSOR g_pfnrtKeShouldYieldProcessor; +/** Pointer to the MmProtectMdlSystemAddress kernel function if it's available. + * This API was introduced in XP. */ +decltype(MmProtectMdlSystemAddress) *g_pfnrtMmProtectMdlSystemAddress; +/** MmAllocatePagesForMdl - Introduced in Windows 2000. */ +decltype(MmAllocatePagesForMdl) *g_pfnrtMmAllocatePagesForMdl; +/** MmAllocatePagesForMdlEx - Introduced in Windows Server 2003 SP1. */ +PFNMMALLOCATEPAGESFORMDLEX g_pfnrtMmAllocatePagesForMdlEx; +/** MmFreePagesFromMdl - Introduced in Windows 2000. */ +decltype(MmFreePagesFromMdl) *g_pfnrtMmFreePagesFromMdl; +/** MmMapLockedPagesSpecifyCache - Introduced in Windows NT4 SP4. */ +decltype(MmMapLockedPagesSpecifyCache) *g_pfnrtMmMapLockedPagesSpecifyCache; +/** MmAllocateContiguousMemorySpecifyCache - Introduced in Windows 2000. */ +decltype(MmAllocateContiguousMemorySpecifyCache) *g_pfnrtMmAllocateContiguousMemorySpecifyCache; +/** MmSecureVirtualMemory - Introduced in NT 3.51. */ +decltype(MmSecureVirtualMemory) *g_pfnrtMmSecureVirtualMemory; +/** MmUnsecureVirtualMemory - Introduced in NT 3.51. */ +decltype(MmUnsecureVirtualMemory) *g_pfnrtMmUnsecureVirtualMemory; +/** PsIsThreadTerminating - Introduced in NT 3.50. */ +decltype(PsIsThreadTerminating) *g_pfnrtPsIsThreadTerminating; +/** RtlGetVersion, introduced in ??. */ +PFNRTRTLGETVERSION g_pfnrtRtlGetVersion; +#ifdef RT_ARCH_X86 +/** KeQueryInterruptTime - exported/new in Windows 2000. */ +PFNRTKEQUERYINTERRUPTTIME g_pfnrtKeQueryInterruptTime; +#endif +/** KeQueryInterruptTimePrecise - new in Windows 8. */ +PFNRTKEQUERYINTERRUPTTIMEPRECISE g_pfnrtKeQueryInterruptTimePrecise; +/** KeQuerySystemTimePrecise - new in Windows 8. */ +PFNRTKEQUERYSYSTEMTIMEPRECISE g_pfnrtKeQuerySystemTimePrecise; + +/** Offset of the _KPRCB::QuantumEnd field. 0 if not found. */ +uint32_t g_offrtNtPbQuantumEnd; +/** Size of the _KPRCB::QuantumEnd field. 0 if not found. */ +uint32_t g_cbrtNtPbQuantumEnd; +/** Offset of the _KPRCB::DpcQueueDepth field. 0 if not found. */ +uint32_t g_offrtNtPbDpcQueueDepth; + +/** The combined NT version, see RTNT_MAKE_VERSION. */ +uint32_t g_uRtNtVersion = RTNT_MAKE_VERSION(4, 0); +/** The major version number. */ +uint8_t g_uRtNtMajorVer; +/** The minor version number. */ +uint8_t g_uRtNtMinorVer; +/** The build number. */ +uint32_t g_uRtNtBuildNo; + +/** Pointer to the MmHighestUserAddress kernel variable - can be NULL. */ +uintptr_t const *g_puRtMmHighestUserAddress; +/** Pointer to the MmSystemRangeStart kernel variable - can be NULL. */ +uintptr_t const *g_puRtMmSystemRangeStart; + + +/** + * Determines the NT kernel verison information. + * + * @param pOsVerInfo Where to return the version information. + * + * @remarks pOsVerInfo->fSmp is only definitive if @c true. + * @remarks pOsVerInfo->uCsdNo is set to MY_NIL_CSD if it cannot be determined. + */ +static void rtR0NtGetOsVersionInfo(PRTNTSDBOSVER pOsVerInfo) +{ + ULONG ulMajorVersion = 0; + ULONG ulMinorVersion = 0; + ULONG ulBuildNumber = 0; + + pOsVerInfo->fChecked = PsGetVersion(&ulMajorVersion, &ulMinorVersion, &ulBuildNumber, NULL) == TRUE; + pOsVerInfo->uMajorVer = (uint8_t)ulMajorVersion; + pOsVerInfo->uMinorVer = (uint8_t)ulMinorVersion; + pOsVerInfo->uBuildNo = ulBuildNumber; +#define MY_NIL_CSD 0x3f + pOsVerInfo->uCsdNo = MY_NIL_CSD; + + if (g_pfnrtRtlGetVersion) + { + RTL_OSVERSIONINFOEXW VerInfo; + RT_ZERO(VerInfo); + VerInfo.dwOSVersionInfoSize = sizeof(VerInfo); + + NTSTATUS rcNt = g_pfnrtRtlGetVersion(&VerInfo); + if (NT_SUCCESS(rcNt)) + pOsVerInfo->uCsdNo = VerInfo.wServicePackMajor; + } + + /* Note! We cannot quite say if something is MP or UNI. So, fSmp is + redefined to indicate that it must be MP. + Note! RTMpGetCount is not available here. */ + pOsVerInfo->fSmp = ulMajorVersion >= 6; /* Vista and later has no UNI kernel AFAIK. */ + if (!pOsVerInfo->fSmp) + { + if ( g_pfnrtKeQueryMaximumProcessorCountEx + && g_pfnrtKeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS) > 1) + pOsVerInfo->fSmp = true; + else if ( g_pfnrtKeQueryMaximumProcessorCount + && g_pfnrtKeQueryMaximumProcessorCount() > 1) + pOsVerInfo->fSmp = true; + else if ( g_pfnrtKeQueryActiveProcessors + && g_pfnrtKeQueryActiveProcessors() > 1) + pOsVerInfo->fSmp = true; + else if (KeNumberProcessors > 1) + pOsVerInfo->fSmp = true; + } +} + + +/** + * Tries a set against the current kernel. + * + * @retval true if it matched up, global variables are updated. + * @retval false otherwise (no globals updated). + * @param pSet The data set. + * @param pbPrcb Pointer to the processor control block. + * @param pszVendor Pointer to the processor vendor string. + * @param pOsVerInfo The OS version info. + */ +static bool rtR0NtTryMatchSymSet(PCRTNTSDBSET pSet, uint8_t *pbPrcb, const char *pszVendor, PCRTNTSDBOSVER pOsVerInfo) +{ + /* + * Don't bother trying stuff where the NT kernel version number differs, or + * if the build type or SMPness doesn't match up. + */ + if ( pSet->OsVerInfo.uMajorVer != pOsVerInfo->uMajorVer + || pSet->OsVerInfo.uMinorVer != pOsVerInfo->uMinorVer + || pSet->OsVerInfo.fChecked != pOsVerInfo->fChecked + || (!pSet->OsVerInfo.fSmp && pOsVerInfo->fSmp /*must-be-smp*/) ) + { + //DbgPrint("IPRT: #%d Version/type mismatch.\n", pSet - &g_artNtSdbSets[0]); + return false; + } + + /* + * Do the CPU vendor test. + * + * Note! The MmIsAddressValid call is the real #PF security here as the + * __try/__except has limited/no ability to catch everything we need. + */ + char *pszPrcbVendorString = (char *)&pbPrcb[pSet->KPRCB.offVendorString]; + if (!MmIsAddressValid(&pszPrcbVendorString[4 * 3 - 1])) + { + //DbgPrint("IPRT: #%d invalid vendor string address.\n", pSet - &g_artNtSdbSets[0]); + return false; + } + __try + { + if (memcmp(pszPrcbVendorString, pszVendor, RT_MIN(4 * 3, pSet->KPRCB.cbVendorString)) != 0) + { + //DbgPrint("IPRT: #%d Vendor string mismatch.\n", pSet - &g_artNtSdbSets[0]); + return false; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + DbgPrint("IPRT: %#d Exception\n", pSet - &g_artNtSdbSets[0]); + return false; + } + + /* + * Got a match, update the global variables and report success. + */ + g_offrtNtPbQuantumEnd = pSet->KPRCB.offQuantumEnd; + g_cbrtNtPbQuantumEnd = pSet->KPRCB.cbQuantumEnd; + g_offrtNtPbDpcQueueDepth = pSet->KPRCB.offDpcQueueDepth; + +#if 0 + DbgPrint("IPRT: Using data set #%u for %u.%usp%u build %u %s %s.\n", + pSet - &g_artNtSdbSets[0], + pSet->OsVerInfo.uMajorVer, + pSet->OsVerInfo.uMinorVer, + pSet->OsVerInfo.uCsdNo, + pSet->OsVerInfo.uBuildNo, + pSet->OsVerInfo.fSmp ? "smp" : "uni", + pSet->OsVerInfo.fChecked ? "checked" : "free"); +#endif + return true; +} + + +DECLHIDDEN(int) rtR0InitNative(void) +{ + /* + * Preinitialize g_uRtNtVersion so RTMemAlloc uses the right kind of pool + * when RTR0DbgKrnlInfoOpen calls it. + */ + RTNTSDBOSVER OsVerInfo; + rtR0NtGetOsVersionInfo(&OsVerInfo); + g_uRtNtVersion = RTNT_MAKE_VERSION(OsVerInfo.uMajorVer, OsVerInfo.uMinorVer); + g_uRtNtMinorVer = OsVerInfo.uMinorVer; + g_uRtNtMajorVer = OsVerInfo.uMajorVer; + g_uRtNtBuildNo = OsVerInfo.uBuildNo; + + /* + * Initialize the function pointers. + */ + RTDBGKRNLINFO hKrnlInfo; + int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0/*fFlags*/); + AssertRCReturn(rc, rc); + +#define GET_SYSTEM_ROUTINE_EX(a_Prf, a_Name, a_pfnType) \ + do { RT_CONCAT3(g_pfnrt, a_Prf, a_Name) = (a_pfnType)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_Name); } while (0) +#define GET_SYSTEM_ROUTINE(a_Name) GET_SYSTEM_ROUTINE_EX(RT_NOTHING, a_Name, decltype(a_Name) *) +#define GET_SYSTEM_ROUTINE_PRF(a_Prf,a_Name) GET_SYSTEM_ROUTINE_EX(a_Prf, a_Name, decltype(a_Name) *) +#define GET_SYSTEM_ROUTINE_TYPE(a_Name, a_pfnType) GET_SYSTEM_ROUTINE_EX(RT_NOTHING, a_Name, a_pfnType) + + GET_SYSTEM_ROUTINE(ExAllocatePoolWithTag); + GET_SYSTEM_ROUTINE(ExFreePoolWithTag); + GET_SYSTEM_ROUTINE_PRF(Nt,ExSetTimerResolution); + GET_SYSTEM_ROUTINE_TYPE(ExAllocateTimer, PFNEXALLOCATETIMER); + GET_SYSTEM_ROUTINE_TYPE(ExDeleteTimer, PFNEXDELETETIMER); + GET_SYSTEM_ROUTINE_TYPE(ExSetTimer, PFNEXSETTIMER); + GET_SYSTEM_ROUTINE_TYPE(ExCancelTimer, PFNEXCANCELTIMER); + GET_SYSTEM_ROUTINE_PRF(Nt,KeFlushQueuedDpcs); + GET_SYSTEM_ROUTINE(KeIpiGenericCall); + GET_SYSTEM_ROUTINE(KeSetTargetProcessorDpcEx); + GET_SYSTEM_ROUTINE(KeInitializeAffinityEx); + GET_SYSTEM_ROUTINE(KeAddProcessorAffinityEx); + GET_SYSTEM_ROUTINE_TYPE(KeGetProcessorIndexFromNumber, PFNKEGETPROCESSORINDEXFROMNUMBER); + GET_SYSTEM_ROUTINE(KeGetProcessorNumberFromIndex); + GET_SYSTEM_ROUTINE_TYPE(KeGetCurrentProcessorNumberEx, PFNKEGETCURRENTPROCESSORNUMBEREX); + GET_SYSTEM_ROUTINE(KeQueryActiveProcessors); + GET_SYSTEM_ROUTINE(KeQueryMaximumProcessorCount); + GET_SYSTEM_ROUTINE(KeQueryMaximumProcessorCountEx); + GET_SYSTEM_ROUTINE(KeQueryMaximumGroupCount); + GET_SYSTEM_ROUTINE(KeQueryActiveProcessorCount); + GET_SYSTEM_ROUTINE(KeQueryActiveProcessorCountEx); + GET_SYSTEM_ROUTINE(KeQueryLogicalProcessorRelationship); + GET_SYSTEM_ROUTINE(KeRegisterProcessorChangeCallback); + GET_SYSTEM_ROUTINE(KeDeregisterProcessorChangeCallback); + GET_SYSTEM_ROUTINE(KeSetImportanceDpc); + GET_SYSTEM_ROUTINE(KeSetTargetProcessorDpc); + GET_SYSTEM_ROUTINE(KeInitializeTimerEx); + GET_SYSTEM_ROUTINE_TYPE(KeShouldYieldProcessor, PFNKESHOULDYIELDPROCESSOR); + GET_SYSTEM_ROUTINE(MmProtectMdlSystemAddress); + GET_SYSTEM_ROUTINE(MmAllocatePagesForMdl); + GET_SYSTEM_ROUTINE(MmAllocatePagesForMdlEx); + GET_SYSTEM_ROUTINE(MmFreePagesFromMdl); + GET_SYSTEM_ROUTINE(MmMapLockedPagesSpecifyCache); + GET_SYSTEM_ROUTINE(MmAllocateContiguousMemorySpecifyCache); + GET_SYSTEM_ROUTINE(MmSecureVirtualMemory); + GET_SYSTEM_ROUTINE(MmUnsecureVirtualMemory); + + GET_SYSTEM_ROUTINE_TYPE(RtlGetVersion, PFNRTRTLGETVERSION); +#ifdef RT_ARCH_X86 + GET_SYSTEM_ROUTINE(KeQueryInterruptTime); +#endif + GET_SYSTEM_ROUTINE_TYPE(KeQueryInterruptTimePrecise, PFNRTKEQUERYINTERRUPTTIMEPRECISE); + GET_SYSTEM_ROUTINE_TYPE(KeQuerySystemTimePrecise, PFNRTKEQUERYSYSTEMTIMEPRECISE); + + g_pfnrtHalRequestIpiW7Plus = (PFNHALREQUESTIPI_W7PLUS)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalRequestIpi"); + g_pfnrtHalRequestIpiPreW7 = (PFNHALREQUESTIPI_PRE_W7)g_pfnrtHalRequestIpiW7Plus; + + g_puRtMmHighestUserAddress = (uintptr_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmHighestUserAddress"); + g_puRtMmSystemRangeStart = (uintptr_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmSystemRangeStart"); + +#ifdef RT_ARCH_X86 + rc = rtR0Nt3InitSymbols(hKrnlInfo); + RTR0DbgKrnlInfoRelease(hKrnlInfo); + if (RT_FAILURE(rc)) + return rc; +#else + RTR0DbgKrnlInfoRelease(hKrnlInfo); +#endif + + /* + * Get and publish the definitive NT version. + */ + rtR0NtGetOsVersionInfo(&OsVerInfo); + g_uRtNtVersion = RTNT_MAKE_VERSION(OsVerInfo.uMajorVer, OsVerInfo.uMinorVer); + g_uRtNtMinorVer = OsVerInfo.uMinorVer; + g_uRtNtMajorVer = OsVerInfo.uMajorVer; + g_uRtNtBuildNo = OsVerInfo.uBuildNo; + + + /* + * HACK ALERT! (and déjà vu warning - remember win32k.sys on OS/2?) + * + * Try find _KPRCB::QuantumEnd and _KPRCB::[DpcData.]DpcQueueDepth. + * For purpose of verification we use the VendorString member (12+1 chars). + * + * The offsets was initially derived by poking around with windbg + * (dt _KPRCB, !prcb ++, and such like). Systematic harvesting was then + * planned using dia2dump, grep and the symbol pack in a manner like this: + * dia2dump -type _KDPC_DATA -type _KPRCB EXE\ntkrnlmp.pdb | grep -wE "QuantumEnd|DpcData|DpcQueueDepth|VendorString" + * + * The final solution ended up using a custom harvester program called + * ntBldSymDb that recursively searches thru unpacked symbol packages for + * the desired structure offsets. The program assumes that the packages + * are unpacked into directories with the same name as the package, with + * exception of some of the w2k packages which requires a 'w2k' prefix to + * be distinguishable from another. + */ + + /* + * Gather consistent CPU vendor string and PRCB pointers. + */ + KIRQL OldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); /* make sure we stay on the same cpu */ + + union + { + uint32_t auRegs[4]; + char szVendor[4*3+1]; + } u; + ASMCpuId(0, &u.auRegs[3], &u.auRegs[0], &u.auRegs[2], &u.auRegs[1]); + u.szVendor[4*3] = '\0'; + + uint8_t *pbPrcb; + __try /* Warning. This try/except statement may provide some false safety. */ + { +#if defined(RT_ARCH_X86) + PKPCR pPcr = (PKPCR)__readfsdword(RT_UOFFSETOF(KPCR,SelfPcr)); + pbPrcb = (uint8_t *)pPcr->Prcb; +#elif defined(RT_ARCH_AMD64) + PKPCR pPcr = (PKPCR)__readgsqword(RT_UOFFSETOF(KPCR,Self)); + pbPrcb = (uint8_t *)pPcr->CurrentPrcb; +#else +# error "port me" + pbPrcb = NULL; +#endif + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + pbPrcb = NULL; + } + + /* + * Search the database + */ + if (pbPrcb) + { + /* Find the best matching kernel version based on build number. */ + uint32_t iBest = UINT32_MAX; + int32_t iBestDelta = INT32_MAX; + for (uint32_t i = 0; i < RT_ELEMENTS(g_artNtSdbSets); i++) + { + if (g_artNtSdbSets[i].OsVerInfo.fChecked != OsVerInfo.fChecked) + continue; + if (OsVerInfo.fSmp /*must-be-smp*/ && !g_artNtSdbSets[i].OsVerInfo.fSmp) + continue; + + int32_t iDelta = RT_ABS((int32_t)OsVerInfo.uBuildNo - (int32_t)g_artNtSdbSets[i].OsVerInfo.uBuildNo); + if ( iDelta == 0 + && (g_artNtSdbSets[i].OsVerInfo.uCsdNo == OsVerInfo.uCsdNo || OsVerInfo.uCsdNo == MY_NIL_CSD)) + { + /* prefect */ + iBestDelta = iDelta; + iBest = i; + break; + } + if ( iDelta < iBestDelta + || iBest == UINT32_MAX + || ( iDelta == iBestDelta + && OsVerInfo.uCsdNo != MY_NIL_CSD + && RT_ABS(g_artNtSdbSets[i ].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo) + < RT_ABS(g_artNtSdbSets[iBest].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo) + ) + ) + { + iBestDelta = iDelta; + iBest = i; + } + } + if (iBest < RT_ELEMENTS(g_artNtSdbSets)) + { + /* Try all sets: iBest -> End; iBest -> Start. */ + bool fDone = false; + int32_t i = iBest; + while ( i < RT_ELEMENTS(g_artNtSdbSets) + && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo))) + i++; + if (!fDone) + { + i = (int32_t)iBest - 1; + while ( i >= 0 + && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo))) + i--; + } + } + else + DbgPrint("IPRT: Failed to locate data set.\n"); + } + else + DbgPrint("IPRT: Failed to get PCBR pointer.\n"); + + KeLowerIrql(OldIrql); /* Lowering the IRQL early in the hope that we may catch exceptions below. */ + +#ifndef IN_GUEST + if (!g_offrtNtPbQuantumEnd && !g_offrtNtPbDpcQueueDepth) + DbgPrint("IPRT: Neither _KPRCB::QuantumEnd nor _KPRCB::DpcQueueDepth was not found! Kernel %u.%u %u %s\n", + OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free"); +# ifdef DEBUG + else + DbgPrint("IPRT: _KPRCB:{.QuantumEnd=%x/%d, .DpcQueueDepth=%x/%d} Kernel %u.%u %u %s\n", + g_offrtNtPbQuantumEnd, g_cbrtNtPbQuantumEnd, g_offrtNtPbDpcQueueDepth, g_offrtNtPbDpcQueueDepth, + OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free"); +# endif +#endif + + /* + * Initialize multi processor stuff. This registers a callback, so + * we call rtR0TermNative to do the deregistration on failure. + */ + rc = rtR0MpNtInit(&OsVerInfo); + if (RT_FAILURE(rc)) + { + rtR0TermNative(); + DbgPrint("IPRT: Fatal: rtR0MpNtInit failed: %d\n", rc); + return rc; + } + + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtR0TermNative(void) +{ + rtR0MpNtTerm(); +} + -- cgit v1.2.3