summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r0drv/nt
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Runtime/r0drv/nt/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp49
-rw-r--r--src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp71
-rw-r--r--src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp145
-rw-r--r--src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm69
-rw-r--r--src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp76
-rw-r--r--src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp918
-rw-r--r--src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp534
-rw-r--r--src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h207
-rw-r--r--src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp1284
-rw-r--r--src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp210
-rw-r--r--src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp1996
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp823
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp52
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm157
-rw-r--r--src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp1232
-rw-r--r--src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp55
-rw-r--r--src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp148
-rw-r--r--src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp246
-rw-r--r--src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp207
-rw-r--r--src/VBox/Runtime/r0drv/nt/symdb.h98
-rw-r--r--src/VBox/Runtime/r0drv/nt/symdbdata.h2998
-rw-r--r--src/VBox/Runtime/r0drv/nt/the-nt-kernel.h99
-rw-r--r--src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp250
-rw-r--r--src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp167
-rw-r--r--src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp159
-rw-r--r--src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp1102
-rw-r--r--src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm52
28 files changed, 13404 insertions, 0 deletions
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
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/Makefile.kup
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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "the-nt-kernel.h"
+#include <iprt/log.h>
+#include <iprt/assert.h>
+
+
+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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/timer.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#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 <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define 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 <iprt/dbg.h>
+
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#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 <iprt/formats/pecoff.h>
+#include <iprt/formats/mz.h>
+
+
+/*********************************************************************************************************************************
+* 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 : "<null>", 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/dbg.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#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 <iprt/cpuset.h>
+#include <iprt/nt/nt.h>
+
+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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/process.h>
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define _IMAGE_NT_HEADERS RT_CONCAT(_IMAGE_NT_HEADERS,ARCH_BITS)
+#include "the-nt-kernel.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/dbg.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/x86.h>
+#include <iprt/formats/mz.h>
+#include <iprt/formats/pecoff.h>
+#include "internal-r0drv-nt.h"
+
+typedef uint32_t DWORD;
+#include <VerRsrc.h>
+
+
+/*********************************************************************************************************************************
+* 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+#include <iprt/errcore.h>
+#include <iprt/dbg.h>
+#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 <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/dbghelp.h>
+
+#include <iprt/alloca.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#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 <https://www.gnu.org/licenses>.\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 <file.h>] <dir1|pdb1> [...]\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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+#include <iprt/process.h>
+
+
+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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+#include <iprt/semaphore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-nt-kernel.h"
+#include <iprt/semaphore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/spinlock.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#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 <iprt/types.h>
+
+
+/**
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#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 <iprt/cdefs.h>
+
+#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 <iprt/asm.h>
+# 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 <iprt/nt/nt.h>
+RT_C_DECLS_END
+# pragma warning(default : 4163)
+# undef _InterlockedExchange
+# undef _InterlockedExchangeAdd
+# undef _InterlockedCompareExchange
+# undef _InterlockedAddLargeStatistic
+#else
+RT_C_DECLS_BEGIN
+# include <iprt/nt/nt.h>
+RT_C_DECLS_END
+#endif
+
+#include <memory.h>
+#if !defined(RT_OS_WINDOWS)
+# error "RT_OS_WINDOWS must be defined!"
+#endif
+
+#include <iprt/param.h>
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/thread.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include "the-nt-kernel.h"
+#include "internal-r0drv-nt.h"
+#include <iprt/time.h>
+
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "the-nt-kernel.h"
+
+#include <iprt/timer.h>
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#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 <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%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
+