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/Makefile.kup | 0 .../r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp | 49 + .../nt/RTTimerGetSystemGranularity-r0drv-nt.cpp | 71 + src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp | 145 + src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm | 69 + src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp | 76 + src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp | 918 ++++++ src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp | 534 ++++ src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h | 207 ++ src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp | 1284 +++++++++ .../Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp | 210 ++ src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp | 1996 +++++++++++++ src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp | 823 ++++++ .../Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp | 52 + src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm | 157 + src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp | 1232 ++++++++ src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp | 55 + .../Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp | 148 + src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp | 246 ++ src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp | 207 ++ src/VBox/Runtime/r0drv/nt/symdb.h | 98 + src/VBox/Runtime/r0drv/nt/symdbdata.h | 2998 ++++++++++++++++++++ src/VBox/Runtime/r0drv/nt/the-nt-kernel.h | 99 + src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp | 250 ++ src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp | 167 ++ src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp | 159 ++ src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp | 1102 +++++++ .../Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm | 52 + 28 files changed, 13404 insertions(+) create mode 100644 src/VBox/Runtime/r0drv/nt/Makefile.kup create mode 100644 src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm create mode 100644 src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h create mode 100644 src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm create mode 100644 src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/symdb.h create mode 100644 src/VBox/Runtime/r0drv/nt/symdbdata.h create mode 100644 src/VBox/Runtime/r0drv/nt/the-nt-kernel.h create mode 100644 src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp create mode 100644 src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm (limited to 'src/VBox/Runtime/r0drv/nt') diff --git a/src/VBox/Runtime/r0drv/nt/Makefile.kup b/src/VBox/Runtime/r0drv/nt/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp new file mode 100644 index 00000000..8ba622c6 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp @@ -0,0 +1,49 @@ +/* $Id: RTLogWriteDebugger-r0drv-nt.cpp $ */ +/** @file + * IPRT - Log To Debugger, Ring-0 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 + */ + +#include "the-nt-kernel.h" +#include +#include + + +RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb) +{ + if (pch[cb] != '\0') + AssertBreakpoint(); + DbgPrint("%s", pch); + return; +} + diff --git a/src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp new file mode 100644 index 00000000..7afcc823 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp @@ -0,0 +1,71 @@ +/* $Id: RTTimerGetSystemGranularity-r0drv-nt.cpp $ */ +/** @file + * IPRT - RTTimerGetSystemGranularity, Ring-0 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-r0drv-nt.h" +#include "internal/magics.h" + + +RTDECL(uint32_t) RTTimerGetSystemGranularity(void) +{ + /* + * Get the default/max timer increment value, return it if ExSetTimerResolution + * isn't available. According to the sysinternals guys NtQueryTimerResolution + * is only available in userland and they find it equally annoying. + */ + ULONG ulTimeInc = KeQueryTimeIncrement(); + if (!g_pfnrtNtExSetTimerResolution) + return ulTimeInc * 100; /* The value is in 100ns, the funny NT unit. */ + + /* + * Use the value returned by ExSetTimerResolution. Since the kernel is keeping + * count of these calls, we have to do two calls that cancel each other out. + */ + g_pfnrtNtExSetTimerResolution(ulTimeInc, TRUE); + ULONG ulResolution = g_pfnrtNtExSetTimerResolution(0 /*ignored*/, FALSE); + return ulResolution * 100; /* NT -> ns */ +} + diff --git a/src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp new file mode 100644 index 00000000..5d9ae530 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp @@ -0,0 +1,145 @@ +/* $Id: alloc-r0drv-nt.cpp $ */ +/** @file + * IPRT - Memory Allocation, Ring-0 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 "internal/iprt.h" +#include + +#include +#include +#include "r0drv/alloc-r0drv.h" +#include "internal-r0drv-nt.h" + + +/** + * OS specific allocation function. + */ +DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr) +{ + if (!(fFlags & RTMEMHDR_FLAG_ANY_CTX)) + { + PRTMEMHDR pHdr; + POOL_TYPE const enmPoolType = g_uRtNtVersion >= RTNT_MAKE_VERSION(8,0) ? NonPagedPoolNx : NonPagedPool; + if (g_pfnrtExAllocatePoolWithTag) + pHdr = (PRTMEMHDR)g_pfnrtExAllocatePoolWithTag(enmPoolType, cb + sizeof(*pHdr), IPRT_NT_POOL_TAG); + else + { + fFlags |= RTMEMHDR_FLAG_UNTAGGED; + pHdr = (PRTMEMHDR)ExAllocatePool(enmPoolType, cb + sizeof(*pHdr)); + } + if (RT_LIKELY(pHdr)) + { + pHdr->u32Magic = RTMEMHDR_MAGIC; + pHdr->fFlags = fFlags; + pHdr->cb = (uint32_t)cb; Assert(pHdr->cb == cb); + pHdr->cbReq = (uint32_t)cb; + *ppHdr = pHdr; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; + } + return VERR_NOT_SUPPORTED; +} + + +/** + * OS specific free function. + */ +DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr) +{ + pHdr->u32Magic = RTMEMHDR_MAGIC_DEAD; + if (g_pfnrtExFreePoolWithTag && !(pHdr->fFlags & RTMEMHDR_FLAG_UNTAGGED)) + g_pfnrtExFreePoolWithTag(pHdr, IPRT_NT_POOL_TAG); + else + ExFreePool(pHdr); +} + + +RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb) +{ + /* + * validate input. + */ + AssertPtr(pPhys); + Assert(cb > 0); + + /* + * Allocate and get physical address. + * Make sure the return is page aligned. + */ + PHYSICAL_ADDRESS MaxPhysAddr; + MaxPhysAddr.HighPart = 0; + MaxPhysAddr.LowPart = 0xffffffff; + cb = RT_ALIGN_Z(cb, PAGE_SIZE); + void *pv = MmAllocateContiguousMemory(cb, MaxPhysAddr); + if (pv) + { + if (!((uintptr_t)pv & PAGE_OFFSET_MASK)) /* paranoia */ + { + PHYSICAL_ADDRESS PhysAddr = MmGetPhysicalAddress(pv); + if (!PhysAddr.HighPart) /* paranoia */ + { + *pPhys = (RTCCPHYS)PhysAddr.LowPart; + return pv; + } + + /* failure */ + AssertMsgFailed(("MMAllocContiguousMemory returned high address! PhysAddr=%RX64\n", (uint64_t)PhysAddr.QuadPart)); + } + else + AssertMsgFailed(("MMAllocContiguousMemory didn't return a page aligned address - %p!\n", pv)); + + MmFreeContiguousMemory(pv); + } + + return NULL; +} + + +RTR0DECL(void) RTMemContFree(void *pv, size_t cb) +{ + if (pv) + { + Assert(cb > 0); NOREF(cb); + AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv)); + MmFreeContiguousMemory(pv); + } +} + diff --git a/src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm b/src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm new file mode 100644 index 00000000..71d99e9d --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm @@ -0,0 +1,69 @@ +; $Id: alloca-x86-r0drv-nt.asm $ +;; @file +; IPRT - Visual C++ __alloca__probe_16. +; + +; +; Copyright (C) 2020-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 "iprt/asmdefs.mac" + +BEGINCODE + +;; +; @returns esp with eax subtracted and aligned to 16 bytes. +; @param eax allocation size. +; +BEGINPROC _alloca_probe_16 + ; Sanitycheck the input. +%ifdef DEBUG + cmp eax, 0 + jne .not_zero + int3 +.not_zero: + cmp eax, 4096 + jbe .four_kb_or_less + int3 +.four_kb_or_less: +%endif + + ; Don't bother probing the stack as the allocation is supposed to be + ; a lot smaller than 4KB. + neg eax + lea eax, [esp + eax + 4] + and eax, 0fffffff0h + xchg eax, esp + jmp [eax] +ENDPROC _alloca_probe_16 + diff --git a/src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp new file mode 100644 index 00000000..cade19b1 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp @@ -0,0 +1,76 @@ +/* $Id: assert-r0drv-nt.cpp $ */ +/** @file + * IPRT - Assertion Workers, Ring-0 Drivers, 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/assert.h" + + +DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction) +{ + DbgPrint("\n!!Assertion Failed!!\n" + "Expression: %s\n" + "Location : %s(%d) %s\n", + pszExpr, pszFile, uLine, pszFunction); +} + + +DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va) +{ + char szMsg[256]; + + RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va); + szMsg[sizeof(szMsg) - 1] = '\0'; + DbgPrint("%s", szMsg); + + NOREF(fInitial); +} + + +RTR0DECL(void) RTR0AssertPanicSystem(void) +{ + /** @todo implement RTR0AssertPanicSystem. */ +} + diff --git a/src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp new file mode 100644 index 00000000..90e7ef6a --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp @@ -0,0 +1,918 @@ +/* $Id: dbgkrnlinfo-r0drv-nt.cpp $ */ +/** @file + * IPRT - Kernel Debug Information, 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 * +*********************************************************************************************************************************/ +#define IMAGE_NT_HEADERS NT_IMAGE_NT_HEADERS +#define IMAGE_NT_HEADERS32 NT_IMAGE_NT_HEADERS32 +#define IMAGE_NT_HEADERS64 NT_IMAGE_NT_HEADERS64 +#define PIMAGE_NT_HEADERS NT_PIMAGE_NT_HEADERS +#define PIMAGE_NT_HEADERS32 NT_PIMAGE_NT_HEADERS32 +#define PIMAGE_NT_HEADERS64 NT_PIMAGE_NT_HEADERS64 +#ifndef IPRT_NT_MAP_TO_ZW +# define IPRT_NT_MAP_TO_ZW +#endif +#include "the-nt-kernel.h" +#include + +#include +#include +#include +#include +#include +#include "internal-r0drv-nt.h" +#include "internal/magics.h" + +#undef IMAGE_NT_HEADERS +#undef IMAGE_NT_HEADERS32 +#undef IMAGE_NT_HEADERS64 +#undef PIMAGE_NT_HEADERS +#undef PIMAGE_NT_HEADERS32 +#undef PIMAGE_NT_HEADERS64 +#include +#include + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Private logging macro, will use DbgPrint! */ +#ifdef IN_GUEST +# define RTR0DBG_NT_ERROR_LOG(a) do { RTLogBackdoorPrintf a; DbgPrint a; } while (0) +# define RTR0DBG_NT_DEBUG_LOG(a) do { RTLogBackdoorPrintf a; DbgPrint a; } while (0) +#else +# define RTR0DBG_NT_ERROR_LOG(a) do { DbgPrint a; } while (0) +# define RTR0DBG_NT_DEBUG_LOG(a) do { DbgPrint a; } while (0) +#endif +#ifndef LOG_ENABLED +# undef RTR0DBG_NT_DEBUG_LOG +# define RTR0DBG_NT_DEBUG_LOG(a) do { } while (0) +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +#define PIMAGE_NT_HEADERS RT_CONCAT(PIMAGE_NT_HEADERS, ARCH_BITS) + +/** + * Information we cache for a kernel module. + */ +typedef struct RTDBGNTKRNLMODINFO +{ + /** The module name. */ + char szName[32]; + + /** The image base. */ + uint8_t const *pbImageBase; + /** The NT headers. */ + PIMAGE_NT_HEADERS pNtHdrs; + /** Set if this module parsed okay and all fields are valid. */ + bool fOkay; + /** The NT header offset/RVA. */ + uint32_t offNtHdrs; + /** The end of the section headers. */ + uint32_t offEndSectHdrs; + /** The end of the image. */ + uint32_t cbImage; + /** Offset of the export directory. */ + uint32_t offExportDir; + /** Size of the export directory. */ + uint32_t cbExportDir; + + /** Exported functions and data by ordinal (RVAs). */ + uint32_t const *paoffExports; + /** The number of exports. */ + uint32_t cExports; + /** The number of exported names. */ + uint32_t cNamedExports; + /** Pointer to the array of exported names (RVAs to strings). */ + uint32_t const *paoffNamedExports; + /** Array parallel to paoffNamedExports with the corresponding ordinals + * (indexes into paoffExports). */ + uint16_t const *pau16NameOrdinals; +} RTDBGNTKRNLMODINFO; +/** Pointer to kernel module info. */ +typedef RTDBGNTKRNLMODINFO *PRTDBGNTKRNLMODINFO; +/** Pointer to const kernel module info. */ +typedef RTDBGNTKRNLMODINFO const *PCRTDBGNTKRNLMODINFO; + + +/** + * NT kernel info instance. + */ +typedef struct RTDBGKRNLINFOINT +{ + /** Magic value (RTDBGKRNLINFO_MAGIC). */ + uint32_t u32Magic; + /** Reference counter. */ + uint32_t volatile cRefs; + /** Number of additional modules in the cache. */ + uint32_t cModules; + /** Additional modules. */ + RTDBGNTKRNLMODINFO aModules[3]; +} RTDBGKRNLINFOINT; + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Pointer to MmGetSystemRoutineAddress. + * @note Added in NT v5.0. */ +static decltype(MmGetSystemRoutineAddress) *g_pfnMmGetSystemRoutineAddress = NULL; +/** Info about the ntoskrnl.exe mapping. */ +static RTDBGNTKRNLMODINFO g_NtOsKrnlInfo = { "ntoskrnl.exe", NULL, NULL, false, 0, 0, 0, 0, 0, NULL, 0, 0, NULL, NULL }; +/** Info about the hal.dll mapping. */ +static RTDBGNTKRNLMODINFO g_HalInfo = { "hal.dll", NULL, NULL, false, 0, 0, 0, 0, 0, NULL, 0, 0, NULL, NULL }; + + + +/** + * Looks up an symbol int the export table. + * + * @returns VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND. + * @param pModInfo The module info. + * @param pszSymbol The symbol to find. + * @param cForwarders Forwarder nesting depth. + * @param ppvSymbol Where to put the symbol address. + * + * @note Support library has similar code for in the importless area. + */ +static int rtR0DbgKrnlInfoLookupSymbol(PCRTDBGNTKRNLMODINFO pModInfo, const char *pszSymbol, unsigned cForwarders, + void **ppvSymbol) +{ + if (pModInfo->fOkay) + { + /* + * Pseudo symbols: + */ + if ( pszSymbol[0] == '_' + && pszSymbol[1] == '_' + && pszSymbol[2] == 'I') + { + if (strcmp(pszSymbol, "__ImageBase") == 0) + { + *ppvSymbol = (void *)pModInfo->pbImageBase; + return VINF_SUCCESS; + } + if (strcmp(pszSymbol, "__ImageSize") == 0) + { + *ppvSymbol = (void *)(uintptr_t)pModInfo->cbImage; + return VINF_SUCCESS; + } + if (strcmp(pszSymbol, "__ImageNtHdrs") == 0) + { + *ppvSymbol = pModInfo->pNtHdrs; + return VINF_SUCCESS; + } + } + + /* + * Binary search. + */ + __try + { + uint32_t iStart = 0; + uint32_t iEnd = pModInfo->cNamedExports; + while (iStart < iEnd) + { + uint32_t iCur = iStart + (iEnd - iStart) / 2; + uint32_t offExpName = pModInfo->paoffNamedExports[iCur]; + if (offExpName >= pModInfo->offEndSectHdrs && offExpName < pModInfo->cbImage) + { /* likely */ } + else + { + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: %s: Bad export name entry: %#x (iCur=%#x)\n", + pModInfo->szName, offExpName, iCur)); + break; + } + + const char *pszExpName = (const char *)&pModInfo->pbImageBase[offExpName]; + int iDiff = strcmp(pszExpName, pszSymbol); + if (iDiff > 0) /* pszExpName > pszSymbol: search chunck before i */ + iEnd = iCur; + else if (iDiff < 0) /* pszExpName < pszSymbol: search chunk after i */ + iStart = iCur + 1; + else /* pszExpName == pszSymbol */ + { + uint16_t iExpOrdinal = pModInfo->pau16NameOrdinals[iCur]; + if (iExpOrdinal < pModInfo->cExports) + { + uint32_t offExport = pModInfo->paoffExports[iExpOrdinal]; + if (offExport - pModInfo->offExportDir >= pModInfo->cbExportDir) + { + *ppvSymbol = (void *)&pModInfo->pbImageBase[offExport]; + return VINF_SUCCESS; + } + + /* + * Deal with forwarders to NT and HAL. No ordinals. + */ + const char *pszForwarder = (const char *)&pModInfo->pbImageBase[offExport]; + uint32_t cbMax = pModInfo->cbImage - offExpName; + size_t cchForwarder = RTStrNLen(pszForwarder, cbMax); + if (cchForwarder < cbMax) + { + if ( cchForwarder > 9 + && pModInfo != &g_NtOsKrnlInfo + && g_NtOsKrnlInfo.pbImageBase != NULL + && cForwarders < 2 + && (pszForwarder[0] == 'n' || pszForwarder[0] == 'N') + && (pszForwarder[1] == 't' || pszForwarder[1] == 'T') + && (pszForwarder[2] == 'o' || pszForwarder[2] == 'O') + && (pszForwarder[3] == 's' || pszForwarder[3] == 'S') + && (pszForwarder[4] == 'k' || pszForwarder[4] == 'K') + && (pszForwarder[5] == 'r' || pszForwarder[5] == 'R') + && (pszForwarder[6] == 'n' || pszForwarder[6] == 'N') + && (pszForwarder[7] == 'l' || pszForwarder[7] == 'L') + && pszForwarder[8] == '.') + return rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, pszForwarder + 9, cForwarders + 1, ppvSymbol); + + if ( cchForwarder > 4 + && pModInfo != &g_HalInfo + && g_HalInfo.pbImageBase != NULL + && cForwarders < 2 + && (pszForwarder[0] == 'h' || pszForwarder[0] == 'H') + && (pszForwarder[1] == 'a' || pszForwarder[1] == 'A') + && (pszForwarder[2] == 'l' || pszForwarder[2] == 'L') + && pszForwarder[3] == '.') + return rtR0DbgKrnlInfoLookupSymbol(&g_HalInfo, pszForwarder + 4, cForwarders + 1, ppvSymbol); + } + + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: %s: Forwarded symbol '%s': offExport=%#x (dir %#x LB %#x)\n", + pModInfo->szName, pszSymbol, offExport, pModInfo->offExportDir, pModInfo->cbExportDir)); + } + else + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: %s: Name ordinal for '%s' is out of bounds: %#x (max %#x)\n", + pModInfo->szName, iExpOrdinal, pModInfo->cExports)); + break; + } + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: Exception searching '%s' for '%s'...\n", + pModInfo->szName, pszSymbol)); + } + } + + *ppvSymbol = NULL; + return VERR_SYMBOL_NOT_FOUND; +} + + +/** + * Parses (PE) module headers and fills in the coresponding module info struct. + * + * @returns true on if success, false if not. + * @param pModInfo The module info structure to fill in with parsed + * data. The szName and fOkay are set by the + * caller, this function does the rest. + * @param pbMapping The image mapping address + * @param cbMapping The image mapping size. + * + * @note Support library has similar code for in the importless area. + */ +static bool rtR0DbgKrnlNtParseModule(PRTDBGNTKRNLMODINFO pModInfo, uint8_t const *pbMapping, size_t cbMapping) +{ +#define MODERR_RETURN(a_LogMsg, ...) \ + do { RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtParseModule: " a_LogMsg, __VA_ARGS__)); return false; } while (0) + + pModInfo->pbImageBase = pbMapping; + + /* + * Locate the PE header, do some basic validations. + */ + IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbMapping; + uint32_t offNtHdrs = 0; + PIMAGE_NT_HEADERS pNtHdrs; + if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE) + { + offNtHdrs = pMzHdr->e_lfanew; + if (offNtHdrs > _2K) + MODERR_RETURN("%s: e_lfanew=%#x, expected a lower value\n", pModInfo->szName, offNtHdrs); + } + pModInfo->pNtHdrs = pNtHdrs = (PIMAGE_NT_HEADERS)&pbMapping[offNtHdrs]; + + if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE) + MODERR_RETURN("%s: Invalid PE signature: %#x", pModInfo->szName, pNtHdrs->Signature); + if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader)) + MODERR_RETURN("%s: Unexpected optional header size: %#x\n", pModInfo->szName, pNtHdrs->FileHeader.SizeOfOptionalHeader); + if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC)) + MODERR_RETURN("%s: Unexpected optional header magic: %#x\n", pModInfo->szName, pNtHdrs->OptionalHeader.Magic); + if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) + MODERR_RETURN("%s: Unexpected number of RVA and sizes: %#x\n", pModInfo->szName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes); + + pModInfo->offNtHdrs = offNtHdrs; + pModInfo->offEndSectHdrs = offNtHdrs + + sizeof(*pNtHdrs) + + pNtHdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + pModInfo->cbImage = pNtHdrs->OptionalHeader.SizeOfImage; + if (pModInfo->cbImage > cbMapping) + MODERR_RETURN("%s: The image size %#x is larger than the mapping: %#x\n", + pModInfo->szName, pModInfo->cbImage, cbMapping); + + /* + * Find the export directory. It's okay if none is present too. + */ + IMAGE_DATA_DIRECTORY ExpDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + if ( ExpDir.Size < sizeof(IMAGE_EXPORT_DIRECTORY) + || ExpDir.VirtualAddress < pModInfo->offEndSectHdrs + || ExpDir.VirtualAddress >= pModInfo->cbImage + || ExpDir.VirtualAddress + ExpDir.Size > pModInfo->cbImage) + { + if (ExpDir.Size == 0 && ExpDir.VirtualAddress == 0) + { + pModInfo->offExportDir = 0; + pModInfo->cbExportDir = 0; + pModInfo->cNamedExports = 0; + pModInfo->cExports = 0; + pModInfo->paoffExports = NULL; + pModInfo->paoffNamedExports = NULL; + pModInfo->pau16NameOrdinals = NULL; + return true; + } + MODERR_RETURN("%s: Missing or invalid export directory: %#lx LB %#x\n", pModInfo->szName, ExpDir.VirtualAddress, ExpDir.Size); + } + pModInfo->offExportDir = ExpDir.VirtualAddress; + pModInfo->cbExportDir = ExpDir.Size; + + IMAGE_EXPORT_DIRECTORY const *pExpDir = (IMAGE_EXPORT_DIRECTORY const *)&pbMapping[ExpDir.VirtualAddress]; + + if ( pExpDir->NumberOfFunctions >= _1M + || pExpDir->NumberOfFunctions < 1 + || pExpDir->NumberOfNames >= _1M + || pExpDir->NumberOfNames < 1) + MODERR_RETURN("%s: NumberOfNames or/and NumberOfFunctions are outside the expected range: nof=%#x non=%#x\n", + pModInfo->szName, pExpDir->NumberOfFunctions, pExpDir->NumberOfNames); + pModInfo->cNamedExports = pExpDir->NumberOfNames; + pModInfo->cExports = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions); + + if ( pExpDir->AddressOfFunctions < pModInfo->offEndSectHdrs + || pExpDir->AddressOfFunctions >= pModInfo->cbImage + || pExpDir->AddressOfFunctions + pModInfo->cExports * sizeof(uint32_t) > pModInfo->cbImage) + MODERR_RETURN("%s: Bad AddressOfFunctions: %#x\n", pModInfo->szName, pExpDir->AddressOfFunctions); + pModInfo->paoffExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfFunctions]; + + if ( pExpDir->AddressOfNames < pModInfo->offEndSectHdrs + || pExpDir->AddressOfNames >= pModInfo->cbImage + || pExpDir->AddressOfNames + pExpDir->NumberOfNames * sizeof(uint32_t) > pModInfo->cbImage) + MODERR_RETURN("%s: Bad AddressOfNames: %#x\n", pModInfo->szName, pExpDir->AddressOfNames); + pModInfo->paoffNamedExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfNames]; + + if ( pExpDir->AddressOfNameOrdinals < pModInfo->offEndSectHdrs + || pExpDir->AddressOfNameOrdinals >= pModInfo->cbImage + || pExpDir->AddressOfNameOrdinals + pExpDir->NumberOfNames * sizeof(uint32_t) > pModInfo->cbImage) + MODERR_RETURN("%s: Bad AddressOfNameOrdinals: %#x\n", pModInfo->szName, pExpDir->AddressOfNameOrdinals); + pModInfo->pau16NameOrdinals = (uint16_t const *)&pbMapping[pExpDir->AddressOfNameOrdinals]; + + /* + * Success. + */ + return true; +#undef MODERR_RETURN +} + + +/** + * Searches the given module information from the kernel for the NT kernel module, the + * HAL module, and optionally one more module. + * + * If the NT kernel or HAL modules have already been found, they'll be skipped. + * + * @returns IPRT status code. + * @retval VERR_LDR_GENERAL_FAILURE if we failed to parse the NT kernel or HAL. + * @retval VERR_BAD_EXE_FORMAT if we failed to parse @a pModInfo. + * @retval VERR_MODULE_NOT_FOUND if @a pModInfo wasn't found. + * + * @param pInfo Pointer to the module information. + * @param cModules Number of valid module entries in the module information pointer. + * @param pModInfo Custom module to search for. Optional. + */ +static int rtR0DbgKrnlNtSearchForModuleWorker(PRTL_PROCESS_MODULES pInfo, uint32_t cModules, PRTDBGNTKRNLMODINFO pModInfo) +{ + AssertPtrReturn(pInfo, VERR_INVALID_PARAMETER); + AssertReturn(cModules >= 2, VERR_INVALID_PARAMETER); + + /* + * Search the info. The information is ordered with the kernel bits first, + * we expect aleast two modules to be returned to us (kernel + hal)! + */ + int rc = VINF_SUCCESS; +#if ARCH_BITS == 32 + uintptr_t const uMinKernelAddr = _2G; /** @todo resolve MmSystemRangeStart */ +#else + uintptr_t const uMinKernelAddr = (uintptr_t)MM_SYSTEM_RANGE_START; +#endif + + for (uint32_t iModule = 0; iModule < cModules; iModule++) + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: [%u]= %p LB %#x %s\n", iModule, pInfo->Modules[iModule].ImageBase, + pInfo->Modules[iModule].ImageSize, pInfo->Modules[iModule].FullPathName)); + + /* + * First time around we serch for the NT kernel and HAL. We'll look for NT + * kerneland HAL in the first 16 entries, and if not found, use the first + * and second entry respectively. + */ + if ( !g_NtOsKrnlInfo.pbImageBase + && !g_HalInfo.pbImageBase) + { + /* Find them. */ + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Looking for kernel and hal...\n")); + uint32_t const cMaxModules = RT_MIN(cModules, 16); + uint32_t idxNtOsKrnl = UINT32_MAX; + uint32_t idxHal = UINT32_MAX; + for (uint32_t iModule = 0; iModule < cMaxModules; iModule++) + { + RTL_PROCESS_MODULE_INFORMATION const * const pModule = &pInfo->Modules[iModule]; + if ( (uintptr_t)pModule->ImageBase >= uMinKernelAddr + && (uintptr_t)pModule->ImageSize >= _4K) + { + const char *pszName = (const char *)&pModule->FullPathName[pModule->OffsetToFileName]; + if ( idxNtOsKrnl == UINT32_MAX + && RTStrICmpAscii(pszName, g_NtOsKrnlInfo.szName) == 0) + { + idxNtOsKrnl = iModule; + if (idxHal != UINT32_MAX) + break; + } + else if ( idxHal == UINT32_MAX + && RTStrICmpAscii(pszName, g_HalInfo.szName) == 0) + { + idxHal = iModule; + if (idxHal != UINT32_MAX) + break; + } + } + } + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: idxNtOsKrnl=%#x idxHal=%#x\n", idxNtOsKrnl, idxHal)); + if (idxNtOsKrnl == UINT32_MAX) + { + idxNtOsKrnl = 0; + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: 'ntoskrnl.exe' not found, picking '%s' instead\n", + pInfo->Modules[idxNtOsKrnl].FullPathName)); + } + if (idxHal == UINT32_MAX) + { + idxHal = 1; + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: 'hal.dll' not found, picking '%s' instead\n", + pInfo->Modules[idxHal].FullPathName)); + } + + /* Parse them. */ + //RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Parsing NT kernel...\n")); + __try + { + g_NtOsKrnlInfo.fOkay = rtR0DbgKrnlNtParseModule(&g_NtOsKrnlInfo, + (uint8_t const *)pInfo->Modules[idxNtOsKrnl].ImageBase, + pInfo->Modules[idxNtOsKrnl].ImageSize); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + g_NtOsKrnlInfo.fOkay = false; + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: Exception in rtR0DbgKrnlNtParseModule parsing ntoskrnl.exe...\n")); + } + + //RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Parsing HAL...\n")); + __try + { + g_HalInfo.fOkay = rtR0DbgKrnlNtParseModule(&g_HalInfo, (uint8_t const *)pInfo->Modules[idxHal].ImageBase, + pInfo->Modules[idxHal].ImageSize); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + g_HalInfo.fOkay = false; + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: Exception in rtR0DbgKrnlNtParseModule parsing hal.dll...\n")); + } + if (!g_NtOsKrnlInfo.fOkay || !g_HalInfo.fOkay) + rc = VERR_LDR_GENERAL_FAILURE; + + /* + * Resolve symbols we may need in the NT kernel (provided it parsed successfully) + */ + if (g_NtOsKrnlInfo.fOkay) + { + if (!g_pfnMmGetSystemRoutineAddress) + { + //RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Looking up 'MmGetSystemRoutineAddress'...\n")); + rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, "MmGetSystemRoutineAddress", 0, + (void **)&g_pfnMmGetSystemRoutineAddress); + } + } + } + + /* + * If we're still good, search for the given module (optional). + */ + if (RT_SUCCESS(rc) && pModInfo) + { + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Locating module '%s'...\n", pModInfo->szName)); + rc = VERR_MODULE_NOT_FOUND; + for (uint32_t iModule = 0; iModule < cModules; iModule++) + { + RTL_PROCESS_MODULE_INFORMATION const * const pModule = &pInfo->Modules[iModule]; + if ( (uintptr_t)pModule->ImageBase >= uMinKernelAddr + && (uintptr_t)pModule->ImageSize >= _4K) + { + const char *pszName = (const char *)&pModule->FullPathName[pModule->OffsetToFileName]; + if ( pModInfo->pbImageBase == NULL + && RTStrICmpAscii(pszName, pModInfo->szName) == 0) + { + /* + * Found the module, try parse it. + */ + __try + { + pModInfo->fOkay = rtR0DbgKrnlNtParseModule(pModInfo, (uint8_t const *)pModule->ImageBase, + pModule->ImageSize); + rc = VINF_SUCCESS; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + pModInfo->fOkay = false; + rc = VERR_BAD_EXE_FORMAT; + } + break; + } + } + } + } + + return rc; +} + + +/** + * Queries the given maximum amount of modules and returns a pointer to the + * allocation holding the modules. + * + * @returns IPRT status code. + * @param ppInfo Where to store the pointer to the module information structure on success. + * Free with RTMemFree() when done. + * @param cModulesMax Maximum number of modules to return. + * @param pcModules Where to store the amount of modules returned upon success, + * can be lower than the requested maximum. + */ +static int rtR0DbgKrnlNtQueryModules(PRTL_PROCESS_MODULES *ppInfo, uint32_t cModulesMax, uint32_t *pcModules) +{ + *ppInfo = NULL; + *pcModules = 0; + + ULONG cbInfo = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[cModulesMax]); + PRTL_PROCESS_MODULES pInfo = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbInfo); + if (!pInfo) + { + cModulesMax = cModulesMax / 4; + cbInfo = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[cModulesMax]); + pInfo = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbInfo); + if (!pInfo) + { + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtQueryModules: Out of memory!\n")); + return VERR_NO_MEMORY; + } + } + + int rc; + ULONG cbActual = 0; + NTSTATUS rcNt = ZwQuerySystemInformation(SystemModuleInformation, pInfo, cbInfo, &cbActual); + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtQueryModules: ZwQuerySystemInformation returned %#x and NumberOfModules=%#x\n", + rcNt, pInfo->NumberOfModules)); + if ( NT_SUCCESS(rcNt) + || rcNt == STATUS_INFO_LENGTH_MISMATCH) + { + *ppInfo = pInfo; + *pcModules = RT_MIN(cModulesMax, pInfo->NumberOfModules); + rc = VINF_SUCCESS; + } + else + { + RTMemFree(pInfo); + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtQueryModules: ZwQuerySystemInformation failed: %#x\n", rcNt)); + rc = RTErrConvertFromNtStatus(rcNt); + } + + return rc; +} + + +/** + * Searches the module information from the kernel for the NT kernel module, the + * HAL module, and optionally one more module. + * + * If the NT kernel or HAL modules have already been found, they'll be skipped. + * + * @returns IPRT status code. + * @retval VERR_LDR_GENERAL_FAILURE if we failed to parse the NT kernel or HAL. + * @retval VERR_BAD_EXE_FORMAT if we failed to parse @a pModInfo. + * @retval VERR_MODULE_NOT_FOUND if @a pModInfo wasn't found. + * @retval VERR_BUFFER_UNDERFLOW if less that two modules was returned by the + * system. + * + * @param pModInfo Custom module to search for. Optional. + */ +static int rtR0DbgKrnlNtInit(PRTDBGNTKRNLMODINFO pModInfo) +{ + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: pModInfo=%p\n", pModInfo)); + +#ifndef IPRT_TARGET_NT4 + /* + * Must manually initialize g_pfnMmGetSystemRoutineAddress, otherwise compiler + * generates its own dynamic init code that might not necessarily be called. + */ + g_pfnMmGetSystemRoutineAddress = MmGetSystemRoutineAddress; +#endif + + /* + * Allocate a reasonably large buffer and get the information we need. We don't + * need everything since the result starts off with the kernel bits in load order. + * + * Note! ZwQuerySystemInformation requires NT4. For 3.51 we could possibly emit + * the syscall ourselves, if we cared. + */ + uint32_t cModules = 0; + PRTL_PROCESS_MODULES pInfo = NULL; + int rc = rtR0DbgKrnlNtQueryModules(&pInfo, pModInfo ? 110 /*32KB*/ : 27 /*8KB*/, &cModules); + if (RT_SUCCESS(rc)) + { + if (cModules >= 2) + { + rc = rtR0DbgKrnlNtSearchForModuleWorker(pInfo, cModules, pModInfo); + if ( rc == VERR_MODULE_NOT_FOUND + && pInfo->NumberOfModules > cModules + && pModInfo) + { + /* Module not found in the first round, reallocate array to maximum size and rerun. */ + cModules = pInfo->NumberOfModules; + + RTMemFree(pInfo); + pInfo = NULL; + + rc = rtR0DbgKrnlNtQueryModules(&pInfo, cModules, &cModules); + if (RT_SUCCESS(rc)) + rc = rtR0DbgKrnlNtSearchForModuleWorker(pInfo, cModules, pModInfo); + } + } + else + { + RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: Error! Only %u module(s) returned!\n", cModules)); + rc = VERR_BUFFER_UNDERFLOW; + } + + RTMemFree(pInfo); + } + + RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: returns %d\n", rc)); + return rc; +} + + + +RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags) +{ + AssertReturn(!fFlags, VERR_INVALID_FLAGS); + + RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTDBGKRNLINFO_MAGIC; + pThis->cRefs = 1; + *phKrnlInfo = pThis; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo) +{ + RTDBGKRNLINFOINT *pThis = hKrnlInfo; + AssertPtrReturn(pThis, UINT32_MAX); + AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs && cRefs < 100000); + return cRefs; +} + + +static void rtR0DbgKrnlNtDtor(RTDBGKRNLINFOINT *pThis) +{ + pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC; + RTMemFree(pThis); +} + + +RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo) +{ + RTDBGKRNLINFOINT *pThis = hKrnlInfo; + if (pThis == NIL_RTDBGKRNLINFO) + return 0; + AssertPtrReturn(pThis, UINT32_MAX); + AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + if (cRefs == 0) + rtR0DbgKrnlNtDtor(pThis); + return cRefs; +} + + +RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszStructure, + const char *pszMember, size_t *poffMember) +{ + RTDBGKRNLINFOINT *pThis = hKrnlInfo; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + AssertPtrReturn(pszMember, VERR_INVALID_POINTER); + AssertPtrNullReturn(pszModule, VERR_INVALID_POINTER); + AssertPtrReturn(pszStructure, VERR_INVALID_POINTER); + AssertPtrReturn(poffMember, VERR_INVALID_POINTER); + return VERR_NOT_FOUND; +} + + +RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszSymbol, void **ppvSymbol) +{ + RTDBGKRNLINFOINT *pThis = hKrnlInfo; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER); + + RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: pszModule=%s pszSymbol=%s\n", pszModule ? pszModule : "", pszSymbol)); + + void *pvTmpSymbol = NULL; + if (!ppvSymbol) + ppvSymbol = &pvTmpSymbol; + + int rc; + if (!pszModule) + { + /* + * Search both ntoskrnl and hal, may use MmGetSystemRoutineAddress as fallback. + * Note! MmGetSystemRoutineAddress was buggy before XP SP2 according to Geoff Chappell. + */ + if (g_NtOsKrnlInfo.pbImageBase) + rc = VINF_SUCCESS; + else + rc = rtR0DbgKrnlNtInit(NULL); + if (RT_SUCCESS(rc)) + { + Assert(g_NtOsKrnlInfo.fOkay); + Assert(g_HalInfo.fOkay); + //RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: Calling RTR0DbgKrnlInfoQuerySymbol on NT kernel...\n")); + rc = rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, pszSymbol, 0, ppvSymbol); + if (RT_FAILURE(rc)) + { + //RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: Calling RTR0DbgKrnlInfoQuerySymbol on HAL kernel...\n")); + rc = rtR0DbgKrnlInfoLookupSymbol(&g_HalInfo, pszSymbol, 0, ppvSymbol); + } + RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: #1 returns %d *ppvSymbol=%p\n", rc, *ppvSymbol)); + } + else + { + /* Init failed. Try resolve symbol, but preserve the status code up to a point. */ + int rc2 = VERR_SYMBOL_NOT_FOUND; + if (g_NtOsKrnlInfo.fOkay) + rc2 = rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, pszSymbol, 0, ppvSymbol); + if (g_HalInfo.fOkay && rc2 == VERR_SYMBOL_NOT_FOUND) + rc2 = rtR0DbgKrnlInfoLookupSymbol(&g_HalInfo, pszSymbol, 0, ppvSymbol); + if ( rc2 == VERR_SYMBOL_NOT_FOUND + && g_pfnMmGetSystemRoutineAddress) + { + /* We'll overwrite init failure status code here since + MmGetSystemRoutineAddress will do the job for us. */ + size_t cwcSymbol; + PRTUTF16 pwszSymbol = NULL; + rc = RTStrToUtf16Ex(pszSymbol, RTSTR_MAX, &pwszSymbol, 0, &cwcSymbol); + if (RT_SUCCESS(rc)) + { + UNICODE_STRING UniStr; + UniStr.Buffer = pwszSymbol; + UniStr.Length = (uint16_t)(cwcSymbol * sizeof(RTUTF16)); + UniStr.MaximumLength = UniStr.Length + sizeof(RTUTF16); + *ppvSymbol = g_pfnMmGetSystemRoutineAddress(&UniStr); + if (*ppvSymbol) + rc = VINF_SUCCESS; + else + rc = VERR_SYMBOL_NOT_FOUND; + RTUtf16Free(pwszSymbol); + RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: #2 returns %d *ppvSymbol=%p\n", rc, *ppvSymbol)); + } + } + } + } + else + { + /* + * Search specified module. + */ + rc = VERR_MODULE_NOT_FOUND; + PRTDBGNTKRNLMODINFO pModInfo; + if (RTStrICmpAscii(pszModule, g_NtOsKrnlInfo.szName) == 0) + pModInfo = &g_NtOsKrnlInfo; + else if (RTStrICmpAscii(pszModule, g_HalInfo.szName) == 0) + pModInfo = &g_NtOsKrnlInfo; + else + { + pModInfo = NULL; + for (unsigned i = 0; i < pThis->cModules; i++) + if (RTStrICmpAscii(pszModule, pThis->aModules[i].szName) == 0) + { + pModInfo = &pThis->aModules[i]; + break; + } + if (!pModInfo) + { + /* + * Not found, try load it. If module table is full, drop the first + * entry and shuffle the other up to make space. + */ + size_t const cchModule = strlen(pszModule); + RTDBGNTKRNLMODINFO NewModInfo; + if (cchModule < sizeof(NewModInfo.szName)) + { + RT_ZERO(NewModInfo); + memcpy(NewModInfo.szName, pszModule, cchModule); + NewModInfo.szName[cchModule] = '\0'; + + rc = rtR0DbgKrnlNtInit(&NewModInfo); + if (RT_SUCCESS(rc)) + { + Assert(NewModInfo.fOkay); + uint32_t iModule = pThis->cModules; + if (iModule >= RT_ELEMENTS(pThis->aModules)) + { + iModule = RT_ELEMENTS(pThis->aModules) - 1; + memmove(&pThis->aModules[0], &pThis->aModules[1], iModule * sizeof(pThis->aModules[0])); + } + pThis->aModules[iModule] = NewModInfo; + pThis->cModules = iModule + 1; + pModInfo = &pThis->aModules[iModule]; + rc = VINF_SUCCESS; + } + } + else + { + AssertMsgFailed(("cchModule=%zu pszModule=%s\n", cchModule, pszModule)); + rc = VERR_FILENAME_TOO_LONG; + } + } + } + if (pModInfo) + { + rc = rtR0DbgKrnlInfoLookupSymbol(pModInfo, pszSymbol, 0, ppvSymbol); + RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: #3 returns %d *ppvSymbol=%p\n", rc, *ppvSymbol)); + } + } + return rc; +} + + +RTR0DECL(int) RTR0DbgKrnlInfoQuerySize(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszType, size_t *pcbType) +{ + RTDBGKRNLINFOINT *pThis = hKrnlInfo; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + AssertPtrNullReturn(pszModule, VERR_INVALID_POINTER); + AssertPtrReturn(pszType, VERR_INVALID_POINTER); + AssertPtrReturn(pcbType, VERR_INVALID_POINTER); + return VERR_NOT_FOUND; +} + 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(); +} + diff --git a/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h b/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h new file mode 100644 index 00000000..f2a63e41 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h @@ -0,0 +1,207 @@ +/* $Id: internal-r0drv-nt.h $ */ +/** @file + * IPRT - Internal Header for the NT Ring-0 Driver Code. + */ + +/* + * Copyright (C) 2008-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 + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_nt_internal_r0drv_nt_h +#define IPRT_INCLUDED_SRC_r0drv_nt_internal_r0drv_nt_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include + +RT_C_DECLS_BEGIN + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef ULONG (__stdcall *PFNMYEXSETTIMERRESOLUTION)(ULONG, BOOLEAN); +typedef VOID (__stdcall *PFNMYKEFLUSHQUEUEDDPCS)(VOID); +typedef VOID (__stdcall *PFNHALSENDSOFTWAREINTERRUPT)(ULONG ProcessorNumber, KIRQL Irql); +typedef int (__stdcall *PFNRTSENDIPI)(RTCPUID idCpu); +typedef ULONG_PTR (__stdcall *PFNRTKEIPIGENERICCALL)(PKIPI_BROADCAST_WORKER BroadcastFunction, ULONG_PTR Context); +typedef ULONG (__stdcall *PFNRTRTLGETVERSION)(PRTL_OSVERSIONINFOEXW pVerInfo); +#ifndef RT_ARCH_AMD64 +typedef ULONGLONG (__stdcall *PFNRTKEQUERYINTERRUPTTIME)(VOID); +typedef VOID (__stdcall *PFNRTKEQUERYSYSTEMTIME)(PLARGE_INTEGER pTime); +#endif +typedef ULONG64 (__stdcall *PFNRTKEQUERYINTERRUPTTIMEPRECISE)(PULONG64 pQpcTS); +typedef VOID (__stdcall *PFNRTKEQUERYSYSTEMTIMEPRECISE)(PLARGE_INTEGER pTime); +typedef PMDL (__stdcall *PFNMMALLOCATEPAGESFORMDLEX)(PHYSICAL_ADDRESS, PHYSICAL_ADDRESS, PHYSICAL_ADDRESS, + SIZE_T, MEMORY_CACHING_TYPE, ULONG); + +#ifndef EX_TIMER_HIGH_RESOLUTION /* Too old DDK, so add missing bits. */ +# define EX_TIMER_NO_WAKE RT_BIT_32(3) +# define EX_TIMER_HIGH_RESOLUTION RT_BIT_32(2) +# define EX_TIMER_NOTIFICATION RT_BIT_32(31) +typedef struct _EX_TIMER *PEX_TIMER; +typedef VOID (__stdcall *PEXT_CALLBACK)(PEX_TIMER, void *); +typedef PEX_TIMER (__stdcall *PFNEXALLOCATETIMER)(PEXT_CALLBACK pfnCallback, void *pvUser, ULONG fFlags); + +typedef VOID (__stdcall *PEXT_DELETE_CALLBACK)(void *); +typedef struct _EXT_DELETE_PARAMETERS +{ + ULONG Version; + ULONG Reserved; + PEXT_DELETE_CALLBACK DeleteCallback; + void *DeleteContext; +} EXT_DELETE_PARAMETERS; +typedef EXT_DELETE_PARAMETERS *PEXT_DELETE_PARAMETERS; +DECLINLINE(void) ExInitializeDeleteTimerParameters(PEXT_DELETE_PARAMETERS pParams) +{ + pParams->Version = 0; + pParams->Reserved = 0; + pParams->DeleteCallback = NULL; + pParams->DeleteContext = NULL; +} +typedef BOOLEAN (__stdcall *PFNEXDELETETIMER)(PEX_TIMER pTimer, BOOLEAN fCancel, BOOLEAN fWait, PEXT_DELETE_PARAMETERS pParams); + +typedef struct _EXT_SET_PARAMETERS_V0 +{ + ULONG Version; + ULONG Reserved; + LONGLONG NoWakeTolerance; +} EXT_SET_PARAMETERS; +typedef EXT_SET_PARAMETERS *PEXT_SET_PARAMETERS; +DECLINLINE(void) ExInitializeSetTimerParameters(PEXT_SET_PARAMETERS pParams) +{ + pParams->Version = 0; + pParams->Reserved = 0; + pParams->NoWakeTolerance = 0; +} +typedef BOOLEAN (__stdcall *PFNEXSETTIMER)(PEX_TIMER pTimer, LONGLONG DueTime, LONGLONG Period, PEXT_SET_PARAMETERS pParams); + +typedef BOOLEAN (__stdcall *PFNEXCANCELTIMER)(PEX_TIMER pTimer, void *pvReserved); + +#else +typedef decltype(ExAllocateTimer) *PFNEXALLOCATETIMER; +typedef decltype(ExDeleteTimer) *PFNEXDELETETIMER; +typedef decltype(ExSetTimer) *PFNEXSETTIMER; +typedef decltype(ExCancelTimer) *PFNEXCANCELTIMER; +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern RTCPUSET g_rtMpNtCpuSet; +extern uint32_t g_cRtMpNtMaxGroups; +extern uint32_t g_cRtMpNtMaxCpus; +extern RTCPUID g_aidRtMpNtByCpuSetIdx[RTCPUSET_MAX_CPUS]; + +extern decltype(ExAllocatePoolWithTag) *g_pfnrtExAllocatePoolWithTag; +extern decltype(ExFreePoolWithTag) *g_pfnrtExFreePoolWithTag; +extern PFNMYEXSETTIMERRESOLUTION g_pfnrtNtExSetTimerResolution; +extern PFNEXALLOCATETIMER g_pfnrtExAllocateTimer; +extern PFNEXDELETETIMER g_pfnrtExDeleteTimer; +extern PFNEXSETTIMER g_pfnrtExSetTimer; +extern PFNEXCANCELTIMER g_pfnrtExCancelTimer; +extern PFNMYKEFLUSHQUEUEDDPCS g_pfnrtNtKeFlushQueuedDpcs; +extern PFNHALREQUESTIPI_W7PLUS g_pfnrtHalRequestIpiW7Plus; +extern PFNHALREQUESTIPI_PRE_W7 g_pfnrtHalRequestIpiPreW7; +extern PFNHALSENDSOFTWAREINTERRUPT g_pfnrtNtHalSendSoftwareInterrupt; +extern PFNRTSENDIPI g_pfnrtMpPokeCpuWorker; +extern PFNRTKEIPIGENERICCALL g_pfnrtKeIpiGenericCall; +extern PFNKESETTARGETPROCESSORDPCEX g_pfnrtKeSetTargetProcessorDpcEx; +extern PFNKEINITIALIZEAFFINITYEX g_pfnrtKeInitializeAffinityEx; +extern PFNKEADDPROCESSORAFFINITYEX g_pfnrtKeAddProcessorAffinityEx; +extern PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnrtKeGetProcessorIndexFromNumber; +extern PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnrtKeGetProcessorNumberFromIndex; +extern PFNKEGETCURRENTPROCESSORNUMBEREX g_pfnrtKeGetCurrentProcessorNumberEx; +extern PFNKEQUERYACTIVEPROCESSORS g_pfnrtKeQueryActiveProcessors; +extern PFNKEQUERYMAXIMUMPROCESSORCOUNT g_pfnrtKeQueryMaximumProcessorCount; +extern PFNKEQUERYMAXIMUMPROCESSORCOUNTEX g_pfnrtKeQueryMaximumProcessorCountEx; +extern PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnrtKeQueryMaximumGroupCount; +extern PFNKEQUERYACTIVEPROCESSORCOUNT g_pfnrtKeQueryActiveProcessorCount; +extern PFNKEQUERYACTIVEPROCESSORCOUNTEX g_pfnrtKeQueryActiveProcessorCountEx; +extern PFNKEQUERYLOGICALPROCESSORRELATIONSHIP g_pfnrtKeQueryLogicalProcessorRelationship; +extern PFNKEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeRegisterProcessorChangeCallback; +extern PFNKEDEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeDeregisterProcessorChangeCallback; +extern decltype(KeSetImportanceDpc) *g_pfnrtKeSetImportanceDpc; +extern decltype(KeSetTargetProcessorDpc) *g_pfnrtKeSetTargetProcessorDpc; +extern decltype(KeInitializeTimerEx) *g_pfnrtKeInitializeTimerEx; +extern PFNKESHOULDYIELDPROCESSOR g_pfnrtKeShouldYieldProcessor; +extern decltype(MmProtectMdlSystemAddress) *g_pfnrtMmProtectMdlSystemAddress; +extern decltype(MmAllocatePagesForMdl) *g_pfnrtMmAllocatePagesForMdl; +extern PFNMMALLOCATEPAGESFORMDLEX g_pfnrtMmAllocatePagesForMdlEx; +extern decltype(MmFreePagesFromMdl) *g_pfnrtMmFreePagesFromMdl; +extern decltype(MmMapLockedPagesSpecifyCache) *g_pfnrtMmMapLockedPagesSpecifyCache; +extern decltype(MmAllocateContiguousMemorySpecifyCache) *g_pfnrtMmAllocateContiguousMemorySpecifyCache; +extern decltype(MmSecureVirtualMemory) *g_pfnrtMmSecureVirtualMemory; +extern decltype(MmUnsecureVirtualMemory) *g_pfnrtMmUnsecureVirtualMemory; +extern decltype(PsIsThreadTerminating) *g_pfnrtPsIsThreadTerminating; + +extern PFNRTRTLGETVERSION g_pfnrtRtlGetVersion; +#ifdef RT_ARCH_X86 +extern PFNRTKEQUERYINTERRUPTTIME g_pfnrtKeQueryInterruptTime; +#endif +extern PFNRTKEQUERYINTERRUPTTIMEPRECISE g_pfnrtKeQueryInterruptTimePrecise; +extern PFNRTKEQUERYSYSTEMTIMEPRECISE g_pfnrtKeQuerySystemTimePrecise; + +extern uint32_t g_offrtNtPbQuantumEnd; +extern uint32_t g_cbrtNtPbQuantumEnd; +extern uint32_t g_offrtNtPbDpcQueueDepth; + +/** Makes an NT version for checking against g_uRtNtVersion. */ +#define RTNT_MAKE_VERSION(uMajor, uMinor) RT_MAKE_U32(uMinor, uMajor) + +extern uint32_t g_uRtNtVersion; +extern uint8_t g_uRtNtMajorVer; +extern uint8_t g_uRtNtMinorVer; +extern uint32_t g_uRtNtBuildNo; + +extern uintptr_t const *g_puRtMmHighestUserAddress; +extern uintptr_t const *g_puRtMmSystemRangeStart; + + +int __stdcall rtMpPokeCpuUsingFailureNotSupported(RTCPUID idCpu); +int __stdcall rtMpPokeCpuUsingDpc(RTCPUID idCpu); +int __stdcall rtMpPokeCpuUsingBroadcastIpi(RTCPUID idCpu); +int __stdcall rtMpPokeCpuUsingHalRequestIpiW7Plus(RTCPUID idCpu); +int __stdcall rtMpPokeCpuUsingHalRequestIpiPreW7(RTCPUID idCpu); + +struct RTNTSDBOSVER; +DECLHIDDEN(int) rtR0MpNtInit(struct RTNTSDBOSVER const *pOsVerInfo); +DECLHIDDEN(void) rtR0MpNtTerm(void); +DECLHIDDEN(int) rtMpNtSetTargetProcessorDpc(KDPC *pDpc, RTCPUID idCpu); +#if defined(RT_ARCH_X86) && defined(NIL_RTDBGKRNLINFO) +DECLHIDDEN(int) rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo); +#endif + +RT_C_DECLS_END + +#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_internal_r0drv_nt_h */ + diff --git a/src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp new file mode 100644 index 00000000..2ec319d7 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp @@ -0,0 +1,1284 @@ +/* $Id: memobj-r0drv-nt.cpp $ */ +/** @file + * IPRT - Ring-0 Memory Objects, 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 +#include +#include +#include +#include "internal/memobj.h" +#include "internal-r0drv-nt.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Maximum number of bytes we try to lock down in one go. + * This is supposed to have a limit right below 256MB, but this appears + * to actually be much lower. The values here have been determined experimentally. + */ +#ifdef RT_ARCH_X86 +# define MAX_LOCK_MEM_SIZE (32*1024*1024) /* 32MB */ +#endif +#ifdef RT_ARCH_AMD64 +# define MAX_LOCK_MEM_SIZE (24*1024*1024) /* 24MB */ +#endif + +/* Newer WDK constants: */ +#ifndef MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS +# define MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS 0x20 +#endif +#ifndef MM_ALLOCATE_FAST_LARGE_PAGES +# define MM_ALLOCATE_FAST_LARGE_PAGES 0x40 +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * The NT version of the memory object structure. + */ +typedef struct RTR0MEMOBJNT +{ + /** The core structure. */ + RTR0MEMOBJINTERNAL Core; + /** Used MmAllocatePagesForMdl(). */ + bool fAllocatedPagesForMdl; + /** Set if this is sub-section of the parent. */ + bool fSubMapping; + /** Pointer returned by MmSecureVirtualMemory */ + PVOID pvSecureMem; + /** The number of PMDLs (memory descriptor lists) in the array. */ + uint32_t cMdls; + /** Array of MDL pointers. (variable size) */ + PMDL apMdls[1]; +} RTR0MEMOBJNT; +/** Pointer to the NT version of the memory object structure. */ +typedef RTR0MEMOBJNT *PRTR0MEMOBJNT; + + + +DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem) +{ + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)pMem; + + /* + * Deal with it on a per type basis (just as a variation). + */ + switch (pMemNt->Core.enmType) + { + case RTR0MEMOBJTYPE_LOW: + if (pMemNt->fAllocatedPagesForMdl) + { + Assert(pMemNt->Core.pv && pMemNt->cMdls == 1 && pMemNt->apMdls[0]); + MmUnmapLockedPages(pMemNt->Core.pv, pMemNt->apMdls[0]); + pMemNt->Core.pv = NULL; + if (pMemNt->pvSecureMem) + { + g_pfnrtMmUnsecureVirtualMemory(pMemNt->pvSecureMem); + pMemNt->pvSecureMem = NULL; + } + + g_pfnrtMmFreePagesFromMdl(pMemNt->apMdls[0]); + ExFreePool(pMemNt->apMdls[0]); + pMemNt->apMdls[0] = NULL; + pMemNt->cMdls = 0; + break; + } + AssertFailed(); + break; + + case RTR0MEMOBJTYPE_PAGE: + Assert(pMemNt->Core.pv); + if (pMemNt->fAllocatedPagesForMdl) + { + Assert(pMemNt->Core.pv && pMemNt->cMdls == 1 && pMemNt->apMdls[0]); + Assert(pMemNt->pvSecureMem == NULL); + MmUnmapLockedPages(pMemNt->Core.pv, pMemNt->apMdls[0]); + g_pfnrtMmFreePagesFromMdl(pMemNt->apMdls[0]); + ExFreePool(pMemNt->apMdls[0]); + } + else + { + if (g_pfnrtExFreePoolWithTag) + g_pfnrtExFreePoolWithTag(pMemNt->Core.pv, IPRT_NT_POOL_TAG); + else + ExFreePool(pMemNt->Core.pv); + + Assert(pMemNt->cMdls == 1 && pMemNt->apMdls[0]); + IoFreeMdl(pMemNt->apMdls[0]); + } + pMemNt->Core.pv = NULL; + pMemNt->apMdls[0] = NULL; + pMemNt->cMdls = 0; + break; + + case RTR0MEMOBJTYPE_CONT: + Assert(pMemNt->Core.pv); + MmFreeContiguousMemory(pMemNt->Core.pv); + pMemNt->Core.pv = NULL; + + Assert(pMemNt->cMdls == 1 && pMemNt->apMdls[0]); + IoFreeMdl(pMemNt->apMdls[0]); + pMemNt->apMdls[0] = NULL; + pMemNt->cMdls = 0; + break; + + case RTR0MEMOBJTYPE_PHYS: + /* rtR0MemObjNativeEnterPhys? */ + if (!pMemNt->Core.u.Phys.fAllocated) + { + Assert(!pMemNt->fAllocatedPagesForMdl); + /* Nothing to do here. */ + break; + } + RT_FALL_THRU(); + + case RTR0MEMOBJTYPE_PHYS_NC: + if (pMemNt->fAllocatedPagesForMdl) + { + g_pfnrtMmFreePagesFromMdl(pMemNt->apMdls[0]); + ExFreePool(pMemNt->apMdls[0]); + pMemNt->apMdls[0] = NULL; + pMemNt->cMdls = 0; + break; + } + AssertFailed(); + break; + + case RTR0MEMOBJTYPE_LOCK: + if (pMemNt->pvSecureMem) + { + g_pfnrtMmUnsecureVirtualMemory(pMemNt->pvSecureMem); + pMemNt->pvSecureMem = NULL; + } + for (uint32_t i = 0; i < pMemNt->cMdls; i++) + { + MmUnlockPages(pMemNt->apMdls[i]); + IoFreeMdl(pMemNt->apMdls[i]); + pMemNt->apMdls[i] = NULL; + } + break; + + case RTR0MEMOBJTYPE_RES_VIRT: +/* if (pMemNt->Core.u.ResVirt.R0Process == NIL_RTR0PROCESS) + { + } + else + { + }*/ + AssertMsgFailed(("RTR0MEMOBJTYPE_RES_VIRT\n")); + return VERR_INTERNAL_ERROR; + break; + + case RTR0MEMOBJTYPE_MAPPING: + { + PRTR0MEMOBJNT pMemNtParent = (PRTR0MEMOBJNT)pMemNt->Core.uRel.Child.pParent; + Assert(pMemNtParent); + Assert(pMemNt->Core.pv); + Assert((pMemNt->cMdls == 0 && !pMemNt->fSubMapping) || (pMemNt->cMdls == 1 && pMemNt->fSubMapping)); + if (pMemNtParent->cMdls) + { + Assert(pMemNtParent->cMdls == 1 && pMemNtParent->apMdls[0]); + Assert( pMemNt->Core.u.Mapping.R0Process == NIL_RTR0PROCESS + || pMemNt->Core.u.Mapping.R0Process == RTR0ProcHandleSelf()); + if (!pMemNt->cMdls) + MmUnmapLockedPages(pMemNt->Core.pv, pMemNtParent->apMdls[0]); + else + { + MmUnmapLockedPages(pMemNt->Core.pv, pMemNt->apMdls[0]); + IoFreeMdl(pMemNt->apMdls[0]); + pMemNt->apMdls[0] = NULL; + } + } + else + { + Assert( pMemNtParent->Core.enmType == RTR0MEMOBJTYPE_PHYS + && !pMemNtParent->Core.u.Phys.fAllocated); + Assert(pMemNt->Core.u.Mapping.R0Process == NIL_RTR0PROCESS); + Assert(!pMemNt->fSubMapping); + MmUnmapIoSpace(pMemNt->Core.pv, pMemNt->Core.cb); + } + pMemNt->Core.pv = NULL; + break; + } + + default: + AssertMsgFailed(("enmType=%d\n", pMemNt->Core.enmType)); + return VERR_INTERNAL_ERROR; + } + + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag) +{ + AssertMsgReturn(cb <= _1G, ("%#x\n", cb), VERR_OUT_OF_RANGE); /* for safe size_t -> ULONG */ + RT_NOREF1(fExecutable); + + /* + * Use MmAllocatePagesForMdl if the allocation is a little bit big. + */ + int rc = VERR_NO_PAGE_MEMORY; + if ( cb > _1M + && g_pfnrtMmAllocatePagesForMdl + && g_pfnrtMmFreePagesFromMdl + && g_pfnrtMmMapLockedPagesSpecifyCache) + { + PHYSICAL_ADDRESS Zero; + Zero.QuadPart = 0; + PHYSICAL_ADDRESS HighAddr; + HighAddr.QuadPart = MAXLONGLONG; + PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb); + if (pMdl) + { + if (MmGetMdlByteCount(pMdl) >= cb) + { + __try + { + void *pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL /* no base address */, + FALSE /* no bug check on failure */, NormalPagePriority); + if (pv) + { +#ifdef RT_ARCH_AMD64 + if (fExecutable) + MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE); +#endif + + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PAGE, pv, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC; + pMemNt->fAllocatedPagesForMdl = true; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + MmUnmapLockedPages(pv, pMdl); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { +# ifdef LOG_ENABLED + NTSTATUS rcNt = GetExceptionCode(); + Log(("rtR0MemObjNativeAllocLow: Exception Code %#x\n", rcNt)); +# endif + /* nothing */ + } + } + g_pfnrtMmFreePagesFromMdl(pMdl); + ExFreePool(pMdl); + } + } + + /* + * Try allocate the memory and create an MDL for them so + * we can query the physical addresses and do mappings later + * without running into out-of-memory conditions and similar problems. + */ + void *pv; + if (g_pfnrtExAllocatePoolWithTag) + pv = g_pfnrtExAllocatePoolWithTag(NonPagedPool, cb, IPRT_NT_POOL_TAG); + else + pv = ExAllocatePool(NonPagedPool, cb); + if (pv) + { + PMDL pMdl = IoAllocateMdl(pv, (ULONG)cb, FALSE, FALSE, NULL); + if (pMdl) + { + MmBuildMdlForNonPagedPool(pMdl); +#ifdef RT_ARCH_AMD64 + if (fExecutable) + MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE); +#endif + + /* + * Create the IPRT memory object. + */ + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PAGE, pv, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + + rc = VERR_NO_MEMORY; + IoFreeMdl(pMdl); + } + ExFreePool(pv); + } + return rc; +} + + +/** + * Helper for rtR0MemObjNativeAllocLarge that verifies the result. + */ +static bool rtR0MemObjNtVerifyLargePageAlloc(PMDL pMdl, size_t cb, size_t cbLargePage) +{ + if (MmGetMdlByteCount(pMdl) >= cb) + { + PPFN_NUMBER const paPfns = MmGetMdlPfnArray(pMdl); + size_t const cPagesPerLargePage = cbLargePage >> PAGE_SHIFT; + size_t const cLargePages = cb / cbLargePage; + size_t iPage = 0; + for (size_t iLargePage = 0; iLargePage < cLargePages; iLargePage++) + { + PFN_NUMBER Pfn = paPfns[iPage]; + if (!(Pfn & (cbLargePage >> PAGE_SHIFT) - 1U)) + { + for (size_t iSubPage = 1; iSubPage < cPagesPerLargePage; iSubPage++) + { + iPage++; + Pfn++; + if (paPfns[iPage] == Pfn) + { /* likely */ } + else + { + Log(("rtR0MemObjNativeAllocLarge: Subpage %#zu in large page #%zu is not contiguous: %#x, expected %#x\n", + iSubPage, iLargePage, paPfns[iPage], Pfn)); + return false; + } + } + } + else + { + Log(("rtR0MemObjNativeAllocLarge: Large page #%zu is misaligned: %#x, cbLargePage=%#zx\n", + iLargePage, Pfn, cbLargePage)); + return false; + } + } + return true; + } + Log(("rtR0MemObjNativeAllocLarge: Got back too few pages: %#zx, requested %#zx\n", MmGetMdlByteCount(pMdl), cb)); + return false; +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags, + const char *pszTag) +{ + /* + * Need the MmAllocatePagesForMdlEx function so we can specify flags. + */ + if ( g_uRtNtVersion >= RTNT_MAKE_VERSION(6,1) /* Windows 7+ */ + && g_pfnrtMmAllocatePagesForMdlEx + && g_pfnrtMmFreePagesFromMdl + && g_pfnrtMmMapLockedPagesSpecifyCache) + { + ULONG fNtFlags = MM_ALLOCATE_FULLY_REQUIRED /* W7+: Make it fail if we don't get all we ask for.*/ + | MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS; /* W7+: The SkipBytes chunks must be physcially contiguous. */ + if ((fFlags & RTMEMOBJ_ALLOC_LARGE_F_FAST) && g_uRtNtVersion >= RTNT_MAKE_VERSION(6, 2)) + fNtFlags |= MM_ALLOCATE_FAST_LARGE_PAGES; /* W8+: Don't try too hard, just fail if not enough handy. */ + + PHYSICAL_ADDRESS Zero; + Zero.QuadPart = 0; + + PHYSICAL_ADDRESS HighAddr; + HighAddr.QuadPart = MAXLONGLONG; + + PHYSICAL_ADDRESS Skip; + Skip.QuadPart = cbLargePage; + + int rc; + PMDL const pMdl = g_pfnrtMmAllocatePagesForMdlEx(Zero, HighAddr, Skip, cb, MmCached, fNtFlags); + if (pMdl) + { + /* Verify the result. */ + if (rtR0MemObjNtVerifyLargePageAlloc(pMdl, cb, cbLargePage)) + { + /* + * Map the allocation into kernel space. Unless the memory is already mapped + * somewhere (seems to be actually), I guess it's unlikely that we'll get a + * large page aligned mapping back here... + */ + __try + { + void *pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL /* no base address */, + FALSE /* no bug check on failure */, NormalPagePriority); + if (pv) + { + /* + * Create the memory object. + */ + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PAGE, pv, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC; + pMemNt->fAllocatedPagesForMdl = true; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + + MmUnmapLockedPages(pv, pMdl); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { +#ifdef LOG_ENABLED + NTSTATUS rcNt = GetExceptionCode(); + Log(("rtR0MemObjNativeAllocLarge: Exception Code %#x\n", rcNt)); +#endif + /* nothing */ + } + } + + g_pfnrtMmFreePagesFromMdl(pMdl); + ExFreePool(pMdl); + rc = VERR_NO_MEMORY; + } + else + rc = fFlags & RTMEMOBJ_ALLOC_LARGE_F_FAST ? VERR_TRY_AGAIN : VERR_NO_MEMORY; + return rc; + } + + return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag) +{ + AssertMsgReturn(cb <= _1G, ("%#x\n", cb), VERR_OUT_OF_RANGE); /* for safe size_t -> ULONG */ + + /* + * Try see if we get lucky first... + * (We could probably just assume we're lucky on NT4.) + */ + int rc = rtR0MemObjNativeAllocPage(ppMem, cb, fExecutable, pszTag); + if (RT_SUCCESS(rc)) + { + size_t iPage = cb >> PAGE_SHIFT; + while (iPage-- > 0) + if (rtR0MemObjNativeGetPagePhysAddr(*ppMem, iPage) >= _4G) + { + rc = VERR_NO_LOW_MEMORY; + break; + } + if (RT_SUCCESS(rc)) + return rc; + + /* The following ASSUMES that rtR0MemObjNativeAllocPage returns a completed object. */ + RTR0MemObjFree(*ppMem, false); + *ppMem = NULL; + } + + /* + * Use MmAllocatePagesForMdl to specify the range of physical addresses we wish to use. + */ + if ( g_pfnrtMmAllocatePagesForMdl + && g_pfnrtMmFreePagesFromMdl + && g_pfnrtMmMapLockedPagesSpecifyCache) + { + PHYSICAL_ADDRESS Zero; + Zero.QuadPart = 0; + PHYSICAL_ADDRESS HighAddr; + HighAddr.QuadPart = _4G - 1; + PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb); + if (pMdl) + { + if (MmGetMdlByteCount(pMdl) >= cb) + { + __try + { + void *pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL /* no base address */, + FALSE /* no bug check on failure */, NormalPagePriority); + if (pv) + { + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_LOW, pv, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC; + pMemNt->fAllocatedPagesForMdl = true; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + MmUnmapLockedPages(pv, pMdl); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { +# ifdef LOG_ENABLED + NTSTATUS rcNt = GetExceptionCode(); + Log(("rtR0MemObjNativeAllocLow: Exception Code %#x\n", rcNt)); +# endif + /* nothing */ + } + } + g_pfnrtMmFreePagesFromMdl(pMdl); + ExFreePool(pMdl); + } + } + + /* + * Fall back on contiguous memory... + */ + return rtR0MemObjNativeAllocCont(ppMem, cb, fExecutable, pszTag); +} + + +/** + * Internal worker for rtR0MemObjNativeAllocCont(), rtR0MemObjNativeAllocPhys() + * and rtR0MemObjNativeAllocPhysNC() that takes a max physical address in addition + * to what rtR0MemObjNativeAllocCont() does. + * + * @returns IPRT status code. + * @param ppMem Where to store the pointer to the ring-0 memory object. + * @param cb The size. + * @param fExecutable Whether the mapping should be executable or not. + * @param PhysHighest The highest physical address for the pages in allocation. + * @param uAlignment The alignment of the physical memory to allocate. + * Supported values are PAGE_SIZE, _2M, _4M and _1G. + * @param pszTag Allocation tag used for statistics and such. + */ +static int rtR0MemObjNativeAllocContEx(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, RTHCPHYS PhysHighest, + size_t uAlignment, const char *pszTag) +{ + AssertMsgReturn(cb <= _1G, ("%#x\n", cb), VERR_OUT_OF_RANGE); /* for safe size_t -> ULONG */ + RT_NOREF1(fExecutable); + + /* + * Allocate the memory and create an MDL for it. + */ + PHYSICAL_ADDRESS PhysAddrHighest; + PhysAddrHighest.QuadPart = PhysHighest; + void *pv; + if (g_pfnrtMmAllocateContiguousMemorySpecifyCache) + { + PHYSICAL_ADDRESS PhysAddrLowest, PhysAddrBoundary; + PhysAddrLowest.QuadPart = 0; + PhysAddrBoundary.QuadPart = (uAlignment == PAGE_SIZE) ? 0 : uAlignment; + pv = g_pfnrtMmAllocateContiguousMemorySpecifyCache(cb, PhysAddrLowest, PhysAddrHighest, PhysAddrBoundary, MmCached); + } + else if (uAlignment == PAGE_SIZE) + pv = MmAllocateContiguousMemory(cb, PhysAddrHighest); + else + return VERR_NOT_SUPPORTED; + if (!pv) + return VERR_NO_MEMORY; + + PMDL pMdl = IoAllocateMdl(pv, (ULONG)cb, FALSE, FALSE, NULL); + if (pMdl) + { + MmBuildMdlForNonPagedPool(pMdl); +#ifdef RT_ARCH_AMD64 + if (fExecutable) + MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE); +#endif + + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_CONT, pv, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; + pMemNt->Core.u.Cont.Phys = (RTHCPHYS)*MmGetMdlPfnArray(pMdl) << PAGE_SHIFT; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + + IoFreeMdl(pMdl); + } + MmFreeContiguousMemory(pv); + return VERR_NO_MEMORY; +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag) +{ + return rtR0MemObjNativeAllocContEx(ppMem, cb, fExecutable, _4G-1, PAGE_SIZE /* alignment */, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, + const char *pszTag) +{ + /* + * Try and see if we're lucky and get a contiguous chunk from MmAllocatePagesForMdl. + * + * This is preferable to using MmAllocateContiguousMemory because there are + * a few situations where the memory shouldn't be mapped, like for instance + * VT-x control memory. Since these are rather small allocations (one or + * two pages) MmAllocatePagesForMdl will probably be able to satisfy the + * request. + * + * If the allocation is big, the chances are *probably* not very good. The + * current limit is kind of random... + */ + if ( cb < _128K + && uAlignment == PAGE_SIZE + && g_pfnrtMmAllocatePagesForMdl + && g_pfnrtMmFreePagesFromMdl) + { + PHYSICAL_ADDRESS Zero; + Zero.QuadPart = 0; + PHYSICAL_ADDRESS HighAddr; + HighAddr.QuadPart = PhysHighest == NIL_RTHCPHYS ? MAXLONGLONG : PhysHighest; + PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb); + if (pMdl) + { + if (MmGetMdlByteCount(pMdl) >= cb) + { + PPFN_NUMBER paPfns = MmGetMdlPfnArray(pMdl); + PFN_NUMBER Pfn = paPfns[0] + 1; + const size_t cPages = cb >> PAGE_SHIFT; + size_t iPage; + for (iPage = 1; iPage < cPages; iPage++, Pfn++) + if (paPfns[iPage] != Pfn) + break; + if (iPage >= cPages) + { + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC; + pMemNt->Core.u.Phys.fAllocated = true; + pMemNt->Core.u.Phys.PhysBase = (RTHCPHYS)paPfns[0] << PAGE_SHIFT; + pMemNt->fAllocatedPagesForMdl = true; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + } + } + g_pfnrtMmFreePagesFromMdl(pMdl); + ExFreePool(pMdl); + } + } + + return rtR0MemObjNativeAllocContEx(ppMem, cb, false, PhysHighest, uAlignment, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag) +{ + if (g_pfnrtMmAllocatePagesForMdl && g_pfnrtMmFreePagesFromMdl) + { + /** @todo use the Ex version with the fail-if-not-all-requested-pages flag + * when possible. */ + PHYSICAL_ADDRESS Zero; + Zero.QuadPart = 0; + PHYSICAL_ADDRESS HighAddr; + HighAddr.QuadPart = PhysHighest == NIL_RTHCPHYS ? MAXLONGLONG : PhysHighest; + PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb); + if (pMdl) + { + if (MmGetMdlByteCount(pMdl) >= cb) + { + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PHYS_NC, NULL, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC; + pMemNt->fAllocatedPagesForMdl = true; + pMemNt->cMdls = 1; + pMemNt->apMdls[0] = pMdl; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + } + g_pfnrtMmFreePagesFromMdl(pMdl); + ExFreePool(pMdl); + } + return VERR_NO_MEMORY; + } + return VERR_NOT_SUPPORTED; +} + + +DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, + const char *pszTag) +{ + AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE || uCachePolicy == RTMEM_CACHE_POLICY_MMIO, VERR_NOT_SUPPORTED); + + /* + * Validate the address range and create a descriptor for it. + */ + PFN_NUMBER Pfn = (PFN_NUMBER)(Phys >> PAGE_SHIFT); + if (((RTHCPHYS)Pfn << PAGE_SHIFT) != Phys) + return VERR_ADDRESS_TOO_BIG; + + /* + * Create the IPRT memory object. + */ + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag); + if (pMemNt) + { + pMemNt->Core.u.Phys.PhysBase = Phys; + pMemNt->Core.u.Phys.fAllocated = false; + pMemNt->Core.u.Phys.uCachePolicy = uCachePolicy; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +/** + * Internal worker for locking down pages. + * + * @return IPRT status code. + * + * @param ppMem Where to store the memory object pointer. + * @param pv First page. + * @param cb Number of bytes. + * @param fAccess The desired access, a combination of RTMEM_PROT_READ + * and RTMEM_PROT_WRITE. + * @param R0Process The process \a pv and \a cb refers to. + * @param pszTag Allocation tag used for statistics and such. + */ +static int rtR0MemObjNtLock(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, RTR0PROCESS R0Process, + const char *pszTag) +{ + /* + * Calc the number of MDLs we need and allocate the memory object structure. + */ + size_t cMdls = cb / MAX_LOCK_MEM_SIZE; + if (cb % MAX_LOCK_MEM_SIZE) + cMdls++; + if (cMdls >= UINT32_MAX) + return VERR_OUT_OF_RANGE; + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJNT, apMdls[cMdls]), + RTR0MEMOBJTYPE_LOCK, pv, cb, pszTag); + if (!pMemNt) + return VERR_NO_MEMORY; + + /* + * Loop locking down the sub parts of the memory. + */ + int rc = VINF_SUCCESS; + size_t cbTotal = 0; + uint8_t *pb = (uint8_t *)pv; + uint32_t iMdl; + for (iMdl = 0; iMdl < cMdls; iMdl++) + { + /* + * Calc the Mdl size and allocate it. + */ + size_t cbCur = cb - cbTotal; + if (cbCur > MAX_LOCK_MEM_SIZE) + cbCur = MAX_LOCK_MEM_SIZE; + AssertMsg(cbCur, ("cbCur: 0!\n")); + PMDL pMdl = IoAllocateMdl(pb, (ULONG)cbCur, FALSE, FALSE, NULL); + if (!pMdl) + { + rc = VERR_NO_MEMORY; + break; + } + + /* + * Lock the pages. + */ + __try + { + MmProbeAndLockPages(pMdl, + R0Process == NIL_RTR0PROCESS ? KernelMode : UserMode, + fAccess == RTMEM_PROT_READ + ? IoReadAccess + : fAccess == RTMEM_PROT_WRITE + ? IoWriteAccess + : IoModifyAccess); + + pMemNt->apMdls[iMdl] = pMdl; + pMemNt->cMdls++; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + IoFreeMdl(pMdl); + rc = VERR_LOCK_FAILED; + break; + } + + if ( R0Process != NIL_RTR0PROCESS + && g_pfnrtMmSecureVirtualMemory + && g_pfnrtMmUnsecureVirtualMemory) + { + /* Make sure the user process can't change the allocation. */ + pMemNt->pvSecureMem = g_pfnrtMmSecureVirtualMemory(pv, cb, + fAccess & RTMEM_PROT_WRITE + ? PAGE_READWRITE + : PAGE_READONLY); + if (!pMemNt->pvSecureMem) + { + rc = VERR_NO_MEMORY; + break; + } + } + + /* next */ + cbTotal += cbCur; + pb += cbCur; + } + if (RT_SUCCESS(rc)) + { + Assert(pMemNt->cMdls == cMdls); + pMemNt->Core.u.Lock.R0Process = R0Process; + *ppMem = &pMemNt->Core; + return rc; + } + + /* + * We failed, perform cleanups. + */ + while (iMdl-- > 0) + { + MmUnlockPages(pMemNt->apMdls[iMdl]); + IoFreeMdl(pMemNt->apMdls[iMdl]); + pMemNt->apMdls[iMdl] = NULL; + } + if (pMemNt->pvSecureMem) + { + if (g_pfnrtMmUnsecureVirtualMemory) + g_pfnrtMmUnsecureVirtualMemory(pMemNt->pvSecureMem); + pMemNt->pvSecureMem = NULL; + } + + rtR0MemObjDelete(&pMemNt->Core); + return rc; +} + + +DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, + RTR0PROCESS R0Process, const char *pszTag) +{ + AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED); + /* (Can use MmProbeAndLockProcessPages if we need to mess with other processes later.) */ + return rtR0MemObjNtLock(ppMem, (void *)R3Ptr, cb, fAccess, R0Process, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag) +{ + return rtR0MemObjNtLock(ppMem, pv, cb, fAccess, NIL_RTR0PROCESS, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment, + const char *pszTag) +{ + /* + * MmCreateSection(SEC_RESERVE) + MmMapViewInSystemSpace perhaps? + * Or MmAllocateMappingAddress? + */ + RT_NOREF(ppMem, pvFixed, cb, uAlignment, pszTag); + return VERR_NOT_SUPPORTED; +} + + +DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, + RTR0PROCESS R0Process, const char *pszTag) +{ + /* + * ZeCreateSection(SEC_RESERVE) + ZwMapViewOfSection perhaps? + */ + RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag); + return VERR_NOT_SUPPORTED; +} + + +/** + * Internal worker for rtR0MemObjNativeMapKernel and rtR0MemObjNativeMapUser. + * + * @returns IPRT status code. + * @param ppMem Where to store the memory object for the mapping. + * @param pMemToMap The memory object to map. + * @param pvFixed Where to map it. (void *)-1 if anywhere is fine. + * @param uAlignment The alignment requirement for the mapping. + * @param fProt The desired page protection for the mapping. + * @param R0Process If NIL_RTR0PROCESS map into system (kernel) memory. + * If not nil, it's the current process. + * @param offSub Offset into @a pMemToMap to start mapping. + * @param cbSub The number of bytes to map from @a pMapToMem. 0 if + * we're to map everything. Non-zero if @a offSub is + * non-zero. + * @param pszTag Allocation tag used for statistics and such. + */ +static int rtR0MemObjNtMap(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment, + unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag) +{ + int rc = VERR_MAP_FAILED; + + /* + * Check that the specified alignment is supported. + */ + if (uAlignment > PAGE_SIZE) + return VERR_NOT_SUPPORTED; + + /* + * There are two basic cases here, either we've got an MDL and can + * map it using MmMapLockedPages, or we've got a contiguous physical + * range (MMIO most likely) and can use MmMapIoSpace. + */ + PRTR0MEMOBJNT pMemNtToMap = (PRTR0MEMOBJNT)pMemToMap; + if (pMemNtToMap->cMdls) + { + /* don't attempt map locked regions with more than one mdl. */ + if (pMemNtToMap->cMdls != 1) + return VERR_NOT_SUPPORTED; + + /* Need g_pfnrtMmMapLockedPagesSpecifyCache to map to a specific address. */ + if (pvFixed != (void *)-1 && g_pfnrtMmMapLockedPagesSpecifyCache == NULL) + return VERR_NOT_SUPPORTED; + + /* we can't map anything to the first page, sorry. */ + if (pvFixed == 0) + return VERR_NOT_SUPPORTED; + + /* only one system mapping for now - no time to figure out MDL restrictions right now. */ + if ( pMemNtToMap->Core.uRel.Parent.cMappings + && R0Process == NIL_RTR0PROCESS) + { + if (pMemNtToMap->Core.enmType != RTR0MEMOBJTYPE_PHYS_NC) + return VERR_NOT_SUPPORTED; + uint32_t iMapping = pMemNtToMap->Core.uRel.Parent.cMappings; + while (iMapping-- > 0) + { + PRTR0MEMOBJNT pMapping = (PRTR0MEMOBJNT)pMemNtToMap->Core.uRel.Parent.papMappings[iMapping]; + if ( pMapping->Core.enmType != RTR0MEMOBJTYPE_MAPPING + || pMapping->Core.u.Mapping.R0Process == NIL_RTR0PROCESS) + return VERR_NOT_SUPPORTED; + } + } + + /* Create a partial MDL if this is a sub-range request. */ + PMDL pMdl; + if (!offSub && !cbSub) + pMdl = pMemNtToMap->apMdls[0]; + else + { + pMdl = IoAllocateMdl(NULL, (ULONG)cbSub, FALSE, FALSE, NULL); + if (pMdl) + IoBuildPartialMdl(pMemNtToMap->apMdls[0], pMdl, + (uint8_t *)MmGetMdlVirtualAddress(pMemNtToMap->apMdls[0]) + offSub, (ULONG)cbSub); + else + { + IoFreeMdl(pMdl); + return VERR_NO_MEMORY; + } + } + + __try + { + /** @todo uAlignment */ + /** @todo How to set the protection on the pages? */ + void *pv; + if (g_pfnrtMmMapLockedPagesSpecifyCache) + pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, + R0Process == NIL_RTR0PROCESS ? KernelMode : UserMode, + MmCached, + pvFixed != (void *)-1 ? pvFixed : NULL, + FALSE /* no bug check on failure */, + NormalPagePriority); + else + pv = MmMapLockedPages(pMdl, R0Process == NIL_RTR0PROCESS ? KernelMode : UserMode); + if (pv) + { + NOREF(fProt); + + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew( !offSub && !cbSub + ? sizeof(*pMemNt) : RT_UOFFSETOF_DYN(RTR0MEMOBJNT, apMdls[1]), + RTR0MEMOBJTYPE_MAPPING, pv, pMemNtToMap->Core.cb, pszTag); + if (pMemNt) + { + pMemNt->Core.u.Mapping.R0Process = R0Process; + if (!offSub && !cbSub) + pMemNt->fSubMapping = false; + else + { + pMemNt->apMdls[0] = pMdl; + pMemNt->cMdls = 1; + pMemNt->fSubMapping = true; + } + + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + + rc = VERR_NO_MEMORY; + MmUnmapLockedPages(pv, pMdl); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { +#ifdef LOG_ENABLED + NTSTATUS rcNt = GetExceptionCode(); + Log(("rtR0MemObjNtMap: Exception Code %#x\n", rcNt)); +#endif + + /* nothing */ + rc = VERR_MAP_FAILED; + } + + } + else + { + AssertReturn( pMemNtToMap->Core.enmType == RTR0MEMOBJTYPE_PHYS + && !pMemNtToMap->Core.u.Phys.fAllocated, VERR_INTERNAL_ERROR); + + /* cannot map phys mem to user space (yet). */ + if (R0Process != NIL_RTR0PROCESS) + return VERR_NOT_SUPPORTED; + + /* Cannot sub-mak these (yet). */ + AssertMsgReturn(!offSub && !cbSub, ("%#zx %#zx\n", offSub, cbSub), VERR_NOT_SUPPORTED); + + + /** @todo uAlignment */ + /** @todo How to set the protection on the pages? */ + PHYSICAL_ADDRESS Phys; + Phys.QuadPart = pMemNtToMap->Core.u.Phys.PhysBase; + void *pv = MmMapIoSpace(Phys, pMemNtToMap->Core.cb, + pMemNtToMap->Core.u.Phys.uCachePolicy == RTMEM_CACHE_POLICY_MMIO ? MmNonCached : MmCached); + if (pv) + { + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_MAPPING, pv, + pMemNtToMap->Core.cb, pszTag); + if (pMemNt) + { + pMemNt->Core.u.Mapping.R0Process = R0Process; + *ppMem = &pMemNt->Core; + return VINF_SUCCESS; + } + + rc = VERR_NO_MEMORY; + MmUnmapIoSpace(pv, pMemNtToMap->Core.cb); + } + } + + NOREF(uAlignment); NOREF(fProt); + return rc; +} + + +DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment, + unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag) +{ + return rtR0MemObjNtMap(ppMem, pMemToMap, pvFixed, uAlignment, fProt, NIL_RTR0PROCESS, offSub, cbSub, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment, + unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag) +{ + AssertReturn(R0Process == RTR0ProcHandleSelf(), VERR_NOT_SUPPORTED); + return rtR0MemObjNtMap(ppMem, pMemToMap, (void *)R3PtrFixed, uAlignment, fProt, R0Process, offSub, cbSub, pszTag); +} + + +DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt) +{ +#if 0 + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)pMem; +#endif + + /* + * Seems there are some issues with this MmProtectMdlSystemAddress API, so + * this code isn't currently enabled until we've tested it with the verifier. + */ +#if 0 + /* + * The API we've got requires a kernel mapping. + */ + if ( pMemNt->cMdls + && g_pfnrtMmProtectMdlSystemAddress + && (g_uRtNtMajorVer > 6 || (g_uRtNtMajorVer == 6 && g_uRtNtMinorVer >= 1)) /* Windows 7 and later. */ + && pMemNt->Core.pv != NULL + && ( pMemNt->Core.enmType == RTR0MEMOBJTYPE_PAGE + || pMemNt->Core.enmType == RTR0MEMOBJTYPE_LOW + || pMemNt->Core.enmType == RTR0MEMOBJTYPE_CONT + || ( pMemNt->Core.enmType == RTR0MEMOBJTYPE_LOCK + && pMemNt->Core.u.Lock.R0Process == NIL_RTPROCESS) + || ( pMemNt->Core.enmType == RTR0MEMOBJTYPE_MAPPING + && pMemNt->Core.u.Mapping.R0Process == NIL_RTPROCESS) ) ) + { + /* Convert the protection. */ + LOCK_OPERATION enmLockOp; + ULONG fAccess; + switch (fProt) + { + case RTMEM_PROT_NONE: + fAccess = PAGE_NOACCESS; + enmLockOp = IoReadAccess; + break; + case RTMEM_PROT_READ: + fAccess = PAGE_READONLY; + enmLockOp = IoReadAccess; + break; + case RTMEM_PROT_WRITE: + case RTMEM_PROT_WRITE | RTMEM_PROT_READ: + fAccess = PAGE_READWRITE; + enmLockOp = IoModifyAccess; + break; + case RTMEM_PROT_EXEC: + fAccess = PAGE_EXECUTE; + enmLockOp = IoReadAccess; + break; + case RTMEM_PROT_EXEC | RTMEM_PROT_READ: + fAccess = PAGE_EXECUTE_READ; + enmLockOp = IoReadAccess; + break; + case RTMEM_PROT_EXEC | RTMEM_PROT_WRITE: + case RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ: + fAccess = PAGE_EXECUTE_READWRITE; + enmLockOp = IoModifyAccess; + break; + default: + AssertFailedReturn(VERR_INVALID_FLAGS); + } + + NTSTATUS rcNt = STATUS_SUCCESS; +# if 0 /** @todo test this against the verifier. */ + if (offSub == 0 && pMemNt->Core.cb == cbSub) + { + uint32_t iMdl = pMemNt->cMdls; + while (iMdl-- > 0) + { + rcNt = g_pfnrtMmProtectMdlSystemAddress(pMemNt->apMdls[i], fAccess); + if (!NT_SUCCESS(rcNt)) + break; + } + } + else +# endif + { + /* + * We ASSUME the following here: + * - MmProtectMdlSystemAddress can deal with nonpaged pool memory + * - MmProtectMdlSystemAddress doesn't actually store anything in the MDL we pass it. + * - We are not required to call MmProtectMdlSystemAddress with PAGE_READWRITE for the + * exact same ranges prior to freeing them. + * + * So, we lock the pages temporarily, call the API and unlock them. + */ + uint8_t *pbCur = (uint8_t *)pMemNt->Core.pv + offSub; + while (cbSub > 0 && NT_SUCCESS(rcNt)) + { + size_t cbCur = cbSub; + if (cbCur > MAX_LOCK_MEM_SIZE) + cbCur = MAX_LOCK_MEM_SIZE; + PMDL pMdl = IoAllocateMdl(pbCur, (ULONG)cbCur, FALSE, FALSE, NULL); + if (pMdl) + { + __try + { + MmProbeAndLockPages(pMdl, KernelMode, enmLockOp); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + rcNt = GetExceptionCode(); + } + if (NT_SUCCESS(rcNt)) + { + rcNt = g_pfnrtMmProtectMdlSystemAddress(pMdl, fAccess); + MmUnlockPages(pMdl); + } + IoFreeMdl(pMdl); + } + else + rcNt = STATUS_NO_MEMORY; + pbCur += cbCur; + cbSub -= cbCur; + } + } + + if (NT_SUCCESS(rcNt)) + return VINF_SUCCESS; + return RTErrConvertFromNtStatus(rcNt); + } +#else + RT_NOREF4(pMem, offSub, cbSub, fProt); +#endif + + return VERR_NOT_SUPPORTED; +} + + +DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage) +{ + PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)pMem; + + if (pMemNt->cMdls) + { + if (pMemNt->cMdls == 1) + { + PPFN_NUMBER paPfns = MmGetMdlPfnArray(pMemNt->apMdls[0]); + return (RTHCPHYS)paPfns[iPage] << PAGE_SHIFT; + } + + size_t iMdl = iPage / (MAX_LOCK_MEM_SIZE >> PAGE_SHIFT); + size_t iMdlPfn = iPage % (MAX_LOCK_MEM_SIZE >> PAGE_SHIFT); + PPFN_NUMBER paPfns = MmGetMdlPfnArray(pMemNt->apMdls[iMdl]); + return (RTHCPHYS)paPfns[iMdlPfn] << PAGE_SHIFT; + } + + switch (pMemNt->Core.enmType) + { + case RTR0MEMOBJTYPE_MAPPING: + return rtR0MemObjNativeGetPagePhysAddr(pMemNt->Core.uRel.Child.pParent, iPage); + + case RTR0MEMOBJTYPE_PHYS: + return pMemNt->Core.u.Phys.PhysBase + (iPage << PAGE_SHIFT); + + case RTR0MEMOBJTYPE_PAGE: + case RTR0MEMOBJTYPE_PHYS_NC: + case RTR0MEMOBJTYPE_LOW: + case RTR0MEMOBJTYPE_CONT: + case RTR0MEMOBJTYPE_LOCK: + default: + AssertMsgFailed(("%d\n", pMemNt->Core.enmType)); + case RTR0MEMOBJTYPE_RES_VIRT: + return NIL_RTHCPHYS; + } +} + diff --git a/src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp new file mode 100644 index 00000000..79df9d61 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp @@ -0,0 +1,210 @@ +/* $Id: memuserkernel-r0drv-nt.cpp $ */ +/** @file + * IPRT - User & Kernel Memory, Ring-0 Driver, NT. + */ + +/* + * Copyright (C) 2009-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 "internal-r0drv-nt.h" + + +RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb) +{ + __try + { + ProbeForRead((PVOID)R3PtrSrc, cb, 1); + memcpy(pvDst, (void const *)R3PtrSrc, cb); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } + return VINF_SUCCESS; +} + + +RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb) +{ + __try + { + ProbeForWrite((PVOID)R3PtrDst, cb, 1); + memcpy((void *)R3PtrDst, pvSrc, cb); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } + return VINF_SUCCESS; +} + + +RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr) +{ +#ifdef IPRT_TARGET_NT4 + uintptr_t const uLast = g_puRtMmHighestUserAddress ? *g_puRtMmHighestUserAddress : ~(uintptr_t)0 / 2; +#else + uintptr_t const uLast = (uintptr_t)MM_HIGHEST_USER_ADDRESS; +#endif + return R3Ptr <= uLast; +} + + +RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv) +{ +#ifdef IPRT_TARGET_NT4 + uintptr_t const uFirst = g_puRtMmSystemRangeStart ? *g_puRtMmSystemRangeStart : ~(uintptr_t)0 / 2 + 1; +#else + uintptr_t const uFirst = (uintptr_t)MM_SYSTEM_RANGE_START; +#endif + return (uintptr_t)pv >= uFirst; +} + + +RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void) +{ + return true; +} + + +RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb) +{ + if (!RTR0MemKernelIsValidAddr((void *)pvSrc)) + return VERR_ACCESS_DENIED; + + uint8_t *pbDst = (uint8_t *)pvDst; + uint8_t const *pbSrc = (uint8_t const *)pvSrc; + +#if 0 + /* + * The try+except stuff does not work for kernel addresses. + */ + __try + { + while (cb-- > 0) + *pbDst++ = *pbSrc++; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } +#else + /* + * This is the best I can come up with for now: Work page-by-page using MmIsAddressValid. + */ + while (cb > 0) + { + if (!MmIsAddressValid((PVOID)pbSrc)) + return VERR_ACCESS_DENIED; + + size_t cbToCopy = (uintptr_t)pbSrc & PAGE_OFFSET_MASK; + if (cbToCopy > cb) + cbToCopy = cb; + cb -= cbToCopy; + + __try /* doesn't work, but can't hurt, right? */ + { + while (cbToCopy-- > 0) + *pbDst++ = *pbSrc++; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } + } +#endif + return VINF_SUCCESS; +} + + +RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb) +{ + if (!RTR0MemKernelIsValidAddr(pvDst)) + return VERR_ACCESS_DENIED; +#if 0 + uint8_t *pbDst = (uint8_t *)pvDst; + uint8_t const *pbSrc = (uint8_t const *)pvSrc; +# if 0 + /* + * The try+except stuff does not work for kernel addresses. + */ + __try + { + while (cb-- > 0) + *pbDst++ = *pbSrc++; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } + +# else + /* + * This is the best I can come up with for now: Work page-by-page using MmIsAddressValid. + * Note! MmIsAddressValid does not indicate that it's writable, so we're a bit buggered if it isn't... + */ + while (cb > 0) + { + if (!MmIsAddressValid((PVOID)pbSrc)) + return VERR_ACCESS_DENIED; + + size_t cbToCopy = (uintptr_t)pbSrc & PAGE_OFFSET_MASK; + if (cbToCopy > cb) + cbToCopy = cb; + cb -= cbToCopy; + + __try /* doesn't work, but can't hurt, right? */ + { + while (cbToCopy-- > 0) + *pbDst++ = *pbSrc++; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } + } +# endif + return VINF_SUCCESS; +#else + RT_NOREF(pvDst, pvSrc, cb); + return VERR_NOT_SUPPORTED; +#endif +} + diff --git a/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp new file mode 100644 index 00000000..a2b27be2 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp @@ -0,0 +1,1996 @@ +/* $Id: mp-r0drv-nt.cpp $ */ +/** @file + * IPRT - Multiprocessor, Ring-0 Driver, NT. + */ + +/* + * Copyright (C) 2008-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 +#include +#include +#include "r0drv/mp-r0drv.h" +#include "symdb.h" +#include "internal-r0drv-nt.h" +#include "internal/mp.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef enum +{ + RT_NT_CPUID_SPECIFIC, + RT_NT_CPUID_PAIR, + RT_NT_CPUID_OTHERS, + RT_NT_CPUID_ALL +} RT_NT_CPUID; + + +/** + * Used by the RTMpOnSpecific. + */ +typedef struct RTMPNTONSPECIFICARGS +{ + /** Set if we're executing. */ + bool volatile fExecuting; + /** Set when done executing. */ + bool volatile fDone; + /** Number of references to this heap block. */ + uint32_t volatile cRefs; + /** Event that the calling thread is waiting on. */ + KEVENT DoneEvt; + /** The deferred procedure call object. */ + KDPC Dpc; + /** The callback argument package. */ + RTMPARGS CallbackArgs; +} RTMPNTONSPECIFICARGS; +/** Pointer to an argument/state structure for RTMpOnSpecific on NT. */ +typedef RTMPNTONSPECIFICARGS *PRTMPNTONSPECIFICARGS; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Inactive bit for g_aidRtMpNtByCpuSetIdx. */ +#define RTMPNT_ID_F_INACTIVE RT_BIT_32(31) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Maximum number of processor groups. */ +uint32_t g_cRtMpNtMaxGroups; +/** Maximum number of processors. */ +uint32_t g_cRtMpNtMaxCpus; +/** Number of active processors. */ +uint32_t volatile g_cRtMpNtActiveCpus; +/** The NT CPU set. + * KeQueryActiveProcssors() cannot be called at all IRQLs and therefore we'll + * have to cache it. Fortunately, NT doesn't really support taking CPUs offline, + * and taking them online was introduced with W2K8 where it is intended for virtual + * machines and not real HW. We update this, g_cRtMpNtActiveCpus and + * g_aidRtMpNtByCpuSetIdx from the rtR0NtMpProcessorChangeCallback. + */ +RTCPUSET g_rtMpNtCpuSet; + +/** Static per group info. + * @remarks With 256 groups this takes up 33KB. */ +static struct +{ + /** The max CPUs in the group. */ + uint16_t cMaxCpus; + /** The number of active CPUs at the time of initialization. */ + uint16_t cActiveCpus; + /** CPU set indexes for each CPU in the group. */ + int16_t aidxCpuSetMembers[64]; +} g_aRtMpNtCpuGroups[256]; +/** Maps CPU set indexes to RTCPUID. + * Inactive CPUs has bit 31 set (RTMPNT_ID_F_INACTIVE) so we can identify them + * and shuffle duplicates during CPU hotplugging. We assign temporary IDs to + * the inactive CPUs starting at g_cRtMpNtMaxCpus - 1, ASSUMING that active + * CPUs has IDs from 0 to g_cRtMpNtActiveCpus. */ +RTCPUID g_aidRtMpNtByCpuSetIdx[RTCPUSET_MAX_CPUS]; +/** The handle of the rtR0NtMpProcessorChangeCallback registration. */ +static PVOID g_pvMpCpuChangeCallback = NULL; +/** Size of the KAFFINITY_EX structure. + * This increased from 20 to 32 bitmap words in the 2020 H2 windows 10 release + * (i.e. 1280 to 2048 CPUs). We expect this to increase in the future. */ +static size_t g_cbRtMpNtKaffinityEx = RT_UOFFSETOF(KAFFINITY_EX, Bitmap) + + RT_SIZEOFMEMB(KAFFINITY_EX, Bitmap[0]) * 256; +/** The size value of the KAFFINITY_EX structure. */ +static uint16_t g_cRtMpNtKaffinityExEntries = 256; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static VOID __stdcall rtR0NtMpProcessorChangeCallback(void *pvUser, PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT pChangeCtx, + PNTSTATUS prcOperationStatus); +static int rtR0NtInitQueryGroupRelations(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX **ppInfo); + + + +/** + * Initalizes multiprocessor globals (called by rtR0InitNative). + * + * @returns IPRT status code. + * @param pOsVerInfo Version information. + */ +DECLHIDDEN(int) rtR0MpNtInit(RTNTSDBOSVER const *pOsVerInfo) +{ +#define MY_CHECK_BREAK(a_Check, a_DbgPrintArgs) \ + AssertMsgBreakStmt(a_Check, a_DbgPrintArgs, DbgPrint a_DbgPrintArgs; rc = VERR_INTERNAL_ERROR_4 ) +#define MY_CHECK_RETURN(a_Check, a_DbgPrintArgs, a_rcRet) \ + AssertMsgReturnStmt(a_Check, a_DbgPrintArgs, DbgPrint a_DbgPrintArgs, a_rcRet) +#define MY_CHECK(a_Check, a_DbgPrintArgs) \ + AssertMsgStmt(a_Check, a_DbgPrintArgs, DbgPrint a_DbgPrintArgs; rc = VERR_INTERNAL_ERROR_4 ) + + /* + * API combination checks. + */ + MY_CHECK_RETURN(!g_pfnrtKeSetTargetProcessorDpcEx || g_pfnrtKeGetProcessorNumberFromIndex, + ("IPRT: Fatal: Missing KeSetTargetProcessorDpcEx without KeGetProcessorNumberFromIndex!\n"), + VERR_SYMBOL_NOT_FOUND); + + /* + * Get max number of processor groups. + * + * We may need to upadjust this number below, because windows likes to keep + * all options open when it comes to hotplugged CPU group assignments. A + * server advertising up to 64 CPUs in the ACPI table will get a result of + * 64 from KeQueryMaximumGroupCount. That makes sense. However, when windows + * server 2012 does a two processor group setup for it, the sum of the + * GroupInfo[*].MaximumProcessorCount members below is 128. This is probably + * because windows doesn't want to make decisions grouping of hotpluggable CPUs. + * So, we need to bump the maximum count to 128 below do deal with this as we + * want to have valid CPU set indexes for all potential CPUs - how could we + * otherwise use the RTMpGetSet() result and also RTCpuSetCount(RTMpGetSet()) + * should equal RTMpGetCount(). + */ + if (g_pfnrtKeQueryMaximumGroupCount) + { + g_cRtMpNtMaxGroups = g_pfnrtKeQueryMaximumGroupCount(); + MY_CHECK_RETURN(g_cRtMpNtMaxGroups <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxGroups > 0, + ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u\n", g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS), + VERR_MP_TOO_MANY_CPUS); + } + else + g_cRtMpNtMaxGroups = 1; + + /* + * Get max number CPUs. + * This also defines the range of NT CPU indexes, RTCPUID and index into RTCPUSET. + */ + if (g_pfnrtKeQueryMaximumProcessorCountEx) + { + g_cRtMpNtMaxCpus = g_pfnrtKeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS); + MY_CHECK_RETURN(g_cRtMpNtMaxCpus <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxCpus > 0, + ("IPRT: Fatal: g_cRtMpNtMaxCpus=%u, max %u [KeQueryMaximumProcessorCountEx]\n", + g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS), + VERR_MP_TOO_MANY_CPUS); + } + else if (g_pfnrtKeQueryMaximumProcessorCount) + { + g_cRtMpNtMaxCpus = g_pfnrtKeQueryMaximumProcessorCount(); + MY_CHECK_RETURN(g_cRtMpNtMaxCpus <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxCpus > 0, + ("IPRT: Fatal: g_cRtMpNtMaxCpus=%u, max %u [KeQueryMaximumProcessorCount]\n", + g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS), + VERR_MP_TOO_MANY_CPUS); + } + else if (g_pfnrtKeQueryActiveProcessors) + { + KAFFINITY fActiveProcessors = g_pfnrtKeQueryActiveProcessors(); + MY_CHECK_RETURN(fActiveProcessors != 0, + ("IPRT: Fatal: KeQueryActiveProcessors returned 0!\n"), + VERR_INTERNAL_ERROR_2); + g_cRtMpNtMaxCpus = 0; + do + { + g_cRtMpNtMaxCpus++; + fActiveProcessors >>= 1; + } while (fActiveProcessors); + } + else + g_cRtMpNtMaxCpus = KeNumberProcessors; + + /* + * Just because we're a bit paranoid about getting something wrong wrt to the + * kernel interfaces, we try 16 times to get the KeQueryActiveProcessorCountEx + * and KeQueryLogicalProcessorRelationship information to match up. + */ + for (unsigned cTries = 0;; cTries++) + { + /* + * Get number of active CPUs. + */ + if (g_pfnrtKeQueryActiveProcessorCountEx) + { + g_cRtMpNtActiveCpus = g_pfnrtKeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS); + MY_CHECK_RETURN(g_cRtMpNtActiveCpus <= g_cRtMpNtMaxCpus && g_cRtMpNtActiveCpus > 0, + ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u [KeQueryActiveProcessorCountEx]\n", + g_cRtMpNtMaxGroups, g_cRtMpNtMaxCpus), + VERR_MP_TOO_MANY_CPUS); + } + else if (g_pfnrtKeQueryActiveProcessorCount) + { + g_cRtMpNtActiveCpus = g_pfnrtKeQueryActiveProcessorCount(NULL); + MY_CHECK_RETURN(g_cRtMpNtActiveCpus <= g_cRtMpNtMaxCpus && g_cRtMpNtActiveCpus > 0, + ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u [KeQueryActiveProcessorCount]\n", + g_cRtMpNtMaxGroups, g_cRtMpNtMaxCpus), + VERR_MP_TOO_MANY_CPUS); + } + else + g_cRtMpNtActiveCpus = g_cRtMpNtMaxCpus; + + /* + * Query the details for the groups to figure out which CPUs are online as + * well as the NT index limit. + */ + for (unsigned i = 0; i < RT_ELEMENTS(g_aidRtMpNtByCpuSetIdx); i++) +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpNtByCpuSetIdx[i] = NIL_RTCPUID; +#else + g_aidRtMpNtByCpuSetIdx[i] = i < g_cRtMpNtMaxCpus ? i : NIL_RTCPUID; +#endif + for (unsigned idxGroup = 0; idxGroup < RT_ELEMENTS(g_aRtMpNtCpuGroups); idxGroup++) + { + g_aRtMpNtCpuGroups[idxGroup].cMaxCpus = 0; + g_aRtMpNtCpuGroups[idxGroup].cActiveCpus = 0; + for (unsigned idxMember = 0; idxMember < RT_ELEMENTS(g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers); idxMember++) + g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1; + } + + if (g_pfnrtKeQueryLogicalProcessorRelationship) + { + MY_CHECK_RETURN(g_pfnrtKeGetProcessorIndexFromNumber, + ("IPRT: Fatal: Found KeQueryLogicalProcessorRelationship but not KeGetProcessorIndexFromNumber!\n"), + VERR_SYMBOL_NOT_FOUND); + MY_CHECK_RETURN(g_pfnrtKeGetProcessorNumberFromIndex, + ("IPRT: Fatal: Found KeQueryLogicalProcessorRelationship but not KeGetProcessorIndexFromNumber!\n"), + VERR_SYMBOL_NOT_FOUND); + MY_CHECK_RETURN(g_pfnrtKeSetTargetProcessorDpcEx, + ("IPRT: Fatal: Found KeQueryLogicalProcessorRelationship but not KeSetTargetProcessorDpcEx!\n"), + VERR_SYMBOL_NOT_FOUND); + + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = NULL; + int rc = rtR0NtInitQueryGroupRelations(&pInfo); + if (RT_FAILURE(rc)) + return rc; + + MY_CHECK(pInfo->Group.MaximumGroupCount == g_cRtMpNtMaxGroups, + ("IPRT: Fatal: MaximumGroupCount=%u != g_cRtMpNtMaxGroups=%u!\n", + pInfo->Group.MaximumGroupCount, g_cRtMpNtMaxGroups)); + MY_CHECK(pInfo->Group.ActiveGroupCount > 0 && pInfo->Group.ActiveGroupCount <= g_cRtMpNtMaxGroups, + ("IPRT: Fatal: ActiveGroupCount=%u != g_cRtMpNtMaxGroups=%u!\n", + pInfo->Group.ActiveGroupCount, g_cRtMpNtMaxGroups)); + + /* + * First we need to recalc g_cRtMpNtMaxCpus (see above). + */ + uint32_t cMaxCpus = 0; + uint32_t idxGroup; + for (idxGroup = 0; RT_SUCCESS(rc) && idxGroup < pInfo->Group.ActiveGroupCount; idxGroup++) + { + const PROCESSOR_GROUP_INFO *pGrpInfo = &pInfo->Group.GroupInfo[idxGroup]; + MY_CHECK_BREAK(pGrpInfo->MaximumProcessorCount <= MAXIMUM_PROC_PER_GROUP, + ("IPRT: Fatal: MaximumProcessorCount=%u\n", pGrpInfo->MaximumProcessorCount)); + MY_CHECK_BREAK(pGrpInfo->ActiveProcessorCount <= pGrpInfo->MaximumProcessorCount, + ("IPRT: Fatal: ActiveProcessorCount=%u > MaximumProcessorCount=%u\n", + pGrpInfo->ActiveProcessorCount, pGrpInfo->MaximumProcessorCount)); + cMaxCpus += pGrpInfo->MaximumProcessorCount; + } + if (cMaxCpus > g_cRtMpNtMaxCpus && RT_SUCCESS(rc)) + { + DbgPrint("IPRT: g_cRtMpNtMaxCpus=%u -> %u\n", g_cRtMpNtMaxCpus, cMaxCpus); +#ifndef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + uint32_t i = RT_MIN(cMaxCpus, RT_ELEMENTS(g_aidRtMpNtByCpuSetIdx)); + while (i-- > g_cRtMpNtMaxCpus) + g_aidRtMpNtByCpuSetIdx[i] = i; +#endif + g_cRtMpNtMaxCpus = cMaxCpus; + if (g_cRtMpNtMaxGroups > RTCPUSET_MAX_CPUS) + { + MY_CHECK(g_cRtMpNtMaxGroups <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxGroups > 0, + ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u\n", g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS)); + rc = VERR_MP_TOO_MANY_CPUS; + } + } + + /* + * Calc online mask, partition IDs and such. + * + * Also check ASSUMPTIONS: + * + * 1. Processor indexes going from 0 and up to + * KeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS) - 1. + * + * 2. Currently valid processor indexes, i.e. accepted by + * KeGetProcessorIndexFromNumber & KeGetProcessorNumberFromIndex, goes + * from 0 thru KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS) - 1. + * + * 3. PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the number of + * relevant bits in the ActiveProcessorMask (from LSB). + * + * 4. Active processor count found in KeQueryLogicalProcessorRelationship + * output matches what KeQueryActiveProcessorCountEx(ALL) returns. + * + * 5. Active + inactive processor counts in same does not exceed + * KeQueryMaximumProcessorCountEx(ALL). + * + * Note! Processor indexes are assigned as CPUs come online and are not + * preallocated according to group maximums. Since CPUS are only taken + * online and never offlined, this means that internal CPU bitmaps are + * never sparse and no time is wasted scanning unused bits. + * + * Unfortunately, it means that ring-3 cannot easily guess the index + * assignments when hotswapping is used, and must use GIP when available. + */ + RTCpuSetEmpty(&g_rtMpNtCpuSet); + uint32_t cInactive = 0; + uint32_t cActive = 0; + uint32_t idxCpuMax = 0; + uint32_t idxCpuSetNextInactive = g_cRtMpNtMaxCpus - 1; + for (idxGroup = 0; RT_SUCCESS(rc) && idxGroup < pInfo->Group.ActiveGroupCount; idxGroup++) + { + const PROCESSOR_GROUP_INFO *pGrpInfo = &pInfo->Group.GroupInfo[idxGroup]; + MY_CHECK_BREAK(pGrpInfo->MaximumProcessorCount <= MAXIMUM_PROC_PER_GROUP, + ("IPRT: Fatal: MaximumProcessorCount=%u\n", pGrpInfo->MaximumProcessorCount)); + MY_CHECK_BREAK(pGrpInfo->ActiveProcessorCount <= pGrpInfo->MaximumProcessorCount, + ("IPRT: Fatal: ActiveProcessorCount=%u > MaximumProcessorCount=%u\n", + pGrpInfo->ActiveProcessorCount, pGrpInfo->MaximumProcessorCount)); + + g_aRtMpNtCpuGroups[idxGroup].cMaxCpus = pGrpInfo->MaximumProcessorCount; + g_aRtMpNtCpuGroups[idxGroup].cActiveCpus = pGrpInfo->ActiveProcessorCount; + + for (uint32_t idxMember = 0; idxMember < pGrpInfo->MaximumProcessorCount; idxMember++) + { + PROCESSOR_NUMBER ProcNum; + ProcNum.Group = (USHORT)idxGroup; + ProcNum.Number = (UCHAR)idxMember; + ProcNum.Reserved = 0; + ULONG idxCpu = g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum); + if (idxCpu != INVALID_PROCESSOR_INDEX) + { + MY_CHECK_BREAK(idxCpu < g_cRtMpNtMaxCpus && idxCpu < RTCPUSET_MAX_CPUS, /* ASSUMPTION #1 */ + ("IPRT: Fatal: idxCpu=%u >= g_cRtMpNtMaxCpus=%u (RTCPUSET_MAX_CPUS=%u)\n", + idxCpu, g_cRtMpNtMaxCpus, RTCPUSET_MAX_CPUS)); + if (idxCpu > idxCpuMax) + idxCpuMax = idxCpu; + g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpu; +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpNtByCpuSetIdx[idxCpu] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember); +#endif + + ProcNum.Group = UINT16_MAX; + ProcNum.Number = UINT8_MAX; + ProcNum.Reserved = UINT8_MAX; + NTSTATUS rcNt = g_pfnrtKeGetProcessorNumberFromIndex(idxCpu, &ProcNum); + MY_CHECK_BREAK(NT_SUCCESS(rcNt), + ("IPRT: Fatal: KeGetProcessorNumberFromIndex(%u,) -> %#x!\n", idxCpu, rcNt)); + MY_CHECK_BREAK(ProcNum.Group == idxGroup && ProcNum.Number == idxMember, + ("IPRT: Fatal: KeGetProcessorXxxxFromYyyy roundtrip error for %#x! Group: %u vs %u, Number: %u vs %u\n", + idxCpu, ProcNum.Group, idxGroup, ProcNum.Number, idxMember)); + + if (pGrpInfo->ActiveProcessorMask & RT_BIT_64(idxMember)) + { + RTCpuSetAddByIndex(&g_rtMpNtCpuSet, idxCpu); + cActive++; + } + else + cInactive++; /* (This is a little unexpected, but not important as long as things add up below.) */ + } + else + { + /* Must be not present / inactive when KeGetProcessorIndexFromNumber fails. */ + MY_CHECK_BREAK(!(pGrpInfo->ActiveProcessorMask & RT_BIT_64(idxMember)), + ("IPRT: Fatal: KeGetProcessorIndexFromNumber(%u/%u) failed but CPU is active! cMax=%u cActive=%u fActive=%p\n", + idxGroup, idxMember, pGrpInfo->MaximumProcessorCount, pGrpInfo->ActiveProcessorCount, + pGrpInfo->ActiveProcessorMask)); + cInactive++; + if (idxCpuSetNextInactive >= g_cRtMpNtActiveCpus) + { + g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive; +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpNtByCpuSetIdx[idxCpuSetNextInactive] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember) + | RTMPNT_ID_F_INACTIVE; +#endif + idxCpuSetNextInactive--; + } + } + } + } + + MY_CHECK(cInactive + cActive <= g_cRtMpNtMaxCpus, /* ASSUMPTION #5 (not '==' because of inactive groups) */ + ("IPRT: Fatal: cInactive=%u + cActive=%u > g_cRtMpNtMaxCpus=%u\n", cInactive, cActive, g_cRtMpNtMaxCpus)); + + /* Deal with inactive groups using KeQueryMaximumProcessorCountEx or as + best as we can by as best we can by stipulating maximum member counts + from the previous group. */ + if ( RT_SUCCESS(rc) + && idxGroup < pInfo->Group.MaximumGroupCount) + { + uint16_t cInactiveLeft = g_cRtMpNtMaxCpus - (cInactive + cActive); + while (idxGroup < pInfo->Group.MaximumGroupCount) + { + uint32_t cMaxMembers = 0; + if (g_pfnrtKeQueryMaximumProcessorCountEx) + cMaxMembers = g_pfnrtKeQueryMaximumProcessorCountEx(idxGroup); + if (cMaxMembers != 0 || cInactiveLeft == 0) + AssertStmt(cMaxMembers <= cInactiveLeft, cMaxMembers = cInactiveLeft); + else + { + uint16_t cGroupsLeft = pInfo->Group.MaximumGroupCount - idxGroup; + cMaxMembers = pInfo->Group.GroupInfo[idxGroup - 1].MaximumProcessorCount; + while (cMaxMembers * cGroupsLeft < cInactiveLeft) + cMaxMembers++; + if (cMaxMembers > cInactiveLeft) + cMaxMembers = cInactiveLeft; + } + + g_aRtMpNtCpuGroups[idxGroup].cMaxCpus = (uint16_t)cMaxMembers; + g_aRtMpNtCpuGroups[idxGroup].cActiveCpus = 0; + for (uint16_t idxMember = 0; idxMember < cMaxMembers; idxMember++) + if (idxCpuSetNextInactive >= g_cRtMpNtActiveCpus) + { + g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive; +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpNtByCpuSetIdx[idxCpuSetNextInactive] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember) + | RTMPNT_ID_F_INACTIVE; +#endif + idxCpuSetNextInactive--; + } + cInactiveLeft -= cMaxMembers; + idxGroup++; + } + } + + /* We're done with pInfo now, free it so we can start returning when assertions fail. */ + RTMemFree(pInfo); + if (RT_FAILURE(rc)) /* MY_CHECK_BREAK sets rc. */ + return rc; + MY_CHECK_RETURN(cActive >= g_cRtMpNtActiveCpus, + ("IPRT: Fatal: cActive=%u < g_cRtMpNtActiveCpus=%u - CPUs removed?\n", cActive, g_cRtMpNtActiveCpus), + VERR_INTERNAL_ERROR_3); + MY_CHECK_RETURN(idxCpuMax < cActive, /* ASSUMPTION #2 */ + ("IPRT: Fatal: idCpuMax=%u >= cActive=%u! Unexpected CPU index allocation. CPUs removed?\n", + idxCpuMax, cActive), + VERR_INTERNAL_ERROR_4); + + /* Retry if CPUs were added. */ + if ( cActive != g_cRtMpNtActiveCpus + && cTries < 16) + continue; + MY_CHECK_RETURN(cActive == g_cRtMpNtActiveCpus, /* ASSUMPTION #4 */ + ("IPRT: Fatal: cActive=%u != g_cRtMpNtActiveCpus=%u\n", cActive, g_cRtMpNtActiveCpus), + VERR_INTERNAL_ERROR_5); + } + else + { + /* Legacy: */ + MY_CHECK_RETURN(g_cRtMpNtMaxGroups == 1, ("IPRT: Fatal: Missing KeQueryLogicalProcessorRelationship!\n"), + VERR_SYMBOL_NOT_FOUND); + + /** @todo Is it possible that the affinity mask returned by + * KeQueryActiveProcessors is sparse? */ + if (g_pfnrtKeQueryActiveProcessors) + RTCpuSetFromU64(&g_rtMpNtCpuSet, g_pfnrtKeQueryActiveProcessors()); + else if (g_cRtMpNtMaxCpus < 64) + RTCpuSetFromU64(&g_rtMpNtCpuSet, (UINT64_C(1) << g_cRtMpNtMaxCpus) - 1); + else + { + MY_CHECK_RETURN(g_cRtMpNtMaxCpus == 64, ("IPRT: Fatal: g_cRtMpNtMaxCpus=%u, expect 64 or less\n", g_cRtMpNtMaxCpus), + VERR_MP_TOO_MANY_CPUS); + RTCpuSetFromU64(&g_rtMpNtCpuSet, UINT64_MAX); + } + + g_aRtMpNtCpuGroups[0].cMaxCpus = g_cRtMpNtMaxCpus; + g_aRtMpNtCpuGroups[0].cActiveCpus = g_cRtMpNtMaxCpus; + for (unsigned i = 0; i < g_cRtMpNtMaxCpus; i++) + { + g_aRtMpNtCpuGroups[0].aidxCpuSetMembers[i] = i; +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpNtByCpuSetIdx[i] = RTMPCPUID_FROM_GROUP_AND_NUMBER(0, i); +#endif + } + } + + /* + * Register CPU hot plugging callback (it also counts active CPUs). + */ + Assert(g_pvMpCpuChangeCallback == NULL); + if (g_pfnrtKeRegisterProcessorChangeCallback) + { + MY_CHECK_RETURN(g_pfnrtKeDeregisterProcessorChangeCallback, + ("IPRT: Fatal: KeRegisterProcessorChangeCallback without KeDeregisterProcessorChangeCallback!\n"), + VERR_SYMBOL_NOT_FOUND); + + RTCPUSET const ActiveSetCopy = g_rtMpNtCpuSet; + RTCpuSetEmpty(&g_rtMpNtCpuSet); + uint32_t const cActiveCpus = g_cRtMpNtActiveCpus; + g_cRtMpNtActiveCpus = 0; + + g_pvMpCpuChangeCallback = g_pfnrtKeRegisterProcessorChangeCallback(rtR0NtMpProcessorChangeCallback, NULL /*pvUser*/, + KE_PROCESSOR_CHANGE_ADD_EXISTING); + if (g_pvMpCpuChangeCallback) + { + if (cActiveCpus == g_cRtMpNtActiveCpus) + { /* likely */ } + else + { + g_pfnrtKeDeregisterProcessorChangeCallback(g_pvMpCpuChangeCallback); + if (cTries < 16) + { + /* Retry if CPUs were added. */ + MY_CHECK_RETURN(g_cRtMpNtActiveCpus >= cActiveCpus, + ("IPRT: Fatal: g_cRtMpNtActiveCpus=%u < cActiveCpus=%u! CPUs removed?\n", + g_cRtMpNtActiveCpus, cActiveCpus), + VERR_INTERNAL_ERROR_2); + MY_CHECK_RETURN(g_cRtMpNtActiveCpus <= g_cRtMpNtMaxCpus, + ("IPRT: Fatal: g_cRtMpNtActiveCpus=%u > g_cRtMpNtMaxCpus=%u!\n", + g_cRtMpNtActiveCpus, g_cRtMpNtMaxCpus), + VERR_INTERNAL_ERROR_2); + continue; + } + MY_CHECK_RETURN(0, ("IPRT: Fatal: g_cRtMpNtActiveCpus=%u cActiveCpus=%u\n", g_cRtMpNtActiveCpus, cActiveCpus), + VERR_INTERNAL_ERROR_3); + } + } + else + { + AssertFailed(); + g_rtMpNtCpuSet = ActiveSetCopy; + g_cRtMpNtActiveCpus = cActiveCpus; + } + } + break; + } /* Retry loop for stable active CPU count. */ + +#undef MY_CHECK_RETURN + + /* + * Special IPI fun for RTMpPokeCpu. + * + * On Vista and later the DPC method doesn't seem to reliably send IPIs, + * so we have to use alternative methods. + * + * On AMD64 We used to use the HalSendSoftwareInterrupt API (also x86 on + * W10+), it looks faster and more convenient to use, however we're either + * using it wrong or it doesn't reliably do what we want (see @bugref{8343}). + * + * The HalRequestIpip API is thus far the only alternative to KeInsertQueueDpc + * for doing targetted IPIs. Trouble with this API is that it changed + * fundamentally in Window 7 when they added support for lots of processors. + * + * If we really think we cannot use KeInsertQueueDpc, we use the broadcast IPI + * API KeIpiGenericCall. + */ + if ( pOsVerInfo->uMajorVer > 6 + || (pOsVerInfo->uMajorVer == 6 && pOsVerInfo->uMinorVer > 0)) + g_pfnrtHalRequestIpiPreW7 = NULL; + else + g_pfnrtHalRequestIpiW7Plus = NULL; + + if ( g_pfnrtHalRequestIpiW7Plus + && g_pfnrtKeInitializeAffinityEx + && g_pfnrtKeAddProcessorAffinityEx + && g_pfnrtKeGetProcessorIndexFromNumber) + { + /* Determine the real size of the KAFFINITY_EX structure. */ + size_t const cbAffinity = _8K; + PKAFFINITY_EX pAffinity = (PKAFFINITY_EX)RTMemAllocZ(cbAffinity); + AssertReturn(pAffinity, VERR_NO_MEMORY); + size_t const cMaxEntries = (cbAffinity - RT_UOFFSETOF(KAFFINITY_EX, Bitmap[0])) / sizeof(pAffinity->Bitmap[0]); + g_pfnrtKeInitializeAffinityEx(pAffinity); + if (pAffinity->Size > 1 && pAffinity->Size <= cMaxEntries) + { + g_cRtMpNtKaffinityExEntries = pAffinity->Size; + g_cbRtMpNtKaffinityEx = pAffinity->Size * sizeof(pAffinity->Bitmap[0]) + RT_UOFFSETOF(KAFFINITY_EX, Bitmap[0]); + g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingHalRequestIpiW7Plus; + RTMemFree(pAffinity); + DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingHalRequestIpiW7Plus\n"); + return VINF_SUCCESS; + } + DbgPrint("IPRT: RTMpPoke can't use rtMpPokeCpuUsingHalRequestIpiW7Plus! pAffinity->Size=%u\n", pAffinity->Size); + AssertReleaseMsg(pAffinity->Size <= cMaxEntries, ("%#x\n", pAffinity->Size)); /* stack is toast if larger (32768 CPUs). */ + RTMemFree(pAffinity); + } + + if (pOsVerInfo->uMajorVer >= 6 && g_pfnrtKeIpiGenericCall) + { + DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingBroadcastIpi\n"); + g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingBroadcastIpi; + } + else if (g_pfnrtKeSetTargetProcessorDpc) + { + DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingDpc\n"); + g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingDpc; + /* Windows XP should send always send an IPI -> VERIFY */ + } + else + { + DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingFailureNotSupported\n"); + Assert(pOsVerInfo->uMajorVer == 3 && pOsVerInfo->uMinorVer <= 50); + g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingFailureNotSupported; + } + + return VINF_SUCCESS; +} + + +/** + * Called by rtR0TermNative. + */ +DECLHIDDEN(void) rtR0MpNtTerm(void) +{ + /* + * Deregister the processor change callback. + */ + PVOID pvMpCpuChangeCallback = g_pvMpCpuChangeCallback; + g_pvMpCpuChangeCallback = NULL; + if (pvMpCpuChangeCallback) + { + AssertReturnVoid(g_pfnrtKeDeregisterProcessorChangeCallback); + g_pfnrtKeDeregisterProcessorChangeCallback(pvMpCpuChangeCallback); + } +} + + +DECLHIDDEN(int) rtR0MpNotificationNativeInit(void) +{ + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void) +{ +} + + +/** + * Implements the NT PROCESSOR_CALLBACK_FUNCTION callback function. + * + * This maintains the g_rtMpNtCpuSet and works MP notification callbacks. When + * registered, it's called for each active CPU in the system, avoiding racing + * CPU hotplugging (as well as testing the callback). + * + * @param pvUser User context (not used). + * @param pChangeCtx Change context (in). + * @param prcOperationStatus Operation status (in/out). + * + * @remarks ASSUMES no concurrent execution of KeProcessorAddCompleteNotify + * notification callbacks. At least during callback registration + * callout, we're owning KiDynamicProcessorLock. + * + * @remarks When registering the handler, we first get KeProcessorAddStartNotify + * callbacks for all active CPUs, and after they all succeed we get the + * KeProcessorAddCompleteNotify callbacks. + */ +static VOID __stdcall rtR0NtMpProcessorChangeCallback(void *pvUser, PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT pChangeCtx, + PNTSTATUS prcOperationStatus) +{ + RT_NOREF(pvUser, prcOperationStatus); + switch (pChangeCtx->State) + { + /* + * Check whether we can deal with the CPU, failing the start operation if we + * can't. The checks we are doing here are to avoid complicated/impossible + * cases in KeProcessorAddCompleteNotify. They are really just verify specs. + */ + case KeProcessorAddStartNotify: + { + NTSTATUS rcNt = STATUS_SUCCESS; + if (pChangeCtx->NtNumber < RTCPUSET_MAX_CPUS) + { + if (pChangeCtx->NtNumber >= g_cRtMpNtMaxCpus) + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: NtNumber=%u is higher than the max CPU count (%u)!\n", + pChangeCtx->NtNumber, g_cRtMpNtMaxCpus); + rcNt = STATUS_INTERNAL_ERROR; + } + + /* The ProcessNumber field was introduced in Windows 7. */ + PROCESSOR_NUMBER ProcNum; + if (g_pfnrtKeGetProcessorIndexFromNumber) + { + ProcNum = pChangeCtx->ProcNumber; + KEPROCESSORINDEX idxCpu = g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum); + if (idxCpu != pChangeCtx->NtNumber) + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: g_pfnrtKeGetProcessorIndexFromNumber(%u.%u) -> %u, expected %u!\n", + ProcNum.Group, ProcNum.Number, idxCpu, pChangeCtx->NtNumber); + rcNt = STATUS_INTERNAL_ERROR; + } + } + else + { + ProcNum.Group = 0; + ProcNum.Number = pChangeCtx->NtNumber; + } + + if ( ProcNum.Group < RT_ELEMENTS(g_aRtMpNtCpuGroups) + && ProcNum.Number < RT_ELEMENTS(g_aRtMpNtCpuGroups[0].aidxCpuSetMembers)) + { + if (ProcNum.Group >= g_cRtMpNtMaxGroups) + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: %u.%u is out of range - max groups: %u!\n", + ProcNum.Group, ProcNum.Number, g_cRtMpNtMaxGroups); + rcNt = STATUS_INTERNAL_ERROR; + } + + if (ProcNum.Number < g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus) + { + Assert(g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] != -1); + if (g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] == -1) + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: Internal error! %u.%u was assigned -1 as set index!\n", + ProcNum.Group, ProcNum.Number); + rcNt = STATUS_INTERNAL_ERROR; + } + + Assert(g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] != NIL_RTCPUID); + if (g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] == NIL_RTCPUID) + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: Internal error! %u (%u.%u) translates to NIL_RTCPUID!\n", + pChangeCtx->NtNumber, ProcNum.Group, ProcNum.Number); + rcNt = STATUS_INTERNAL_ERROR; + } + } + else + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: max processors in group %u is %u, cannot add %u to it!\n", + ProcNum.Group, g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus, ProcNum.Group, ProcNum.Number); + rcNt = STATUS_INTERNAL_ERROR; + } + } + else + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: %u.%u is out of range (max %u.%u)!\n", + ProcNum.Group, ProcNum.Number, RT_ELEMENTS(g_aRtMpNtCpuGroups), RT_ELEMENTS(g_aRtMpNtCpuGroups[0].aidxCpuSetMembers)); + rcNt = STATUS_INTERNAL_ERROR; + } + } + else + { + DbgPrint("IPRT: KeProcessorAddStartNotify failure: NtNumber=%u is outside RTCPUSET_MAX_CPUS (%u)!\n", + pChangeCtx->NtNumber, RTCPUSET_MAX_CPUS); + rcNt = STATUS_INTERNAL_ERROR; + } + if (!NT_SUCCESS(rcNt)) + *prcOperationStatus = rcNt; + break; + } + + /* + * Update the globals. Since we've checked out range limits and other + * limitations already we just AssertBreak here. + */ + case KeProcessorAddCompleteNotify: + { + /* + * Calc the processor number and assert conditions checked in KeProcessorAddStartNotify. + */ + AssertBreak(pChangeCtx->NtNumber < RTCPUSET_MAX_CPUS); + AssertBreak(pChangeCtx->NtNumber < g_cRtMpNtMaxCpus); + Assert(pChangeCtx->NtNumber == g_cRtMpNtActiveCpus); /* light assumption */ + PROCESSOR_NUMBER ProcNum; + if (g_pfnrtKeGetProcessorIndexFromNumber) + { + ProcNum = pChangeCtx->ProcNumber; + AssertBreak(g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum) == pChangeCtx->NtNumber); + AssertBreak(ProcNum.Group < RT_ELEMENTS(g_aRtMpNtCpuGroups)); + AssertBreak(ProcNum.Group < g_cRtMpNtMaxGroups); + } + else + { + ProcNum.Group = 0; + ProcNum.Number = pChangeCtx->NtNumber; + } + AssertBreak(ProcNum.Number < RT_ELEMENTS(g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers)); + AssertBreak(ProcNum.Number < g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus); + AssertBreak(g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] != -1); + AssertBreak(g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] != NIL_RTCPUID); + + /* + * Add ourselves to the online CPU set and update the active CPU count. + */ + RTCpuSetAddByIndex(&g_rtMpNtCpuSet, pChangeCtx->NtNumber); + ASMAtomicIncU32(&g_cRtMpNtActiveCpus); + + /* + * Update the group info. + * + * If the index prediction failed (real hotplugging callbacks only) we + * have to switch it around. This is particularly annoying when we + * use the index as the ID. + */ +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + RTCPUID idCpu = RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number); + RTCPUID idOld = g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber]; + if ((idOld & ~RTMPNT_ID_F_INACTIVE) != idCpu) + { + Assert(idOld & RTMPNT_ID_F_INACTIVE); + int idxDest = g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number]; + g_aRtMpNtCpuGroups[rtMpCpuIdGetGroup(idOld)].aidxCpuSetMembers[rtMpCpuIdGetGroupMember(idOld)] = idxDest; + g_aidRtMpNtByCpuSetIdx[idxDest] = idOld; + } + g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] = idCpu; +#else + Assert(g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] == pChangeCtx->NtNumber); + int idxDest = g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number]; + if ((ULONG)idxDest != pChangeCtx->NtNumber) + { + bool fFound = false; + uint32_t idxOldGroup = g_cRtMpNtMaxGroups; + while (idxOldGroup-- > 0 && !fFound) + { + uint32_t idxMember = g_aRtMpNtCpuGroups[idxOldGroup].cMaxCpus; + while (idxMember-- > 0) + if (g_aRtMpNtCpuGroups[idxOldGroup].aidxCpuSetMembers[idxMember] == (int)pChangeCtx->NtNumber) + { + g_aRtMpNtCpuGroups[idxOldGroup].aidxCpuSetMembers[idxMember] = idxDest; + fFound = true; + break; + } + } + Assert(fFound); + } +#endif + g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] = pChangeCtx->NtNumber; + + /* + * Do MP notification callbacks. + */ + rtMpNotificationDoCallbacks(RTMPEVENT_ONLINE, pChangeCtx->NtNumber); + break; + } + + case KeProcessorAddFailureNotify: + /* ignore */ + break; + + default: + AssertMsgFailed(("State=%u\n", pChangeCtx->State)); + } +} + + +/** + * Wrapper around KeQueryLogicalProcessorRelationship. + * + * @returns IPRT status code. + * @param ppInfo Where to return the info. Pass to RTMemFree when done. + */ +static int rtR0NtInitQueryGroupRelations(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX **ppInfo) +{ + ULONG cbInfo = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) + + g_cRtMpNtMaxGroups * sizeof(GROUP_RELATIONSHIP); + NTSTATUS rcNt; + do + { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)RTMemAlloc(cbInfo); + if (pInfo) + { + rcNt = g_pfnrtKeQueryLogicalProcessorRelationship(NULL /*pProcNumber*/, RelationGroup, pInfo, &cbInfo); + if (NT_SUCCESS(rcNt)) + { + *ppInfo = pInfo; + return VINF_SUCCESS; + } + + RTMemFree(pInfo); + pInfo = NULL; + } + else + rcNt = STATUS_NO_MEMORY; + } while (rcNt == STATUS_INFO_LENGTH_MISMATCH); + DbgPrint("IPRT: Fatal: KeQueryLogicalProcessorRelationship failed: %#x\n", rcNt); + AssertMsgFailed(("KeQueryLogicalProcessorRelationship failed: %#x\n", rcNt)); + return RTErrConvertFromNtStatus(rcNt); +} + + + + + +RTDECL(RTCPUID) RTMpCpuId(void) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + PROCESSOR_NUMBER ProcNum; + ProcNum.Group = 0; + if (g_pfnrtKeGetCurrentProcessorNumberEx) + { + ProcNum.Number = 0; + g_pfnrtKeGetCurrentProcessorNumberEx(&ProcNum); + } + else + ProcNum.Number = KeGetCurrentProcessorNumber(); /* Number is 8-bit, so we're not subject to BYTE -> WORD upgrade in WDK. */ + return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number); + +#else + + if (g_pfnrtKeGetCurrentProcessorNumberEx) + { + KEPROCESSORINDEX idxCpu = g_pfnrtKeGetCurrentProcessorNumberEx(NULL); + Assert(idxCpu < RTCPUSET_MAX_CPUS); + return idxCpu; + } + + return (uint8_t)KeGetCurrentProcessorNumber(); /* PCR->Number was changed from BYTE to WORD in the WDK, thus the cast. */ +#endif +} + + +RTDECL(int) RTMpCurSetIndex(void) +{ +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + if (g_pfnrtKeGetCurrentProcessorNumberEx) + { + KEPROCESSORINDEX idxCpu = g_pfnrtKeGetCurrentProcessorNumberEx(NULL); + Assert(idxCpu < RTCPUSET_MAX_CPUS); + return idxCpu; + } + return (uint8_t)KeGetCurrentProcessorNumber(); /* PCR->Number was changed from BYTE to WORD in the WDK, thus the cast. */ +#else + return (int)RTMpCpuId(); +#endif +} + + +RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu) +{ +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + PROCESSOR_NUMBER ProcNum = { 0 , 0, 0 }; + KEPROCESSORINDEX idxCpu = g_pfnrtKeGetCurrentProcessorNumberEx(&ProcNum); + Assert(idxCpu < RTCPUSET_MAX_CPUS); + *pidCpu = RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number); + return idxCpu; +#else + return *pidCpu = RTMpCpuId(); +#endif +} + + +RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu) +{ +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + if (idCpu != NIL_RTCPUID) + { + if (g_pfnrtKeGetProcessorIndexFromNumber) + { + PROCESSOR_NUMBER ProcNum; + ProcNum.Group = rtMpCpuIdGetGroup(idCpu); + ProcNum.Number = rtMpCpuIdGetGroupMember(idCpu); + ProcNum.Reserved = 0; + KEPROCESSORINDEX idxCpu = g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum); + if (idxCpu != INVALID_PROCESSOR_INDEX) + { + Assert(idxCpu < g_cRtMpNtMaxCpus); + Assert((ULONG)g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] == idxCpu); + return idxCpu; + } + + /* Since NT assigned indexes as the CPUs come online, we cannot produce an ID <-> index + mapping for not-yet-onlined CPUS that is consistent. We just have to do our best... */ + if ( ProcNum.Group < g_cRtMpNtMaxGroups + && ProcNum.Number < g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus) + return g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number]; + } + else if (rtMpCpuIdGetGroup(idCpu) == 0) + return rtMpCpuIdGetGroupMember(idCpu); + } + return -1; +#else + /* 1:1 mapping, just do range checks. */ + return idCpu < RTCPUSET_MAX_CPUS ? (int)idCpu : -1; +#endif +} + + +RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu) +{ +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + if ((unsigned)iCpu < g_cRtMpNtMaxCpus) + { + if (g_pfnrtKeGetProcessorIndexFromNumber) + { + PROCESSOR_NUMBER ProcNum = { 0, 0, 0 }; + NTSTATUS rcNt = g_pfnrtKeGetProcessorNumberFromIndex(iCpu, &ProcNum); + if (NT_SUCCESS(rcNt)) + { + Assert(ProcNum.Group <= g_cRtMpNtMaxGroups); + Assert( (g_aidRtMpNtByCpuSetIdx[iCpu] & ~RTMPNT_ID_F_INACTIVE) + == RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number)); + return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number); + } + } + return g_aidRtMpNtByCpuSetIdx[iCpu]; + } + return NIL_RTCPUID; +#else + /* 1:1 mapping, just do range checks. */ + return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID; +#endif +} + + +RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + if (idxGroup < g_cRtMpNtMaxGroups) + if (idxMember < g_aRtMpNtCpuGroups[idxGroup].cMaxCpus) + return g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember]; + return -1; +} + + +RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive) +{ + if (idxGroup < g_cRtMpNtMaxGroups) + { + if (pcActive) + *pcActive = g_aRtMpNtCpuGroups[idxGroup].cActiveCpus; + return g_aRtMpNtCpuGroups[idxGroup].cMaxCpus; + } + if (pcActive) + *pcActive = 0; + return 0; +} + + +RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void) +{ + return g_cRtMpNtMaxGroups; +} + + +RTDECL(RTCPUID) RTMpGetMaxCpuId(void) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + return RTMPCPUID_FROM_GROUP_AND_NUMBER(g_cRtMpNtMaxGroups - 1, g_aRtMpNtCpuGroups[g_cRtMpNtMaxGroups - 1].cMaxCpus - 1); +#else + /* According to MSDN the processor indexes goes from 0 to the maximum + number of CPUs in the system. We've check this in initterm-r0drv-nt.cpp. */ + return g_cRtMpNtMaxCpus - 1; +#endif +} + + +RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + return RTCpuSetIsMember(&g_rtMpNtCpuSet, idCpu); +} + + +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + if (idCpu != NIL_RTCPUID) + { + unsigned idxGroup = rtMpCpuIdGetGroup(idCpu); + if (idxGroup < g_cRtMpNtMaxGroups) + return rtMpCpuIdGetGroupMember(idCpu) < g_aRtMpNtCpuGroups[idxGroup].cMaxCpus; + } + return false; + +#else + /* A possible CPU ID is one with a value lower than g_cRtMpNtMaxCpus (see + comment in RTMpGetMaxCpuId). */ + return idCpu < g_cRtMpNtMaxCpus; +#endif +} + + + +RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + /* The set of possible CPU IDs(/indexes) are from 0 up to + g_cRtMpNtMaxCpus (see comment in RTMpGetMaxCpuId). */ + RTCpuSetEmpty(pSet); + int idxCpu = g_cRtMpNtMaxCpus; + while (idxCpu-- > 0) + RTCpuSetAddByIndex(pSet, idxCpu); + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetCount(void) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + return g_cRtMpNtMaxCpus; +} + + +RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + *pSet = g_rtMpNtCpuSet; + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetOnlineCount(void) +{ + RTCPUSET Set; + RTMpGetOnlineSet(&Set); + return RTCpuSetCount(&Set); +} + + +RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void) +{ + /** @todo fix me */ + return RTMpGetOnlineCount(); +} + + + +#if 0 +/* Experiment with checking the undocumented KPRCB structure + * 'dt nt!_kprcb 0xaddress' shows the layout + */ +typedef struct +{ + LIST_ENTRY DpcListHead; + ULONG_PTR DpcLock; + volatile ULONG DpcQueueDepth; + ULONG DpcQueueCount; +} KDPC_DATA, *PKDPC_DATA; + +RTDECL(bool) RTMpIsCpuWorkPending(void) +{ + uint8_t *pkprcb; + PKDPC_DATA pDpcData; + + _asm { + mov eax, fs:0x20 + mov pkprcb, eax + } + pDpcData = (PKDPC_DATA)(pkprcb + 0x19e0); + if (pDpcData->DpcQueueDepth) + return true; + + pDpcData++; + if (pDpcData->DpcQueueDepth) + return true; + return false; +} +#else +RTDECL(bool) RTMpIsCpuWorkPending(void) +{ + /** @todo not implemented */ + return false; +} +#endif + + +/** + * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for + * the RTMpOnAll case. + * + * @param uUserCtx The user context argument (PRTMPARGS). + */ +static ULONG_PTR rtmpNtOnAllBroadcastIpiWrapper(ULONG_PTR uUserCtx) +{ + PRTMPARGS pArgs = (PRTMPARGS)uUserCtx; + /*ASMAtomicIncU32(&pArgs->cHits); - not needed */ + pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2); + return 0; +} + + +/** + * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for + * the RTMpOnOthers case. + * + * @param uUserCtx The user context argument (PRTMPARGS). + */ +static ULONG_PTR rtmpNtOnOthersBroadcastIpiWrapper(ULONG_PTR uUserCtx) +{ + PRTMPARGS pArgs = (PRTMPARGS)uUserCtx; + RTCPUID idCpu = RTMpCpuId(); + if (pArgs->idCpu != idCpu) + { + /*ASMAtomicIncU32(&pArgs->cHits); - not needed */ + pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2); + } + return 0; +} + + +/** + * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for + * the RTMpOnPair case. + * + * @param uUserCtx The user context argument (PRTMPARGS). + */ +static ULONG_PTR rtmpNtOnPairBroadcastIpiWrapper(ULONG_PTR uUserCtx) +{ + PRTMPARGS pArgs = (PRTMPARGS)uUserCtx; + RTCPUID idCpu = RTMpCpuId(); + if ( pArgs->idCpu == idCpu + || pArgs->idCpu2 == idCpu) + { + ASMAtomicIncU32(&pArgs->cHits); + pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2); + } + return 0; +} + + +/** + * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for + * the RTMpOnSpecific case. + * + * @param uUserCtx The user context argument (PRTMPARGS). + */ +static ULONG_PTR rtmpNtOnSpecificBroadcastIpiWrapper(ULONG_PTR uUserCtx) +{ + PRTMPARGS pArgs = (PRTMPARGS)uUserCtx; + RTCPUID idCpu = RTMpCpuId(); + if (pArgs->idCpu == idCpu) + { + ASMAtomicIncU32(&pArgs->cHits); + pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2); + } + return 0; +} + + +/** + * Internal worker for the RTMpOn* APIs using KeIpiGenericCall. + * + * @returns VINF_SUCCESS. + * @param pfnWorker The callback. + * @param pvUser1 User argument 1. + * @param pvUser2 User argument 2. + * @param pfnNativeWrapper The wrapper between the NT and IPRT callbacks. + * @param idCpu First CPU to match, ultimately specific to the + * pfnNativeWrapper used. + * @param idCpu2 Second CPU to match, ultimately specific to the + * pfnNativeWrapper used. + * @param pcHits Where to return the number of this. Optional. + */ +static int rtMpCallUsingBroadcastIpi(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2, + PKIPI_BROADCAST_WORKER pfnNativeWrapper, RTCPUID idCpu, RTCPUID idCpu2, + uint32_t *pcHits) +{ + RTMPARGS Args; + Args.pfnWorker = pfnWorker; + Args.pvUser1 = pvUser1; + Args.pvUser2 = pvUser2; + Args.idCpu = idCpu; + Args.idCpu2 = idCpu2; + Args.cRefs = 0; + Args.cHits = 0; + + AssertPtr(g_pfnrtKeIpiGenericCall); + g_pfnrtKeIpiGenericCall(pfnNativeWrapper, (uintptr_t)&Args); + if (pcHits) + *pcHits = Args.cHits; + return VINF_SUCCESS; +} + + +/** + * Wrapper between the native nt per-cpu callbacks and PFNRTWORKER + * + * @param Dpc DPC object + * @param DeferredContext Context argument specified by KeInitializeDpc + * @param SystemArgument1 Argument specified by KeInsertQueueDpc + * @param SystemArgument2 Argument specified by KeInsertQueueDpc + */ +static VOID rtmpNtDPCWrapper(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) +{ + PRTMPARGS pArgs = (PRTMPARGS)DeferredContext; + RT_NOREF3(Dpc, SystemArgument1, SystemArgument2); + + ASMAtomicIncU32(&pArgs->cHits); + pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2); + + /* Dereference the argument structure. */ + int32_t cRefs = ASMAtomicDecS32(&pArgs->cRefs); + Assert(cRefs >= 0); + if (cRefs == 0) + RTMemFree(pArgs); +} + + +/** + * Wrapper around KeSetTargetProcessorDpcEx / KeSetTargetProcessorDpc. + * + * This is shared with the timer code. + * + * @returns IPRT status code (errors are asserted). + * @retval VERR_CPU_NOT_FOUND if impossible CPU. Not asserted. + * @param pDpc The DPC. + * @param idCpu The ID of the new target CPU. + * @note Callable at any IRQL. + */ +DECLHIDDEN(int) rtMpNtSetTargetProcessorDpc(KDPC *pDpc, RTCPUID idCpu) +{ + if (g_pfnrtKeSetTargetProcessorDpcEx) + { + /* Convert to stupid process number (bet KeSetTargetProcessorDpcEx does + the reverse conversion internally). */ + PROCESSOR_NUMBER ProcNum; + NTSTATUS rcNt = g_pfnrtKeGetProcessorNumberFromIndex(RTMpCpuIdToSetIndex(idCpu), &ProcNum); + if (NT_SUCCESS(rcNt)) + { + rcNt = g_pfnrtKeSetTargetProcessorDpcEx(pDpc, &ProcNum); + AssertLogRelMsgReturn(NT_SUCCESS(rcNt), + ("KeSetTargetProcessorDpcEx(,%u(%u/%u)) -> %#x\n", idCpu, ProcNum.Group, ProcNum.Number, rcNt), + RTErrConvertFromNtStatus(rcNt)); + } + else if (rcNt == STATUS_INVALID_PARAMETER) + return VERR_CPU_NOT_FOUND; + else + AssertLogRelMsgReturn(NT_SUCCESS(rcNt), ("KeGetProcessorNumberFromIndex(%u) -> %#x\n", idCpu, rcNt), + RTErrConvertFromNtStatus(rcNt)); + + } + else if (g_pfnrtKeSetTargetProcessorDpc) + g_pfnrtKeSetTargetProcessorDpc(pDpc, RTMpCpuIdToSetIndex(idCpu)); + else + return VERR_NOT_SUPPORTED; + return VINF_SUCCESS; +} + + +/** + * Internal worker for the RTMpOn* APIs. + * + * @returns IPRT status code. + * @param pfnWorker The callback. + * @param pvUser1 User argument 1. + * @param pvUser2 User argument 2. + * @param enmCpuid What to do / is idCpu valid. + * @param idCpu Used if enmCpuid is RT_NT_CPUID_SPECIFIC or + * RT_NT_CPUID_PAIR, otherwise ignored. + * @param idCpu2 Used if enmCpuid is RT_NT_CPUID_PAIR, otherwise ignored. + * @param pcHits Where to return the number of this. Optional. + */ +static int rtMpCallUsingDpcs(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2, + RT_NT_CPUID enmCpuid, RTCPUID idCpu, RTCPUID idCpu2, uint32_t *pcHits) +{ +#if 0 + /* KeFlushQueuedDpcs must be run at IRQL PASSIVE_LEVEL according to MSDN, but the + * driver verifier doesn't complain... + */ + AssertMsg(KeGetCurrentIrql() == PASSIVE_LEVEL, ("%d != %d (PASSIVE_LEVEL)\n", KeGetCurrentIrql(), PASSIVE_LEVEL)); +#endif + /* KeFlushQueuedDpcs is not present in Windows 2000; import it dynamically so we can just fail this call. */ + if (!g_pfnrtNtKeFlushQueuedDpcs) + return VERR_NOT_SUPPORTED; + + /* + * Make a copy of the active CPU set and figure out how many KDPCs we really need. + * We must not try setup DPCs for CPUs which aren't there, because that may fail. + */ + RTCPUSET OnlineSet = g_rtMpNtCpuSet; + uint32_t cDpcsNeeded; + switch (enmCpuid) + { + case RT_NT_CPUID_SPECIFIC: + cDpcsNeeded = 1; + break; + case RT_NT_CPUID_PAIR: + cDpcsNeeded = 2; + break; + default: + do + { + cDpcsNeeded = g_cRtMpNtActiveCpus; + OnlineSet = g_rtMpNtCpuSet; + } while (cDpcsNeeded != g_cRtMpNtActiveCpus); + break; + } + + /* + * Allocate an RTMPARGS structure followed by cDpcsNeeded KDPCs + * and initialize them. + */ + PRTMPARGS pArgs = (PRTMPARGS)RTMemAllocZ(sizeof(RTMPARGS) + cDpcsNeeded * sizeof(KDPC)); + if (!pArgs) + return VERR_NO_MEMORY; + + pArgs->pfnWorker = pfnWorker; + pArgs->pvUser1 = pvUser1; + pArgs->pvUser2 = pvUser2; + pArgs->idCpu = NIL_RTCPUID; + pArgs->idCpu2 = NIL_RTCPUID; + pArgs->cHits = 0; + pArgs->cRefs = 1; + + int rc; + KDPC *paExecCpuDpcs = (KDPC *)(pArgs + 1); + if (enmCpuid == RT_NT_CPUID_SPECIFIC) + { + KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance); + rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[0], idCpu); + pArgs->idCpu = idCpu; + } + else if (enmCpuid == RT_NT_CPUID_PAIR) + { + KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance); + rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[0], idCpu); + pArgs->idCpu = idCpu; + + KeInitializeDpc(&paExecCpuDpcs[1], rtmpNtDPCWrapper, pArgs); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[1], HighImportance); + if (RT_SUCCESS(rc)) + rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[1], (int)idCpu2); + pArgs->idCpu2 = idCpu2; + } + else + { + rc = VINF_SUCCESS; + for (uint32_t i = 0; i < cDpcsNeeded && RT_SUCCESS(rc); i++) + if (RTCpuSetIsMemberByIndex(&OnlineSet, i)) + { + KeInitializeDpc(&paExecCpuDpcs[i], rtmpNtDPCWrapper, pArgs); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[i], HighImportance); + rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[i], RTMpCpuIdFromSetIndex(i)); + } + } + if (RT_FAILURE(rc)) + { + RTMemFree(pArgs); + return rc; + } + + /* + * Raise the IRQL to DISPATCH_LEVEL so we can't be rescheduled to another cpu. + * KeInsertQueueDpc must also be executed at IRQL >= DISPATCH_LEVEL. + */ + KIRQL oldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + + /* + * We cannot do other than assume a 1:1 relationship between the + * affinity mask and the process despite the warnings in the docs. + * If someone knows a better way to get this done, please let bird know. + */ + ASMCompilerBarrier(); /* paranoia */ + if (enmCpuid == RT_NT_CPUID_SPECIFIC) + { + ASMAtomicIncS32(&pArgs->cRefs); + BOOLEAN fRc = KeInsertQueueDpc(&paExecCpuDpcs[0], 0, 0); + Assert(fRc); NOREF(fRc); + } + else if (enmCpuid == RT_NT_CPUID_PAIR) + { + ASMAtomicIncS32(&pArgs->cRefs); + BOOLEAN fRc = KeInsertQueueDpc(&paExecCpuDpcs[0], 0, 0); + Assert(fRc); NOREF(fRc); + + ASMAtomicIncS32(&pArgs->cRefs); + fRc = KeInsertQueueDpc(&paExecCpuDpcs[1], 0, 0); + Assert(fRc); NOREF(fRc); + } + else + { + uint32_t iSelf = RTMpCurSetIndex(); + for (uint32_t i = 0; i < cDpcsNeeded; i++) + { + if ( (i != iSelf) + && RTCpuSetIsMemberByIndex(&OnlineSet, i)) + { + ASMAtomicIncS32(&pArgs->cRefs); + BOOLEAN fRc = KeInsertQueueDpc(&paExecCpuDpcs[i], 0, 0); + Assert(fRc); NOREF(fRc); + } + } + if (enmCpuid != RT_NT_CPUID_OTHERS) + pfnWorker(iSelf, pvUser1, pvUser2); + } + + KeLowerIrql(oldIrql); + + /* + * Flush all DPCs and wait for completion. (can take long!) + */ + /** @todo Consider changing this to an active wait using some atomic inc/dec + * stuff (and check for the current cpu above in the specific case). */ + /** @todo Seems KeFlushQueuedDpcs doesn't wait for the DPCs to be completely + * executed. Seen pArgs being freed while some CPU was using it before + * cRefs was added. */ + if (g_pfnrtNtKeFlushQueuedDpcs) + g_pfnrtNtKeFlushQueuedDpcs(); + + if (pcHits) + *pcHits = pArgs->cHits; + + /* Dereference the argument structure. */ + int32_t cRefs = ASMAtomicDecS32(&pArgs->cRefs); + Assert(cRefs >= 0); + if (cRefs == 0) + RTMemFree(pArgs); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + if (g_pfnrtKeIpiGenericCall) + return rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnAllBroadcastIpiWrapper, + NIL_RTCPUID, NIL_RTCPUID, NULL); + return rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_ALL, NIL_RTCPUID, NIL_RTCPUID, NULL); +} + + +RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + if (g_pfnrtKeIpiGenericCall) + return rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnOthersBroadcastIpiWrapper, + NIL_RTCPUID, NIL_RTCPUID, NULL); + return rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_OTHERS, NIL_RTCPUID, NIL_RTCPUID, NULL); +} + + +RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + int rc; + AssertReturn(idCpu1 != idCpu2, VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & RTMPON_F_VALID_MASK), VERR_INVALID_FLAGS); + if ((fFlags & RTMPON_F_CONCURRENT_EXEC) && !g_pfnrtKeIpiGenericCall) + return VERR_NOT_SUPPORTED; + + /* + * Check that both CPUs are online before doing the broadcast call. + */ + if ( RTMpIsCpuOnline(idCpu1) + && RTMpIsCpuOnline(idCpu2)) + { + /* + * The broadcast IPI isn't quite as bad as it could have been, because + * it looks like windows doesn't synchronize CPUs on the way out, they + * seems to get back to normal work while the pair is still busy. + */ + uint32_t cHits = 0; + if (g_pfnrtKeIpiGenericCall) + rc = rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnPairBroadcastIpiWrapper, idCpu1, idCpu2, &cHits); + else + rc = rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_PAIR, idCpu1, idCpu2, &cHits); + if (RT_SUCCESS(rc)) + { + Assert(cHits <= 2); + if (cHits == 2) + rc = VINF_SUCCESS; + else if (cHits == 1) + rc = VERR_NOT_ALL_CPUS_SHOWED; + else if (cHits == 0) + rc = VERR_CPU_OFFLINE; + else + rc = VERR_CPU_IPE_1; + } + } + /* + * A CPU must be present to be considered just offline. + */ + else if ( RTMpIsCpuPresent(idCpu1) + && RTMpIsCpuPresent(idCpu2)) + rc = VERR_CPU_OFFLINE; + else + rc = VERR_CPU_NOT_FOUND; + return rc; +} + + +RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void) +{ + return g_pfnrtKeIpiGenericCall != NULL; +} + + +/** + * Releases a reference to a RTMPNTONSPECIFICARGS heap allocation, freeing it + * when the last reference is released. + */ +DECLINLINE(void) rtMpNtOnSpecificRelease(PRTMPNTONSPECIFICARGS pArgs) +{ + uint32_t cRefs = ASMAtomicDecU32(&pArgs->cRefs); + AssertMsg(cRefs <= 1, ("cRefs=%#x\n", cRefs)); + if (cRefs == 0) + RTMemFree(pArgs); +} + + +/** + * Wrapper between the native nt per-cpu callbacks and PFNRTWORKER + * + * @param Dpc DPC object + * @param DeferredContext Context argument specified by KeInitializeDpc + * @param SystemArgument1 Argument specified by KeInsertQueueDpc + * @param SystemArgument2 Argument specified by KeInsertQueueDpc + */ +static VOID rtMpNtOnSpecificDpcWrapper(IN PKDPC Dpc, IN PVOID DeferredContext, + IN PVOID SystemArgument1, IN PVOID SystemArgument2) +{ + PRTMPNTONSPECIFICARGS pArgs = (PRTMPNTONSPECIFICARGS)DeferredContext; + RT_NOREF3(Dpc, SystemArgument1, SystemArgument2); + + ASMAtomicWriteBool(&pArgs->fExecuting, true); + + pArgs->CallbackArgs.pfnWorker(RTMpCpuId(), pArgs->CallbackArgs.pvUser1, pArgs->CallbackArgs.pvUser2); + + ASMAtomicWriteBool(&pArgs->fDone, true); + KeSetEvent(&pArgs->DoneEvt, 1 /*PriorityIncrement*/, FALSE /*Wait*/); + + rtMpNtOnSpecificRelease(pArgs); +} + + +RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + /* + * Don't try mess with an offline CPU. + */ + if (!RTMpIsCpuOnline(idCpu)) + return !RTMpIsCpuPossible(idCpu) + ? VERR_CPU_NOT_FOUND + : VERR_CPU_OFFLINE; + + /* + * Use the broadcast IPI routine if there are no more than two CPUs online, + * or if the current IRQL is unsuitable for KeWaitForSingleObject. + */ + int rc; + uint32_t cHits = 0; + if ( g_pfnrtKeIpiGenericCall + && ( RTMpGetOnlineCount() <= 2 + || KeGetCurrentIrql() > APC_LEVEL) + ) + { + rc = rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnSpecificBroadcastIpiWrapper, + idCpu, NIL_RTCPUID, &cHits); + if (RT_SUCCESS(rc)) + { + if (cHits == 1) + return VINF_SUCCESS; + rc = cHits == 0 ? VERR_CPU_OFFLINE : VERR_CPU_IPE_1; + } + return rc; + } + +#if 0 + rc = rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_SPECIFIC, idCpu, NIL_RTCPUID, &cHits); + if (RT_SUCCESS(rc)) + { + if (cHits == 1) + return VINF_SUCCESS; + rc = cHits == 0 ? VERR_CPU_OFFLINE : VERR_CPU_IPE_1; + } + return rc; + +#else + /* + * Initialize the argument package and the objects within it. + * The package is referenced counted to avoid unnecessary spinning to + * synchronize cleanup and prevent stack corruption. + */ + PRTMPNTONSPECIFICARGS pArgs = (PRTMPNTONSPECIFICARGS)RTMemAllocZ(sizeof(*pArgs)); + if (!pArgs) + return VERR_NO_MEMORY; + pArgs->cRefs = 2; + pArgs->fExecuting = false; + pArgs->fDone = false; + pArgs->CallbackArgs.pfnWorker = pfnWorker; + pArgs->CallbackArgs.pvUser1 = pvUser1; + pArgs->CallbackArgs.pvUser2 = pvUser2; + pArgs->CallbackArgs.idCpu = idCpu; + pArgs->CallbackArgs.cHits = 0; + pArgs->CallbackArgs.cRefs = 2; + KeInitializeEvent(&pArgs->DoneEvt, SynchronizationEvent, FALSE /* not signalled */); + KeInitializeDpc(&pArgs->Dpc, rtMpNtOnSpecificDpcWrapper, pArgs); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&pArgs->Dpc, HighImportance); + rc = rtMpNtSetTargetProcessorDpc(&pArgs->Dpc, idCpu); + if (RT_FAILURE(rc)) + { + RTMemFree(pArgs); + return rc; + } + + /* + * Disable preemption while we check the current processor and inserts the DPC. + */ + KIRQL bOldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &bOldIrql); + ASMCompilerBarrier(); /* paranoia */ + + if (RTMpCpuId() == idCpu) + { + /* Just execute the callback on the current CPU. */ + pfnWorker(idCpu, pvUser1, pvUser2); + KeLowerIrql(bOldIrql); + + RTMemFree(pArgs); + return VINF_SUCCESS; + } + + /* Different CPU, so queue it if the CPU is still online. */ + if (RTMpIsCpuOnline(idCpu)) + { + BOOLEAN fRc = KeInsertQueueDpc(&pArgs->Dpc, 0, 0); + Assert(fRc); NOREF(fRc); + KeLowerIrql(bOldIrql); + + uint64_t const nsRealWaitTS = RTTimeNanoTS(); + + /* + * Wait actively for a while in case the CPU/thread responds quickly. + */ + uint32_t cLoopsLeft = 0x20000; + while (cLoopsLeft-- > 0) + { + if (pArgs->fDone) + { + rtMpNtOnSpecificRelease(pArgs); + return VINF_SUCCESS; + } + ASMNopPause(); + } + + /* + * It didn't respond, so wait on the event object, poking the CPU if it's slow. + */ + LARGE_INTEGER Timeout; + Timeout.QuadPart = -10000; /* 1ms */ + NTSTATUS rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout); + if (rcNt == STATUS_SUCCESS) + { + rtMpNtOnSpecificRelease(pArgs); + return VINF_SUCCESS; + } + + /* If it hasn't respondend yet, maybe poke it and wait some more. */ + if (rcNt == STATUS_TIMEOUT) + { + if ( !pArgs->fExecuting + && ( g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalRequestIpiW7Plus + || g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalRequestIpiPreW7)) + RTMpPokeCpu(idCpu); + + Timeout.QuadPart = -1280000; /* 128ms */ + rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout); + if (rcNt == STATUS_SUCCESS) + { + rtMpNtOnSpecificRelease(pArgs); + return VINF_SUCCESS; + } + } + + /* + * Something weird is happening, try bail out. + */ + if (KeRemoveQueueDpc(&pArgs->Dpc)) + { + RTMemFree(pArgs); /* DPC was still queued, so we can return without further ado. */ + LogRel(("RTMpOnSpecific(%#x): Not processed after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt)); + } + else + { + /* DPC is running, wait a good while for it to complete. */ + LogRel(("RTMpOnSpecific(%#x): Still running after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt)); + + Timeout.QuadPart = -30*1000*1000*10; /* 30 seconds */ + rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout); + if (rcNt != STATUS_SUCCESS) + LogRel(("RTMpOnSpecific(%#x): Giving up on running worker after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt)); + } + rc = RTErrConvertFromNtStatus(rcNt); + } + else + { + /* CPU is offline.*/ + KeLowerIrql(bOldIrql); + rc = !RTMpIsCpuPossible(idCpu) ? VERR_CPU_NOT_FOUND : VERR_CPU_OFFLINE; + } + + rtMpNtOnSpecificRelease(pArgs); + return rc; +#endif +} + + + + +static VOID rtMpNtPokeCpuDummy(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) +{ + NOREF(Dpc); + NOREF(DeferredContext); + NOREF(SystemArgument1); + NOREF(SystemArgument2); +} + + +/** Callback used by rtMpPokeCpuUsingBroadcastIpi. */ +static ULONG_PTR rtMpIpiGenericCall(ULONG_PTR Argument) +{ + NOREF(Argument); + return 0; +} + + +/** + * RTMpPokeCpu worker that uses broadcast IPIs for doing the work. + * + * @returns VINF_SUCCESS + * @param idCpu The CPU identifier. + */ +int rtMpPokeCpuUsingBroadcastIpi(RTCPUID idCpu) +{ + NOREF(idCpu); + g_pfnrtKeIpiGenericCall(rtMpIpiGenericCall, 0); + return VINF_SUCCESS; +} + + +/** + * RTMpPokeCpu worker that uses the Windows 7 and later version of + * HalRequestIpip to get the job done. + * + * @returns VINF_SUCCESS + * @param idCpu The CPU identifier. + */ +int rtMpPokeCpuUsingHalRequestIpiW7Plus(RTCPUID idCpu) +{ + /* idCpu is an HAL processor index, so we can use it directly. */ + PKAFFINITY_EX pTarget = (PKAFFINITY_EX)alloca(g_cbRtMpNtKaffinityEx); + pTarget->Size = g_cRtMpNtKaffinityExEntries; /* (just in case KeInitializeAffinityEx starts using it) */ + g_pfnrtKeInitializeAffinityEx(pTarget); + g_pfnrtKeAddProcessorAffinityEx(pTarget, idCpu); + + g_pfnrtHalRequestIpiW7Plus(0, pTarget); + return VINF_SUCCESS; +} + + +/** + * RTMpPokeCpu worker that uses the Vista and earlier version of HalRequestIpip + * to get the job done. + * + * @returns VINF_SUCCESS + * @param idCpu The CPU identifier. + */ +int rtMpPokeCpuUsingHalRequestIpiPreW7(RTCPUID idCpu) +{ + __debugbreak(); /** @todo this code needs testing!! */ + KAFFINITY Target = 1; + Target <<= idCpu; + g_pfnrtHalRequestIpiPreW7(Target); + return VINF_SUCCESS; +} + + +int rtMpPokeCpuUsingFailureNotSupported(RTCPUID idCpu) +{ + NOREF(idCpu); + return VERR_NOT_SUPPORTED; +} + + +int rtMpPokeCpuUsingDpc(RTCPUID idCpu) +{ + Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */ + + /* + * APC fallback. + */ + static KDPC s_aPokeDpcs[RTCPUSET_MAX_CPUS] = {{0}}; + static bool s_fPokeDPCsInitialized = false; + + if (!s_fPokeDPCsInitialized) + { + for (unsigned i = 0; i < g_cRtMpNtMaxCpus; i++) + { + KeInitializeDpc(&s_aPokeDpcs[i], rtMpNtPokeCpuDummy, NULL); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&s_aPokeDpcs[i], HighImportance); + int rc = rtMpNtSetTargetProcessorDpc(&s_aPokeDpcs[i], idCpu); + if (RT_FAILURE(rc) && rc != VERR_CPU_NOT_FOUND) + return rc; + } + + s_fPokeDPCsInitialized = true; + } + + /* Raise the IRQL to DISPATCH_LEVEL so we can't be rescheduled to another cpu. + KeInsertQueueDpc must also be executed at IRQL >= DISPATCH_LEVEL. */ + KIRQL oldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&s_aPokeDpcs[idCpu], HighImportance); + g_pfnrtKeSetTargetProcessorDpc(&s_aPokeDpcs[idCpu], (int)idCpu); + + /* Assuming here that high importance DPCs will be delivered immediately; or at least an IPI will be sent immediately. + Note! Not true on at least Vista & Windows 7 */ + BOOLEAN fRet = KeInsertQueueDpc(&s_aPokeDpcs[idCpu], 0, 0); + + KeLowerIrql(oldIrql); + return fRet == TRUE ? VINF_SUCCESS : VERR_ACCESS_DENIED /* already queued */; +} + + +RTDECL(int) RTMpPokeCpu(RTCPUID idCpu) +{ + if (!RTMpIsCpuOnline(idCpu)) + return !RTMpIsCpuPossible(idCpu) + ? VERR_CPU_NOT_FOUND + : VERR_CPU_OFFLINE; + /* Calls rtMpPokeCpuUsingDpc, rtMpPokeCpuUsingHalRequestIpiW7Plus or rtMpPokeCpuUsingBroadcastIpi. */ + return g_pfnrtMpPokeCpuWorker(idCpu); +} + + +RTDECL(bool) RTMpOnAllIsConcurrentSafe(void) +{ + return false; +} + diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp new file mode 100644 index 00000000..8a21e12a --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp @@ -0,0 +1,823 @@ +/* $Id: nt3fakes-r0drv-nt.cpp $ */ +/** @file + * IPRT - NT 3.x fakes for NT 4.0 KPIs. + */ + +/* + * 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 * +*********************************************************************************************************************************/ +#define _IMAGE_NT_HEADERS RT_CONCAT(_IMAGE_NT_HEADERS,ARCH_BITS) +#include "the-nt-kernel.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal-r0drv-nt.h" + +typedef uint32_t DWORD; +#include + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +DECLASM(void) rtNt3InitSymbolsAssembly(void); /* in nt3fakesA-r0drv-nt.asm */ + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static uint32_t g_uNt3MajorVer = 3; +static uint32_t g_uNt3MinorVer = 51; +static uint32_t g_uNt3BuildNo = 1057; +static bool g_fNt3Checked = false; +static bool g_fNt3Smp = false; /**< Not reliable. */ +static bool volatile g_fNt3VersionInitialized = false; + +static uint8_t *g_pbNt3OsKrnl = (uint8_t *)UINT32_C(0x80100000); +static uint32_t g_cbNt3OsKrnl = 0x300000; +static uint8_t *g_pbNt3Hal = (uint8_t *)UINT32_C(0x80400000); +static uint32_t g_cbNt3Hal = _512K; +static bool volatile g_fNt3ModuleInfoInitialized = false; + + +RT_C_DECLS_BEGIN +/** @name KPIs we provide fallback implementations for. + * + * The assembly init routine will point the __imp_xxx variable to the NT + * implementation if available, using the fallback if not. + * @{ */ +decltype(PsGetVersion) *g_pfnrtPsGetVersion; +decltype(ZwQuerySystemInformation) *g_pfnrtZwQuerySystemInformation; +decltype(KeSetTimerEx) *g_pfnrtKeSetTimerEx; +decltype(IoAttachDeviceToDeviceStack) *g_pfnrtIoAttachDeviceToDeviceStack; +decltype(PsGetCurrentProcessId) *g_pfnrtPsGetCurrentProcessId; +decltype(ZwYieldExecution) *g_pfnrtZwYieldExecution; +decltype(ExAcquireFastMutex) *g_pfnrtExAcquireFastMutex; +decltype(ExReleaseFastMutex) *g_pfnrtExReleaseFastMutex; +/** @} */ + +/** @name Fastcall optimizations not present in NT 3.1. + * + * We try resolve both the stdcall and fastcall variants and patch it up in + * assembly. The last four routines are in the hal. + * + * @{ */ +decltype(IofCompleteRequest) *g_pfnrtIofCompleteRequest; +decltype(ObfDereferenceObject) *g_pfnrtObfDereferenceObject; +decltype(IofCallDriver) *g_pfnrtIofCallDriver; +decltype(KfAcquireSpinLock) *g_pfnrtKfAcquireSpinLock; +decltype(KfReleaseSpinLock) *g_pfnrtKfReleaseSpinLock; +decltype(KefAcquireSpinLockAtDpcLevel) *g_pfnrtKefAcquireSpinLockAtDpcLevel; +decltype(KefReleaseSpinLockFromDpcLevel) *g_pfnrtKefReleaseSpinLockFromDpcLevel; +decltype(KfLowerIrql) *g_pfnrtKfLowerIrql; +decltype(KfRaiseIrql) *g_pfnrtKfRaiseIrql; + +VOID (__stdcall *g_pfnrtIoCompleteRequest)(PIRP, CCHAR); +LONG_PTR (__stdcall *g_pfnrtObDereferenceObject)(PVOID); +NTSTATUS (__stdcall *g_pfnrtIoCallDriver)(PDEVICE_OBJECT, PIRP); +KIRQL (__stdcall *g_pfnrtKeAcquireSpinLock)(PKSPIN_LOCK); +VOID (__stdcall *g_pfnrtKeReleaseSpinLock)(PKSPIN_LOCK, KIRQL); +KIRQL (__stdcall *g_pfnrtKeAcquireSpinLockAtDpcLevel)(PKSPIN_LOCK); +VOID (__stdcall *g_pfnrtKeReleaseSpinLockFromDpcLevel)(PKSPIN_LOCK); +VOID (__stdcall *g_pfnrtKeLowerIrql)(KIRQL); +KIRQL (__stdcall *g_pfnrtKeRaiseIrql)(KIRQL); +/** @} */ + +/** @name DATA exports and associated stuff + * @{ */ +/** Import address table entry for KeTickCount (defined in asm). */ +extern KSYSTEM_TIME *_imp__KeTickCount; +/** @} */ + +RT_C_DECLS_END + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void rtR0Nt3InitModuleInfo(void); + + +/** + * Converts a string to a number, stopping at the first non-digit. + * + * @returns The value + * @param ppwcValue Pointer to the string pointer variable. Updated. + * @param pcwcValue Pointer to the string length variable. Updated. + */ +static uint32_t rtR0Nt3StringToNum(PCRTUTF16 *ppwcValue, size_t *pcwcValue) +{ + uint32_t uValue = 0; + PCRTUTF16 pwcValue = *ppwcValue; + size_t cwcValue = *pcwcValue; + + while (cwcValue > 0) + { + RTUTF16 uc = *pwcValue; + unsigned uDigit = (unsigned)uc - (unsigned)'0'; + if (uDigit < (unsigned)10) + { + uValue *= 10; + uValue += uDigit; + } + else + break; + pwcValue++; + cwcValue--; + } + + *ppwcValue = pwcValue; + *pcwcValue = cwcValue; + return uValue; +} + + +/** + * Implements RTL_QUERY_REGISTRY_ROUTINE for processing + * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentVersion' + */ +static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentVersion(PWSTR pwszValueName, ULONG uValueType, + PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx) +{ + RT_NOREF(pwszValueName, pvEntryCtx); + if ( uValueType == REG_SZ + || uValueType == REG_EXPAND_SZ) + { + PCRTUTF16 pwcValue = (PCRTUTF16)pvValue; + size_t cwcValue = cbValue / sizeof(*pwcValue); + uint32_t uMajor = rtR0Nt3StringToNum(&pwcValue, &cwcValue); + uint32_t uMinor = 0; + if (cwcValue > 1) + { + pwcValue++; + cwcValue--; + uMinor = rtR0Nt3StringToNum(&pwcValue, &cwcValue); + } + + if (uMajor >= 3) + { + g_uNt3MajorVer = uMajor; + g_uNt3MinorVer = uMinor; + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion found: uMajor=%u uMinor=%u\n", uMajor, uMinor); + *(uint32_t *)pvUser |= RT_BIT_32(0); + return STATUS_SUCCESS; + } + + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue); + } + else + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue); + return STATUS_SUCCESS; +} + + +/** + * Implements RTL_QUERY_REGISTRY_ROUTINE for processing + * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentBuildNumber' + */ +static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentBuildNumber(PWSTR pwszValueName, ULONG uValueType, + PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx) +{ + RT_NOREF(pwszValueName, pvEntryCtx); + if ( uValueType == REG_SZ + || uValueType == REG_EXPAND_SZ) + { + PCRTUTF16 pwcValue = (PCRTUTF16)pvValue; + size_t cwcValue = cbValue / sizeof(*pwcValue); + uint32_t uBuildNo = rtR0Nt3StringToNum(&pwcValue, &cwcValue); + + if (uBuildNo >= 100 && uBuildNo < _1M) + { + g_uNt3BuildNo = uBuildNo; + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber found: uBuildNo=%u\n", uBuildNo); + *(uint32_t *)pvUser |= RT_BIT_32(1); + return STATUS_SUCCESS; + } + + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue); + } + else + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue); + return STATUS_SUCCESS; +} + + +/** + * Implements RTL_QUERY_REGISTRY_ROUTINE for processing + * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentType' + */ +static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentType(PWSTR pwszValueName, ULONG uValueType, + PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx) +{ + RT_NOREF(pwszValueName, pvEntryCtx); + if ( uValueType == REG_SZ + || uValueType == REG_EXPAND_SZ) + { + PCRTUTF16 pwcValue = (PCRTUTF16)pvValue; + size_t cwcValue = cbValue / sizeof(*pwcValue); + + int fSmp = -1; + if (cwcValue >= 12 && RTUtf16NICmpAscii(pwcValue, "Uniprocessor", 12) == 0) + { + cwcValue -= 12; + pwcValue += 12; + fSmp = 0; + } + else if (cwcValue >= 14 && RTUtf16NICmpAscii(pwcValue, "Multiprocessor", 14) == 0) + { + cwcValue -= 14; + pwcValue += 14; + fSmp = 1; + } + if (fSmp != -1) + { + while (cwcValue > 0 && RT_C_IS_SPACE(*pwcValue)) + cwcValue--, pwcValue++; + + int fChecked = -1; + if (cwcValue >= 4 && RTUtf16NICmpAscii(pwcValue, "Free", 4) == 0) + fChecked = 0; + else if (cwcValue >= 7 && RTUtf16NICmpAscii(pwcValue, "Checked", 7) == 0) + fChecked = 1; + if (fChecked != -1) + { + g_fNt3Smp = fSmp != 0; + g_fNt3Checked = fChecked != 0; + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType found: fSmp=%d fChecked=%d\n", fSmp, fChecked); + *(uint32_t *)pvUser |= RT_BIT_32(2); + return STATUS_SUCCESS; + } + } + + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue); + } + else + RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue); + return STATUS_SUCCESS; +} + + +/** + * Figure out the NT 3 version from the registry. + * + * @note this will be called before the rtR0Nt3InitSymbols is called. + */ +static void rtR0Nt3InitVersion(void) +{ + /* + * No PsGetVersion, so try the registry. Unfortunately not necessarily + * initialized when we're loaded. + */ + RTL_QUERY_REGISTRY_TABLE aQuery[4]; + RT_ZERO(aQuery); + aQuery[0].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentVersion; + aQuery[0].Flags = 0; + aQuery[0].Name = L"CurrentVersion"; + aQuery[0].EntryContext = NULL; + aQuery[0].DefaultType = REG_NONE; + + aQuery[1].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentBuildNumber; + aQuery[1].Flags = 0; + aQuery[1].Name = L"CurrentBuildNumber"; + aQuery[1].EntryContext = NULL; + aQuery[1].DefaultType = REG_NONE; + + aQuery[2].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentType; + aQuery[2].Flags = 0; + aQuery[2].Name = L"CurrentType"; + aQuery[2].EntryContext = NULL; + aQuery[2].DefaultType = REG_NONE; + + uint32_t fFound = 0; + //NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, NULL, &aQuery[0], &fFound, NULL /*Environment*/); + NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion", + &aQuery[0], &fFound, NULL /*Environment*/); + if (!NT_SUCCESS(rcNt)) + RTLogBackdoorPrintf("rtR0Nt3InitVersion: RtlQueryRegistryValues failed: %#x\n", rcNt); + else + RTLogBackdoorPrintf("rtR0Nt3InitVersion: Didn't get all values: fFound=%#x\n", fFound); + + /* + * We really need the version number. Build, type and SMP is off less importance. + * Derive it from the NT kernel PE header. + */ + if (!(fFound & RT_BIT_32(0))) + { + if (!g_fNt3ModuleInfoInitialized) + rtR0Nt3InitModuleInfo(); + + PIMAGE_DOS_HEADER pMzHdr = (PIMAGE_DOS_HEADER)g_pbNt3OsKrnl; + PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)&g_pbNt3OsKrnl[pMzHdr->e_lfanew]; + if (pNtHdrs->OptionalHeader.MajorOperatingSystemVersion == 1) + { + /* NT 3.1 and NT 3.50 both set OS version to 1.0 in the optional header. */ + g_uNt3MajorVer = 3; + if ( pNtHdrs->OptionalHeader.MajorLinkerVersion == 2 + && pNtHdrs->OptionalHeader.MinorLinkerVersion < 50) + g_uNt3MinorVer = 10; + else + g_uNt3MinorVer = 50; + } + else + { + g_uNt3MajorVer = pNtHdrs->OptionalHeader.MajorOperatingSystemVersion; + g_uNt3MinorVer = pNtHdrs->OptionalHeader.MinorOperatingSystemVersion; + } + RTLogBackdoorPrintf("rtR0Nt3InitVersion: guessed %u.%u from PE header\n", g_uNt3MajorVer, g_uNt3MinorVer); + + /* Check out the resource section, looking for VS_FIXEDFILEINFO. */ + __try /* (pointless) */ + { + PIMAGE_SECTION_HEADER paShdrs = (PIMAGE_SECTION_HEADER)(pNtHdrs + 1); + uint32_t const cShdrs = pNtHdrs->FileHeader.NumberOfSections; + uint32_t iShdr = 0; + while (iShdr < cShdrs && memcmp(paShdrs[iShdr].Name, ".rsrc", 6) != 0) + iShdr++; + if (iShdr < cShdrs) + { + if ( paShdrs[iShdr].VirtualAddress > 0 + && paShdrs[iShdr].VirtualAddress < pNtHdrs->OptionalHeader.SizeOfImage) + { + uint32_t const cbRsrc = RT_MIN(paShdrs[iShdr].Misc.VirtualSize + ? paShdrs[iShdr].Misc.VirtualSize : paShdrs[iShdr].SizeOfRawData, + pNtHdrs->OptionalHeader.SizeOfImage - paShdrs[iShdr].VirtualAddress); + uint8_t const *pbRsrc = &g_pbNt3OsKrnl[paShdrs[iShdr].VirtualAddress]; + uint32_t const *puDwords = (uint32_t const *)pbRsrc; + uint32_t cDWords = (cbRsrc - sizeof(VS_FIXEDFILEINFO) + sizeof(uint32_t)) / sizeof(uint32_t); + while (cDWords-- > 0) + { + if ( puDwords[0] == VS_FFI_SIGNATURE + && puDwords[1] == VS_FFI_STRUCVERSION) + { + VS_FIXEDFILEINFO const *pVerInfo = (VS_FIXEDFILEINFO const *)puDwords; + g_uNt3MajorVer = pVerInfo->dwProductVersionMS >> 16; + g_uNt3MinorVer = pVerInfo->dwProductVersionMS >> 16; + g_uNt3BuildNo = pVerInfo->dwProductVersionLS >> 16; + RTLogBackdoorPrintf("rtR0Nt3InitVersion: Found version info %u.%u build %u\n", + g_uNt3MajorVer, g_uNt3MinorVer, g_uNt3BuildNo); + break; + } + puDwords++; + } + } + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + RTLogBackdoorPrintf("rtR0Nt3InitVersion: Exception scanning .rsrc section for version info!\n"); + } + } + + /* + * If we've got PsGetVersion, use it to override the above finding! + * (We may end up here for reasons other than the PsGetVersion fallback.) + */ + if (g_pfnrtPsGetVersion) + { + WCHAR wszCsd[64]; + UNICODE_STRING UniStr; + UniStr.Buffer = wszCsd; + UniStr.MaximumLength = sizeof(wszCsd) - sizeof(WCHAR); + UniStr.Length = 0; + RT_ZERO(wszCsd); + ULONG uMajor = 3; + ULONG uMinor = 51; + ULONG uBuildNo = 1057; + BOOLEAN fChecked = g_pfnrtPsGetVersion(&uMajor, &uMinor, &uBuildNo, &UniStr); + + g_uNt3MajorVer = uMajor; + g_uNt3MinorVer = uMinor; + g_uNt3BuildNo = uBuildNo; + g_fNt3Checked = fChecked != FALSE; + } + + g_fNt3VersionInitialized = true; +} + + +extern "C" DECLEXPORT(BOOLEAN) __stdcall +Nt3Fb_PsGetVersion(ULONG *puMajor, ULONG *puMinor, ULONG *puBuildNo, UNICODE_STRING *pCsdStr) +{ + if (!g_fNt3VersionInitialized) + rtR0Nt3InitVersion(); + if (puMajor) + *puMajor = g_uNt3MajorVer; + if (puMinor) + *puMinor = g_uNt3MinorVer; + if (puBuildNo) + *puBuildNo = g_uNt3BuildNo; + if (pCsdStr) + { + pCsdStr->Buffer[0] = '\0'; + pCsdStr->Length = 0; + } + return g_fNt3Checked; +} + + +/** + * Worker for rtR0Nt3InitModuleInfo. + */ +static bool rtR0Nt3InitModuleInfoOne(const char *pszImage, uint8_t const *pbCode, uint8_t **ppbModule, uint32_t *pcbModule) +{ + uintptr_t const uImageAlign = _4K; /* XP may put the kernel at */ + + /* Align pbCode. */ + pbCode = (uint8_t const *)((uintptr_t)pbCode & ~(uintptr_t)(uImageAlign - 1)); + + /* Scan backwards till we find a PE signature. */ + for (uint32_t cbChecked = 0; cbChecked < _64M; cbChecked += uImageAlign, pbCode -= uImageAlign) + { + if (!MmIsAddressValid((void *)pbCode)) + continue; + + uint32_t uZero = 0; + uint32_t offNewHdr = 0; + __try /* pointless */ + { + uZero = *(uint32_t const *)pbCode; + offNewHdr = *(uint32_t const *)&pbCode[RT_UOFFSETOF(IMAGE_DOS_HEADER, e_lfanew)]; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p scanning for DOS header...\n", pbCode); + continue; + } + if ( (uint16_t)uZero == IMAGE_DOS_SIGNATURE + && offNewHdr < _2K + && offNewHdr >= sizeof(IMAGE_DOS_HEADER)) + { + RT_CONCAT(IMAGE_NT_HEADERS,ARCH_BITS) NtHdrs; + __try /* pointless */ + { + NtHdrs = *(decltype(NtHdrs) const *)&pbCode[offNewHdr]; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p reading NT headers...\n", pbCode); + continue; + } + if ( NtHdrs.Signature == IMAGE_NT_SIGNATURE + && NtHdrs.FileHeader.SizeOfOptionalHeader == sizeof(NtHdrs.OptionalHeader) + && NtHdrs.FileHeader.NumberOfSections > 2 + && NtHdrs.FileHeader.NumberOfSections < _4K + && NtHdrs.OptionalHeader.Magic == RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC)) + { + *ppbModule = (uint8_t *)pbCode; + *pcbModule = NtHdrs.OptionalHeader.SizeOfImage; + RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Found %s at %#p LB %#x\n", + pszImage, pbCode, NtHdrs.OptionalHeader.SizeOfImage); + return true; + } + } + } + RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Warning! Unable to locate %s...\n"); + return false; +} + + +/** + * Initializes the module information (NTOSKRNL + HAL) using exported symbols. + * This only works as long as noone is intercepting the symbols. + */ +static void rtR0Nt3InitModuleInfo(void) +{ + rtR0Nt3InitModuleInfoOne("ntoskrnl.exe", (uint8_t const *)(uintptr_t)IoGetCurrentProcess, &g_pbNt3OsKrnl, &g_cbNt3OsKrnl); + rtR0Nt3InitModuleInfoOne("hal.dll", (uint8_t const *)(uintptr_t)HalGetBusData, &g_pbNt3Hal, &g_cbNt3Hal); + g_fNt3ModuleInfoInitialized = true; +} + + +extern "C" DECLEXPORT(NTSTATUS) __stdcall +Nt3Fb_ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS enmClass, PVOID pvBuf, ULONG cbBuf, PULONG pcbActual) +{ + switch (enmClass) + { + case SystemModuleInformation: + { + PRTL_PROCESS_MODULES pInfo = (PRTL_PROCESS_MODULES)pvBuf; + ULONG cbNeeded = RT_UOFFSETOF(RTL_PROCESS_MODULES, Modules[2]); + if (pcbActual) + *pcbActual = cbNeeded; + if (cbBuf < cbNeeded) + return STATUS_INFO_LENGTH_MISMATCH; + + if (!g_fNt3ModuleInfoInitialized) + rtR0Nt3InitModuleInfo(); + + pInfo->NumberOfModules = 2; + + /* ntoskrnl.exe */ + pInfo->Modules[0].Section = NULL; + pInfo->Modules[0].MappedBase = g_pbNt3OsKrnl; + pInfo->Modules[0].ImageBase = g_pbNt3OsKrnl; + pInfo->Modules[0].ImageSize = g_cbNt3OsKrnl; + pInfo->Modules[0].Flags = 0; + pInfo->Modules[0].LoadOrderIndex = 0; + pInfo->Modules[0].InitOrderIndex = 0; + pInfo->Modules[0].LoadCount = 1024; + pInfo->Modules[0].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1; + memcpy(pInfo->Modules[0].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\ntoskrnl.exe")); + + /* hal.dll */ + pInfo->Modules[1].Section = NULL; + pInfo->Modules[1].MappedBase = g_pbNt3Hal; + pInfo->Modules[1].ImageBase = g_pbNt3Hal; + pInfo->Modules[1].ImageSize = g_cbNt3Hal; + pInfo->Modules[1].Flags = 0; + pInfo->Modules[1].LoadOrderIndex = 1; + pInfo->Modules[1].InitOrderIndex = 0; + pInfo->Modules[1].LoadCount = 1024; + pInfo->Modules[1].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1; + memcpy(pInfo->Modules[1].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\hal.dll")); + + return STATUS_SUCCESS; + } + + default: + return STATUS_INVALID_INFO_CLASS; + } +} + +/** + * Calculates the length indicated by an ModR/M sequence. + * + * @returns Length, including RM byte. + * @param bRm The RM byte. + */ +static uint32_t rtR0Nt3CalcModRmLength(uint8_t bRm) +{ + uint32_t cbRm = 1; + + if ( (bRm & X86_MODRM_MOD_MASK) == (3 << X86_MODRM_MOD_SHIFT) + || (bRm & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5) + cbRm += 4; /* disp32 */ + else if ((bRm & X86_MODRM_MOD_MASK) == (1 << X86_MODRM_MOD_SHIFT)) + cbRm += 1; /* disp8 */ + else if ((bRm & X86_MODRM_MOD_MASK) == (2 << X86_MODRM_MOD_SHIFT)) + cbRm += 2; /* disp16 */ + + if ((bRm & X86_MODRM_RM_MASK) == 4 && (bRm & X86_MODRM_MOD_MASK) != (3 << X86_MODRM_MOD_SHIFT)) + cbRm += 1; /* SIB */ + + return cbRm; +} + + +/** + * Init symbols. + * + * This is called after both ZwQuerySystemInformation and PsGetVersion are used + * for the first time. + * + * @returns IPRT status code + * @param hKrnlInfo Kernel symbol digger handle. + */ +DECLHIDDEN(int) rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo) +{ + /* + * Resolve symbols. (We set C variables (g_pfnrtXxx) here, not the __imp__Xxx ones.) + */ +#define GET_SYSTEM_ROUTINE(a_fnName) do { \ + RT_CONCAT(g_pfnrt, a_fnName) = (decltype(RT_CONCAT(g_pfnrt, a_fnName)))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_fnName); \ + } while (0) + + GET_SYSTEM_ROUTINE(PsGetVersion); + GET_SYSTEM_ROUTINE(ZwQuerySystemInformation); + GET_SYSTEM_ROUTINE(KeSetTimerEx); + GET_SYSTEM_ROUTINE(IoAttachDeviceToDeviceStack); + GET_SYSTEM_ROUTINE(PsGetCurrentProcessId); + GET_SYSTEM_ROUTINE(ZwYieldExecution); + GET_SYSTEM_ROUTINE(ExAcquireFastMutex); + GET_SYSTEM_ROUTINE(ExReleaseFastMutex); + +#define GET_FAST_CALL_SYSTEM_ROUTINE(a_fnFastcall, a_fnStdcall) do { \ + GET_SYSTEM_ROUTINE(a_fnFastcall); \ + GET_SYSTEM_ROUTINE(a_fnStdcall); \ + AssertLogRelReturn(RT_CONCAT(g_pfnrt,a_fnFastcall) || RT_CONCAT(g_pfnrt,a_fnStdcall), VERR_INTERNAL_ERROR_3); \ + } while (0) + GET_FAST_CALL_SYSTEM_ROUTINE(IofCompleteRequest, IoCompleteRequest); + GET_FAST_CALL_SYSTEM_ROUTINE(ObfDereferenceObject, ObDereferenceObject); + GET_FAST_CALL_SYSTEM_ROUTINE(IofCallDriver, IoCallDriver); + GET_FAST_CALL_SYSTEM_ROUTINE(KfAcquireSpinLock, KeAcquireSpinLock); + GET_FAST_CALL_SYSTEM_ROUTINE(KfReleaseSpinLock, KeReleaseSpinLock); + GET_FAST_CALL_SYSTEM_ROUTINE(KfLowerIrql, KeLowerIrql); + GET_FAST_CALL_SYSTEM_ROUTINE(KfRaiseIrql, KeRaiseIrql); + GET_FAST_CALL_SYSTEM_ROUTINE(KefAcquireSpinLockAtDpcLevel, KeAcquireSpinLockAtDpcLevel); + GET_FAST_CALL_SYSTEM_ROUTINE(KefReleaseSpinLockFromDpcLevel, KeReleaseSpinLockFromDpcLevel); + + /* + * We need to call assembly to update the __imp__Xxx entries, since C + * doesn't allow '@' in symbols. + */ + rtNt3InitSymbolsAssembly(); + + /* + * Tick count data. We disassemble KeQueryTickCount until we find the + * first absolute address referenced in it. + * %80105b70 8b 44 24 04 mov eax, dword [esp+004h] + * %80105b74 c7 40 04 00 00 00 00 mov dword [eax+004h], 000000000h + * %80105b7b 8b 0d 88 70 19 80 mov ecx, dword [080197088h] + * %80105b81 89 08 mov dword [eax], ecx + * %80105b83 c2 04 00 retn 00004h + */ + _imp__KeTickCount = (decltype(_imp__KeTickCount))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeTickCount"); + if (!_imp__KeTickCount) + { + if (!g_fNt3VersionInitialized) + rtR0Nt3InitVersion(); + Assert(g_uNt3MajorVer == 3 && g_uNt3MinorVer < 50); + + uint8_t const *pbCode = (uint8_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeQueryTickCount"); + AssertLogRelReturn(pbCode, VERR_INTERNAL_ERROR_2); + + for (uint32_t off = 0; off < 128 && _imp__KeTickCount == NULL;) + { + uint8_t const b1 = pbCode[off++]; + switch (b1) + { + case 0x8b: /* mov reg, r/m ; We're looking for absolute address in r/m. */ + if ((pbCode[off] & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5 /*disp32*/) + _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off + 1]; + RT_FALL_THRU(); + case 0x89: /* mov r/m, reg */ + off += rtR0Nt3CalcModRmLength(pbCode[off]); + break; + + case 0xc7: + if ((pbCode[off] & X86_MODRM_REG_MASK) == 0) /* mov r/m, imm32 */ + off += rtR0Nt3CalcModRmLength(pbCode[off]) + 4; + else + { + RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n", + off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode); + return VERR_INTERNAL_ERROR_3; + } + break; + + case 0xc2: /* ret iw */ + RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered RET! %.*Rhxs\n", + off + 2, pbCode); + return VERR_INTERNAL_ERROR_3; + + default: + RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n", + off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode); + return VERR_INTERNAL_ERROR_3; + + /* Just in case: */ + + case 0xa1: /* mov eax, [m32] */ + _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off]; + off += 4; + break; + + case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: /* push reg */ + break; + } + } + if (!_imp__KeTickCount) + { + RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount after 128 bytes! %.*Rhxs\n", 128, pbCode); + return VERR_INTERNAL_ERROR_3; + } + } + + return VINF_SUCCESS; +} + + +extern "C" DECLEXPORT(VOID) +Nt3Fb_KeInitializeTimerEx(PKTIMER pTimer, TIMER_TYPE enmType) +{ + KeInitializeTimer(pTimer); + NOREF(enmType); + /** @todo Default is NotificationTimer, for SyncrhonizationTimer we need to + * do more work. timer-r0drv-nt.cpp is using the latter. :/ */ +} + + +extern "C" DECLEXPORT(BOOLEAN) __stdcall +Nt3Fb_KeSetTimerEx(PKTIMER pTimer, LARGE_INTEGER DueTime, LONG cMsPeriod, PKDPC pDpc) +{ + AssertReturn(cMsPeriod == 0, FALSE); + return KeSetTimer(pTimer, DueTime, pDpc); +} + + +extern "C" DECLEXPORT(PDEVICE_OBJECT) +Nt3Fb_IoAttachDeviceToDeviceStack(PDEVICE_OBJECT pSourceDevice, PDEVICE_OBJECT pTargetDevice) +{ + NOREF(pSourceDevice); NOREF(pTargetDevice); + return NULL; +} + + +extern "C" DECLEXPORT(HANDLE) +Nt3Fb_PsGetCurrentProcessId(void) +{ + if (!g_fNt3VersionInitialized) + rtR0Nt3InitVersion(); + + uint8_t const *pbProcess = (uint8_t const *)IoGetCurrentProcess(); + if ( g_uNt3MajorVer > 3 + || g_uNt3MinorVer >= 50) + return *(HANDLE const *)&pbProcess[0x94]; + return *(HANDLE const *)&pbProcess[0xb0]; +} + + +extern "C" DECLEXPORT(NTSTATUS) +Nt3Fb_ZwYieldExecution(VOID) +{ + LARGE_INTEGER Interval; + Interval.QuadPart = 0; + KeDelayExecutionThread(KernelMode, FALSE, &Interval); + return STATUS_SUCCESS; +} + + +/** + * This is a simple implementation of the fast mutex api introduced in 3.50. + */ +extern "C" DECLEXPORT(VOID) FASTCALL +Nt3Fb_ExAcquireFastMutex(PFAST_MUTEX pFastMtx) +{ + PETHREAD pSelf = PsGetCurrentThread(); + KIRQL OldIrql; + KeRaiseIrql(APC_LEVEL, &OldIrql); + + /* The Count member is initialized to 1. So if we decrement it to zero, we're + the first locker and owns the mutex. Otherwise we must wait for our turn. */ + int32_t cLockers = ASMAtomicDecS32((int32_t volatile *)&pFastMtx->Count); + if (cLockers != 0) + { + ASMAtomicIncU32((uint32_t volatile *)&pFastMtx->Contention); + KeWaitForSingleObject(&pFastMtx->Event, Executive, KernelMode, FALSE /*fAlertable*/, NULL /*pTimeout*/); + } + + pFastMtx->Owner = (PKTHREAD)pSelf; + pFastMtx->OldIrql = OldIrql; +} + + +/** + * This is a simple implementation of the fast mutex api introduced in 3.50. + */ +extern "C" DECLEXPORT(VOID) FASTCALL +Nt3Fb_ExReleaseFastMutex(PFAST_MUTEX pFastMtx) +{ + AssertMsg(pFastMtx->Owner == (PKTHREAD)PsGetCurrentThread(), ("Owner=%p, expected %p\n", pFastMtx->Owner, PsGetCurrentThread())); + + KIRQL OldIrql = pFastMtx->OldIrql; + pFastMtx->Owner = NULL; + int32_t cLockers = ASMAtomicIncS32((int32_t volatile *)&pFastMtx->Count); + if (cLockers <= 0) + KeSetEvent(&pFastMtx->Event, EVENT_INCREMENT, FALSE /*fWait*/); + if (OldIrql != APC_LEVEL) + KeLowerIrql(OldIrql); +} + diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp new file mode 100644 index 00000000..b23ec713 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp @@ -0,0 +1,52 @@ +/* $Id: nt3fakes-stub-r0drv-nt.cpp $ */ +/** @file + * IPRT - NT 3.x fakes for NT 4.0+ KPIs, init stub. + */ + +/* + * 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 "internal-r0drv-nt.h" + + +int rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo) +{ + NOREF(hKrnlInfo); + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm b/src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm new file mode 100644 index 00000000..d0ed3199 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm @@ -0,0 +1,157 @@ +; $Id: nt3fakesA-r0drv-nt.asm $ +;; @file +; IPRT - Companion to nt3fakes-r0drv-nt.cpp that provides import stuff to satisfy the linker. +; + +; +; 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 "iprt/asmdefs.mac" + +%undef NAME +%define NAME(name) NAME_OVERLOAD(name) + +BEGINCODE + +;; +; Called from rtR0Nt3InitSymbols after symbols have been resolved. +BEGINPROC _rtNt3InitSymbolsAssembly + push ebp + mov ebp, esp + +;; +; @param 1 The fastcall name. +; @param 2 Byte size of arguments. +%macro DefineImportDataAndInitCode 3 +extern $%1 %+ Nt3Fb_ %+ %2 %+ @ %+ %3 +BEGINDATA +extern _g_pfnrt %+ %2 +GLOBALNAME __imp_ %+ %1 %+ %2 %+ @ %+ %3 + dd $%1 %+ Nt3Fb_ %+ %2 %+ @ %+ %3 +BEGINCODE + mov eax, [_g_pfnrt %+ %2] + test eax, eax + jz %%next + mov [__imp_ %+ %1 %+ %2 %+ @ %+ %3], eax +%%next: +%endmacro + + DefineImportDataAndInitCode _,PsGetVersion, 16 + DefineImportDataAndInitCode _,ZwQuerySystemInformation, 16 + DefineImportDataAndInitCode _,KeSetTimerEx, 20 + DefineImportDataAndInitCode _,IoAttachDeviceToDeviceStack, 8 + DefineImportDataAndInitCode _,PsGetCurrentProcessId, 0 + DefineImportDataAndInitCode _,ZwYieldExecution, 0 + DefineImportDataAndInitCode @,ExAcquireFastMutex, 4 + DefineImportDataAndInitCode @,ExReleaseFastMutex, 4 + + xor eax, eax + leave + ret +ENDPROC _rtNt3InitSymbolsAssembly + + +;; +; @param 1 The fastcall name. +; @param 2 The stdcall name. +; @param 3 Byte size of arguments. +; @param 4 Zero if 1:1 mapping; +; One if 2nd parameter is a byte pointer that the farcall version +; instead returns in al. +%macro FastOrStdCallWrapper 4 +BEGINCODE +extern _g_pfnrt %+ %1 +extern _g_pfnrt %+ %2 +BEGINPROC_EXPORTED $@ %+ %1 %+ @ %+ %3 + mov eax, [_g_pfnrt %+ %1] + cmp eax, 0 + jnz .got_fast_call + mov eax, .stdcall_wrapper + mov [__imp_@ %+ %1 %+ @ %+ %3], eax + +.stdcall_wrapper: + push ebp + mov ebp, esp +%if %4 == 1 + push dword 0 + push esp +%else + push edx +%endif + push ecx + call [_g_pfnrt %+ %2] +%if %4 == 1 + movzx eax, byte [ebp - 4] +%endif + leave + ret + +.got_fast_call: + mov [__imp_@ %+ %1 %+ @ %+ %3], eax + jmp eax +ENDPROC $@ %+ %1 %+ @ %+ %3 + +BEGINDATA +GLOBALNAME __imp_@ %+ %1 %+ @ %+ %3 + dd $@ %+ %1 %+ @ %+ %3 +%endmacro + +FastOrStdCallWrapper IofCompleteRequest, IoCompleteRequest, 8, 0 +FastOrStdCallWrapper IofCallDriver, IoCallDriver, 8, 0 +FastOrStdCallWrapper ObfDereferenceObject, ObDereferenceObject, 4, 0 +FastOrStdCallWrapper KfAcquireSpinLock, KeAcquireSpinLock, 4, 1 +FastOrStdCallWrapper KfReleaseSpinLock, KeReleaseSpinLock, 8, 0 +FastOrStdCallWrapper KfRaiseIrql, KeRaiseIrql, 4, 1 +FastOrStdCallWrapper KfLowerIrql, KeLowerIrql, 4, 0 +FastOrStdCallWrapper KefAcquireSpinLockAtDpcLevel, KeAcquireSpinLockAtDpcLevel, 4, 0 +FastOrStdCallWrapper KefReleaseSpinLockFromDpcLevel,KeReleaseSpinLockFromDpcLevel, 4, 0 + + +BEGINCODE +; LONG FASTCALL InterlockedExchange(LONG volatile *,LONG ); +BEGINPROC_EXPORTED $@InterlockedExchange@8 + mov eax, edx + xchg [ecx], eax + ret + +BEGINDATA +GLOBALNAME __imp_@InterlockedExchange@8 + dd $@InterlockedExchange@8 + + +BEGINDATA +GLOBALNAME __imp__KeTickCount +GLOBALNAME _KeTickCount + dd 0 + diff --git a/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp b/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp new file mode 100644 index 00000000..9bb46e99 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp @@ -0,0 +1,1232 @@ +/* $Id: ntBldSymDb.cpp $ */ +/** @file + * IPRT - RTDirCreateUniqueNumbered, generic implementation. + */ + +/* + * Copyright (C) 2013-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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "r0drv/nt/symdb.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** A structure member we're interested in. */ +typedef struct MYMEMBER +{ + /** The member name. */ + const char * const pszName; + /** Reserved. */ + uint32_t const fFlags; + /** The offset of the member. UINT32_MAX if not found. */ + uint32_t off; + /** The size of the member. */ + uint32_t cb; + /** Alternative names, optional. + * This is a string of zero terminated strings, ending with an zero length + * string (or double '\\0' if you like). */ + const char * const pszzAltNames; +} MYMEMBER; +/** Pointer to a member we're interested. */ +typedef MYMEMBER *PMYMEMBER; + +/** Members we're interested in. */ +typedef struct MYSTRUCT +{ + /** The structure name. */ + const char * const pszName; + /** Array of members we're interested in. */ + MYMEMBER *paMembers; + /** The number of members we're interested in. */ + uint32_t const cMembers; + /** Reserved. */ + uint32_t const fFlags; +} MYSTRUCT; + +/** Architecture. */ +typedef enum MYARCH +{ + MYARCH_X86, + MYARCH_AMD64, + MYARCH_DETECT +} MYARCH; + +/** Set of structures for one kernel. */ +typedef struct MYSET +{ + /** The list entry. */ + RTLISTNODE ListEntry; + /** The source PDB. */ + char *pszPdb; + /** The OS version we've harvested structs for */ + RTNTSDBOSVER OsVerInfo; + /** The architecture. */ + MYARCH enmArch; + /** The structures and their member. */ + MYSTRUCT aStructs[1]; +} MYSET; +/** Pointer a set of structures for one kernel. */ +typedef MYSET *PMYSET; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Verbosity level (-v, --verbose). */ +static uint32_t g_iOptVerbose = 1; +/** Set if we should force ahead despite errors. */ +static bool g_fOptForce = false; + +/** The members of the KPRCB structure that we're interested in. */ +static MYMEMBER g_aKprcbMembers[] = +{ + { "QuantumEnd", 0, UINT32_MAX, UINT32_MAX, NULL }, + { "DpcQueueDepth", 0, UINT32_MAX, UINT32_MAX, "DpcData[0].DpcQueueDepth\0" }, + { "VendorString", 0, UINT32_MAX, UINT32_MAX, NULL }, +}; + +/** The structures we're interested in. */ +static MYSTRUCT g_aStructs[] = +{ + { "_KPRCB", &g_aKprcbMembers[0], RT_ELEMENTS(g_aKprcbMembers), 0 }, +}; + +/** List of data we've found. This is sorted by version info. */ +static RTLISTANCHOR g_SetList; + + + + + +/** + * For debug/verbose output. + * + * @param pszFormat The format string. + * @param ... The arguments referenced in the format string. + */ +static void MyDbgPrintf(const char *pszFormat, ...) +{ + if (g_iOptVerbose > 1) + { + va_list va; + va_start(va, pszFormat); + RTPrintf("debug: "); + RTPrintfV(pszFormat, va); + va_end(va); + } +} + + +/** + * Returns the name we wish to use in the C code. + * @returns Structure name. + * @param pStruct The structure descriptor. + */ +static const char *figureCStructName(MYSTRUCT const *pStruct) +{ + const char *psz = pStruct->pszName; + while (*psz == '_') + psz++; + return psz; +} + + +/** + * Returns the name we wish to use in the C code. + * @returns Member name. + * @param pMember The member descriptor. + */ +static const char *figureCMemberName(MYMEMBER const *pMember) +{ + return pMember->pszName; +} + + +/** + * Creates a MYSET with copies of all the data and inserts it into the + * g_SetList in a orderly fashion. + * + * @param pOut The output stream. + */ +static void generateHeader(PRTSTREAM pOut) +{ + RTStrmPrintf(pOut, + "/* $" "I" "d" ": $ */\n" /* avoid it being expanded */ + "/** @file\n" + " * IPRT - NT kernel type helpers - Autogenerated, do NOT edit.\n" + " */\n" + "\n" + "/*\n" + " * Copyright (C) 2013-2023 Oracle and/or its affiliates.\n" + " *\n" + " * This file is part of VirtualBox base platform packages, as\n" + " * available from https://www.virtualbox.org.\n" + " *\n" + " * This program is free software; you can redistribute it and/or\n" + " * modify it under the terms of the GNU General Public License\n" + " * as published by the Free Software Foundation, in version 3 of the\n" + " * License.\n" + " *\n" + " * This program is distributed in the hope that it will be useful, but\n" + " * WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + " * General Public License for more details.\n" + " *\n" + " * You should have received a copy of the GNU General Public License\n" + " * along with this program; if not, see .\n" + " *\n" + " * The contents of this file may alternatively be used under the terms\n" + " * of the Common Development and Distribution License Version 1.0\n" + " * (CDDL), a copy of it is provided in the \"COPYING.CDDL\" file included\n" + " * in the VirtualBox distribution, in which case the provisions of the\n" + " * CDDL are applicable instead of those of the GPL.\n" + " *\n" + " * You may elect to license modified versions of this file under the\n" + " * terms and conditions of either the GPL or the CDDL or both.\n" + " *\n" + " * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0\n" + " */\n" + "\n" + "\n" + "#ifndef IPRT_INCLUDED_SRC_nt_symdbdata_h\n" + "#define IPRT_INCLUDED_SRC_nt_symdbdata_h\n" + "\n" + "#include \"r0drv/nt/symdb.h\"\n" + "\n" + ); + + /* + * Generate types. + */ + for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++) + { + const char *pszStructName = figureCStructName(&g_aStructs[i]); + + RTStrmPrintf(pOut, + "typedef struct RTNTSDBTYPE_%s\n" + "{\n", + pszStructName); + PMYMEMBER paMembers = g_aStructs[i].paMembers; + for (uint32_t j = 0; j < g_aStructs->cMembers; j++) + { + const char *pszMemName = figureCMemberName(&paMembers[j]); + RTStrmPrintf(pOut, + " uint32_t off%s;\n" + " uint32_t cb%s;\n", + pszMemName, pszMemName); + } + + RTStrmPrintf(pOut, + "} RTNTSDBTYPE_%s;\n" + "\n", + pszStructName); + } + + RTStrmPrintf(pOut, + "\n" + "typedef struct RTNTSDBSET\n" + "{\n" + " RTNTSDBOSVER%-20s OsVerInfo;\n", ""); + for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++) + { + const char *pszStructName = figureCStructName(&g_aStructs[i]); + RTStrmPrintf(pOut, " RTNTSDBTYPE_%-20s %s;\n", pszStructName, pszStructName); + } + RTStrmPrintf(pOut, + "} RTNTSDBSET;\n" + "typedef RTNTSDBSET const *PCRTNTSDBSET;\n" + "\n"); + + /* + * Output the data. + */ + RTStrmPrintf(pOut, + "\n" + "#ifndef RTNTSDB_NO_DATA\n" + "const RTNTSDBSET g_artNtSdbSets[] = \n" + "{\n"); + PMYSET pSet; + RTListForEach(&g_SetList, pSet, MYSET, ListEntry) + { + const char *pszArch = pSet->enmArch == MYARCH_AMD64 ? "AMD64" : "X86"; + RTStrmPrintf(pOut, + "# ifdef RT_ARCH_%s\n" + " { /* Source: %s */\n" + " /*.OsVerInfo = */\n" + " {\n" + " /* .uMajorVer = */ %u,\n" + " /* .uMinorVer = */ %u,\n" + " /* .fChecked = */ %s,\n" + " /* .fSmp = */ %s,\n" + " /* .uCsdNo = */ %u,\n" + " /* .uBuildNo = */ %u,\n" + " },\n", + pszArch, + pSet->pszPdb, + pSet->OsVerInfo.uMajorVer, + pSet->OsVerInfo.uMinorVer, + pSet->OsVerInfo.fChecked ? "true" : "false", + pSet->OsVerInfo.fSmp ? "true" : "false", + pSet->OsVerInfo.uCsdNo, + pSet->OsVerInfo.uBuildNo); + for (uint32_t i = 0; i < RT_ELEMENTS(pSet->aStructs); i++) + { + const char *pszStructName = figureCStructName(&pSet->aStructs[i]); + RTStrmPrintf(pOut, + " /* .%s = */\n" + " {\n", pszStructName); + PMYMEMBER paMembers = pSet->aStructs[i].paMembers; + for (uint32_t j = 0; j < pSet->aStructs[i].cMembers; j++) + { + const char *pszMemName = figureCMemberName(&paMembers[j]); + RTStrmPrintf(pOut, + " /* .off%-25s = */ %#06x,\n" + " /* .cb%-26s = */ %#06x,\n", + pszMemName, paMembers[j].off, + pszMemName, paMembers[j].cb); + } + RTStrmPrintf(pOut, + " },\n"); + } + RTStrmPrintf(pOut, + " },\n" + "# endif\n" + ); + } + + RTStrmPrintf(pOut, + "};\n" + "#endif /* !RTNTSDB_NO_DATA */\n" + "\n"); + + RTStrmPrintf(pOut, "\n#endif\n\n"); +} + + +/** + * Creates a MYSET with copies of all the data and inserts it into the + * g_SetList in a orderly fashion. + * + * @returns Fully complained exit code. + * @param pOsVerInfo The OS version info. + * @param enmArch The NT architecture of the incoming PDB. + * @param pszPdb The PDB file name. + */ +static RTEXITCODE saveStructures(PRTNTSDBOSVER pOsVerInfo, MYARCH enmArch, const char *pszPdb) +{ + /* + * Allocate one big chunk, figure it's size once. + */ + static size_t s_cbNeeded = 0; + if (s_cbNeeded == 0) + { + s_cbNeeded = RT_UOFFSETOF(MYSET, aStructs[RT_ELEMENTS(g_aStructs)]); + for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++) + s_cbNeeded += sizeof(MYMEMBER) * g_aStructs[i].cMembers; + } + + size_t cbPdb = strlen(pszPdb) + 1; + PMYSET pSet = (PMYSET)RTMemAlloc(s_cbNeeded + cbPdb); + if (!pSet) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory!\n"); + + /* + * Copy over the data. + */ + pSet->enmArch = enmArch; + memcpy(&pSet->OsVerInfo, pOsVerInfo, sizeof(pSet->OsVerInfo)); + memcpy(&pSet->aStructs[0], g_aStructs, sizeof(g_aStructs)); + + PMYMEMBER pDst = (PMYMEMBER)&pSet->aStructs[RT_ELEMENTS(g_aStructs)]; + for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++) + { + pSet->aStructs[i].paMembers = pDst; + memcpy(pDst, g_aStructs[i].paMembers, g_aStructs[i].cMembers * sizeof(*pDst)); + pDst += g_aStructs[i].cMembers; + } + + pSet->pszPdb = (char *)pDst; + memcpy(pDst, pszPdb, cbPdb); + + /* + * Link it. + */ + PMYSET pInsertBefore; + RTListForEach(&g_SetList, pInsertBefore, MYSET, ListEntry) + { + int iDiff = rtNtOsVerInfoCompare(&pInsertBefore->OsVerInfo, &pSet->OsVerInfo); + if (iDiff >= 0) + { + if (iDiff > 0 || pInsertBefore->enmArch > pSet->enmArch) + { + RTListNodeInsertBefore(&pInsertBefore->ListEntry, &pSet->ListEntry); + return RTEXITCODE_SUCCESS; + } + } + } + + RTListAppend(&g_SetList, &pSet->ListEntry); + return RTEXITCODE_SUCCESS; +} + + +/** + * Checks that we found everything. + * + * @returns Fully complained exit code. + */ +static RTEXITCODE checkThatWeFoundEverything(void) +{ + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++) + { + PMYMEMBER paMembers = g_aStructs[i].paMembers; + uint32_t j = g_aStructs[i].cMembers; + while (j-- > 0) + { + if (paMembers[j].off == UINT32_MAX) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, " Missing %s::%s\n", g_aStructs[i].pszName, paMembers[j].pszName); + } + } + return rcExit; +} + + +/** + * Matches the member against what we're looking for. + * + * @returns Number of hits. + * @param cWantedMembers The number members in paWantedMembers. + * @param paWantedMembers The members we're looking for. + * @param pszPrefix The member name prefix. + * @param pszMember The member name. + * @param offMember The member offset. + * @param cbMember The member size. + */ +static uint32_t matchUpStructMembers(unsigned cWantedMembers, PMYMEMBER paWantedMembers, + const char *pszPrefix, const char *pszMember, + uint32_t offMember, uint32_t cbMember) +{ + size_t cchPrefix = strlen(pszPrefix); + uint32_t cHits = 0; + uint32_t iMember = cWantedMembers; + while (iMember-- > 0) + { + if ( !strncmp(pszPrefix, paWantedMembers[iMember].pszName, cchPrefix) + && !strcmp(pszMember, paWantedMembers[iMember].pszName + cchPrefix)) + { + paWantedMembers[iMember].off = offMember; + paWantedMembers[iMember].cb = cbMember; + cHits++; + } + else if (paWantedMembers[iMember].pszzAltNames) + { + char const *pszCur = paWantedMembers[iMember].pszzAltNames; + while (*pszCur) + { + size_t cchCur = strlen(pszCur); + if ( !strncmp(pszPrefix, pszCur, cchPrefix) + && !strcmp(pszMember, pszCur + cchPrefix)) + { + paWantedMembers[iMember].off = offMember; + paWantedMembers[iMember].cb = cbMember; + cHits++; + break; + } + pszCur += cchCur + 1; + } + } + } + return cHits; +} + + +#if 0 +/** + * Resets the writable structure members prior to processing a PDB. + * + * While processing the PDB, will fill in the sizes and offsets of what we find. + * Afterwards we'll use look for reset values to see that every structure and + * member was located successfully. + */ +static void resetMyStructs(void) +{ + for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++) + { + PMYMEMBER paMembers = g_aStructs[i].paMembers; + uint32_t j = g_aStructs[i].cMembers; + while (j-- > 0) + { + paMembers[j].off = UINT32_MAX; + paMembers[j].cb = UINT32_MAX; + } + } +} +#endif + + +/** + * Find members in the specified structure type (@a idxType). + * + * @returns Fully bitched exit code. + * @param hFake Fake process handle. + * @param uModAddr The module address. + * @param idxType The type index of the structure which members we're + * going to process. + * @param cWantedMembers The number of wanted members. + * @param paWantedMembers The wanted members. This will be modified. + * @param offDisp Displacement when calculating member offsets. + * @param pszStructNm The top level structure name. + * @param pszPrefix The member name prefix. + * @param pszLogTag The log tag. + */ +static RTEXITCODE findMembers(HANDLE hFake, uint64_t uModAddr, uint32_t idxType, + uint32_t cWantedMembers, PMYMEMBER paWantedMembers, + uint32_t offDisp, const char *pszStructNm, const char *pszPrefix, const char *pszLogTag) +{ + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + + DWORD cChildren = 0; + if (!SymGetTypeInfo(hFake, uModAddr, idxType, TI_GET_CHILDRENCOUNT, &cChildren)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: TI_GET_CHILDRENCOUNT failed on _KPRCB: %u\n", pszLogTag, GetLastError()); + + MyDbgPrintf(" %s: cChildren=%u (%#x)\n", pszStructNm, cChildren); + TI_FINDCHILDREN_PARAMS *pChildren; + pChildren = (TI_FINDCHILDREN_PARAMS *)alloca(RT_UOFFSETOF_DYN(TI_FINDCHILDREN_PARAMS, ChildId[cChildren])); + pChildren->Start = 0; + pChildren->Count = cChildren; + if (!SymGetTypeInfo(hFake, uModAddr, idxType, TI_FINDCHILDREN, pChildren)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: TI_FINDCHILDREN failed on _KPRCB: %u\n", pszLogTag, GetLastError()); + + for (uint32_t i = 0; i < cChildren; i++) + { + //MyDbgPrintf(" %s: child#%u: TypeIndex=%u\n", pszStructNm, i, pChildren->ChildId[i]); + IMAGEHLP_SYMBOL_TYPE_INFO enmErr; + PWCHAR pwszMember = NULL; + uint32_t idxRefType = 0; + uint32_t offMember = 0; + uint64_t cbMember = 0; + uint32_t cMemberChildren = 0; + if ( SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_SYMNAME, &pwszMember) + && SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_OFFSET, &offMember) + && SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_TYPE, &idxRefType) + && SymGetTypeInfo(hFake, uModAddr, idxRefType, enmErr = TI_GET_LENGTH, &cbMember) + && SymGetTypeInfo(hFake, uModAddr, idxRefType, enmErr = TI_GET_CHILDRENCOUNT, &cMemberChildren) + ) + { + offMember += offDisp; + + char *pszMember; + int rc = RTUtf16ToUtf8(pwszMember, &pszMember); + if (RT_SUCCESS(rc)) + { + matchUpStructMembers(cWantedMembers, paWantedMembers, pszPrefix, pszMember, offMember, cbMember); + + /* + * Gather more info and do some debug printing. We'll use some + * of this info below when recursing into sub-structures + * and arrays. + */ + uint32_t fNested = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_NESTED, &fNested); + uint32_t uDataKind = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_DATAKIND, &uDataKind); + uint32_t uBaseType = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_BASETYPE, &uBaseType); + uint32_t uMembTag = 0; SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], TI_GET_SYMTAG, &uMembTag); + uint32_t uBaseTag = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_SYMTAG, &uBaseTag); + uint32_t cElements = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_COUNT, &cElements); + uint32_t idxArrayType = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_ARRAYINDEXTYPEID, &idxArrayType); + MyDbgPrintf(" %#06x LB %#06llx %c%c %2d %2d %2d %2d %2d %4d %s::%s%s\n", + offMember, cbMember, + cMemberChildren > 0 ? 'c' : '-', + fNested != 0 ? 'n' : '-', + uDataKind, + uBaseType, + uMembTag, + uBaseTag, + cElements, + idxArrayType, + pszStructNm, + pszPrefix, + pszMember); + + /* + * Recurse into children. + */ + if (cMemberChildren > 0) + { + size_t cbNeeded = strlen(pszMember) + strlen(pszPrefix) + sizeof("."); + char *pszSubPrefix = (char *)RTMemTmpAlloc(cbNeeded); + if (pszSubPrefix) + { + strcat(strcat(strcpy(pszSubPrefix, pszPrefix), pszMember), "."); + RTEXITCODE rcExit2 = findMembers(hFake, uModAddr, idxRefType, cWantedMembers, + paWantedMembers, offMember, + pszStructNm, + pszSubPrefix, + pszLogTag); + if (rcExit2 != RTEXITCODE_SUCCESS) + rcExit = rcExit2; + RTMemTmpFree(pszSubPrefix); + } + else + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory\n"); + } + /* + * Recurse into arrays too. + */ + else if (cElements > 0 && idxArrayType > 0) + { + BOOL fRc; + uint32_t idxElementRefType = 0; + fRc = SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_TYPE, &idxElementRefType); Assert(fRc); + uint64_t cbElement = cbMember / cElements; + fRc = SymGetTypeInfo(hFake, uModAddr, idxElementRefType, TI_GET_LENGTH, &cbElement); Assert(fRc); + MyDbgPrintf("idxArrayType=%u idxElementRefType=%u cbElement=%u\n", idxArrayType, idxElementRefType, cbElement); + + size_t cbNeeded = strlen(pszMember) + strlen(pszPrefix) + sizeof("[xxxxxxxxxxxxxxxx]."); + char *pszSubPrefix = (char *)RTMemTmpAlloc(cbNeeded); + if (pszSubPrefix) + { + for (uint32_t iElement = 0; iElement < cElements; iElement++) + { + RTStrPrintf(pszSubPrefix, cbNeeded, "%s%s[%u].", pszPrefix, pszMember, iElement); + RTEXITCODE rcExit2 = findMembers(hFake, uModAddr, idxElementRefType, cWantedMembers, + paWantedMembers, + offMember + iElement * cbElement, + pszStructNm, + pszSubPrefix, + pszLogTag); + if (rcExit2 != RTEXITCODE_SUCCESS) + rcExit = rcExit2; + } + RTMemTmpFree(pszSubPrefix); + } + else + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory\n"); + } + + RTStrFree(pszMember); + } + else + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTUtf16ToUtf8 failed on %s child#%u: %Rrc\n", + pszLogTag, pszStructNm, i, rc); + } + /* TI_GET_OFFSET fails on bitfields, so just ignore+skip those. */ + else if (enmErr != TI_GET_OFFSET || GetLastError() != ERROR_INVALID_FUNCTION) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: SymGetTypeInfo(,,,%d,) failed on %s child#%u: %u\n", + pszLogTag, enmErr, pszStructNm, i, GetLastError()); + LocalFree(pwszMember); + } /* For each child. */ + + return rcExit; +} + + +/** + * Lookup up structures and members in the given module. + * + * @returns Fully bitched exit code. + * @param hFake Fake process handle. + * @param uModAddr The module address. + * @param pszLogTag The log tag. + * @param pszPdb The full PDB path. + * @param pOsVerInfo The OS version info for altering the error handling + * for older OSes. + */ +static RTEXITCODE findStructures(HANDLE hFake, uint64_t uModAddr, const char *pszLogTag, const char *pszPdb, + PCRTNTSDBOSVER pOsVerInfo) +{ + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + PSYMBOL_INFO pSymInfo = (PSYMBOL_INFO)alloca(sizeof(*pSymInfo)); + for (uint32_t iStruct = 0; iStruct < RT_ELEMENTS(g_aStructs); iStruct++) + { + pSymInfo->SizeOfStruct = sizeof(*pSymInfo); + pSymInfo->MaxNameLen = 0; + if (!SymGetTypeFromName(hFake, uModAddr, g_aStructs[iStruct].pszName, pSymInfo)) + { + if (!(pOsVerInfo->uMajorVer == 5 && pOsVerInfo->uMinorVer == 0) /* w2k */) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to find _KPRCB: %u\n", pszPdb, GetLastError()); + RTMsgInfo("%s: Skipping - failed to find _KPRCB: %u\n", pszPdb, GetLastError()); + return RTEXITCODE_SKIPPED; + } + + MyDbgPrintf(" %s: TypeIndex=%u\n", g_aStructs[iStruct].pszName, pSymInfo->TypeIndex); + MyDbgPrintf(" %s: Size=%u (%#x)\n", g_aStructs[iStruct].pszName, pSymInfo->Size, pSymInfo->Size); + + rcExit = findMembers(hFake, uModAddr, pSymInfo->TypeIndex, + g_aStructs[iStruct].cMembers, g_aStructs[iStruct].paMembers, 0 /* offDisp */, + g_aStructs[iStruct].pszName, "", pszLogTag); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + } /* for each struct we want */ + return rcExit; +} + + +#if 0 /* unused */ +static bool strIEndsWith(const char *pszString, const char *pszSuffix) +{ + size_t cchString = strlen(pszString); + size_t cchSuffix = strlen(pszSuffix); + if (cchString < cchSuffix) + return false; + return RTStrICmp(pszString + cchString - cchSuffix, pszSuffix) == 0; +} +#endif + + +/** + * Use various hysterics to figure out the OS version details from the PDB path. + * + * This ASSUMES quite a bunch of things: + * -# Working on unpacked symbol packages. This does not work for + * windbg symbol stores/caches. + * -# The symbol package has been unpacked into a directory with the same + * name as the symbol package (sans suffixes). + * + * @returns Fully complained exit code. + * @param pszPdb The path to the PDB. + * @param pVerInfo Where to return the version info. + * @param penmArch Where to return the architecture. + */ +static RTEXITCODE FigurePdbVersionInfo(const char *pszPdb, PRTNTSDBOSVER pVerInfo, MYARCH *penmArch) +{ + /* + * Split the path. + */ + union + { + RTPATHSPLIT Split; + uint8_t abPad[RTPATH_MAX + 1024]; + } u; + int rc = RTPathSplit(pszPdb, &u.Split, sizeof(u), 0); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathSplit failed on '%s': %Rrc", pszPdb, rc); + if (!(u.Split.fProps & RTPATH_PROP_FILENAME)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPATH_PROP_FILENAME not set for: '%s'", pszPdb); + const char *pszFilename = u.Split.apszComps[u.Split.cComps - 1]; + + /* + * SMP or UNI kernel? + */ + if ( !RTStrICmp(pszFilename, "ntkrnlmp.pdb") + || !RTStrICmp(pszFilename, "ntkrpamp.pdb") + ) + pVerInfo->fSmp = true; + else if ( !RTStrICmp(pszFilename, "ntoskrnl.pdb") + || !RTStrICmp(pszFilename, "ntkrnlpa.pdb") + ) + pVerInfo->fSmp = false; + else + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Doesn't recognize the filename '%s'...", pszFilename); + + /* + * Look for symbol pack names in the path. This is stuff like: + * - WindowsVista.6002.090410-1830.x86fre + * - WindowsVista.6002.090410-1830.amd64chk + * - Windows_Win7.7600.16385.090713-1255.X64CHK + * - Windows_Win7SP1.7601.17514.101119-1850.AMD64FRE + * - Windows_Win8.9200.16384.120725-1247.X86CHK + * - en_windows_8_1_symbols_debug_checked_x64_2712568 + */ + uint32_t i = u.Split.cComps - 1; + while (i-- > 0) + { + static struct + { + const char *pszPrefix; + size_t cchPrefix; + uint8_t uMajorVer; + uint8_t uMinorVer; + uint8_t uCsdNo; + uint32_t uBuildNo; /**< UINT32_MAX means the number immediately after the prefix. */ + } const s_aSymPacks[] = + { + { RT_STR_TUPLE("w2kSP1SYM"), 5, 0, 1, 2195 }, + { RT_STR_TUPLE("w2ksp2srp1"), 5, 0, 2, 2195 }, + { RT_STR_TUPLE("w2ksp2sym"), 5, 0, 2, 2195 }, + { RT_STR_TUPLE("w2ksp3sym"), 5, 0, 3, 2195 }, + { RT_STR_TUPLE("w2ksp4sym"), 5, 0, 4, 2195 }, + { RT_STR_TUPLE("Windows2000-KB891861"), 5, 0, 4, 2195 }, + { RT_STR_TUPLE("windowsxp"), 5, 1, 0, 2600 }, + { RT_STR_TUPLE("xpsp1sym"), 5, 1, 1, 2600 }, + { RT_STR_TUPLE("WindowsXP-KB835935-SP2-"), 5, 1, 2, 2600 }, + { RT_STR_TUPLE("WindowsXP-KB936929-SP3-"), 5, 1, 3, 2600 }, + { RT_STR_TUPLE("Windows2003."), 5, 2, 0, 3790 }, + { RT_STR_TUPLE("Windows2003_sp1."), 5, 2, 1, 3790 }, + { RT_STR_TUPLE("WindowsServer2003-KB933548-v1"), 5, 2, 1, 3790 }, + { RT_STR_TUPLE("WindowsVista.6000."), 6, 0, 0, 6000 }, + { RT_STR_TUPLE("Windows_Longhorn.6001."), 6, 0, 1, 6001 }, /* incl w2k8 */ + { RT_STR_TUPLE("WindowsVista.6002."), 6, 0, 2, 6002 }, /* incl w2k8 */ + { RT_STR_TUPLE("Windows_Winmain.7000"), 6, 1, 0, 7000 }, /* Beta */ + { RT_STR_TUPLE("Windows_Winmain.7100"), 6, 1, 0, 7100 }, /* RC */ + { RT_STR_TUPLE("Windows_Win7.7600"), 6, 1, 0, 7600 }, /* RC */ + { RT_STR_TUPLE("Windows_Win7SP1.7601"), 6, 1, 1, 7601 }, /* RC */ + { RT_STR_TUPLE("Windows_Winmain.8102"), 6, 2, 0, 8102 }, /* preview */ + { RT_STR_TUPLE("Windows_Winmain.8250"), 6, 2, 0, 8250 }, /* beta */ + { RT_STR_TUPLE("Windows_Winmain.8400"), 6, 2, 0, 8400 }, /* RC */ + { RT_STR_TUPLE("Windows_Win8.9200"), 6, 2, 0, 9200 }, /* RTM */ + { RT_STR_TUPLE("en_windows_8_1"), 6, 3, 0, 9600 }, /* RTM */ + { RT_STR_TUPLE("en_windows_10_symbols_"), 10, 0, 0,10240 }, /* RTM */ + { RT_STR_TUPLE("en_windows_10_symbols_"), 10, 0, 0,10240 }, /* RTM */ + { RT_STR_TUPLE("en_windows_10_17134_"), 10, 0, 0,17134 }, /* 1803 */ + }; + + const char *pszComp = u.Split.apszComps[i]; + uint32_t iSymPack = RT_ELEMENTS(s_aSymPacks); + while (iSymPack-- > 0) + if (!RTStrNICmp(pszComp, s_aSymPacks[iSymPack].pszPrefix, s_aSymPacks[iSymPack].cchPrefix)) + break; + if (iSymPack >= RT_ELEMENTS(s_aSymPacks)) + continue; + + pVerInfo->uMajorVer = s_aSymPacks[iSymPack].uMajorVer; + pVerInfo->uMinorVer = s_aSymPacks[iSymPack].uMinorVer; + pVerInfo->uCsdNo = s_aSymPacks[iSymPack].uCsdNo; + pVerInfo->fChecked = false; + pVerInfo->uBuildNo = s_aSymPacks[iSymPack].uBuildNo; + + /* Parse build number if necessary. */ + if (s_aSymPacks[iSymPack].uBuildNo == UINT32_MAX) + { + char *pszNext; + rc = RTStrToUInt32Ex(pszComp + s_aSymPacks[iSymPack].cchPrefix, &pszNext, 10, &pVerInfo->uBuildNo); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to decode build number in '%s': %Rrc", pszComp, rc); + if (*pszNext != '.' && *pszNext != '_' && *pszNext != '-') + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to decode build number in '%s': '%c'", pszComp, *pszNext); + } + + /* Look for build arch and checked/free. */ + if ( RTStrIStr(pszComp, ".x86.chk.") + || RTStrIStr(pszComp, ".x86chk.") + || RTStrIStr(pszComp, "_x86_chk_") + || RTStrIStr(pszComp, "_x86chk_") + || RTStrIStr(pszComp, "-x86-DEBUG") + || (RTStrIStr(pszComp, "-x86-") && RTStrIStr(pszComp, "-DEBUG")) + || RTStrIStr(pszComp, "_debug_checked_x86") + ) + { + pVerInfo->fChecked = true; + *penmArch = MYARCH_X86; + } + else if ( RTStrIStr(pszComp, ".amd64.chk.") + || RTStrIStr(pszComp, ".amd64chk.") + || RTStrIStr(pszComp, ".x64.chk.") + || RTStrIStr(pszComp, ".x64chk.") + || RTStrIStr(pszComp, "_debug_checked_x64") + ) + { + pVerInfo->fChecked = true; + *penmArch = MYARCH_AMD64; + } + else if ( RTStrIStr(pszComp, ".amd64.fre.") + || RTStrIStr(pszComp, ".amd64fre.") + || RTStrIStr(pszComp, ".x64.fre.") + || RTStrIStr(pszComp, ".x64fre.") + ) + { + pVerInfo->fChecked = false; + *penmArch = MYARCH_AMD64; + } + else if ( RTStrIStr(pszComp, "DEBUG") + || RTStrIStr(pszComp, "_chk") + ) + { + pVerInfo->fChecked = true; + *penmArch = MYARCH_X86; + } + else if (RTStrIStr(pszComp, "_x64")) + { + pVerInfo->fChecked = false; + *penmArch = MYARCH_AMD64; + } + else + { + pVerInfo->fChecked = false; + *penmArch = MYARCH_X86; + } + return RTEXITCODE_SUCCESS; + } + + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Giving up on '%s'...\n", pszPdb); +} + + +/** + * Process one PDB. + * + * @returns Fully bitched exit code. + * @param pszPdb The path to the PDB. + */ +static RTEXITCODE processPdb(const char *pszPdb) +{ + /* + * We need the size later on, so get that now and present proper IPRT error + * info if the file is missing or inaccessible. + */ + RTFSOBJINFO ObjInfo; + int rc = RTPathQueryInfoEx(pszPdb, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathQueryInfo fail on '%s': %Rrc\n", pszPdb, rc); + + /* + * Figure the windows version details for the given PDB. + */ + MYARCH enmArch; + RTNTSDBOSVER OsVerInfo; + RTEXITCODE rcExit = FigurePdbVersionInfo(pszPdb, &OsVerInfo, &enmArch); + if (rcExit != RTEXITCODE_SUCCESS) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to figure the OS version info for '%s'.\n'", pszPdb); + + /* + * Create a fake handle and open the PDB. + */ + static uintptr_t s_iHandle = 0; + HANDLE hFake = (HANDLE)++s_iHandle; + if (!SymInitialize(hFake, NULL, FALSE)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "SymInitialied failed: %u\n", GetLastError()); + + uint64_t uModAddr = UINT64_C(0x1000000); + uModAddr = SymLoadModuleEx(hFake, NULL /*hFile*/, pszPdb, NULL /*pszModuleName*/, + uModAddr, ObjInfo.cbObject, NULL /*pData*/, 0 /*fFlags*/); + if (uModAddr != 0) + { + MyDbgPrintf("*** uModAddr=%#llx \"%s\" ***\n", uModAddr, pszPdb); + + char szLogTag[32]; + RTStrCopy(szLogTag, sizeof(szLogTag), RTPathFilename(pszPdb)); + + /* + * Find the structures. + */ + rcExit = findStructures(hFake, uModAddr, szLogTag, pszPdb, &OsVerInfo); + if (rcExit == RTEXITCODE_SUCCESS) + rcExit = checkThatWeFoundEverything(); + if (rcExit == RTEXITCODE_SUCCESS) + { + /* + * Save the details for later when we produce the header. + */ + rcExit = saveStructures(&OsVerInfo, enmArch, pszPdb); + } + } + else + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "SymLoadModuleEx failed: %u\n", GetLastError()); + + if (!SymCleanup(hFake)) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "SymCleanup failed: %u\n", GetLastError()); + + if (rcExit == RTEXITCODE_SKIPPED) + rcExit = RTEXITCODE_SUCCESS; + return rcExit; +} + + +/** The size of the directory entry buffer we're using. */ +#define MY_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX) + +/** + * Checks if the name is of interest to us. + * + * @returns true/false. + * @param pszName The name. + * @param cchName The length of the name. + */ +static bool isInterestingName(const char *pszName, size_t cchName) +{ + static struct { const char *psz; size_t cch; } const s_aNames[] = + { + { RT_STR_TUPLE("ntoskrnl.pdb") }, + { RT_STR_TUPLE("ntkrnlmp.pdb") }, + { RT_STR_TUPLE("ntkrnlpa.pdb") }, + { RT_STR_TUPLE("ntkrpamp.pdb") }, + }; + + if ( cchName == s_aNames[0].cch + && (pszName[0] == 'n' || pszName[0] == 'N') + && (pszName[1] == 't' || pszName[1] == 'T') + ) + { + int i = RT_ELEMENTS(s_aNames); + while (i-- > 0) + if ( s_aNames[i].cch == cchName + && !RTStrICmp(s_aNames[i].psz, pszName)) + return true; + } + return false; +} + + +/** + * Recursively processes relevant files in the specified directory. + * + * @returns Fully complained exit code. + * @param pszDir Pointer to the directory buffer. + * @param cchDir The length of pszDir in pszDir. + * @param pDirEntry Pointer to the directory buffer. + * @param iLogDepth The logging depth. + */ +static RTEXITCODE processDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry, int iLogDepth) +{ + Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0'); + + /* Make sure we've got some room in the path, to save us extra work further down. */ + if (cchDir + 3 >= RTPATH_MAX) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'\n", pszDir); + + /* Open directory. */ + RTDIR hDir; + int rc = RTDirOpen(&hDir, pszDir); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirOpen failed on '%s': %Rrc\n", pszDir, rc); + + /* Ensure we've got a trailing slash (there is space for it see above). */ + if (!RTPATH_IS_SEP(pszDir[cchDir - 1])) + { + pszDir[cchDir++] = RTPATH_SLASH; + pszDir[cchDir] = '\0'; + } + + /* + * Process the files and subdirs. + */ + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + for (;;) + { + /* Get the next directory. */ + size_t cbDirEntry = MY_DIRENTRY_BUF_SIZE; + rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK); + if (RT_FAILURE(rc)) + break; + + /* Skip the dot and dot-dot links. */ + if (RTDirEntryExIsStdDotLink(pDirEntry)) + continue; + + /* Check length. */ + if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX) + { + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir); + break; + } + + if (RTFS_IS_FILE(pDirEntry->Info.Attr.fMode)) + { + /* + * Process debug info files of interest. + */ + if (isInterestingName(pDirEntry->szName, pDirEntry->cbName)) + { + memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1); + RTEXITCODE rcExit2 = processPdb(pszDir); + if (rcExit2 != RTEXITCODE_SUCCESS) + rcExit = rcExit2; + } + } + else if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode)) + { + /* + * Recurse into the subdirectory. In order to speed up Win7+ + * symbol pack traversals, we skip directories with ".pdb" suffixes + * unless they match any of the .pdb files we're looking for. + * + * Note! When we get back pDirEntry will be invalid. + */ + if ( pDirEntry->cbName <= 4 + || RTStrICmp(&pDirEntry->szName[pDirEntry->cbName - 4], ".pdb") + || isInterestingName(pDirEntry->szName, pDirEntry->cbName)) + { + memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1); + if (iLogDepth > 0) + RTMsgInfo("%s%s ...\n", pszDir, RTPATH_SLASH_STR); + RTEXITCODE rcExit2 = processDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, iLogDepth - 1); + if (rcExit2 != RTEXITCODE_SUCCESS) + rcExit = rcExit2; + } + } + } + if (rc != VERR_NO_MORE_FILES) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir); + + rc = RTDirClose(hDir); + if (RT_FAILURE(rc)) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir); + return rcExit; +} + + +/** + * Recursively processes relevant files in the specified directory. + * + * @returns Fully complained exit code. + * @param pszDir The directory to search. + */ +static RTEXITCODE processDir(const char *pszDir) +{ + char szPath[RTPATH_MAX]; + int rc = RTPathAbs(pszDir, szPath, sizeof(szPath)); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc\n", pszDir, rc); + + union + { + uint8_t abPadding[MY_DIRENTRY_BUF_SIZE]; + RTDIRENTRYEX DirEntry; + } uBuf; + return processDirSub(szPath, strlen(szPath), &uBuf.DirEntry, g_iOptVerbose); +} + + +int main(int argc, char **argv) +{ + int rc = RTR3InitExe(argc, &argv, 0 /*fFlags*/); + if (RT_FAILURE(rc)) + return RTMsgInitFailure(rc); + + RTListInit(&g_SetList); + + /* + * Parse options. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--force", 'f', RTGETOPT_REQ_NOTHING }, + { "--output", 'o', RTGETOPT_REQ_STRING }, + { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, + }; + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + const char *pszOutput = "-"; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, + RTGETOPTINIT_FLAGS_OPTS_FIRST); + while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (ch) + { + case 'f': + g_fOptForce = true; + break; + + case 'v': + g_iOptVerbose++; + break; + + case 'q': + g_iOptVerbose++; + break; + + case 'o': + pszOutput = ValueUnion.psz; + break; + + case 'V': + RTPrintf("$Revision: 155249 $"); + break; + + case 'h': + RTPrintf("usage: %s [-v|--verbose] [-q|--quiet] [-f|--force] [-o|--output ] [...]\n" + " or: %s [-V|--version]\n" + " or: %s [-h|--help]\n", + argv[0], argv[0], argv[0]); + return RTEXITCODE_SUCCESS; + + case VINF_GETOPT_NOT_OPTION: + { + RTEXITCODE rcExit2; + if (RTFileExists(ValueUnion.psz)) + rcExit2 = processPdb(ValueUnion.psz); + else + rcExit2 = processDir(ValueUnion.psz); + if (rcExit2 != RTEXITCODE_SUCCESS) + { + if (!g_fOptForce) + return rcExit2; + rcExit = rcExit2; + } + break; + } + + default: + return RTGetOptPrintError(ch, &ValueUnion); + } + } + if (RTListIsEmpty(&g_SetList)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "No usable debug files found.\n"); + + /* + * Generate the output. + */ + PRTSTREAM pOut = g_pStdOut; + if (strcmp(pszOutput, "-")) + { + rc = RTStrmOpen(pszOutput, "w", &pOut); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s' for writing: %Rrc\n", pszOutput, rc); + } + + generateHeader(pOut); + + if (pOut != g_pStdOut) + rc = RTStrmClose(pOut); + else + rc = RTStrmFlush(pOut); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error %s '%s': %Rrc\n", pszOutput, + pOut != g_pStdOut ? "closing" : "flushing", rc); + return rcExit; +} diff --git a/src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp new file mode 100644 index 00000000..b3de7f03 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp @@ -0,0 +1,55 @@ +/* $Id: process-r0drv-nt.cpp $ */ +/** @file + * IPRT - Process, Ring-0 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 + + +RTDECL(RTPROCESS) RTProcSelf(void) +{ + return (RTPROCESS)(uintptr_t)PsGetCurrentProcessId(); +} + + +RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void) +{ + return (RTR0PROCESS)PsGetCurrentProcess(); +} + diff --git a/src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp new file mode 100644 index 00000000..0758f1e1 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp @@ -0,0 +1,148 @@ +/* $Id: semfastmutex-r0drv-nt.cpp $ */ +/** @file + * IPRT - Fast Mutex Semaphores, Ring-0 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 + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Wrapper for the linux semaphore structure. + */ +typedef struct RTSEMFASTMUTEXINTERNAL +{ + /** Magic value (RTSEMFASTMUTEX_MAGIC). */ + uint32_t u32Magic; + /** the NT fast mutex. */ + FAST_MUTEX Mutex; +} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL; + + + +RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx) +{ + /* + * Allocate. + */ + PRTSEMFASTMUTEXINTERNAL pThis; + Assert(sizeof(*pThis) > sizeof(void *)); + pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + /* + * Initialize. + */ + pThis->u32Magic = RTSEMFASTMUTEX_MAGIC; + ExInitializeFastMutex(&pThis->Mutex); + + *phFastMtx = pThis; + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx) +{ + /* + * Validate. + */ + PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx; + if (pThis == NIL_RTSEMFASTMUTEX) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD); + Assert(pThis->Mutex.Count == 1); + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx) +{ + /* + * Validate. + */ + PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + +#if 1 + /* + * ExAcquireFastMutex will set the IRQL to APC regardless of our current + * level. Lowering the IRQL may screw things up, so do not allow this. + */ +# if 0 /** @todo enable this when the logger has been fixed. */ + AssertMsg(KeGetCurrentIrql() <= APC_LEVEL, ("%d\n", KeGetCurrentIrql()), VERR_INVALID_STATE); +# else /* the gentler approach. */ + KIRQL Irql = KeGetCurrentIrql(); + if (Irql > APC_LEVEL) + return VERR_INVALID_STATE; +# endif +#endif + + ExAcquireFastMutex(&pThis->Mutex); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx) +{ + /* + * Validate. + */ + PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + ExReleaseFastMutex(&pThis->Mutex); + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp new file mode 100644 index 00000000..30e8c408 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp @@ -0,0 +1,246 @@ +/* $Id: semmutex-r0drv-nt.cpp $ */ +/** @file + * IPRT - Mutex Semaphores, Ring-0 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 * +*********************************************************************************************************************************/ +#define RTSEMMUTEX_WITHOUT_REMAPPING +#include "the-nt-kernel.h" +#include +#include +#include +#include +#include + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * NT mutex semaphore. + */ +typedef struct RTSEMMUTEXINTERNAL +{ + /** Magic value (RTSEMMUTEX_MAGIC). */ + uint32_t volatile u32Magic; +#ifdef RT_USE_FAST_MUTEX + /** The fast mutex object. */ + FAST_MUTEX Mutex; +#else + /** The NT Mutex object. */ + KMUTEX Mutex; +#endif +} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL; + + + +RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem) +{ + return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL); +} + + +RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags, + RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + RT_NOREF3(hClass, uSubClass, pszNameFmt); + + AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *)); + PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTSEMMUTEX_MAGIC; +#ifdef RT_USE_FAST_MUTEX + ExInitializeFastMutex(&pThis->Mutex); +#else + KeInitializeMutex(&pThis->Mutex, 0); +#endif + + *phMutexSem = pThis; + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem) +{ + /* + * Validate input. + */ + PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem; + if (pThis == NIL_RTSEMMUTEX) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE); + + /* + * Invalidate it and signal the object just in case. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE); + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +/** + * Internal worker for RTSemMutexRequest and RTSemMutexRequestNoResume + * + * @returns IPRT status code. + * @param hMutexSem The mutex handle. + * @param cMillies The timeout. + * @param fInterruptible Whether it's interruptible + * (RTSemMutexRequestNoResume) or not + * (RTSemMutexRequest). + */ +static int rtSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, BOOLEAN fInterruptible) +{ + /* + * Validate input. + */ + PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE); + + /* + * Get the mutex. + */ +#ifdef RT_USE_FAST_MUTEX + AssertMsg(cMillies == RT_INDEFINITE_WAIT, ("timeouts are not supported when using fast mutexes!\n")); + ExAcquireFastMutex(&pThis->Mutex); + return VINF_SUCCESS; + +#else /* !RT_USE_FAST_MUTEX */ + NTSTATUS rcNt; + if (cMillies == RT_INDEFINITE_WAIT) + rcNt = KeWaitForSingleObject(&pThis->Mutex, Executive, KernelMode, fInterruptible, NULL); + else + { + LARGE_INTEGER Timeout; + Timeout.QuadPart = -(int64_t)cMillies * 10000; + rcNt = KeWaitForSingleObject(&pThis->Mutex, Executive, KernelMode, fInterruptible, &Timeout); + } + switch (rcNt) + { + case STATUS_SUCCESS: + if (pThis->u32Magic == RTSEMMUTEX_MAGIC) + return VINF_SUCCESS; + return VERR_SEM_DESTROYED; + + case STATUS_ALERTED: + case STATUS_USER_APC: + Assert(fInterruptible); + return VERR_INTERRUPTED; + + case STATUS_TIMEOUT: + return VERR_TIMEOUT; + + default: + AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %lx!\n", + pThis->u32Magic, pThis, (long)rcNt)); + return VERR_INTERNAL_ERROR; + } +#endif /* !RT_USE_FAST_MUTEX */ +} + + +RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) +{ + return rtSemMutexRequest(hMutexSem, cMillies, FALSE /*fInterruptible*/); +} + + +RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RT_NOREF1(uId); RT_SRC_POS_NOREF(); + return RTSemMutexRequest(hMutexSem, cMillies); +} + + +RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) +{ + return rtSemMutexRequest(hMutexSem, cMillies, TRUE /*fInterruptible*/); +} + + +RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RT_NOREF1(uId); RT_SRC_POS_NOREF(); + return RTSemMutexRequestNoResume(hMutexSem, cMillies); +} + + +RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem) +{ + /* + * Validate input. + */ + PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE); + + /* + * Release the mutex. + */ +#ifdef RT_USE_FAST_MUTEX + ExReleaseFastMutex(&pThis->Mutex); +#else + KeReleaseMutex(&pThis->Mutex, FALSE /*Wait*/); +#endif + return VINF_SUCCESS; +} + + +RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem) +{ + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + AssertPtrReturn(pThis, false); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false); + +#ifdef RT_USE_FAST_MUTEX + return pThis->Mutex && pThis->Mutex->Owner != NULL; +#else + return KeReadStateMutex(&pThis->Mutex) == 0; +#endif +} + diff --git a/src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp new file mode 100644 index 00000000..2e7b1455 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp @@ -0,0 +1,207 @@ +/* $Id: spinlock-r0drv-nt.cpp $ */ +/** @file + * IPRT - Spinlocks, Ring-0 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 +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Apply the NoIrq hack if defined. */ +#define RTSPINLOCK_NT_HACK_NOIRQ + +#ifdef RTSPINLOCK_NT_HACK_NOIRQ +/** Indicates that the spinlock is taken. */ +# define RTSPINLOCK_NT_HACK_NOIRQ_TAKEN UINT32(0x00c0ffee) +/** Indicates that the spinlock is taken. */ +# define RTSPINLOCK_NT_HACK_NOIRQ_FREE UINT32(0xfe0000fe) +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Wrapper for the KSPIN_LOCK type. + */ +typedef struct RTSPINLOCKINTERNAL +{ + /** Spinlock magic value (RTSPINLOCK_MAGIC). */ + uint32_t volatile u32Magic; +#ifdef RTSPINLOCK_NT_HACK_NOIRQ + /** Spinlock hack. */ + uint32_t volatile u32Hack; +#endif + /** The saved IRQL. */ + KIRQL volatile SavedIrql; + /** The saved interrupt flag. */ + RTCCUINTREG volatile fIntSaved; + /** The spinlock creation flags. */ + uint32_t fFlags; + /** The NT spinlock structure. */ + KSPIN_LOCK Spinlock; +} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL; + + +RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName) +{ + AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER); + RT_NOREF1(pszName); + + /* + * Allocate. + */ + Assert(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *)); + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + /* + * Initialize & return. + */ + pThis->u32Magic = RTSPINLOCK_MAGIC; +#ifdef RTSPINLOCK_NT_HACK_NOIRQ + pThis->u32Hack = RTSPINLOCK_NT_HACK_NOIRQ_FREE; +#endif + pThis->SavedIrql = 0; + pThis->fIntSaved = 0; + pThis->fFlags = fFlags; + KeInitializeSpinLock(&pThis->Spinlock); + + *pSpinlock = pThis; + return VINF_SUCCESS; +} + + +RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock) +{ + /* + * Validate input. + */ + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock; + if (!pThis) + return VERR_INVALID_PARAMETER; + if (pThis->u32Magic != RTSPINLOCK_MAGIC) + { + AssertMsgFailed(("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic)); + return VERR_INVALID_PARAMETER; + } + + ASMAtomicIncU32(&pThis->u32Magic); + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock) +{ + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock; + AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_MAGIC, ("magic=%#x\n", pThis->u32Magic)); + + KIRQL SavedIrql; + if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) + { +#ifndef RTSPINLOCK_NT_HACK_NOIRQ + RTCCUINTREG fIntSaved = ASMGetFlags(); + ASMIntDisable(); + KeAcquireSpinLock(&pThis->Spinlock, &SavedIrql); +#else + SavedIrql = KeGetCurrentIrql(); + if (SavedIrql < DISPATCH_LEVEL) + { + KeRaiseIrql(DISPATCH_LEVEL, &SavedIrql); + Assert(SavedIrql < DISPATCH_LEVEL); + } + RTCCUINTREG fIntSaved = ASMGetFlags(); + ASMIntDisable(); + + if (!ASMAtomicCmpXchgU32(&pThis->u32Hack, RTSPINLOCK_NT_HACK_NOIRQ_TAKEN, RTSPINLOCK_NT_HACK_NOIRQ_FREE)) + { + while (!ASMAtomicCmpXchgU32(&pThis->u32Hack, RTSPINLOCK_NT_HACK_NOIRQ_TAKEN, RTSPINLOCK_NT_HACK_NOIRQ_FREE)) + ASMNopPause(); + } +#endif + pThis->fIntSaved = fIntSaved; + } + else + KeAcquireSpinLock(&pThis->Spinlock, &SavedIrql); + pThis->SavedIrql = SavedIrql; +} + + +RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock) +{ + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock; + AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_MAGIC, ("magic=%#x\n", pThis->u32Magic)); + + KIRQL SavedIrql = pThis->SavedIrql; + if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) + { + RTCCUINTREG fIntSaved = pThis->fIntSaved; + pThis->fIntSaved = 0; + +#ifndef RTSPINLOCK_NT_HACK_NOIRQ + KeReleaseSpinLock(&pThis->Spinlock, SavedIrql); + ASMSetFlags(fIntSaved); +#else + Assert(pThis->u32Hack == RTSPINLOCK_NT_HACK_NOIRQ_TAKEN); + + ASMAtomicWriteU32(&pThis->u32Hack, RTSPINLOCK_NT_HACK_NOIRQ_FREE); + ASMSetFlags(fIntSaved); + if (SavedIrql < DISPATCH_LEVEL) + KeLowerIrql(SavedIrql); +#endif + } + else + KeReleaseSpinLock(&pThis->Spinlock, SavedIrql); +} + diff --git a/src/VBox/Runtime/r0drv/nt/symdb.h b/src/VBox/Runtime/r0drv/nt/symdb.h new file mode 100644 index 00000000..931a8797 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/symdb.h @@ -0,0 +1,98 @@ +/* $Id: symdb.h $ */ +/** @file + * IPRT - Internal Header for the NT Ring-0 Driver Symbol DB. + */ + +/* + * Copyright (C) 2013-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 + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_nt_symdb_h +#define IPRT_INCLUDED_SRC_r0drv_nt_symdb_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include + + +/** + * NT Version info. + */ +typedef struct RTNTSDBOSVER +{ + /** The major version number. */ + uint8_t uMajorVer; + /** The minor version number. */ + uint8_t uMinorVer; + /** Set if checked build, clear if free (retail) build. */ + uint8_t fChecked : 1; + /** Set if multi processor kernel. */ + uint8_t fSmp : 1; + /** The service pack number. */ + uint8_t uCsdNo : 6; + /** The build number. */ + uint32_t uBuildNo; +} RTNTSDBOSVER; +/** Pointer to NT version info. */ +typedef RTNTSDBOSVER *PRTNTSDBOSVER; +/** Pointer to const NT version info. */ +typedef RTNTSDBOSVER const *PCRTNTSDBOSVER; + + +/** + * Compare NT OS version structures. + * + * @retval 0 if equal + * @retval 1 if @a pInfo1 is newer/greater than @a pInfo2 + * @retval -1 if @a pInfo1 is older/less than @a pInfo2 + * + * @param pInfo1 The first version info structure. + * @param pInfo2 The second version info structure. + */ +DECLINLINE(int) rtNtOsVerInfoCompare(PCRTNTSDBOSVER pInfo1, PCRTNTSDBOSVER pInfo2) +{ + if (pInfo1->uMajorVer != pInfo2->uMajorVer) + return pInfo1->uMajorVer > pInfo2->uMajorVer ? 1 : -1; + if (pInfo1->uMinorVer != pInfo2->uMinorVer) + return pInfo1->uMinorVer > pInfo2->uMinorVer ? 1 : -1; + if (pInfo1->uBuildNo != pInfo2->uBuildNo) + return pInfo1->uBuildNo > pInfo2->uBuildNo ? 1 : -1; + if (pInfo1->uCsdNo != pInfo2->uCsdNo) + return pInfo1->uCsdNo > pInfo2->uCsdNo ? 1 : -1; + if (pInfo1->fSmp != pInfo2->fSmp) + return pInfo1->fSmp > pInfo2->fSmp ? 1 : -1; + if (pInfo1->fChecked != pInfo2->fChecked) + return pInfo1->fChecked > pInfo2->fChecked ? 1 : -1; + return 0; +} + +#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_symdb_h */ + diff --git a/src/VBox/Runtime/r0drv/nt/symdbdata.h b/src/VBox/Runtime/r0drv/nt/symdbdata.h new file mode 100644 index 00000000..3bb8961a --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/symdbdata.h @@ -0,0 +1,2998 @@ +/* $Id: symdbdata.h $ */ +/** @file + * IPRT - NT kernel type helpers - Autogenerated, do NOT edit. + */ + +/* + * Copyright (C) 2013-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 + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_nt_symdbdata_h +#define IPRT_INCLUDED_SRC_r0drv_nt_symdbdata_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "r0drv/nt/symdb.h" + +typedef struct RTNTSDBTYPE_KPRCB +{ + uint32_t offQuantumEnd; + uint32_t cbQuantumEnd; + uint32_t offDpcQueueDepth; + uint32_t cbDpcQueueDepth; + uint32_t offVendorString; + uint32_t cbVendorString; +} RTNTSDBTYPE_KPRCB; + + +typedef struct RTNTSDBSET +{ + RTNTSDBOSVER OsVerInfo; + RTNTSDBTYPE_KPRCB KPRCB; +} RTNTSDBSET; +typedef RTNTSDBSET const *PCRTNTSDBSET; + + +#ifndef RTNTSDB_NO_DATA +const RTNTSDBSET g_artNtSdbSets[] = +{ +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_nec98\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_nec98\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_nec98\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_nec98\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_nec98\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_nec98\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 4, + /* .uBuildNo = */ 2195, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0750, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x06e8, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x072d, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 3, + /* .uBuildNo = */ 2600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x088c, + /* .cbQuantumEnd = */ 0x0004, + /* .offDpcQueueDepth = */ 0x0870, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0900, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x08c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x086c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0a78, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.fre.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU-DEBUG\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntkrnlpa.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.chk.rtm.symbols\exe\ntoskrnl.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ false, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.fre.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU-DEBUG\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x0981, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x092c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x0b60, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.chk.rtm.symbols\exe\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 5, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 3790, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1f75, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x1f18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x22b4, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86fre.Symbols\EXE\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 6000, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x19c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x196c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1bac, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86fre.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 6000, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x19c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x196c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1bac, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.amd64fre.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 6000, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x3375, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x3318, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x38bc, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86chk.Symbols\EXE\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 6000, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x19c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x196c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1bac, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86chk.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 6000, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x19c1, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x196c, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1bac, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.amd64chk.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 6000, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x3375, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x3318, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x38bc, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86fre.Symbols\EXE\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 6001, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86fre.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 6001, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.amd64fre.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 6001, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x3475, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x3418, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x399c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86chk.Symbols\EXE\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 6001, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86chk.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 6001, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.amd64chk.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 6001, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x3475, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x3418, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x399c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86fre.Symbols\EXE\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 6002, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86fre.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 6002, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.amd64fre.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 6002, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x3475, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x3418, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x399c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86chk.Symbols\EXE\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 6002, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86chk.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 6002, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1a41, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x19ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x1c2c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.amd64chk.Symbols\EXE\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 0, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 2, + /* .uBuildNo = */ 6002, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x3475, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x3418, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x399c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86FRE.Symbols\Symbols\ntkrpamp.pdb\5B308B4ED6464159B87117C711E7340C2\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 7600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86FRE.Symbols\Symbols\ntkrnlmp.pdb\998A3472EEA6405CB8C089DE868F26222\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 7600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X64FRE.Symbols\Symbols\ntkrnlmp.pdb\F8E2A8B5C9B74BF4A6E4A48F180099942\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 7600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x21d9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2198, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x4bb8, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86CHK.Symbols\Symbols\ntkrnlmp.pdb\9E7882E37C3E4AC9BB60F4EAD9DB492A1\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 7600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86CHK.Symbols\Symbols\ntkrpamp.pdb\3269AC66C11B41FC995991F129E95D5C1\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 7600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X64CHK.Symbols\Symbols\ntkrnlmp.pdb\C491E3167994497FA91338D08A7787041\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 7600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x21d9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2198, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x4bb8, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86FRE.Symbols\Symbols\ntkrpamp.pdb\684DA42A30CC450F81C535B4D18944B12\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 7601, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86FRE.Symbols\Symbols\ntkrnlmp.pdb\00625D7D36754CBEBA4533BA9A0F3FE22\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 7601, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.AMD64FRE.Symbols\Symbols\ntkrnlmp.pdb\3844DBB920174967BE7AA4A2C20430FA2\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 7601, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x21d9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2198, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x4bb8, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86CHK.Symbols\Symbols\ntkrpamp.pdb\C3355A163C47464183D85DE0B836F83A1\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 7601, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86CHK.Symbols\Symbols\ntkrnlmp.pdb\1477BEA3E003427CB248D5233B0601951\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 7601, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x1931, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x18ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x336c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.AMD64CHK.Symbols\Symbols\ntkrnlmp.pdb\FF0DE75C807A4B85A7668D2113A62EF11\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 1, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 1, + /* .uBuildNo = */ 7601, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x21d9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2198, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x4bb8, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.X86FRE.Symbols\Symbols\ntkrpamp.pdb\E2342527EA214C109CD28A19ED4FBCCE2\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9200, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2231, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x21ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x3c7c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.x64FRE.Symbols\Symbols\ntkrnlmp.pdb\724821001C1C4A03AED8C4C71C2E8D1D2\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 2, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9200, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2dd9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2d98, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x5948, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.X86CHK.Symbols\Symbols\ntkrpamp.pdb\C4F414C9D1854DE495BDAD814A722C4D1\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9200, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2231, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x21ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x3c7c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.x64CHK.Symbols\Symbols\ntkrnlmp.pdb\FC0361C3243D459496EE02EF1A7ACD271\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 2, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9200, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2dd9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2d98, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x5948, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_x86_2712593\ntkrpamp.pdb\9DC1F995475C456C8D1AA9606E3106931\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 3, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2239, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x21ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x3c7c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_x64_2712576\ntkrnlmp.pdb\A9BBA3C139724A738BE17665DB4393CA1\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 3, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2de9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2d98, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x5958, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_debug_checked_x86_2712583\ntkrpamp.pdb\77DAB075113647B5888133D3F79B7B171\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 3, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2239, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x21ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x3c7c, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_debug_checked_x64_2712568\ntkrnlmp.pdb\4C5FFE3E839647C5B9471D0C8F9710E11\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 6, + /* .uMinorVer = */ 3, + /* .fChecked = */ true, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 9600, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2de9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2d98, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x5958, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_X86 + { /* Source: s:\WinSyms\u\en_windows_10_symbols_x86_6903197\ntkrpamp.pdb\3A07902D18FD40CE929445D1777703241\ntkrpamp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 10, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 10240, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2239, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x21ec, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x3cfc, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\en_windows_10_symbols_x64_6903177\ntkrnlmp.pdb\C68EE22FDCF6477895C54A862BE165671\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 10, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 10240, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2de9, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2d98, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x6258, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +# ifdef RT_ARCH_AMD64 + { /* Source: s:\WinSyms\u\en_windows_10_17134_x64_symserv\ntkrnlmp.pdb\1E2C949B928244638C2A7406B9F3824A1\ntkrnlmp.pdb */ + /*.OsVerInfo = */ + { + /* .uMajorVer = */ 10, + /* .uMinorVer = */ 0, + /* .fChecked = */ false, + /* .fSmp = */ true, + /* .uCsdNo = */ 0, + /* .uBuildNo = */ 17134, + }, + /* .KPRCB = */ + { + /* .offQuantumEnd = */ 0x2e69, + /* .cbQuantumEnd = */ 0x0001, + /* .offDpcQueueDepth = */ 0x2e18, + /* .cbDpcQueueDepth = */ 0x0004, + /* .offVendorString = */ 0x6290, + /* .cbVendorString = */ 0x000d, + }, + }, +# endif +}; +#endif /* !RTNTSDB_NO_DATA */ + + +#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_symdbdata_h */ + diff --git a/src/VBox/Runtime/r0drv/nt/the-nt-kernel.h b/src/VBox/Runtime/r0drv/nt/the-nt-kernel.h new file mode 100644 index 00000000..f11ef67d --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/the-nt-kernel.h @@ -0,0 +1,99 @@ +/* $Id: the-nt-kernel.h $ */ +/** @file + * IPRT - Include all necessary headers for the NT kernel. + */ + +/* + * 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 + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_nt_the_nt_kernel_h +#define IPRT_INCLUDED_SRC_r0drv_nt_the_nt_kernel_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include + +#if defined(RT_ARCH_X86) && !defined(NO_INTERLOCKED_INTRINSICS) +# define NO_INTERLOCKED_INTRINSICS /* avoid trouble */ +#endif +#if (_MSC_VER >= 1400) && !defined(VBOX_WITH_PATCHED_DDK) +# include +# define _InterlockedExchange _InterlockedExchange_StupidDDKVsCompilerCrap +# define _InterlockedExchangeAdd _InterlockedExchangeAdd_StupidDDKVsCompilerCrap +# define _InterlockedCompareExchange _InterlockedCompareExchange_StupidDDKVsCompilerCrap +# define _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap +# pragma warning(disable : 4163) +RT_C_DECLS_BEGIN +# include +RT_C_DECLS_END +# pragma warning(default : 4163) +# undef _InterlockedExchange +# undef _InterlockedExchangeAdd +# undef _InterlockedCompareExchange +# undef _InterlockedAddLargeStatistic +#else +RT_C_DECLS_BEGIN +# include +RT_C_DECLS_END +#endif + +#include +#if !defined(RT_OS_WINDOWS) +# error "RT_OS_WINDOWS must be defined!" +#endif + +#include +#ifndef PAGE_OFFSET_MASK +# define PAGE_OFFSET_MASK (PAGE_SIZE - 1) +#endif + +/* Missing if we're compiling against older WDKs. */ +#ifndef NonPagedPoolNx +# define NonPagedPoolNx ((POOL_TYPE)512) +#endif + +/* + * When targeting NT4 we have to undo some of the nice macros + * installed by the later DDKs. + */ +#undef ExAllocatePool +#undef ExFreePool + +/** @def IPRT_NT_POOL_TAG + * Tag to use with the NT Pool APIs. + * In memory and in the various windbg tools it appears in the reverse order of + * what it is given as here, so it'll read "IPRT". + */ +#define IPRT_NT_POOL_TAG 'TRPI' + +#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_the_nt_kernel_h */ + diff --git a/src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp new file mode 100644 index 00000000..27d20d3d --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp @@ -0,0 +1,250 @@ +/* $Id: thread-r0drv-nt.cpp $ */ +/** @file + * IPRT - Threads, Ring-0 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 "internal/iprt.h" +#include + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include "internal-r0drv-nt.h" + + + +RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void) +{ + return (RTNATIVETHREAD)PsGetCurrentThread(); +} + + +static int rtR0ThreadNtSleepCommon(RTMSINTERVAL cMillies) +{ + LARGE_INTEGER Interval; + Interval.QuadPart = -(int64_t)cMillies * 10000; + NTSTATUS rcNt = KeDelayExecutionThread(KernelMode, TRUE, &Interval); + switch (rcNt) + { + case STATUS_SUCCESS: + return VINF_SUCCESS; + case STATUS_ALERTED: + case STATUS_USER_APC: + return VERR_INTERRUPTED; + default: + return RTErrConvertFromNtStatus(rcNt); + } +} + + +RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies) +{ + return rtR0ThreadNtSleepCommon(cMillies); +} + + +RTDECL(bool) RTThreadYield(void) +{ + return ZwYieldExecution() != STATUS_NO_YIELD_PERFORMED; +} + + +RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread) +{ + Assert(hThread == NIL_RTTHREAD); RT_NOREF1(hThread); + KIRQL Irql = KeGetCurrentIrql(); + if (Irql > APC_LEVEL) + return false; + if (!ASMIntAreEnabled()) + return false; + return true; +} + + +RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread) +{ + Assert(hThread == NIL_RTTHREAD); RT_NOREF1(hThread); + + /* + * The KeShouldYieldProcessor API introduced in Windows 10 looks like exactly + * what we want. But of course there is a snag. It may return with interrupts + * enabled when called with them disabled. Let's just hope it doesn't get upset + * by disabled interrupts in other ways... + */ + if (g_pfnrtKeShouldYieldProcessor) + { + RTCCUINTREG fSavedFlags = ASMGetFlags(); + bool fReturn = g_pfnrtKeShouldYieldProcessor() != FALSE; + ASMSetFlags(fSavedFlags); + return fReturn; + } + + /* + * Fallback approach for pre W10 kernels. + * + * If W10 is anything to go by, we should also check and yield when: + * - pPrcb->NextThread != NULL && pPrcb->NextThread != pPrcb->CurrentThread + * when QuantumEnd is zero. + * - pPrcb->DpcRequestSummary & 1 + * - pPrcb->DpcRequestSummary & 0x1e + */ + + /* + * Read the globals and check if they are useful. + */ +/** @todo Should we check KPRCB.InterruptRequest and KPRCB.DpcInterruptRequested (older kernels). */ + uint32_t const offQuantumEnd = g_offrtNtPbQuantumEnd; + uint32_t const cbQuantumEnd = g_cbrtNtPbQuantumEnd; + uint32_t const offDpcQueueDepth = g_offrtNtPbDpcQueueDepth; + if (!offQuantumEnd && !cbQuantumEnd && !offDpcQueueDepth) + return false; + Assert((offQuantumEnd && cbQuantumEnd) || (!offQuantumEnd && !cbQuantumEnd)); + + /* + * Disable interrupts so we won't be messed around. + */ + bool fPending; + RTCCUINTREG fSavedFlags = ASMIntDisableFlags(); + +#ifdef RT_ARCH_X86 + PKPCR pPcr = (PKPCR)__readfsdword(RT_UOFFSETOF(KPCR,SelfPcr)); + uint8_t *pbPrcb = (uint8_t *)pPcr->Prcb; + +#elif defined(RT_ARCH_AMD64) + /* HACK ALERT! The offset is from windbg/vista64. */ + PKPCR pPcr = (PKPCR)__readgsqword(RT_UOFFSETOF(KPCR,Self)); + uint8_t *pbPrcb = (uint8_t *)pPcr->CurrentPrcb; + +#else +# error "port me" +#endif + + /* Check QuantumEnd. */ + if (cbQuantumEnd == 1) + { + uint8_t volatile *pbQuantumEnd = (uint8_t volatile *)(pbPrcb + offQuantumEnd); + fPending = *pbQuantumEnd == TRUE; + } + else if (cbQuantumEnd == sizeof(uint32_t)) + { + uint32_t volatile *pu32QuantumEnd = (uint32_t volatile *)(pbPrcb + offQuantumEnd); + fPending = *pu32QuantumEnd != 0; + } + else + fPending = false; + + /* Check DpcQueueDepth. */ + if ( !fPending + && offDpcQueueDepth) + { + uint32_t volatile *pu32DpcQueueDepth = (uint32_t volatile *)(pbPrcb + offDpcQueueDepth); + fPending = *pu32DpcQueueDepth > 0; + } + + ASMSetFlags(fSavedFlags); + return fPending; +} + + +RTDECL(bool) RTThreadPreemptIsPendingTrusty(void) +{ + if (g_pfnrtKeShouldYieldProcessor) + return true; +#if 0 /** @todo RTThreadPreemptIsPending isn't good enough on w7 and possibly elsewhere. */ + /* RTThreadPreemptIsPending is only reliable if we've got both offsets and size. */ + return g_offrtNtPbQuantumEnd != 0 + && g_cbrtNtPbQuantumEnd != 0 + && g_offrtNtPbDpcQueueDepth != 0; +#else + return false; +#endif +} + + +RTDECL(bool) RTThreadPreemptIsPossible(void) +{ + /* yes, kernel preemption is possible. */ + return true; +} + + +RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState) +{ + AssertPtr(pState); + Assert(pState->uchOldIrql == 255); + Assert(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + KeRaiseIrql(DISPATCH_LEVEL, &pState->uchOldIrql); + RT_ASSERT_PREEMPT_CPUID_DISABLE(pState); +} + + +RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState) +{ + AssertPtr(pState); + + RT_ASSERT_PREEMPT_CPUID_RESTORE(pState); + KeLowerIrql(pState->uchOldIrql); + pState->uchOldIrql = 255; +} + + +RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread) +{ + Assert(hThread == NIL_RTTHREAD); NOREF(hThread); + + KIRQL CurIrql = KeGetCurrentIrql(); + return CurIrql > PASSIVE_LEVEL; /** @todo Is there a more correct way? */ +} + + +RTDECL(int) RTThreadQueryTerminationStatus(RTTHREAD hThread) +{ + AssertReturn(hThread == NIL_RTTHREAD, VERR_INVALID_HANDLE); + if (RT_LIKELY(g_pfnrtPsIsThreadTerminating)) + { + BOOLEAN fRc = g_pfnrtPsIsThreadTerminating(PsGetCurrentThread()); + return !fRc ? VINF_SUCCESS : VINF_THREAD_IS_TERMINATING; + } + return VERR_NOT_SUPPORTED; +} + diff --git a/src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp new file mode 100644 index 00000000..b93853a5 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp @@ -0,0 +1,167 @@ +/* $Id: thread2-r0drv-nt.cpp $ */ +/** @file + * IPRT - Threads (Part 2), Ring-0 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 "internal/thread.h" + + +DECLHIDDEN(int) rtThreadNativeInit(void) +{ + /* No TLS in Ring-0. :-/ */ + return VINF_SUCCESS; +} + + +RTDECL(RTTHREAD) RTThreadSelf(void) +{ + return rtThreadGetByNative((RTNATIVETHREAD)PsGetCurrentThread()); +} + + +DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType) +{ + /* + * Convert the IPRT priority type to NT priority. + * + * The NT priority is in the range 0..32, with realtime starting + * at 16 and the default for user processes at 8. (Should try find + * the appropriate #defines for some of this...) + */ + KPRIORITY Priority; + switch (enmType) + { + case RTTHREADTYPE_INFREQUENT_POLLER: Priority = 6; break; + case RTTHREADTYPE_EMULATION: Priority = 7; break; + case RTTHREADTYPE_DEFAULT: Priority = 8; break; + case RTTHREADTYPE_MSG_PUMP: Priority = 9; break; + case RTTHREADTYPE_IO: Priority = LOW_REALTIME_PRIORITY; break; + case RTTHREADTYPE_TIMER: Priority = MAXIMUM_PRIORITY; break; + + default: + AssertMsgFailed(("enmType=%d\n", enmType)); + return VERR_INVALID_PARAMETER; + } + + /* + * Do the actual modification. + */ + /*KPRIORITY oldPririty = */KeSetPriorityThread((PKTHREAD)pThread->Core.Key, Priority); + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread) +{ + RT_NOREF1(pThread); + return VERR_NOT_IMPLEMENTED; +} + + +DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread) +{ + PVOID pvThreadObj = pThread->Core.Key; + NTSTATUS rcNt = KeWaitForSingleObject(pvThreadObj, Executive, KernelMode, FALSE, NULL); + AssertMsg(rcNt == STATUS_SUCCESS, ("rcNt=%#x\n", rcNt)); RT_NOREF1(rcNt); +} + + +DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread) +{ + NOREF(pThread); +} + + +/** + * Native kernel thread wrapper function. + * + * This will forward to rtThreadMain and do termination upon return. + * + * @param pvArg Pointer to the argument package. + */ +static VOID rtThreadNativeMain(PVOID pvArg) +{ + PETHREAD Self = PsGetCurrentThread(); + PRTTHREADINT pThread = (PRTTHREADINT)pvArg; + + rtThreadMain(pThread, (RTNATIVETHREAD)Self, &pThread->szName[0]); + + ObDereferenceObject(Self); /* the rtThreadNativeCreate ref. */ +} + + +DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread) +{ + /* + * PsCreateSysemThread create a thread an give us a handle in return. + * We requests the object for that handle and then close it, so what + * we keep around is the pointer to the thread object and not a handle. + * The thread will dereference the object before returning. + */ + HANDLE hThread = NULL; + OBJECT_ATTRIBUTES ObjAttr; + InitializeObjectAttributes(&ObjAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + NTSTATUS rc = PsCreateSystemThread(&hThread, + THREAD_ALL_ACCESS, + &ObjAttr, + NULL /* ProcessHandle - kernel */, + NULL /* ClientID - kernel */, + rtThreadNativeMain, + pThreadInt); + if (NT_SUCCESS(rc)) + { + PVOID pvThreadObj; + rc = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL /* object type */, + KernelMode, &pvThreadObj, NULL /* handle info */); + if (NT_SUCCESS(rc)) + { + ZwClose(hThread); + *pNativeThread = (RTNATIVETHREAD)pvThreadObj; + } + else + AssertMsgFailed(("%#x\n", rc)); + } + return RTErrConvertFromNtStatus(rc); +} + diff --git a/src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp new file mode 100644 index 00000000..32d0aa74 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp @@ -0,0 +1,159 @@ +/* $Id: time-r0drv-nt.cpp $ */ +/** @file + * IPRT - Time, Ring-0 Driver, Nt. + */ + +/* + * Copyright (C) 2007-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 RTLOGGROUP_TIME +#include "the-nt-kernel.h" +#include "internal-r0drv-nt.h" +#include + + +/* + * The KeQueryTickCount macro isn't compatible with NT 3.1, use the + * exported KPI instead. + */ +#ifdef RT_ARCH_X86 +# undef KeQueryTickCount +extern "C" NTKERNELAPI void NTAPI KeQueryTickCount(PLARGE_INTEGER); +#endif + + +DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void) +{ + /* + * Note! The time source we use here must be exactly the same as in + * the ring-3 code! + * + * Using interrupt time is the simplest and requires the least calculation. + * It is also accounting for suspended time. Unfortuantely, there is no + * ring-3 for reading it... but that won't stop us. + * + * Using the tick count is problematic in ring-3 on older windows version + * as we can only get the 32-bit tick value, i.e. we'll roll over sooner or + * later. + */ +#if 1 + /* Interrupt time. */ + LARGE_INTEGER InterruptTime; + if (g_pfnrtKeQueryInterruptTimePrecise) + { + ULONG64 QpcTsIgnored; + InterruptTime.QuadPart = g_pfnrtKeQueryInterruptTimePrecise(&QpcTsIgnored); + } +# ifdef RT_ARCH_X86 + else if (g_pfnrtKeQueryInterruptTime) /* W2K+ */ + InterruptTime.QuadPart = g_pfnrtKeQueryInterruptTime(); + else if (g_uRtNtVersion >= RTNT_MAKE_VERSION(3, 50)) + { + /* NT 3.50 and later, also pre-init: Use the user shared data. */ + do + { + InterruptTime.HighPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High1Time; + InterruptTime.LowPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.LowPart; + } while (((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High2Time != InterruptTime.HighPart); + } + else + { + /* + * There is no KUSER_SHARED_DATA structure on NT 3.1, so we have no choice + * but to use the tick count. We must also avoid the KeQueryTickCount macro + * in the WDK, since NT 3.1 doesn't have the KeTickCount data export either (see above). + */ + static ULONG volatile s_uTimeIncrement = 0; + ULONG uTimeIncrement = s_uTimeIncrement; + if (!uTimeIncrement) + { + uTimeIncrement = KeQueryTimeIncrement(); + Assert(uTimeIncrement != 0); + Assert(uTimeIncrement * 100 / 100 == uTimeIncrement); + uTimeIncrement *= 100; + s_uTimeIncrement = uTimeIncrement; + } + + KeQueryTickCount(&InterruptTime); + return (uint64_t)InterruptTime.QuadPart * uTimeIncrement; + } +# else + else + InterruptTime.QuadPart = KeQueryInterruptTime(); /* Macro on AMD64. */ +# endif + return (uint64_t)InterruptTime.QuadPart * 100; +#else + /* Tick count. Works all the way back to NT 3.1 with #undef above. */ + LARGE_INTEGER Tick; + KeQueryTickCount(&Tick); + return (uint64_t)Tick.QuadPart * KeQueryTimeIncrement() * 100; +#endif +} + + +RTDECL(uint64_t) RTTimeNanoTS(void) +{ + return rtTimeGetSystemNanoTS(); +} + + +RTDECL(uint64_t) RTTimeMilliTS(void) +{ + return rtTimeGetSystemNanoTS() / RT_NS_1MS; +} + + +RTDECL(uint64_t) RTTimeSystemNanoTS(void) +{ + return rtTimeGetSystemNanoTS(); +} + + +RTDECL(uint64_t) RTTimeSystemMilliTS(void) +{ + return rtTimeGetSystemNanoTS() / RT_NS_1MS; +} + + +RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime) +{ + LARGE_INTEGER SystemTime; + if (g_pfnrtKeQuerySystemTimePrecise) + g_pfnrtKeQuerySystemTimePrecise(&SystemTime); + else + KeQuerySystemTime(&SystemTime); /* Macro on AMD64, export on X86. */ + return RTTimeSpecSetNtTime(pTime, SystemTime.QuadPart); +} + diff --git a/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp new file mode 100644 index 00000000..c1d2f121 --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp @@ -0,0 +1,1102 @@ +/* $Id: timer-r0drv-nt.cpp $ */ +/** @file + * IPRT - Timers, Ring-0 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 +#include +#include +#include + +#include "internal-r0drv-nt.h" +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** This seems to provide better accuracy. */ +#define RTR0TIMER_NT_MANUAL_RE_ARM 1 + +#if !defined(IN_GUEST) || defined(DOXYGEN_RUNNING) +/** This using high resolution timers introduced with windows 8.1. */ +# define RTR0TIMER_NT_HIGH_RES 1 +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * A sub timer structure. + * + * This is used for keeping the per-cpu tick and DPC object. + */ +typedef struct RTTIMERNTSUBTIMER +{ + /** The tick counter. */ + uint64_t iTick; + /** Pointer to the parent timer. */ + PRTTIMER pParent; + /** Thread active executing the worker function, NIL if inactive. */ + RTNATIVETHREAD volatile hActiveThread; + /** The NT DPC object. */ + KDPC NtDpc; + /** Whether we failed to set the target CPU for the DPC and that this needs + * to be done at RTTimerStart (simple timers) or during timer callback (omni). */ + bool fDpcNeedTargetCpuSet; +} RTTIMERNTSUBTIMER; +/** Pointer to a NT sub-timer structure. */ +typedef RTTIMERNTSUBTIMER *PRTTIMERNTSUBTIMER; + +/** + * The internal representation of an Linux timer handle. + */ +typedef struct RTTIMER +{ + /** Magic. + * This is RTTIMER_MAGIC, but changes to something else before the timer + * is destroyed to indicate clearly that thread should exit. */ + uint32_t volatile u32Magic; + /** Suspend count down for single shot omnit timers. */ + int32_t volatile cOmniSuspendCountDown; + /** Flag indicating the timer is suspended. */ + bool volatile fSuspended; + /** Whether the timer must run on one specific CPU or not. */ + bool fSpecificCpu; + /** Whether the timer must run on all CPUs or not. */ + bool fOmniTimer; + /** The CPU it must run on if fSpecificCpu is set. + * The master CPU for an omni-timer. */ + RTCPUID idCpu; + /** Callback. */ + PFNRTTIMER pfnTimer; + /** User argument. */ + void *pvUser; + + /** @name Periodic scheduling / RTTimerChangeInterval. + * @{ */ + /** Spinlock protecting the u64NanoInterval, iMasterTick, uNtStartTime, + * uNtDueTime and (at least for updating) fSuspended. */ + KSPIN_LOCK Spinlock; + /** The timer interval. 0 if one-shot. */ + uint64_t volatile u64NanoInterval; + /** The the current master tick. This does not necessarily follow that of + * the subtimer, as RTTimerChangeInterval may cause it to reset. */ + uint64_t volatile iMasterTick; +#ifdef RTR0TIMER_NT_MANUAL_RE_ARM + /** The desired NT time of the first tick. + * This is not set for one-shot timers, only periodic ones. */ + uint64_t volatile uNtStartTime; + /** The current due time (absolute interrupt time). + * This is not set for one-shot timers, only periodic ones. */ + uint64_t volatile uNtDueTime; +#endif + /** @} */ + + /** The NT timer object. */ + KTIMER NtTimer; +#ifdef RTR0TIMER_NT_HIGH_RES + /** High resolution timer. If not NULL, this must be used instead of NtTimer. */ + PEX_TIMER pHighResTimer; +#endif + /** The number of sub-timers. */ + RTCPUID cSubTimers; + /** Sub-timers. + * Normally there is just one, but for RTTIMER_FLAGS_CPU_ALL this will contain + * an entry for all possible cpus. In that case the index will be the same as + * for the RTCpuSet. */ + RTTIMERNTSUBTIMER aSubTimers[1]; +} RTTIMER; + + +#ifdef RTR0TIMER_NT_MANUAL_RE_ARM + +/** + * Get current NT interrupt time. + * @return NT interrupt time + */ +static uint64_t rtTimerNtQueryInterruptTime(void) +{ +# ifdef RT_ARCH_AMD64 + return KeQueryInterruptTime(); /* macro */ +# else + if (g_pfnrtKeQueryInterruptTime) + return g_pfnrtKeQueryInterruptTime(); + + /* NT4 */ + ULARGE_INTEGER InterruptTime; + do + { + InterruptTime.HighPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High1Time; + InterruptTime.LowPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.LowPart; + } while (((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High2Time != (LONG)InterruptTime.HighPart); + return InterruptTime.QuadPart; +# endif +} + +/** + * Get current NT interrupt time, high resolution variant. + * @return High resolution NT interrupt time + */ +static uint64_t rtTimerNtQueryInterruptTimeHighRes(void) +{ + if (g_pfnrtKeQueryInterruptTimePrecise) + { + ULONG64 uQpcIgnored; + return g_pfnrtKeQueryInterruptTimePrecise(&uQpcIgnored); + } + return rtTimerNtQueryInterruptTime(); +} + +#endif /* RTR0TIMER_NT_MANUAL_RE_ARM */ + + +/** + * Worker for rtTimerNtRearmInternval that calculates the next due time. + * + * @returns The next due time (relative, so always negative). + * @param uNtNow The current time. + * @param uNtStartTime The start time of the timer. + * @param iTick The next tick number (zero being @a uNtStartTime). + * @param cNtInterval The timer interval in NT ticks. + * @param cNtNegDueSaftyMargin The due time safety margin in negative NT + * ticks. + * @param cNtMinNegInterval The minium interval to use when in catchup + * mode, also negative NT ticks. + */ +DECLINLINE(int64_t) rtTimerNtCalcNextDueTime(uint64_t uNtNow, uint64_t uNtStartTime, uint64_t iTick, uint64_t cNtInterval, + int32_t const cNtNegDueSaftyMargin, int32_t const cNtMinNegInterval) +{ + /* Calculate the actual time elapsed since timer start: */ + int64_t iDueTime = uNtNow - uNtStartTime; + if (iDueTime < 0) + iDueTime = 0; + + /* Now calculate the nominal time since timer start for the next tick: */ + uint64_t const uNtNextRelStart = iTick * cNtInterval; + + /* Calulate now much time we have to the next tick: */ + iDueTime -= uNtNextRelStart; + + /* If we haven't already overshot the due time, including some safety margin, we're good: */ + if (iDueTime < cNtNegDueSaftyMargin) + return iDueTime; + + /* Okay, we've overshot it and are in catchup mode: */ + if (iDueTime < (int64_t)cNtInterval) + iDueTime = -(int64_t)(cNtInterval / 2); /* double time */ + else if (iDueTime < (int64_t)(cNtInterval * 4)) + iDueTime = -(int64_t)(cNtInterval / 4); /* quadruple time */ + else + return cNtMinNegInterval; + + /* Make sure we don't try intervals smaller than the minimum specified by the caller: */ + if (iDueTime > cNtMinNegInterval) + iDueTime = cNtMinNegInterval; + return iDueTime; +} + +/** + * Manually re-arms an internval timer. + * + * Turns out NT doesn't necessarily do a very good job at re-arming timers + * accurately, this is in part due to KeSetTimerEx API taking the interval in + * milliseconds. + * + * @param pTimer The timer. + * @param pMasterDpc The master timer DPC for passing to KeSetTimerEx + * in low-resolution mode. Ignored for high-res. + */ +static void rtTimerNtRearmInternval(PRTTIMER pTimer, PKDPC pMasterDpc) +{ +#ifdef RTR0TIMER_NT_MANUAL_RE_ARM + Assert(pTimer->u64NanoInterval); + + /* + * For simplicity we acquire the spinlock for the whole operation. + * This should be perfectly fine as it doesn't change the IRQL. + */ + Assert(KeGetCurrentIrql() >= DISPATCH_LEVEL); + KeAcquireSpinLockAtDpcLevel(&pTimer->Spinlock); + + /* + * Make sure it wasn't suspended + */ + if (!ASMAtomicUoReadBool(&pTimer->fSuspended)) + { + uint64_t const cNtInterval = ASMAtomicUoReadU64(&pTimer->u64NanoInterval) / 100; + uint64_t const uNtStartTime = ASMAtomicUoReadU64(&pTimer->uNtStartTime); + uint64_t const iTick = ++pTimer->iMasterTick; + + /* + * Calculate the deadline for the next timer tick and arm the timer. + * We always use a relative tick, i.e. negative DueTime value. This is + * crucial for the the high resolution API as it will bugcheck otherwise. + */ + int64_t iDueTime; + uint64_t uNtNow; +# ifdef RTR0TIMER_NT_HIGH_RES + if (pTimer->pHighResTimer) + { + /* Must use highres time here. */ + uNtNow = rtTimerNtQueryInterruptTimeHighRes(); + iDueTime = rtTimerNtCalcNextDueTime(uNtNow, uNtStartTime, iTick, cNtInterval, + -100 /* 10us safety */, -2000 /* 200us min interval*/); + g_pfnrtExSetTimer(pTimer->pHighResTimer, iDueTime, 0, NULL); + } + else +# endif + { + /* Expect interrupt time and timers to expire at the same time, so + don't use high res time api here. */ + uNtNow = rtTimerNtQueryInterruptTime(); + iDueTime = rtTimerNtCalcNextDueTime(uNtNow, uNtStartTime, iTick, cNtInterval, + -100 /* 10us safety */, -2500 /* 250us min interval*/); /** @todo use max interval here */ + LARGE_INTEGER DueTime; + DueTime.QuadPart = iDueTime; + KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, pMasterDpc); + } + + pTimer->uNtDueTime = uNtNow + -iDueTime; + } + + KeReleaseSpinLockFromDpcLevel(&pTimer->Spinlock); +#else + RT_NOREF(pTimer, iTick, pMasterDpc); +#endif +} + + +/** + * Common timer callback worker for the non-omni timers. + * + * @param pTimer The timer. + */ +static void rtTimerNtSimpleCallbackWorker(PRTTIMER pTimer) +{ + /* + * Check that we haven't been suspended before doing the callout. + */ + if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) + && pTimer->u32Magic == RTTIMER_MAGIC) + { + ASMAtomicWriteHandle(&pTimer->aSubTimers[0].hActiveThread, RTThreadNativeSelf()); + + if (!pTimer->u64NanoInterval) + ASMAtomicWriteBool(&pTimer->fSuspended, true); + uint64_t iTick = ++pTimer->aSubTimers[0].iTick; + + pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick); + + /* We re-arm the timer after calling pfnTimer, as it may stop the timer + or change the interval, which would mean doing extra work. */ + if (!pTimer->fSuspended && pTimer->u64NanoInterval) + rtTimerNtRearmInternval(pTimer, &pTimer->aSubTimers[0].NtDpc); + + ASMAtomicWriteHandle(&pTimer->aSubTimers[0].hActiveThread, NIL_RTNATIVETHREAD); + } +} + + +/** + * Timer callback function for the low-resolution non-omni timers. + * + * @param pDpc Pointer to the DPC. + * @param pvUser Pointer to our internal timer structure. + * @param SystemArgument1 Some system argument. + * @param SystemArgument2 Some system argument. + */ +static void _stdcall rtTimerNtSimpleCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) +{ + PRTTIMER pTimer = (PRTTIMER)pvUser; + AssertPtr(pTimer); +#ifdef RT_STRICT + if (KeGetCurrentIrql() < DISPATCH_LEVEL) + RTAssertMsg2Weak("rtTimerNtSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); +#endif + + rtTimerNtSimpleCallbackWorker(pTimer); + + RT_NOREF(pDpc, SystemArgument1, SystemArgument2); +} + + +#ifdef RTR0TIMER_NT_HIGH_RES +/** + * Timer callback function for the high-resolution non-omni timers. + * + * @param pExTimer The windows timer. + * @param pvUser Pointer to our internal timer structure. + */ +static void _stdcall rtTimerNtHighResSimpleCallback(PEX_TIMER pExTimer, void *pvUser) +{ + PRTTIMER pTimer = (PRTTIMER)pvUser; + AssertPtr(pTimer); + Assert(pTimer->pHighResTimer == pExTimer); +# ifdef RT_STRICT + if (KeGetCurrentIrql() < DISPATCH_LEVEL) + RTAssertMsg2Weak("rtTimerNtHighResSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); +# endif + + /* If we're not on the desired CPU, trigger the DPC. That will rearm the + timer and such. */ + if ( !pTimer->fSpecificCpu + || pTimer->idCpu == RTMpCpuId()) + rtTimerNtSimpleCallbackWorker(pTimer); + else + KeInsertQueueDpc(&pTimer->aSubTimers[0].NtDpc, 0, 0); + + RT_NOREF(pExTimer); +} +#endif /* RTR0TIMER_NT_HIGH_RES */ + + +/** + * The slave DPC callback for an omni timer. + * + * @param pDpc The DPC object. + * @param pvUser Pointer to the sub-timer. + * @param SystemArgument1 Some system stuff. + * @param SystemArgument2 Some system stuff. + */ +static void _stdcall rtTimerNtOmniSlaveCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) +{ + PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser; + PRTTIMER pTimer = pSubTimer->pParent; + + AssertPtr(pTimer); +#ifdef RT_STRICT + if (KeGetCurrentIrql() < DISPATCH_LEVEL) + RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); + int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId()); + if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf) + RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]); +#endif + + /* + * Check that we haven't been suspended before doing the callout. + */ + if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) + && pTimer->u32Magic == RTTIMER_MAGIC) + { + ASMAtomicWriteHandle(&pSubTimer->hActiveThread, RTThreadNativeSelf()); + + if (!pTimer->u64NanoInterval) + if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0) + ASMAtomicWriteBool(&pTimer->fSuspended, true); + + pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick); + + ASMAtomicWriteHandle(&pSubTimer->hActiveThread, NIL_RTNATIVETHREAD); + } + + NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2); +} + + +/** + * Called when we have an impcomplete DPC object. + * + * @returns KeInsertQueueDpc return value. + * @param pSubTimer The sub-timer to queue an DPC for. + * @param iCpu The CPU set index corresponding to that sub-timer. + */ +DECL_NO_INLINE(static, BOOLEAN) rtTimerNtOmniQueueDpcSlow(PRTTIMERNTSUBTIMER pSubTimer, int iCpu) +{ + int rc = rtMpNtSetTargetProcessorDpc(&pSubTimer->NtDpc, RTMpCpuIdFromSetIndex(iCpu)); + if (RT_SUCCESS(rc)) + { + pSubTimer->fDpcNeedTargetCpuSet = false; + return KeInsertQueueDpc(&pSubTimer->NtDpc, 0, 0); + } + return FALSE; +} + + +/** + * Wrapper around KeInsertQueueDpc that makes sure the target CPU has been set. + * + * This is for handling deferred rtMpNtSetTargetProcessorDpc failures during + * creation. These errors happens for offline CPUs which probably never every + * will come online, as very few systems do CPU hotplugging. + * + * @returns KeInsertQueueDpc return value. + * @param pSubTimer The sub-timer to queue an DPC for. + * @param iCpu The CPU set index corresponding to that sub-timer. + */ +DECLINLINE(BOOLEAN) rtTimerNtOmniQueueDpc(PRTTIMERNTSUBTIMER pSubTimer, int iCpu) +{ + if (RT_LIKELY(!pSubTimer->fDpcNeedTargetCpuSet)) + return KeInsertQueueDpc(&pSubTimer->NtDpc, 0, 0); + return rtTimerNtOmniQueueDpcSlow(pSubTimer, iCpu); +} + + +/** + * Common timer callback worker for omni-timers. + * + * This is responsible for queueing the DPCs for the other CPUs and + * perform the callback on the CPU on which it is called. + * + * @param pTimer The timer. + * @param pSubTimer The sub-timer of the calling CPU. + * @param iCpuSelf The set index of the CPU we're running on. + */ +static void rtTimerNtOmniMasterCallbackWorker(PRTTIMER pTimer, PRTTIMERNTSUBTIMER pSubTimer, int iCpuSelf) +{ + /* + * Check that we haven't been suspended before scheduling the other DPCs + * and doing the callout. + */ + if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) + && pTimer->u32Magic == RTTIMER_MAGIC) + { + RTCPUSET OnlineSet; + RTMpGetOnlineSet(&OnlineSet); + + ASMAtomicWriteHandle(&pSubTimer->hActiveThread, RTThreadNativeSelf()); + + if (pTimer->u64NanoInterval) + { + /* + * Recurring timer. + */ + for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) + if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu) + && iCpuSelf != iCpu) + rtTimerNtOmniQueueDpc(&pTimer->aSubTimers[iCpu], iCpu); + + pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick); + + /* We re-arm the timer after calling pfnTimer, as it may stop the timer + or change the interval, which would mean doing extra work. */ + if (!pTimer->fSuspended && pTimer->u64NanoInterval) + rtTimerNtRearmInternval(pTimer, &pSubTimer->NtDpc); + } + else + { + /* + * Single shot timers gets complicated wrt to fSuspended maintance. + */ + uint32_t cCpus = 0; + for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) + if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)) + cCpus++; + ASMAtomicAddS32(&pTimer->cOmniSuspendCountDown, cCpus); /** @todo this is bogus bogus bogus. The counter is only used here. */ + + for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) + if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu) + && iCpuSelf != iCpu) + if (!rtTimerNtOmniQueueDpc(&pTimer->aSubTimers[iCpu], iCpu)) + ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown); /* already queued and counted. */ + + if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0) + ASMAtomicWriteBool(&pTimer->fSuspended, true); + + pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick); + } + + ASMAtomicWriteHandle(&pSubTimer->hActiveThread, NIL_RTNATIVETHREAD); + } +} + + +/** + * The timer callback for an omni-timer, low-resolution. + * + * @param pDpc The DPC object. + * @param pvUser Pointer to the sub-timer. + * @param SystemArgument1 Some system stuff. + * @param SystemArgument2 Some system stuff. + */ +static void _stdcall rtTimerNtOmniMasterCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) +{ + PRTTIMERNTSUBTIMER const pSubTimer = (PRTTIMERNTSUBTIMER)pvUser; + PRTTIMER const pTimer = pSubTimer->pParent; + RTCPUID idCpu = RTMpCpuId(); + int const iCpuSelf = RTMpCpuIdToSetIndex(idCpu); + + AssertPtr(pTimer); +#ifdef RT_STRICT + if (KeGetCurrentIrql() < DISPATCH_LEVEL) + RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); + /* We must be called on the master CPU or the tick variable goes south. */ + if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf) + RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]); + if (pTimer->idCpu != idCpu) + RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: pTimer->idCpu=%d vs idCpu=%d\n", pTimer->idCpu, idCpu); +#endif + + rtTimerNtOmniMasterCallbackWorker(pTimer, pSubTimer, iCpuSelf); + + RT_NOREF(pDpc, SystemArgument1, SystemArgument2); +} + + +#ifdef RTR0TIMER_NT_HIGH_RES +/** + * The timer callback for an high-resolution omni-timer. + * + * @param pExTimer The windows timer. + * @param pvUser Pointer to our internal timer structure. + */ +static void __stdcall rtTimerNtHighResOmniCallback(PEX_TIMER pExTimer, void *pvUser) +{ + PRTTIMER const pTimer = (PRTTIMER)pvUser; + int const iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId()); + PRTTIMERNTSUBTIMER const pSubTimer = &pTimer->aSubTimers[iCpuSelf]; + + AssertPtr(pTimer); + Assert(pTimer->pHighResTimer == pExTimer); +# ifdef RT_STRICT + if (KeGetCurrentIrql() < DISPATCH_LEVEL) + RTAssertMsg2Weak("rtTimerNtHighResOmniCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); +# endif + + rtTimerNtOmniMasterCallbackWorker(pTimer, pSubTimer, iCpuSelf); + + RT_NOREF(pExTimer); +} +#endif /* RTR0TIMER_NT_HIGH_RES */ + + +RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First) +{ + /* + * Validate. + */ + AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE); + + /* + * The operation is protected by the spinlock. + */ + KIRQL bSavedIrql; + KeAcquireSpinLock(&pTimer->Spinlock, &bSavedIrql); + + /* + * Check the state. + */ + if (ASMAtomicUoReadBool(&pTimer->fSuspended)) + { /* likely */ } + else + { + KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql); + return VERR_TIMER_ACTIVE; + } + if ( !pTimer->fSpecificCpu + || RTMpIsCpuOnline(pTimer->idCpu)) + { /* likely */ } + else + { + KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql); + return VERR_CPU_OFFLINE; + } + + /* + * Lazy set the DPC target CPU if needed. + */ + if ( !pTimer->fSpecificCpu + || !pTimer->aSubTimers[0].fDpcNeedTargetCpuSet) + { /* likely */ } + else + { + int rc = rtMpNtSetTargetProcessorDpc(&pTimer->aSubTimers[0].NtDpc, pTimer->idCpu); + if (RT_FAILURE(rc)) + { + KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql); + return rc; + } + } + + /* + * Do the starting. + */ +#ifndef RTR0TIMER_NT_MANUAL_RE_ARM + /* Calculate the interval time: */ + uint64_t u64Interval = pTimer->u64NanoInterval / 1000000; /* This is ms, believe it or not. */ + ULONG ulInterval = (ULONG)u64Interval; + if (ulInterval != u64Interval) + ulInterval = MAXLONG; + else if (!ulInterval && pTimer->u64NanoInterval) + ulInterval = 1; +#endif + + /* Translate u64First to a DueTime: */ + LARGE_INTEGER DueTime; + DueTime.QuadPart = -(int64_t)(u64First / 100); /* Relative, NT time. */ + if (!DueTime.QuadPart) + DueTime.QuadPart = -10; /* 1us */ + + /* Reset tick counters: */ + unsigned cSubTimers = pTimer->fOmniTimer ? pTimer->cSubTimers : 1; + for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++) + pTimer->aSubTimers[iCpu].iTick = 0; + pTimer->iMasterTick = 0; + + /* Update timer state: */ +#ifdef RTR0TIMER_NT_MANUAL_RE_ARM + if (pTimer->u64NanoInterval > 0) + { +#ifdef RTR0TIMER_NT_HIGH_RES + uint64_t const uNtNow = pTimer->pHighResTimer ? rtTimerNtQueryInterruptTimeHighRes() : rtTimerNtQueryInterruptTime(); +# else + uint64_t const uNtNow = rtTimerNtQueryInterruptTime(); +# endif + pTimer->uNtStartTime = uNtNow + -DueTime.QuadPart; + pTimer->uNtDueTime = pTimer->uNtStartTime; + } +#endif + pTimer->cOmniSuspendCountDown = 0; + ASMAtomicWriteBool(&pTimer->fSuspended, false); + + /* + * Finally start the NT timer. + * + * We do this without holding the spinlock to err on the side of + * caution in case ExSetTimer or KeSetTimerEx ever should have the idea + * of running the callback before returning. + */ + KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql); + +#ifdef RTR0TIMER_NT_HIGH_RES + if (pTimer->pHighResTimer) + { +# ifdef RTR0TIMER_NT_MANUAL_RE_ARM + g_pfnrtExSetTimer(pTimer->pHighResTimer, DueTime.QuadPart, 0, NULL); +# else + g_pfnrtExSetTimer(pTimer->pHighResTimer, DueTime.QuadPart, RT_MIN(pTimer->u64NanoInterval / 100, MAXLONG), NULL); +# endif + } + else +#endif + { + PKDPC const pMasterDpc = &pTimer->aSubTimers[pTimer->fOmniTimer ? RTMpCpuIdToSetIndex(pTimer->idCpu) : 0].NtDpc; +#ifdef RTR0TIMER_NT_MANUAL_RE_ARM + KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, pMasterDpc); +#else + KeSetTimerEx(&pTimer->NtTimer, DueTime, ulInterval, pMasterDpc); +#endif + } + return VINF_SUCCESS; +} + + +/** + * Worker function that stops an active timer. + * + * Shared by RTTimerStop and RTTimerDestroy. + * + * @param pTimer The active timer. + */ +static int rtTimerNtStopWorker(PRTTIMER pTimer) +{ + /* + * Update the state from with the spinlock context. + */ + KIRQL bSavedIrql; + KeAcquireSpinLock(&pTimer->Spinlock, &bSavedIrql); + + bool const fWasSuspended = ASMAtomicXchgBool(&pTimer->fSuspended, true); + + KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql); + if (!fWasSuspended) + { + /* + * We should cacnel the timer and dequeue DPCs. + */ +#ifdef RTR0TIMER_NT_HIGH_RES + if (pTimer->pHighResTimer) + { + g_pfnrtExCancelTimer(pTimer->pHighResTimer, NULL); + + /* We can skip the DPC stuff, unless this is an omni timer or for a specific CPU. */ + if (!pTimer->fSpecificCpu && !pTimer->fOmniTimer) + return VINF_SUCCESS; + } + else +#endif + KeCancelTimer(&pTimer->NtTimer); + + for (RTCPUID iCpu = 0; iCpu < pTimer->cSubTimers; iCpu++) + KeRemoveQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc); + return VINF_SUCCESS; + } + return VERR_TIMER_SUSPENDED; +} + + +RTDECL(int) RTTimerStop(PRTTIMER pTimer) +{ + /* + * Validate. + */ + AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE); + + /* + * Call the worker we share with RTTimerDestroy. + */ + return rtTimerNtStopWorker(pTimer); +} + + +RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval) +{ + AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE); + + /* + * We do all the state changes while holding the spinlock. + */ + int rc = VINF_SUCCESS; + KIRQL bSavedIrql; + KeAcquireSpinLock(&pTimer->Spinlock, &bSavedIrql); + + /* + * When the timer isn't running, this is an simple job: + */ + if (!ASMAtomicUoReadBool(&pTimer->fSuspended)) + pTimer->u64NanoInterval = u64NanoInterval; + else + { + /* + * We only implement changing the interval in RTR0TIMER_NT_MANUAL_RE_ARM + * mode right now. We typically let the new interval take effect after + * the next timer callback, unless that's too far ahead. + */ +#ifdef RTR0TIMER_NT_MANUAL_RE_ARM + pTimer->u64NanoInterval = u64NanoInterval; + pTimer->iMasterTick = 0; +# ifdef RTR0TIMER_NT_HIGH_RES + uint64_t const uNtNow = pTimer->pHighResTimer ? rtTimerNtQueryInterruptTimeHighRes() : rtTimerNtQueryInterruptTime(); +# else + uint64_t const uNtNow = rtTimerNtQueryInterruptTime(); +# endif + if (uNtNow >= pTimer->uNtDueTime) + pTimer->uNtStartTime = uNtNow; + else + { + pTimer->uNtStartTime = pTimer->uNtDueTime; + + /* + * Re-arm the timer if the next DueTime is both more than 1.25 new + * intervals and at least 0.5 ms ahead. + */ + uint64_t cNtToNext = pTimer->uNtDueTime - uNtNow; + if ( cNtToNext >= RT_NS_1MS / 2 / 100 /* 0.5 ms */ + && cNtToNext * 100 > u64NanoInterval + u64NanoInterval / 4) + { + pTimer->uNtStartTime = pTimer->uNtDueTime = uNtNow + u64NanoInterval / 100; +# ifdef RTR0TIMER_NT_HIGH_RES + if (pTimer->pHighResTimer) + g_pfnrtExSetTimer(pTimer->pHighResTimer, -(int64_t)u64NanoInterval / 100, 0, NULL); + else +# endif + { + LARGE_INTEGER DueTime; + DueTime.QuadPart = -(int64_t)u64NanoInterval / 100; + KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, + &pTimer->aSubTimers[pTimer->fOmniTimer ? RTMpCpuIdToSetIndex(pTimer->idCpu) : 0].NtDpc); + } + } + } +#else + rc = VERR_NOT_SUPPORTED; +#endif + } + + KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql); + + return rc; +} + + +RTDECL(int) RTTimerDestroy(PRTTIMER pTimer) +{ + /* It's ok to pass NULL pointer. */ + if (pTimer == /*NIL_RTTIMER*/ NULL) + return VINF_SUCCESS; + AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE); + + /* + * We do not support destroying a timer from the callback because it is + * not 101% safe since we cannot flush DPCs. Solaris has the same restriction. + */ + AssertReturn(KeGetCurrentIrql() == PASSIVE_LEVEL, VERR_INVALID_CONTEXT); + + /* + * Invalidate the timer, stop it if it's running and finally free up the memory. + */ + ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC); + rtTimerNtStopWorker(pTimer); + +#ifdef RTR0TIMER_NT_HIGH_RES + /* + * Destroy the high-resolution timer before flushing DPCs. + */ + if (pTimer->pHighResTimer) + { + g_pfnrtExDeleteTimer(pTimer->pHighResTimer, TRUE /*fCancel*/, TRUE /*fWait*/, NULL); + pTimer->pHighResTimer = NULL; + } +#endif + + /* + * Flush DPCs to be on the safe side. + */ + if (g_pfnrtNtKeFlushQueuedDpcs) + g_pfnrtNtKeFlushQueuedDpcs(); + + RTMemFree(pTimer); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser) +{ + *ppTimer = NULL; + + /* + * Validate flags. + */ + if (!RTTIMER_FLAGS_ARE_VALID(fFlags)) + return VERR_INVALID_FLAGS; + if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) + && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL + && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK))) + return VERR_CPU_NOT_FOUND; + + /* + * Allocate the timer handler. + */ + RTCPUID cSubTimers = 1; + if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL) + { + cSubTimers = RTMpGetMaxCpuId() + 1; + Assert(cSubTimers <= RTCPUSET_MAX_CPUS); /* On Windows we have a 1:1 relationship between cpuid and set index. */ + } + + PRTTIMER pTimer = (PRTTIMER)RTMemAllocZ(RT_UOFFSETOF_DYN(RTTIMER, aSubTimers[cSubTimers])); + if (!pTimer) + return VERR_NO_MEMORY; + + /* + * Initialize it. + * + * Note! The difference between a SynchronizationTimer and a NotificationTimer + * (KeInitializeTimer) is, as far as I can gather, only that the former + * will wake up exactly one waiting thread and the latter will wake up + * everyone. Since we don't do any waiting on the NtTimer, that is not + * relevant to us. + */ + pTimer->u32Magic = RTTIMER_MAGIC; + pTimer->cOmniSuspendCountDown = 0; + pTimer->fSuspended = true; + pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL; + pTimer->fOmniTimer = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL; + pTimer->idCpu = pTimer->fSpecificCpu ? RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK) : NIL_RTCPUID; + pTimer->cSubTimers = cSubTimers; + pTimer->pfnTimer = pfnTimer; + pTimer->pvUser = pvUser; + KeInitializeSpinLock(&pTimer->Spinlock); + pTimer->u64NanoInterval = u64NanoInterval; + + int rc = VINF_SUCCESS; +#ifdef RTR0TIMER_NT_HIGH_RES + if ( (fFlags & RTTIMER_FLAGS_HIGH_RES) + && RTTimerCanDoHighResolution()) + { + pTimer->pHighResTimer = g_pfnrtExAllocateTimer(pTimer->fOmniTimer ? rtTimerNtHighResOmniCallback + : rtTimerNtHighResSimpleCallback, pTimer, + EX_TIMER_HIGH_RESOLUTION | EX_TIMER_NOTIFICATION); + if (!pTimer->pHighResTimer) + rc = VERR_OUT_OF_RESOURCES; + } + else +#endif + { + if (g_pfnrtKeInitializeTimerEx) /** @todo just call KeInitializeTimer. */ + g_pfnrtKeInitializeTimerEx(&pTimer->NtTimer, SynchronizationTimer); + else + KeInitializeTimer(&pTimer->NtTimer); + } + if (RT_SUCCESS(rc)) + { + RTCPUSET OnlineSet; + RTMpGetOnlineSet(&OnlineSet); + + if (pTimer->fOmniTimer) + { + /* + * Initialize the per-cpu "sub-timers", select the first online cpu to be + * the master. This ASSUMES that no cpus will ever go offline. + * + * Note! For the high-resolution scenario, all DPC callbacks are slaves as + * we have a dedicated timer callback, set above during allocation, + * and don't control which CPU it (rtTimerNtHighResOmniCallback) is + * called on. + */ + pTimer->iMasterTick = 0; + pTimer->idCpu = NIL_RTCPUID; + for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++) + { + pTimer->aSubTimers[iCpu].iTick = 0; + pTimer->aSubTimers[iCpu].pParent = pTimer; + + if ( pTimer->idCpu == NIL_RTCPUID + && RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)) + { + pTimer->idCpu = RTMpCpuIdFromSetIndex(iCpu); +#ifdef RTR0TIMER_NT_HIGH_RES + if (pTimer->pHighResTimer) + KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniSlaveCallback, &pTimer->aSubTimers[iCpu]); + else +#endif + KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniMasterCallback, &pTimer->aSubTimers[iCpu]); + } + else + KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniSlaveCallback, &pTimer->aSubTimers[iCpu]); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&pTimer->aSubTimers[iCpu].NtDpc, HighImportance); + + /* This does not necessarily work for offline CPUs that could potentially be onlined + at runtime, so postpone it. (See troubles on testboxmem1 after r148799.) */ + int rc2 = rtMpNtSetTargetProcessorDpc(&pTimer->aSubTimers[iCpu].NtDpc, iCpu); + if (RT_SUCCESS(rc2)) + pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = false; + else if (!RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)) + pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = true; + else + { + rc = rc2; + break; + } + } + Assert(pTimer->idCpu != NIL_RTCPUID); + } + else + { + /* + * Initialize the first "sub-timer", target the DPC on a specific processor + * if requested to do so. + */ + pTimer->iMasterTick = 0; + pTimer->aSubTimers[0].iTick = 0; + pTimer->aSubTimers[0].pParent = pTimer; + + KeInitializeDpc(&pTimer->aSubTimers[0].NtDpc, rtTimerNtSimpleCallback, pTimer); + if (g_pfnrtKeSetImportanceDpc) + g_pfnrtKeSetImportanceDpc(&pTimer->aSubTimers[0].NtDpc, HighImportance); + if (pTimer->fSpecificCpu) + { + /* This does not necessarily work for offline CPUs that could potentially be onlined + at runtime, so postpone it. (See troubles on testboxmem1 after r148799.) */ + int rc2 = rtMpNtSetTargetProcessorDpc(&pTimer->aSubTimers[0].NtDpc, pTimer->idCpu); + if (RT_SUCCESS(rc2)) + pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = false; + else if (!RTCpuSetIsMember(&OnlineSet, pTimer->idCpu)) + pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = true; + else + rc = rc2; + } + } + if (RT_SUCCESS(rc)) + { + *ppTimer = pTimer; + return VINF_SUCCESS; + } + +#ifdef RTR0TIMER_NT_HIGH_RES + if (pTimer->pHighResTimer) + { + g_pfnrtExDeleteTimer(pTimer->pHighResTimer, FALSE, FALSE, NULL); + pTimer->pHighResTimer = NULL; + } +#endif + } + + RTMemFree(pTimer); + return rc; +} + + +RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted) +{ + if (!g_pfnrtNtExSetTimerResolution) + return VERR_NOT_SUPPORTED; + + ULONG ulGranted = g_pfnrtNtExSetTimerResolution(u32Request / 100, TRUE); + if (pu32Granted) + *pu32Granted = ulGranted * 100; /* NT -> ns */ + return VINF_SUCCESS; +} + + +RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted) +{ + if (!g_pfnrtNtExSetTimerResolution) + return VERR_NOT_SUPPORTED; + + g_pfnrtNtExSetTimerResolution(0 /* ignored */, FALSE); + NOREF(u32Granted); + return VINF_SUCCESS; +} + + +RTDECL(bool) RTTimerCanDoHighResolution(void) +{ +#ifdef RTR0TIMER_NT_HIGH_RES + return g_pfnrtExAllocateTimer != NULL + && g_pfnrtExDeleteTimer != NULL + && g_pfnrtExSetTimer != NULL + && g_pfnrtExCancelTimer != NULL; +#else + return false; +#endif +} + diff --git a/src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm b/src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm new file mode 100644 index 00000000..eeb0045f --- /dev/null +++ b/src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm @@ -0,0 +1,52 @@ +; $Id: toxic-chkstk-r0drv-nt.asm $ +;; @file +; IPRT - Toxic _chkstk symbol. +; + +; +; 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 "iprt/asmdefs.mac" + +BEGINCODE + +;; +; Bad function to drag into kernel code as you're eating up too much stack. +; +BEGINPROC _chkstk +%define MY_SYM _chkstk_is_considered_toxic_in_kernel_code__you_should_locate_code_using_too_much_stack_and_change_it_to_use_heap + extern MY_SYM + jmp MY_SYM +ENDPROC _chkstk + -- cgit v1.2.3