VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp@ 107120

Last change on this file since 107120 was 106552, checked in by vboxsync, 4 months ago

Runtime/r0drv/nt/initterm-r0drv-nt.cpp: Make it build on win.arm64, disable QuantumEnd and DpcQueueDepth hack on ARM, they shouldn't be required there, bugref:10732 [scm fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.8 KB
Line 
1/* $Id: initterm-r0drv-nt.cpp 106552 2024-10-21 10:33:37Z vboxsync $ */
2/** @file
3 * IPRT - Initialization & Termination, R0 Driver, NT.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "the-nt-kernel.h"
42#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
43# include <iprt/asm-amd64-x86.h>
44#elif defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
45# include <iprt/asm-arm.h>
46#else
47# error "Port me"
48#endif
49#include <iprt/dbg.h>
50#include <iprt/errcore.h>
51#include <iprt/string.h>
52#include "internal/initterm.h"
53#include "internal-r0drv-nt.h"
54#include "symdb.h"
55#if !defined(RT_ARCH_AMD64) && !defined(RT_ARCH_X86)
56# define RTNTSDB_NO_DATA
57#endif
58#include "symdbdata.h"
59
60
61/*********************************************************************************************************************************
62* Global Variables *
63*********************************************************************************************************************************/
64/** ExAllocatePoolWithTag, introduced in W2K. */
65decltype(ExAllocatePoolWithTag) *g_pfnrtExAllocatePoolWithTag;
66/** ExFreePoolWithTag, introduced in W2K. */
67decltype(ExFreePoolWithTag) *g_pfnrtExFreePoolWithTag;
68/** ExSetTimerResolution, introduced in W2K. */
69PFNMYEXSETTIMERRESOLUTION g_pfnrtNtExSetTimerResolution;
70/** ExAllocateTimer, introduced in Windows 8.1 */
71PFNEXALLOCATETIMER g_pfnrtExAllocateTimer;
72/** ExDeleteTimer, introduced in Windows 8.1 */
73PFNEXDELETETIMER g_pfnrtExDeleteTimer;
74/** ExSetTimer, introduced in Windows 8.1 */
75PFNEXSETTIMER g_pfnrtExSetTimer;
76/** ExCancelTimer, introduced in Windows 8.1 */
77PFNEXCANCELTIMER g_pfnrtExCancelTimer;
78/** KeFlushQueuedDpcs, introduced in XP. */
79PFNMYKEFLUSHQUEUEDDPCS g_pfnrtNtKeFlushQueuedDpcs;
80/** HalRequestIpi, version introduced with windows 7. */
81PFNHALREQUESTIPI_W7PLUS g_pfnrtHalRequestIpiW7Plus;
82/** HalRequestIpi, version valid up to windows vista?? */
83PFNHALREQUESTIPI_PRE_W7 g_pfnrtHalRequestIpiPreW7;
84/** Worker for RTMpPokeCpu. */
85PFNRTSENDIPI g_pfnrtMpPokeCpuWorker;
86/** KeIpiGenericCall - Introduced in Windows Server 2003. */
87PFNRTKEIPIGENERICCALL g_pfnrtKeIpiGenericCall;
88/** KeSetTargetProcessorDpcEx - Introduced in Windows 7. */
89PFNKESETTARGETPROCESSORDPCEX g_pfnrtKeSetTargetProcessorDpcEx;
90/** KeInitializeAffinityEx - Introducted in Windows 7. */
91PFNKEINITIALIZEAFFINITYEX g_pfnrtKeInitializeAffinityEx;
92/** KeAddProcessorAffinityEx - Introducted in Windows 7. */
93PFNKEADDPROCESSORAFFINITYEX g_pfnrtKeAddProcessorAffinityEx;
94/** KeGetProcessorIndexFromNumber - Introducted in Windows 7. */
95PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnrtKeGetProcessorIndexFromNumber;
96/** KeGetProcessorNumberFromIndex - Introducted in Windows 7. */
97PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnrtKeGetProcessorNumberFromIndex;
98/** KeGetCurrentProcessorNumberEx - Introducted in Windows 7. */
99PFNKEGETCURRENTPROCESSORNUMBEREX g_pfnrtKeGetCurrentProcessorNumberEx;
100/** KeQueryActiveProcessors - Introducted in Windows 2000. */
101PFNKEQUERYACTIVEPROCESSORS g_pfnrtKeQueryActiveProcessors;
102/** KeQueryMaximumProcessorCount - Introducted in Vista and obsoleted W7. */
103PFNKEQUERYMAXIMUMPROCESSORCOUNT g_pfnrtKeQueryMaximumProcessorCount;
104/** KeQueryMaximumProcessorCountEx - Introducted in Windows 7. */
105PFNKEQUERYMAXIMUMPROCESSORCOUNTEX g_pfnrtKeQueryMaximumProcessorCountEx;
106/** KeQueryMaximumGroupCount - Introducted in Windows 7. */
107PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnrtKeQueryMaximumGroupCount;
108/** KeQueryActiveProcessorCount - Introducted in Vista and obsoleted W7. */
109PFNKEQUERYACTIVEPROCESSORCOUNT g_pfnrtKeQueryActiveProcessorCount;
110/** KeQueryActiveProcessorCountEx - Introducted in Windows 7. */
111PFNKEQUERYACTIVEPROCESSORCOUNTEX g_pfnrtKeQueryActiveProcessorCountEx;
112/** KeQueryLogicalProcessorRelationship - Introducted in Windows 7. */
113PFNKEQUERYLOGICALPROCESSORRELATIONSHIP g_pfnrtKeQueryLogicalProcessorRelationship;
114/** KeRegisterProcessorChangeCallback - Introducted in Windows 7. */
115PFNKEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeRegisterProcessorChangeCallback;
116/** KeDeregisterProcessorChangeCallback - Introducted in Windows 7. */
117PFNKEDEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeDeregisterProcessorChangeCallback;
118/** KeSetImportanceDpc - Introducted in NT 3.51. */
119decltype(KeSetImportanceDpc) *g_pfnrtKeSetImportanceDpc;
120/** KeSetTargetProcessorDpc - Introducted in NT 3.51. */
121decltype(KeSetTargetProcessorDpc) *g_pfnrtKeSetTargetProcessorDpc;
122/** KeInitializeTimerEx - Introduced in NT 4. */
123decltype(KeInitializeTimerEx) *g_pfnrtKeInitializeTimerEx;
124/** KeShouldYieldProcessor - Introduced in Windows 10. */
125PFNKESHOULDYIELDPROCESSOR g_pfnrtKeShouldYieldProcessor;
126/** Pointer to the MmProtectMdlSystemAddress kernel function if it's available.
127 * This API was introduced in XP. */
128decltype(MmProtectMdlSystemAddress) *g_pfnrtMmProtectMdlSystemAddress;
129/** MmAllocatePagesForMdl - Introduced in Windows 2000. */
130decltype(MmAllocatePagesForMdl) *g_pfnrtMmAllocatePagesForMdl;
131/** MmAllocatePagesForMdlEx - Introduced in Windows Server 2003 SP1. */
132PFNMMALLOCATEPAGESFORMDLEX g_pfnrtMmAllocatePagesForMdlEx;
133/** MmFreePagesFromMdl - Introduced in Windows 2000. */
134decltype(MmFreePagesFromMdl) *g_pfnrtMmFreePagesFromMdl;
135/** MmMapLockedPagesSpecifyCache - Introduced in Windows NT4 SP4. */
136decltype(MmMapLockedPagesSpecifyCache) *g_pfnrtMmMapLockedPagesSpecifyCache;
137/** MmAllocateContiguousMemorySpecifyCache - Introduced in Windows 2000. */
138decltype(MmAllocateContiguousMemorySpecifyCache) *g_pfnrtMmAllocateContiguousMemorySpecifyCache;
139/** MmSecureVirtualMemory - Introduced in NT 3.51. */
140decltype(MmSecureVirtualMemory) *g_pfnrtMmSecureVirtualMemory;
141/** MmUnsecureVirtualMemory - Introduced in NT 3.51. */
142decltype(MmUnsecureVirtualMemory) *g_pfnrtMmUnsecureVirtualMemory;
143/** PsIsThreadTerminating - Introduced in NT 3.50. */
144decltype(PsIsThreadTerminating) *g_pfnrtPsIsThreadTerminating;
145/** RtlGetVersion, introduced in ??. */
146PFNRTRTLGETVERSION g_pfnrtRtlGetVersion;
147#ifdef RT_ARCH_X86
148/** KeQueryInterruptTime - exported/new in Windows 2000. */
149PFNRTKEQUERYINTERRUPTTIME g_pfnrtKeQueryInterruptTime;
150#endif
151/** KeQueryInterruptTimePrecise - new in Windows 8. */
152PFNRTKEQUERYINTERRUPTTIMEPRECISE g_pfnrtKeQueryInterruptTimePrecise;
153/** KeQuerySystemTimePrecise - new in Windows 8. */
154PFNRTKEQUERYSYSTEMTIMEPRECISE g_pfnrtKeQuerySystemTimePrecise;
155
156/** Offset of the _KPRCB::QuantumEnd field. 0 if not found. */
157uint32_t g_offrtNtPbQuantumEnd;
158/** Size of the _KPRCB::QuantumEnd field. 0 if not found. */
159uint32_t g_cbrtNtPbQuantumEnd;
160/** Offset of the _KPRCB::DpcQueueDepth field. 0 if not found. */
161uint32_t g_offrtNtPbDpcQueueDepth;
162
163/** The combined NT version, see RTNT_MAKE_VERSION. */
164uint32_t g_uRtNtVersion = RTNT_MAKE_VERSION(4, 0);
165/** The major version number. */
166uint8_t g_uRtNtMajorVer;
167/** The minor version number. */
168uint8_t g_uRtNtMinorVer;
169/** The build number. */
170uint32_t g_uRtNtBuildNo;
171
172/** Pointer to the MmHighestUserAddress kernel variable - can be NULL. */
173uintptr_t const *g_puRtMmHighestUserAddress;
174/** Pointer to the MmSystemRangeStart kernel variable - can be NULL. */
175uintptr_t const *g_puRtMmSystemRangeStart;
176
177
178/**
179 * Determines the NT kernel verison information.
180 *
181 * @param pOsVerInfo Where to return the version information.
182 *
183 * @remarks pOsVerInfo->fSmp is only definitive if @c true.
184 * @remarks pOsVerInfo->uCsdNo is set to MY_NIL_CSD if it cannot be determined.
185 */
186static void rtR0NtGetOsVersionInfo(PRTNTSDBOSVER pOsVerInfo)
187{
188 ULONG ulMajorVersion = 0;
189 ULONG ulMinorVersion = 0;
190 ULONG ulBuildNumber = 0;
191
192 pOsVerInfo->fChecked = PsGetVersion(&ulMajorVersion, &ulMinorVersion, &ulBuildNumber, NULL) == TRUE;
193 pOsVerInfo->uMajorVer = (uint8_t)ulMajorVersion;
194 pOsVerInfo->uMinorVer = (uint8_t)ulMinorVersion;
195 pOsVerInfo->uBuildNo = ulBuildNumber;
196#define MY_NIL_CSD 0x3f
197 pOsVerInfo->uCsdNo = MY_NIL_CSD;
198
199 if (g_pfnrtRtlGetVersion)
200 {
201 RTL_OSVERSIONINFOEXW VerInfo;
202 RT_ZERO(VerInfo);
203 VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
204
205 NTSTATUS rcNt = g_pfnrtRtlGetVersion(&VerInfo);
206 if (NT_SUCCESS(rcNt))
207 pOsVerInfo->uCsdNo = VerInfo.wServicePackMajor;
208 }
209
210 /* Note! We cannot quite say if something is MP or UNI. So, fSmp is
211 redefined to indicate that it must be MP.
212 Note! RTMpGetCount is not available here. */
213 pOsVerInfo->fSmp = ulMajorVersion >= 6; /* Vista and later has no UNI kernel AFAIK. */
214 if (!pOsVerInfo->fSmp)
215 {
216 if ( g_pfnrtKeQueryMaximumProcessorCountEx
217 && g_pfnrtKeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS) > 1)
218 pOsVerInfo->fSmp = true;
219 else if ( g_pfnrtKeQueryMaximumProcessorCount
220 && g_pfnrtKeQueryMaximumProcessorCount() > 1)
221 pOsVerInfo->fSmp = true;
222 else if ( g_pfnrtKeQueryActiveProcessors
223 && g_pfnrtKeQueryActiveProcessors() > 1)
224 pOsVerInfo->fSmp = true;
225 else if (KeNumberProcessors > 1)
226 pOsVerInfo->fSmp = true;
227 }
228}
229
230
231/**
232 * Tries a set against the current kernel.
233 *
234 * @retval true if it matched up, global variables are updated.
235 * @retval false otherwise (no globals updated).
236 * @param pSet The data set.
237 * @param pbPrcb Pointer to the processor control block.
238 * @param pszVendor Pointer to the processor vendor string.
239 * @param pOsVerInfo The OS version info.
240 */
241static bool rtR0NtTryMatchSymSet(PCRTNTSDBSET pSet, uint8_t *pbPrcb, const char *pszVendor, PCRTNTSDBOSVER pOsVerInfo)
242{
243 /*
244 * Don't bother trying stuff where the NT kernel version number differs, or
245 * if the build type or SMPness doesn't match up.
246 */
247 if ( pSet->OsVerInfo.uMajorVer != pOsVerInfo->uMajorVer
248 || pSet->OsVerInfo.uMinorVer != pOsVerInfo->uMinorVer
249 || pSet->OsVerInfo.fChecked != pOsVerInfo->fChecked
250 || (!pSet->OsVerInfo.fSmp && pOsVerInfo->fSmp /*must-be-smp*/) )
251 {
252 //DbgPrint("IPRT: #%d Version/type mismatch.\n", pSet - &g_artNtSdbSets[0]);
253 return false;
254 }
255
256 /*
257 * Do the CPU vendor test.
258 *
259 * Note! The MmIsAddressValid call is the real #PF security here as the
260 * __try/__except has limited/no ability to catch everything we need.
261 */
262 char *pszPrcbVendorString = (char *)&pbPrcb[pSet->KPRCB.offVendorString];
263 if (!MmIsAddressValid(&pszPrcbVendorString[4 * 3 - 1]))
264 {
265 //DbgPrint("IPRT: #%d invalid vendor string address.\n", pSet - &g_artNtSdbSets[0]);
266 return false;
267 }
268 __try
269 {
270 if (memcmp(pszPrcbVendorString, pszVendor, RT_MIN(4 * 3, pSet->KPRCB.cbVendorString)) != 0)
271 {
272 //DbgPrint("IPRT: #%d Vendor string mismatch.\n", pSet - &g_artNtSdbSets[0]);
273 return false;
274 }
275 }
276 __except(EXCEPTION_EXECUTE_HANDLER)
277 {
278#ifndef RTNTSDB_NO_DATA
279 DbgPrint("IPRT: %#d Exception\n", pSet - &g_artNtSdbSets[0]);
280#endif
281 return false;
282 }
283
284 /*
285 * Got a match, update the global variables and report success.
286 */
287 g_offrtNtPbQuantumEnd = pSet->KPRCB.offQuantumEnd;
288 g_cbrtNtPbQuantumEnd = pSet->KPRCB.cbQuantumEnd;
289 g_offrtNtPbDpcQueueDepth = pSet->KPRCB.offDpcQueueDepth;
290
291#if 0
292 DbgPrint("IPRT: Using data set #%u for %u.%usp%u build %u %s %s.\n",
293 pSet - &g_artNtSdbSets[0],
294 pSet->OsVerInfo.uMajorVer,
295 pSet->OsVerInfo.uMinorVer,
296 pSet->OsVerInfo.uCsdNo,
297 pSet->OsVerInfo.uBuildNo,
298 pSet->OsVerInfo.fSmp ? "smp" : "uni",
299 pSet->OsVerInfo.fChecked ? "checked" : "free");
300#endif
301 return true;
302}
303
304
305DECLHIDDEN(int) rtR0InitNative(void)
306{
307 /*
308 * Preinitialize g_uRtNtVersion so RTMemAlloc uses the right kind of pool
309 * when RTR0DbgKrnlInfoOpen calls it.
310 */
311 RTNTSDBOSVER OsVerInfo;
312 rtR0NtGetOsVersionInfo(&OsVerInfo);
313 g_uRtNtVersion = RTNT_MAKE_VERSION(OsVerInfo.uMajorVer, OsVerInfo.uMinorVer);
314 g_uRtNtMinorVer = OsVerInfo.uMinorVer;
315 g_uRtNtMajorVer = OsVerInfo.uMajorVer;
316 g_uRtNtBuildNo = OsVerInfo.uBuildNo;
317
318 /*
319 * Initialize the function pointers.
320 */
321 RTDBGKRNLINFO hKrnlInfo;
322 int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0/*fFlags*/);
323 AssertRCReturn(rc, rc);
324
325#define GET_SYSTEM_ROUTINE_EX(a_Prf, a_Name, a_pfnType) \
326 do { RT_CONCAT3(g_pfnrt, a_Prf, a_Name) = (a_pfnType)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_Name); } while (0)
327#define GET_SYSTEM_ROUTINE(a_Name) GET_SYSTEM_ROUTINE_EX(RT_NOTHING, a_Name, decltype(a_Name) *)
328#define GET_SYSTEM_ROUTINE_PRF(a_Prf,a_Name) GET_SYSTEM_ROUTINE_EX(a_Prf, a_Name, decltype(a_Name) *)
329#define GET_SYSTEM_ROUTINE_TYPE(a_Name, a_pfnType) GET_SYSTEM_ROUTINE_EX(RT_NOTHING, a_Name, a_pfnType)
330
331 GET_SYSTEM_ROUTINE(ExAllocatePoolWithTag);
332 GET_SYSTEM_ROUTINE(ExFreePoolWithTag);
333 GET_SYSTEM_ROUTINE_PRF(Nt,ExSetTimerResolution);
334 GET_SYSTEM_ROUTINE_TYPE(ExAllocateTimer, PFNEXALLOCATETIMER);
335 GET_SYSTEM_ROUTINE_TYPE(ExDeleteTimer, PFNEXDELETETIMER);
336 GET_SYSTEM_ROUTINE_TYPE(ExSetTimer, PFNEXSETTIMER);
337 GET_SYSTEM_ROUTINE_TYPE(ExCancelTimer, PFNEXCANCELTIMER);
338 GET_SYSTEM_ROUTINE_PRF(Nt,KeFlushQueuedDpcs);
339 GET_SYSTEM_ROUTINE(KeIpiGenericCall);
340 GET_SYSTEM_ROUTINE(KeSetTargetProcessorDpcEx);
341 GET_SYSTEM_ROUTINE(KeInitializeAffinityEx);
342 GET_SYSTEM_ROUTINE(KeAddProcessorAffinityEx);
343 GET_SYSTEM_ROUTINE_TYPE(KeGetProcessorIndexFromNumber, PFNKEGETPROCESSORINDEXFROMNUMBER);
344 GET_SYSTEM_ROUTINE(KeGetProcessorNumberFromIndex);
345 GET_SYSTEM_ROUTINE_TYPE(KeGetCurrentProcessorNumberEx, PFNKEGETCURRENTPROCESSORNUMBEREX);
346 GET_SYSTEM_ROUTINE(KeQueryActiveProcessors);
347 GET_SYSTEM_ROUTINE(KeQueryMaximumProcessorCount);
348 GET_SYSTEM_ROUTINE(KeQueryMaximumProcessorCountEx);
349 GET_SYSTEM_ROUTINE(KeQueryMaximumGroupCount);
350 GET_SYSTEM_ROUTINE(KeQueryActiveProcessorCount);
351 GET_SYSTEM_ROUTINE(KeQueryActiveProcessorCountEx);
352 GET_SYSTEM_ROUTINE(KeQueryLogicalProcessorRelationship);
353 GET_SYSTEM_ROUTINE(KeRegisterProcessorChangeCallback);
354 GET_SYSTEM_ROUTINE(KeDeregisterProcessorChangeCallback);
355 GET_SYSTEM_ROUTINE(KeSetImportanceDpc);
356 GET_SYSTEM_ROUTINE(KeSetTargetProcessorDpc);
357 GET_SYSTEM_ROUTINE(KeInitializeTimerEx);
358 GET_SYSTEM_ROUTINE_TYPE(KeShouldYieldProcessor, PFNKESHOULDYIELDPROCESSOR);
359 GET_SYSTEM_ROUTINE(MmProtectMdlSystemAddress);
360 GET_SYSTEM_ROUTINE(MmAllocatePagesForMdl);
361 GET_SYSTEM_ROUTINE(MmAllocatePagesForMdlEx);
362 GET_SYSTEM_ROUTINE(MmFreePagesFromMdl);
363#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
364 GET_SYSTEM_ROUTINE(MmMapLockedPagesSpecifyCache);
365#else
366 /* MmMapLockedPagesSpecifyCache is a pre processor macro on arm64. */
367 g_pfnrtMmMapLockedPagesSpecifyCache = (decltype(MmMapLockedPagesSpecifyCache) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmMapLockedPagesSpecifyCache");
368#endif
369 GET_SYSTEM_ROUTINE(MmAllocateContiguousMemorySpecifyCache);
370 GET_SYSTEM_ROUTINE(MmSecureVirtualMemory);
371 GET_SYSTEM_ROUTINE(MmUnsecureVirtualMemory);
372
373 GET_SYSTEM_ROUTINE_TYPE(RtlGetVersion, PFNRTRTLGETVERSION);
374#ifdef RT_ARCH_X86
375 GET_SYSTEM_ROUTINE(KeQueryInterruptTime);
376#endif
377 GET_SYSTEM_ROUTINE_TYPE(KeQueryInterruptTimePrecise, PFNRTKEQUERYINTERRUPTTIMEPRECISE);
378 GET_SYSTEM_ROUTINE_TYPE(KeQuerySystemTimePrecise, PFNRTKEQUERYSYSTEMTIMEPRECISE);
379
380 g_pfnrtHalRequestIpiW7Plus = (PFNHALREQUESTIPI_W7PLUS)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalRequestIpi");
381 g_pfnrtHalRequestIpiPreW7 = (PFNHALREQUESTIPI_PRE_W7)g_pfnrtHalRequestIpiW7Plus;
382
383 g_puRtMmHighestUserAddress = (uintptr_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmHighestUserAddress");
384 g_puRtMmSystemRangeStart = (uintptr_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmSystemRangeStart");
385
386#ifdef RT_ARCH_X86
387 rc = rtR0Nt3InitSymbols(hKrnlInfo);
388 RTR0DbgKrnlInfoRelease(hKrnlInfo);
389 if (RT_FAILURE(rc))
390 return rc;
391#else
392 RTR0DbgKrnlInfoRelease(hKrnlInfo);
393#endif
394
395 /*
396 * Get and publish the definitive NT version.
397 */
398 rtR0NtGetOsVersionInfo(&OsVerInfo);
399 g_uRtNtVersion = RTNT_MAKE_VERSION(OsVerInfo.uMajorVer, OsVerInfo.uMinorVer);
400 g_uRtNtMinorVer = OsVerInfo.uMinorVer;
401 g_uRtNtMajorVer = OsVerInfo.uMajorVer;
402 g_uRtNtBuildNo = OsVerInfo.uBuildNo;
403
404
405#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
406 /*
407 * HACK ALERT! (and déjà vu warning - remember win32k.sys on OS/2?)
408 *
409 * Try find _KPRCB::QuantumEnd and _KPRCB::[DpcData.]DpcQueueDepth.
410 * For purpose of verification we use the VendorString member (12+1 chars).
411 *
412 * The offsets was initially derived by poking around with windbg
413 * (dt _KPRCB, !prcb ++, and such like). Systematic harvesting was then
414 * planned using dia2dump, grep and the symbol pack in a manner like this:
415 * dia2dump -type _KDPC_DATA -type _KPRCB EXE\ntkrnlmp.pdb | grep -wE "QuantumEnd|DpcData|DpcQueueDepth|VendorString"
416 *
417 * The final solution ended up using a custom harvester program called
418 * ntBldSymDb that recursively searches thru unpacked symbol packages for
419 * the desired structure offsets. The program assumes that the packages
420 * are unpacked into directories with the same name as the package, with
421 * exception of some of the w2k packages which requires a 'w2k' prefix to
422 * be distinguishable from another.
423 */
424
425 /*
426 * Gather consistent CPU vendor string and PRCB pointers.
427 */
428 KIRQL OldIrql;
429 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); /* make sure we stay on the same cpu */
430
431 union
432 {
433 uint32_t auRegs[4];
434 char szVendor[4*3+1];
435 } u;
436 ASMCpuId(0, &u.auRegs[3], &u.auRegs[0], &u.auRegs[2], &u.auRegs[1]);
437 u.szVendor[4*3] = '\0';
438
439 uint8_t *pbPrcb;
440 __try /* Warning. This try/except statement may provide some false safety. */
441 {
442# if defined(RT_ARCH_X86)
443 PKPCR pPcr = (PKPCR)__readfsdword(RT_UOFFSETOF(KPCR,SelfPcr));
444 pbPrcb = (uint8_t *)pPcr->Prcb;
445# elif defined(RT_ARCH_AMD64)
446 PKPCR pPcr = (PKPCR)__readgsqword(RT_UOFFSETOF(KPCR,Self));
447 pbPrcb = (uint8_t *)pPcr->CurrentPrcb;
448# else
449# error "port me"
450 pbPrcb = NULL;
451# endif
452 }
453 __except(EXCEPTION_EXECUTE_HANDLER)
454 {
455 pbPrcb = NULL;
456 }
457
458 /*
459 * Search the database
460 */
461 if (pbPrcb)
462 {
463 /* Find the best matching kernel version based on build number. */
464 uint32_t iBest = UINT32_MAX;
465 int32_t iBestDelta = INT32_MAX;
466 for (uint32_t i = 0; i < RT_ELEMENTS(g_artNtSdbSets); i++)
467 {
468 if (g_artNtSdbSets[i].OsVerInfo.fChecked != OsVerInfo.fChecked)
469 continue;
470 if (OsVerInfo.fSmp /*must-be-smp*/ && !g_artNtSdbSets[i].OsVerInfo.fSmp)
471 continue;
472
473 int32_t iDelta = RT_ABS((int32_t)OsVerInfo.uBuildNo - (int32_t)g_artNtSdbSets[i].OsVerInfo.uBuildNo);
474 if ( iDelta == 0
475 && (g_artNtSdbSets[i].OsVerInfo.uCsdNo == OsVerInfo.uCsdNo || OsVerInfo.uCsdNo == MY_NIL_CSD))
476 {
477 /* prefect */
478 iBestDelta = iDelta;
479 iBest = i;
480 break;
481 }
482 if ( iDelta < iBestDelta
483 || iBest == UINT32_MAX
484 || ( iDelta == iBestDelta
485 && OsVerInfo.uCsdNo != MY_NIL_CSD
486 && RT_ABS(g_artNtSdbSets[i ].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo)
487 < RT_ABS(g_artNtSdbSets[iBest].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo)
488 )
489 )
490 {
491 iBestDelta = iDelta;
492 iBest = i;
493 }
494 }
495 if (iBest < RT_ELEMENTS(g_artNtSdbSets))
496 {
497 /* Try all sets: iBest -> End; iBest -> Start. */
498 bool fDone = false;
499 int32_t i = iBest;
500 while ( i < RT_ELEMENTS(g_artNtSdbSets)
501 && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo)))
502 i++;
503 if (!fDone)
504 {
505 i = (int32_t)iBest - 1;
506 while ( i >= 0
507 && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo)))
508 i--;
509 }
510 }
511 else
512 DbgPrint("IPRT: Failed to locate data set.\n");
513 }
514 else
515 DbgPrint("IPRT: Failed to get PCBR pointer.\n");
516
517 KeLowerIrql(OldIrql); /* Lowering the IRQL early in the hope that we may catch exceptions below. */
518
519# ifndef IN_GUEST
520 if (!g_offrtNtPbQuantumEnd && !g_offrtNtPbDpcQueueDepth)
521 DbgPrint("IPRT: Neither _KPRCB::QuantumEnd nor _KPRCB::DpcQueueDepth was not found! Kernel %u.%u %u %s\n",
522 OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free");
523# ifdef DEBUG
524 else
525 DbgPrint("IPRT: _KPRCB:{.QuantumEnd=%x/%d, .DpcQueueDepth=%x/%d} Kernel %u.%u %u %s\n",
526 g_offrtNtPbQuantumEnd, g_cbrtNtPbQuantumEnd, g_offrtNtPbDpcQueueDepth, g_offrtNtPbDpcQueueDepth,
527 OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free");
528# endif
529# endif
530#endif /* RT_ARCH_AMD64 || RT_ARCH_X86 */
531
532 /*
533 * Initialize multi processor stuff. This registers a callback, so
534 * we call rtR0TermNative to do the deregistration on failure.
535 */
536 rc = rtR0MpNtInit(&OsVerInfo);
537 if (RT_FAILURE(rc))
538 {
539 rtR0TermNative();
540 DbgPrint("IPRT: Fatal: rtR0MpNtInit failed: %d\n", rc);
541 return rc;
542 }
543
544 return VINF_SUCCESS;
545}
546
547
548DECLHIDDEN(void) rtR0TermNative(void)
549{
550 rtR0MpNtTerm();
551}
552
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette