VirtualBox

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

Last change on this file since 48935 was 48935, checked in by vboxsync, 11 years ago

Runtime: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.4 KB
Line 
1/* $Id: initterm-r0drv-nt.cpp 48935 2013-10-07 21:19:37Z vboxsync $ */
2/** @file
3 * IPRT - Initialization & Termination, R0 Driver, NT.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include "the-nt-kernel.h"
31#include <iprt/asm-amd64-x86.h>
32#include <iprt/assert.h>
33#include <iprt/err.h>
34#include <iprt/mp.h>
35#include <iprt/string.h>
36#include "internal/initterm.h"
37#include "internal-r0drv-nt.h"
38#include "symdb.h"
39#include "symdbdata.h"
40
41
42/*******************************************************************************
43* Global Variables *
44*******************************************************************************/
45/** The NT CPU set.
46 * KeQueryActiveProcssors() cannot be called at all IRQLs and therefore we'll
47 * have to cache it. Fortunately, Nt doesn't really support taking CPUs offline
48 * or online. It's first with W2K8 that support for CPU hotplugging was added.
49 * Once we start caring about this, we'll simply let the native MP event callback
50 * and update this variable as CPUs comes online. (The code is done already.)
51 */
52RTCPUSET g_rtMpNtCpuSet;
53
54/** ExSetTimerResolution, introduced in W2K. */
55PFNMYEXSETTIMERRESOLUTION g_pfnrtNtExSetTimerResolution;
56/** KeFlushQueuedDpcs, introduced in XP. */
57PFNMYKEFLUSHQUEUEDDPCS g_pfnrtNtKeFlushQueuedDpcs;
58/** HalRequestIpi, introduced in ??. */
59PFNHALREQUESTIPI g_pfnrtNtHalRequestIpi;
60/** HalSendSoftwareInterrupt */
61PFNHALSENDSOFTWAREINTERRUPT g_pfnrtNtHalSendSoftwareInterrupt;
62/** SendIpi handler based on Windows version */
63PFNRTSENDIPI g_pfnrtSendIpi;
64/** KeIpiGenericCall - Windows Server 2003+ only */
65PFNRTKEIPIGENERICCALL g_pfnrtKeIpiGenericCall;
66/** RtlGetVersion, introduced in ??. */
67PFNRTRTLGETVERSION g_pfnrtRtlGetVersion;
68
69/** Offset of the _KPRCB::QuantumEnd field. 0 if not found. */
70uint32_t g_offrtNtPbQuantumEnd;
71/** Size of the _KPRCB::QuantumEnd field. 0 if not found. */
72uint32_t g_cbrtNtPbQuantumEnd;
73/** Offset of the _KPRCB::DpcQueueDepth field. 0 if not found. */
74uint32_t g_offrtNtPbDpcQueueDepth;
75
76
77/**
78 * Determines the NT kernel verison information.
79 *
80 * @param pOsVerInfo Where to return the version information.
81 *
82 * @remarks pOsVerInfo->fSmp is only definitive if @c true.
83 * @remarks pOsVerInfo->uCsdNo is set to MY_NIL_CSD if it cannot be determined.
84 */
85static void rtR0NtGetOsVersionInfo(PRTNTSDBOSVER pOsVerInfo)
86{
87 ULONG ulMajorVersion = 0;
88 ULONG ulMinorVersion = 0;
89 ULONG ulBuildNumber = 0;
90
91 pOsVerInfo->fChecked = PsGetVersion(&ulMajorVersion, &ulMinorVersion, &ulBuildNumber, NULL) == TRUE;
92 pOsVerInfo->uMajorVer = (uint8_t)ulMajorVersion;
93 pOsVerInfo->uMinorVer = (uint8_t)ulMinorVersion;
94 pOsVerInfo->uBuildNo = ulBuildNumber;
95#define MY_NIL_CSD 0x3f
96 pOsVerInfo->uCsdNo = MY_NIL_CSD;
97
98 if (g_pfnrtRtlGetVersion)
99 {
100 RTL_OSVERSIONINFOEXW VerInfo;
101 RT_ZERO(VerInfo);
102 VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
103
104 NTSTATUS rcNt = g_pfnrtRtlGetVersion(&VerInfo);
105 if (NT_SUCCESS(rcNt))
106 pOsVerInfo->uCsdNo = VerInfo.wServicePackMajor;
107 }
108
109 /* Note! We cannot quite say if something is MP or UNI. So, fSmp is
110 redefined to indicate that it must be MP. */
111 pOsVerInfo->fSmp = RTMpGetCount() > 1
112 || ulMajorVersion >= 6; /* Vista and later has no UNI kernel AFAIK. */
113}
114
115
116/**
117 * Tries a set against the current kernel.
118 *
119 * @retval @c true if it matched up, global variables are updated.
120 * @retval @c false otherwise (no globals updated).
121 * @param pSet The data set.
122 * @param pbPrcb Pointer to the processor control block.
123 * @param pszVendor Pointer to the processor vendor string.
124 * @param pOsVerInfo The OS version info.
125 */
126static bool rtR0NtTryMatchSymSet(PCRTNTSDBSET pSet, uint8_t *pbPrcb, const char *pszVendor, PCRTNTSDBOSVER pOsVerInfo)
127{
128 /*
129 * Don't bother trying stuff where the NT kernel version number differs, or
130 * if the build type or SMPness doesn't match up.
131 */
132 if ( pSet->OsVerInfo.uMajorVer != pOsVerInfo->uMajorVer
133 || pSet->OsVerInfo.uMinorVer != pOsVerInfo->uMinorVer
134 || pSet->OsVerInfo.fChecked != pOsVerInfo->fChecked
135 || (!pSet->OsVerInfo.fSmp && pOsVerInfo->fSmp /*must-be-smp*/) )
136 {
137 //DbgPrint("IPRT: #%d Version/type mismatch.\n", pSet - &g_artNtSdbSets[0]);
138 return false;
139 }
140
141 /*
142 * Do the CPU vendor test.
143 *
144 * Note! The MmIsAddressValid call is the real #PF security here as the
145 * __try/__except has limited/no ability to catch everything we need.
146 */
147 char *pszPrcbVendorString = (char *)&pbPrcb[pSet->KPRCB.offVendorString];
148 if (!MmIsAddressValid(&pszPrcbVendorString[4 * 3 - 1]))
149 {
150 //DbgPrint("IPRT: #%d invalid vendor string address.\n", pSet - &g_artNtSdbSets[0]);
151 return false;
152 }
153 __try
154 {
155 if (memcmp(pszPrcbVendorString, pszVendor, RT_MIN(4 * 3, pSet->KPRCB.cbVendorString)) != 0)
156 {
157 //DbgPrint("IPRT: #%d Vendor string mismatch.\n", pSet - &g_artNtSdbSets[0]);
158 return false;
159 }
160 }
161 __except(EXCEPTION_EXECUTE_HANDLER)
162 {
163 DbgPrint("IPRT: %#d Exception\n", pSet - &g_artNtSdbSets[0]);
164 return false;
165 }
166
167 /*
168 * Got a match, update the global variables and report succcess.
169 */
170 g_offrtNtPbQuantumEnd = pSet->KPRCB.offQuantumEnd;
171 g_cbrtNtPbQuantumEnd = pSet->KPRCB.cbQuantumEnd;
172 g_offrtNtPbDpcQueueDepth = pSet->KPRCB.offDpcQueueDepth;
173
174#if 0
175 DbgPrint("IPRT: Using data set #%u for %u.%usp%u build %u %s %s.\n",
176 pSet - &g_artNtSdbSets[0],
177 pSet->OsVerInfo.uMajorVer,
178 pSet->OsVerInfo.uMinorVer,
179 pSet->OsVerInfo.uCsdNo,
180 pSet->OsVerInfo.uBuildNo,
181 pSet->OsVerInfo.fSmp ? "smp" : "uni",
182 pSet->OsVerInfo.fChecked ? "checked" : "free");
183#endif
184 return true;
185}
186
187
188DECLHIDDEN(int) rtR0InitNative(void)
189{
190 /*
191 * Init the Nt cpu set.
192 */
193#ifdef IPRT_TARGET_NT4
194 KAFFINITY ActiveProcessors = (UINT64_C(1) << KeNumberProcessors) - UINT64_C(1);
195#else
196 KAFFINITY ActiveProcessors = KeQueryActiveProcessors();
197#endif
198 RTCpuSetEmpty(&g_rtMpNtCpuSet);
199 RTCpuSetFromU64(&g_rtMpNtCpuSet, ActiveProcessors);
200/** @todo Port to W2K8 with > 64 cpus/threads. */
201
202#ifdef IPRT_TARGET_NT4
203 g_pfnrtNtExSetTimerResolution = NULL;
204 g_pfnrtNtKeFlushQueuedDpcs = NULL;
205 g_pfnrtNtHalRequestIpi = NULL;
206 g_pfnrtNtHalSendSoftwareInterrupt = NULL;
207 g_pfnrtKeIpiGenericCall = NULL;
208 g_pfnrtRtlGetVersion = NULL;
209#else
210 /*
211 * Initialize the function pointers.
212 */
213 UNICODE_STRING RoutineName;
214 RtlInitUnicodeString(&RoutineName, L"ExSetTimerResolution");
215 g_pfnrtNtExSetTimerResolution = (PFNMYEXSETTIMERRESOLUTION)MmGetSystemRoutineAddress(&RoutineName);
216
217 RtlInitUnicodeString(&RoutineName, L"KeFlushQueuedDpcs");
218 g_pfnrtNtKeFlushQueuedDpcs = (PFNMYKEFLUSHQUEUEDDPCS)MmGetSystemRoutineAddress(&RoutineName);
219
220 RtlInitUnicodeString(&RoutineName, L"HalRequestIpi");
221 g_pfnrtNtHalRequestIpi = (PFNHALREQUESTIPI)MmGetSystemRoutineAddress(&RoutineName);
222
223 RtlInitUnicodeString(&RoutineName, L"HalSendSoftwareInterrupt");
224 g_pfnrtNtHalSendSoftwareInterrupt = (PFNHALSENDSOFTWAREINTERRUPT)MmGetSystemRoutineAddress(&RoutineName);
225
226 RtlInitUnicodeString(&RoutineName, L"KeIpiGenericCall");
227 g_pfnrtKeIpiGenericCall = (PFNRTKEIPIGENERICCALL)MmGetSystemRoutineAddress(&RoutineName);
228
229 RtlInitUnicodeString(&RoutineName, L"RtlGetVersion");
230 g_pfnrtRtlGetVersion = (PFNRTRTLGETVERSION)MmGetSystemRoutineAddress(&RoutineName);
231#endif
232
233 /*
234 * HACK ALERT! (and déjà vu warning - remember win32k.sys?)
235 *
236 * Try find _KPRCB::QuantumEnd and _KPRCB::[DpcData.]DpcQueueDepth.
237 * For purpose of verification we use the VendorString member (12+1 chars).
238 *
239 * The offsets was initially derived by poking around with windbg
240 * (dt _KPRCB, !prcb ++, and such like). Systematic harvesting was then
241 * planned using dia2dump, grep and the symbol pack in a manner like this:
242 * dia2dump -type _KDPC_DATA -type _KPRCB EXE\ntkrnlmp.pdb | grep -wE "QuantumEnd|DpcData|DpcQueueDepth|VendorString"
243 *
244 * The final solution ended up using a custom harvester program called
245 * ntBldSymDb that recursively searches thru unpacked symbol packages for
246 * the desired structure offsets. The program assumes that the packages
247 * are unpacked into directories with the same name as the package, with
248 * exception of some of the w2k packages which requires a 'w2k' prefix to
249 * be distinguishable from another.
250 */
251
252 RTNTSDBOSVER OsVerInfo;
253 rtR0NtGetOsVersionInfo(&OsVerInfo);
254
255 /*
256 * Gather consistent CPU vendor string and PRCB pointers.
257 */
258 KIRQL OldIrql;
259 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); /* make sure we stay on the same cpu */
260
261 union
262 {
263 uint32_t auRegs[4];
264 char szVendor[4*3+1];
265 } u;
266 ASMCpuId(0, &u.auRegs[3], &u.auRegs[0], &u.auRegs[2], &u.auRegs[1]);
267 u.szVendor[4*3] = '\0';
268
269 uint8_t *pbPrcb;
270 __try /* Warning. This try/except statement may provide some false safety. */
271 {
272#if defined(RT_ARCH_X86)
273 PKPCR pPcr = (PKPCR)__readfsdword(RT_OFFSETOF(KPCR,SelfPcr));
274 pbPrcb = (uint8_t *)pPcr->Prcb;
275#elif defined(RT_ARCH_AMD64)
276 PKPCR pPcr = (PKPCR)__readgsqword(RT_OFFSETOF(KPCR,Self));
277 pbPrcb = (uint8_t *)pPcr->CurrentPrcb;
278#else
279# error "port me"
280 pbPrcb = NULL;
281#endif
282 }
283 __except(EXCEPTION_EXECUTE_HANDLER)
284 {
285 pbPrcb = NULL;
286 }
287
288 /*
289 * Search the database
290 */
291 if (pbPrcb)
292 {
293 /* Find the best matching kernel version based on build number. */
294 uint32_t iBest = UINT32_MAX;
295 int32_t iBestDelta = INT32_MAX;
296 for (uint32_t i = 0; i < RT_ELEMENTS(g_artNtSdbSets); i++)
297 {
298 if (g_artNtSdbSets[i].OsVerInfo.fChecked != OsVerInfo.fChecked)
299 continue;
300 if (OsVerInfo.fSmp /*must-be-smp*/ && !g_artNtSdbSets[i].OsVerInfo.fSmp)
301 continue;
302
303 int32_t iDelta = RT_ABS((int32_t)OsVerInfo.uBuildNo - (int32_t)g_artNtSdbSets[i].OsVerInfo.uBuildNo);
304 if ( iDelta == 0
305 && (g_artNtSdbSets[i].OsVerInfo.uCsdNo == OsVerInfo.uCsdNo || OsVerInfo.uCsdNo == MY_NIL_CSD))
306 {
307 /* prefect */
308 iBestDelta = iDelta;
309 iBest = i;
310 break;
311 }
312 if ( iDelta < iBestDelta
313 || iBest == UINT32_MAX
314 || ( iDelta == iBestDelta
315 && OsVerInfo.uCsdNo != MY_NIL_CSD
316 && RT_ABS(g_artNtSdbSets[i ].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo)
317 < RT_ABS(g_artNtSdbSets[iBest].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo)
318 )
319 )
320 {
321 iBestDelta = iDelta;
322 iBest = i;
323 }
324 }
325 if (iBest < RT_ELEMENTS(g_artNtSdbSets))
326 {
327 /* Try all sets: iBest -> End; iBest -> Start. */
328 bool fDone = false;
329 int32_t i = iBest;
330 while ( i < RT_ELEMENTS(g_artNtSdbSets)
331 && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo)))
332 i++;
333 if (!fDone)
334 {
335 i = (int32_t)iBest - 1;
336 while ( i >= 0
337 && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo)))
338 i--;
339 }
340 }
341 else
342 DbgPrint("IPRT: Failed to locate data set.\n");
343 }
344 else
345 DbgPrint("IPRT: Failed to get PCBR pointer.\n");
346
347 KeLowerIrql(OldIrql); /* Lowering the IRQL early in the hope that we may catch exceptions below. */
348
349#ifndef IN_GUEST
350 if (!g_offrtNtPbQuantumEnd && !g_offrtNtPbDpcQueueDepth)
351 DbgPrint("IPRT: Neither _KPRCB::QuantumEnd nor _KPRCB::DpcQueueDepth was not found! Kernel %u.%u %u %s\n",
352 OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free");
353# ifdef DEBUG
354 else
355 DbgPrint("IPRT: _KPRCB:{.QuantumEnd=%x/%d, .DpcQueueDepth=%x/%d} Kernel %u.%u %u %s\n",
356 g_offrtNtPbQuantumEnd, g_cbrtNtPbQuantumEnd, g_offrtNtPbDpcQueueDepth,
357 OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free");
358# endif
359#endif
360
361 /*
362 * Special IPI fun.
363 */
364 g_pfnrtSendIpi = rtMpSendIpiDummy;
365#ifndef IPRT_TARGET_NT4
366 if ( g_pfnrtNtHalRequestIpi
367 && OsVerInfo.uMajorVer == 6
368 && OsVerInfo.uMinorVer == 0)
369 {
370 /* Vista or Windows Server 2008 */
371 g_pfnrtSendIpi = rtMpSendIpiVista;
372 }
373 else if ( g_pfnrtNtHalSendSoftwareInterrupt
374 && OsVerInfo.uMajorVer == 6
375 && OsVerInfo.uMinorVer == 1)
376 {
377 /* Windows 7 or Windows Server 2008 R2 */
378 g_pfnrtSendIpi = rtMpSendIpiWin7;
379 }
380 /* Windows XP should send always send an IPI -> VERIFY */
381#endif
382
383 return VINF_SUCCESS;
384}
385
386
387DECLHIDDEN(void) rtR0TermNative(void)
388{
389}
390
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