Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1558 lines
42 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. dmpaddr.c
  5. Abstract:
  6. Routines to examine pages and addresses.
  7. Author:
  8. Lou Perazzoli (loup) 20-Mar-1989
  9. Landy Wang (landyw) 02-Jun-1997
  10. Environment:
  11. Kernel Mode.
  12. Revision History:
  13. --*/
  14. #include "mi.h"
  15. #if DBG
  16. LOGICAL
  17. MiFlushUnusedSectionInternal (
  18. IN PCONTROL_AREA ControlArea
  19. );
  20. #endif
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE,MmPerfSnapShotValidPhysicalMemory)
  23. #endif
  24. extern PFN_NUMBER MiStartOfInitialPoolFrame;
  25. extern PFN_NUMBER MiEndOfInitialPoolFrame;
  26. extern PMMPTE MmSystemPtesEnd[MaximumPtePoolTypes];
  27. #if DBG
  28. PFN_NUMBER MiIdentifyFrame = (PFN_NUMBER)-1;
  29. ULONG MiIdentifyCounters[64];
  30. #define MI_INCREMENT_IDENTIFY_COUNTER(x) { \
  31. ASSERT (x < 64); \
  32. MiIdentifyCounters[x] += 1; \
  33. }
  34. #else
  35. #define MI_INCREMENT_IDENTIFY_COUNTER(x)
  36. #endif
  37. #if DBG
  38. VOID
  39. MiDumpValidAddresses (
  40. )
  41. {
  42. ULONG_PTR va;
  43. ULONG i;
  44. ULONG j;
  45. PMMPTE PointerPde;
  46. PMMPTE PointerPte;
  47. va = 0;
  48. PointerPde = MiGetPdeAddress ((PVOID)va);
  49. for (i = 0; i < PDE_PER_PAGE; i += 1) {
  50. if (PointerPde->u.Hard.Valid) {
  51. DbgPrint(" **valid PDE, element %ld %lx %lx\n",i,i,
  52. PointerPde->u.Long);
  53. PointerPte = MiGetPteAddress ((PVOID)va);
  54. for (j = 0 ; j < PTE_PER_PAGE; j += 1) {
  55. if (PointerPte->u.Hard.Valid) {
  56. DbgPrint("Valid address at %p PTE %p\n", (ULONG)va,
  57. PointerPte->u.Long);
  58. }
  59. va += PAGE_SIZE;
  60. PointerPte += 1;
  61. }
  62. }
  63. else {
  64. va += (ULONG_PTR)PDE_PER_PAGE * (ULONG_PTR)PAGE_SIZE;
  65. }
  66. PointerPde += 1;
  67. }
  68. return;
  69. }
  70. VOID
  71. MiFormatPte (
  72. IN PMMPTE PointerPte
  73. )
  74. {
  75. PMMPTE proto_pte;
  76. PSUBSECTION subsect;
  77. if (MmIsAddressValid (PointerPte) == FALSE) {
  78. DbgPrint(" cannot dump PTE %p - it's not valid\n\n",
  79. PointerPte);
  80. return;
  81. }
  82. DbgPrint("***DumpPTE at %p contains %p\n",
  83. PointerPte,
  84. PointerPte->u.Long);
  85. proto_pte = MiPteToProto(PointerPte);
  86. subsect = MiGetSubsectionAddress(PointerPte);
  87. DbgPrint(" protoaddr %p subsectaddr %p\n\n",
  88. proto_pte,
  89. (ULONG_PTR)subsect);
  90. return;
  91. }
  92. VOID
  93. MiDumpWsl (
  94. VOID
  95. )
  96. {
  97. ULONG i;
  98. PMMWSLE wsle;
  99. PEPROCESS CurrentProcess;
  100. CurrentProcess = PsGetCurrentProcess();
  101. DbgPrint("***WSLE cursize %lx frstfree %lx Min %lx Max %lx\n",
  102. CurrentProcess->Vm.WorkingSetSize,
  103. MmWorkingSetList->FirstFree,
  104. CurrentProcess->Vm.MinimumWorkingSetSize,
  105. CurrentProcess->Vm.MaximumWorkingSetSize);
  106. DbgPrint(" quota %lx firstdyn %lx last ent %lx next slot %lx\n",
  107. MmWorkingSetList->Quota,
  108. MmWorkingSetList->FirstDynamic,
  109. MmWorkingSetList->LastEntry,
  110. MmWorkingSetList->NextSlot);
  111. wsle = MmWsle;
  112. for (i = 0; i < MmWorkingSetList->LastEntry; i += 1) {
  113. DbgPrint(" index %lx %p\n",i,wsle->u1.Long);
  114. wsle += 1;
  115. }
  116. return;
  117. }
  118. #define ALLOC_SIZE ((ULONG)8*1024)
  119. #define MM_SAVED_CONTROL 64
  120. //
  121. // Note these are deliberately sign-extended so they will always be greater
  122. // than the highest user address.
  123. //
  124. #define MM_NONPAGED_POOL_MARK ((PUCHAR)(LONG_PTR)0xfffff123)
  125. #define MM_PAGED_POOL_MARK ((PUCHAR)(LONG_PTR)0xfffff124)
  126. #define MM_KERNEL_STACK_MARK ((PUCHAR)(LONG_PTR)0xfffff125)
  127. #define MM_PAGEFILE_BACKED_SHMEM_MARK ((PUCHAR)(LONG_PTR)0xfffff126)
  128. #define MM_DUMP_ONLY_VALID_PAGES 1
  129. typedef struct _KERN_MAP {
  130. PVOID StartVa;
  131. PVOID EndVa;
  132. PKLDR_DATA_TABLE_ENTRY Entry;
  133. } KERN_MAP, *PKERN_MAP;
  134. ULONG
  135. MiBuildKernelMap (
  136. OUT PKERN_MAP *KernelMapOut
  137. );
  138. LOGICAL
  139. MiIsAddressRangeValid (
  140. IN PVOID VirtualAddress,
  141. IN SIZE_T Length
  142. );
  143. NTSTATUS
  144. MmMemoryUsage (
  145. IN PVOID Buffer,
  146. IN ULONG Size,
  147. IN ULONG Type,
  148. OUT PULONG OutLength
  149. )
  150. /*++
  151. Routine Description:
  152. This routine (debugging only) dumps the current memory usage by
  153. walking the PFN database.
  154. Arguments:
  155. Buffer - Supplies a *USER SPACE* buffer in which to copy the data.
  156. Size - Supplies the size of the buffer.
  157. Type - Supplies a value of 0 to dump everything,
  158. a value of 1 to dump only valid pages.
  159. OutLength - Returns how much data was written into the buffer.
  160. Return Value:
  161. NTSTATUS.
  162. --*/
  163. {
  164. ULONG i;
  165. MMPFN_IDENTITY PfnId;
  166. PMMPFN LastPfn;
  167. PMMPFN Pfn1;
  168. KIRQL OldIrql;
  169. PSYSTEM_MEMORY_INFORMATION MemInfo;
  170. PSYSTEM_MEMORY_INFO Info;
  171. PSYSTEM_MEMORY_INFO InfoStart;
  172. PSYSTEM_MEMORY_INFO InfoEnd;
  173. PUCHAR String;
  174. PUCHAR Master;
  175. PCONTROL_AREA ControlArea;
  176. NTSTATUS status;
  177. ULONG Length;
  178. PEPROCESS Process;
  179. PUCHAR End;
  180. PCONTROL_AREA SavedControl[MM_SAVED_CONTROL];
  181. PSYSTEM_MEMORY_INFO SavedInfo[MM_SAVED_CONTROL];
  182. ULONG j;
  183. ULONG ControlCount;
  184. UCHAR PageFileMappedString[] = "PageFile Mapped";
  185. UCHAR MetaFileString[] = "Fs Meta File";
  186. UCHAR NoNameString[] = "No File Name";
  187. UCHAR NonPagedPoolString[] = "NonPagedPool";
  188. UCHAR PagedPoolString[] = "PagedPool";
  189. UCHAR KernelStackString[] = "Kernel Stack";
  190. PUCHAR NameString;
  191. PKERN_MAP KernMap;
  192. ULONG KernSize;
  193. PVOID VirtualAddress;
  194. PSUBSECTION Subsection;
  195. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  196. String = NULL;
  197. ControlCount = 0;
  198. Master = NULL;
  199. status = STATUS_SUCCESS;
  200. KernSize = MiBuildKernelMap (&KernMap);
  201. if (KernSize == 0) {
  202. return STATUS_INSUFFICIENT_RESOURCES;
  203. }
  204. MemInfo = ExAllocatePoolWithTag (NonPagedPool, (SIZE_T) Size, 'lMmM');
  205. if (MemInfo == NULL) {
  206. ExFreePool (KernMap);
  207. return STATUS_INSUFFICIENT_RESOURCES;
  208. }
  209. InfoStart = &MemInfo->Memory[0];
  210. InfoEnd = InfoStart;
  211. End = (PUCHAR)MemInfo + Size;
  212. //
  213. // Walk through the ranges identifying pages.
  214. //
  215. LOCK_PFN (OldIrql);
  216. for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
  217. Pfn1 = MI_PFN_ELEMENT (MmPhysicalMemoryBlock->Run[i].BasePage);
  218. LastPfn = Pfn1 + MmPhysicalMemoryBlock->Run[i].PageCount;
  219. for ( ; Pfn1 < LastPfn; Pfn1 += 1) {
  220. RtlZeroMemory (&PfnId, sizeof(PfnId));
  221. MiIdentifyPfn (Pfn1, &PfnId);
  222. if ((PfnId.u1.e1.ListDescription == FreePageList) ||
  223. (PfnId.u1.e1.ListDescription == ZeroedPageList) ||
  224. (PfnId.u1.e1.ListDescription == BadPageList) ||
  225. (PfnId.u1.e1.ListDescription == TransitionPage)) {
  226. continue;
  227. }
  228. if (PfnId.u1.e1.ListDescription != ActiveAndValid) {
  229. if (Type == MM_DUMP_ONLY_VALID_PAGES) {
  230. continue;
  231. }
  232. }
  233. if (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGEFILEMAPPED) {
  234. //
  235. // This page belongs to a pagefile-backed shared memory section.
  236. //
  237. Master = MM_PAGEFILE_BACKED_SHMEM_MARK;
  238. }
  239. else if ((PfnId.u1.e1.UseDescription == MMPFNUSE_FILE) ||
  240. (PfnId.u1.e1.UseDescription == MMPFNUSE_METAFILE)) {
  241. //
  242. // This shared page maps a file or file metadata.
  243. //
  244. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  245. ControlArea = Subsection->ControlArea;
  246. Master = (PUCHAR) ControlArea;
  247. }
  248. else if (PfnId.u1.e1.UseDescription == MMPFNUSE_NONPAGEDPOOL) {
  249. //
  250. // This is nonpaged pool, put it in the nonpaged pool cell.
  251. //
  252. Master = MM_NONPAGED_POOL_MARK;
  253. }
  254. else if (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGEDPOOL) {
  255. //
  256. // This is paged pool, put it in the paged pool cell.
  257. //
  258. Master = MM_PAGED_POOL_MARK;
  259. }
  260. else if (PfnId.u1.e1.UseDescription == MMPFNUSE_SESSIONPRIVATE) {
  261. //
  262. // Call this paged pool for now.
  263. //
  264. Master = MM_PAGED_POOL_MARK;
  265. }
  266. else if (PfnId.u1.e1.UseDescription == MMPFNUSE_DRIVERLOCKPAGE) {
  267. //
  268. // Call this nonpaged pool for now.
  269. //
  270. Master = MM_NONPAGED_POOL_MARK;
  271. }
  272. else if (PfnId.u1.e1.UseDescription == MMPFNUSE_AWEPAGE) {
  273. //
  274. // Call this nonpaged pool for now.
  275. //
  276. Master = MM_NONPAGED_POOL_MARK;
  277. }
  278. else {
  279. //
  280. // See if the page is part of the kernel or a driver image.
  281. // If not but it's in system PTEs, call it a kernel thread
  282. // stack.
  283. //
  284. // If neither of the above, then see if the page belongs to
  285. // a user address or a session pagetable page.
  286. //
  287. VirtualAddress = PfnId.u2.VirtualAddress;
  288. for (j = 0; j < KernSize; j += 1) {
  289. if ((VirtualAddress >= KernMap[j].StartVa) &&
  290. (VirtualAddress < KernMap[j].EndVa)) {
  291. Master = (PUCHAR)&KernMap[j];
  292. break;
  293. }
  294. }
  295. if (j == KernSize) {
  296. if (PfnId.u1.e1.UseDescription == MMPFNUSE_SYSTEMPTE) {
  297. Master = MM_KERNEL_STACK_MARK;
  298. }
  299. else if (MI_IS_SESSION_PTE (VirtualAddress)) {
  300. Master = MM_NONPAGED_POOL_MARK;
  301. }
  302. else {
  303. ASSERT ((PfnId.u1.e1.UseDescription == MMPFNUSE_PROCESSPRIVATE) || (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGETABLE));
  304. Master = (PUCHAR) (ULONG_PTR) PfnId.u1.e3.PageDirectoryBase;
  305. }
  306. }
  307. }
  308. //
  309. // The page has been identified.
  310. // See if there is already a bucket allocated for it.
  311. //
  312. for (Info = InfoStart; Info < InfoEnd; Info += 1) {
  313. if (Info->StringOffset == Master) {
  314. break;
  315. }
  316. }
  317. if (Info == InfoEnd) {
  318. InfoEnd += 1;
  319. if ((PUCHAR)InfoEnd > End) {
  320. status = STATUS_DATA_OVERRUN;
  321. goto Done;
  322. }
  323. RtlZeroMemory (Info, sizeof(*Info));
  324. Info->StringOffset = Master;
  325. }
  326. if (PfnId.u1.e1.ListDescription == ActiveAndValid) {
  327. Info->ValidCount += 1;
  328. }
  329. else if ((PfnId.u1.e1.ListDescription == StandbyPageList) ||
  330. (PfnId.u1.e1.ListDescription == TransitionPage)) {
  331. Info->TransitionCount += 1;
  332. }
  333. else if ((PfnId.u1.e1.ListDescription == ModifiedPageList) ||
  334. (PfnId.u1.e1.ListDescription == ModifiedNoWritePageList)) {
  335. Info->ModifiedCount += 1;
  336. }
  337. if (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGETABLE) {
  338. Info->PageTableCount += 1;
  339. }
  340. }
  341. }
  342. MemInfo->StringStart = (ULONG_PTR)Buffer + (ULONG_PTR)InfoEnd - (ULONG_PTR)MemInfo;
  343. String = (PUCHAR)InfoEnd;
  344. //
  345. // Process the buckets ...
  346. //
  347. for (Info = InfoStart; Info < InfoEnd; Info += 1) {
  348. ControlArea = NULL;
  349. if (Info->StringOffset == MM_PAGEFILE_BACKED_SHMEM_MARK) {
  350. Length = 16;
  351. NameString = PageFileMappedString;
  352. }
  353. else if (Info->StringOffset == MM_NONPAGED_POOL_MARK) {
  354. Length = 14;
  355. NameString = NonPagedPoolString;
  356. }
  357. else if (Info->StringOffset == MM_PAGED_POOL_MARK) {
  358. Length = 14;
  359. NameString = PagedPoolString;
  360. }
  361. else if (Info->StringOffset == MM_KERNEL_STACK_MARK) {
  362. Length = 14;
  363. NameString = KernelStackString;
  364. }
  365. else if (((PUCHAR)Info->StringOffset >= (PUCHAR)&KernMap[0]) &&
  366. ((PUCHAR)Info->StringOffset <= (PUCHAR)&KernMap[KernSize])) {
  367. DataTableEntry = ((PKERN_MAP)Info->StringOffset)->Entry;
  368. NameString = (PUCHAR)DataTableEntry->BaseDllName.Buffer;
  369. Length = DataTableEntry->BaseDllName.Length;
  370. }
  371. else if (Info->StringOffset > (PUCHAR)MM_HIGHEST_USER_ADDRESS) {
  372. //
  373. // This points to a control area - get the file name.
  374. //
  375. ControlArea = (PCONTROL_AREA)(Info->StringOffset);
  376. NameString = (PUCHAR)&ControlArea->FilePointer->FileName.Buffer[0];
  377. Length = ControlArea->FilePointer->FileName.Length;
  378. if (Length == 0) {
  379. if (ControlArea->u.Flags.NoModifiedWriting) {
  380. NameString = MetaFileString;
  381. Length = 14;
  382. }
  383. else if (ControlArea->u.Flags.File == 0) {
  384. NameString = PageFileMappedString;
  385. Length = 16;
  386. }
  387. else {
  388. NameString = NoNameString;
  389. Length = 14;
  390. }
  391. }
  392. }
  393. else {
  394. //
  395. // This is a process (or session) top-level page directory.
  396. //
  397. Pfn1 = MI_PFN_ELEMENT (PtrToUlong(Info->StringOffset));
  398. ASSERT (Pfn1->u4.PteFrame == (ULONG_PTR)(Pfn1 - MmPfnDatabase));
  399. Process = (PEPROCESS)Pfn1->u1.Event;
  400. NameString = &Process->ImageFileName[0];
  401. Length = 16;
  402. }
  403. if ((String+Length+2) >= End) {
  404. status = STATUS_DATA_OVERRUN;
  405. Info->StringOffset = NULL;
  406. goto Done;
  407. }
  408. if ((ControlArea == NULL) ||
  409. (MiIsAddressRangeValid (NameString, Length))) {
  410. RtlCopyMemory (String, NameString, Length);
  411. Info->StringOffset = (PUCHAR)Buffer + ((PUCHAR)String - (PUCHAR)MemInfo);
  412. String[Length] = 0;
  413. String[Length + 1] = 0;
  414. String += Length + 2;
  415. }
  416. else {
  417. if (!(ControlArea->u.Flags.BeingCreated ||
  418. ControlArea->u.Flags.BeingDeleted) &&
  419. (ControlCount < MM_SAVED_CONTROL)) {
  420. SavedControl[ControlCount] = ControlArea;
  421. SavedInfo[ControlCount] = Info;
  422. ControlArea->NumberOfSectionReferences += 1;
  423. ControlCount += 1;
  424. }
  425. Info->StringOffset = NULL;
  426. }
  427. }
  428. Done:
  429. UNLOCK_PFN (OldIrql);
  430. ExFreePool (KernMap);
  431. while (ControlCount != 0) {
  432. //
  433. // Process all the pagable name strings.
  434. //
  435. ControlCount -= 1;
  436. ControlArea = SavedControl[ControlCount];
  437. Info = SavedInfo[ControlCount];
  438. NameString = (PUCHAR)&ControlArea->FilePointer->FileName.Buffer[0];
  439. Length = ControlArea->FilePointer->FileName.Length;
  440. if (Length == 0) {
  441. if (ControlArea->u.Flags.NoModifiedWriting) {
  442. Length = 12;
  443. NameString = MetaFileString;
  444. }
  445. else if (ControlArea->u.Flags.File == 0) {
  446. NameString = PageFileMappedString;
  447. Length = 16;
  448. }
  449. else {
  450. NameString = NoNameString;
  451. Length = 12;
  452. }
  453. }
  454. if ((String+Length+2) >= End) {
  455. status = STATUS_DATA_OVERRUN;
  456. }
  457. if (status != STATUS_DATA_OVERRUN) {
  458. RtlCopyMemory (String, NameString, Length);
  459. Info->StringOffset = (PUCHAR)Buffer + ((PUCHAR)String - (PUCHAR)MemInfo);
  460. String[Length] = 0;
  461. String[Length + 1] = 0;
  462. String += Length + 2;
  463. }
  464. LOCK_PFN (OldIrql);
  465. ControlArea->NumberOfSectionReferences -= 1;
  466. MiCheckForControlAreaDeletion (ControlArea);
  467. UNLOCK_PFN (OldIrql);
  468. }
  469. *OutLength = (ULONG)((PUCHAR)String - (PUCHAR)MemInfo);
  470. //
  471. // Carefully copy the results to the user buffer.
  472. //
  473. try {
  474. RtlCopyMemory (Buffer, MemInfo, (ULONG_PTR)String - (ULONG_PTR)MemInfo);
  475. } except (EXCEPTION_EXECUTE_HANDLER) {
  476. status = GetExceptionCode();
  477. }
  478. ExFreePool (MemInfo);
  479. return status;
  480. }
  481. ULONG
  482. MiBuildKernelMap (
  483. OUT PKERN_MAP *KernelMapOut
  484. )
  485. {
  486. PKTHREAD CurrentThread;
  487. PLIST_ENTRY NextEntry;
  488. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  489. PKERN_MAP KernelMap;
  490. ULONG i;
  491. i = 0;
  492. CurrentThread = KeGetCurrentThread ();
  493. KeEnterCriticalRegionThread (CurrentThread);
  494. ExAcquireResourceShared (&PsLoadedModuleResource, TRUE);
  495. //
  496. // The caller wants us to allocate the return result buffer. Size it
  497. // by allocating the maximum possibly needed as this should not be
  498. // very big (relatively). It is the caller's responsibility to free
  499. // this. Obviously this option can only be requested after pool has
  500. // been initialized.
  501. //
  502. NextEntry = PsLoadedModuleList.Flink;
  503. while (NextEntry != &PsLoadedModuleList) {
  504. i += 1;
  505. NextEntry = NextEntry->Flink;
  506. }
  507. KernelMap = ExAllocatePoolWithTag (NonPagedPool,
  508. i * sizeof(KERN_MAP),
  509. 'lMmM');
  510. if (KernelMap == NULL) {
  511. return 0;
  512. }
  513. *KernelMapOut = KernelMap;
  514. i = 0;
  515. NextEntry = PsLoadedModuleList.Flink;
  516. while (NextEntry != &PsLoadedModuleList) {
  517. DataTableEntry = CONTAINING_RECORD (NextEntry,
  518. KLDR_DATA_TABLE_ENTRY,
  519. InLoadOrderLinks);
  520. KernelMap[i].Entry = DataTableEntry;
  521. KernelMap[i].StartVa = DataTableEntry->DllBase;
  522. KernelMap[i].EndVa = (PVOID)((ULONG_PTR)KernelMap[i].StartVa +
  523. DataTableEntry->SizeOfImage);
  524. i += 1;
  525. NextEntry = NextEntry->Flink;
  526. }
  527. ExReleaseResourceLite(&PsLoadedModuleResource);
  528. KeLeaveCriticalRegionThread (CurrentThread);
  529. return i;
  530. }
  531. VOID
  532. MiDumpReferencedPages (
  533. VOID
  534. )
  535. /*++
  536. Routine Description:
  537. This routine (debugging only) dumps all PFN entries which appear
  538. to be locked in memory for i/o.
  539. Arguments:
  540. None.
  541. Return Value:
  542. None.
  543. --*/
  544. {
  545. KIRQL OldIrql;
  546. PMMPFN Pfn1;
  547. PMMPFN PfnLast;
  548. LOCK_PFN (OldIrql);
  549. Pfn1 = MI_PFN_ELEMENT (MmLowestPhysicalPage);
  550. PfnLast = MI_PFN_ELEMENT (MmHighestPhysicalPage);
  551. while (Pfn1 <= PfnLast) {
  552. if ((Pfn1->u2.ShareCount == 0) && (Pfn1->u3.e2.ReferenceCount != 0)) {
  553. MiFormatPfn (Pfn1);
  554. }
  555. if (Pfn1->u3.e2.ReferenceCount > 1) {
  556. MiFormatPfn (Pfn1);
  557. }
  558. Pfn1 += 1;
  559. }
  560. UNLOCK_PFN (OldIrql);
  561. return;
  562. }
  563. #else //DBG
  564. NTSTATUS
  565. MmMemoryUsage (
  566. IN PVOID Buffer,
  567. IN ULONG Size,
  568. IN ULONG Type,
  569. OUT PULONG OutLength
  570. )
  571. {
  572. UNREFERENCED_PARAMETER (Buffer);
  573. UNREFERENCED_PARAMETER (Size);
  574. UNREFERENCED_PARAMETER (Type);
  575. UNREFERENCED_PARAMETER (OutLength);
  576. return STATUS_NOT_IMPLEMENTED;
  577. }
  578. #endif //DBG
  579. //
  580. // One benefit of using run length maximums of less than 4GB is that even
  581. // frame numbers above 4GB are handled properly despite the 32-bit limitations
  582. // of the bitmap routines.
  583. //
  584. #define MI_MAXIMUM_PFNID_RUN 4096
  585. NTSTATUS
  586. MmPerfSnapShotValidPhysicalMemory (
  587. VOID
  588. )
  589. /*++
  590. Routine Description:
  591. This routine logs the PFN numbers of all ActiveAndValid pages.
  592. Arguments:
  593. None.
  594. Return Value:
  595. NTSTATUS.
  596. Environment:
  597. Kernel mode. PASSIVE level. No locks held.
  598. --*/
  599. {
  600. ULONG i;
  601. PFN_NUMBER StartPage;
  602. PFN_NUMBER EndPage;
  603. ULONG_PTR MemSnapLocal[(sizeof(MMPFN_MEMSNAP_INFORMATION)/sizeof(ULONG_PTR)) + (MI_MAXIMUM_PFNID_RUN / (8*sizeof(ULONG_PTR))) ];
  604. PMMPFN_MEMSNAP_INFORMATION MemSnap;
  605. PMMPFN Pfn1;
  606. PMMPFN FirstPfn;
  607. PMMPFN LastPfn;
  608. PMMPFN MaxPfn;
  609. PMMPFN InitialPfn;
  610. RTL_BITMAP BitMap;
  611. PULONG ActualBits;
  612. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  613. ASSERT ((MI_MAXIMUM_PFNID_RUN % (8 * sizeof(ULONG_PTR))) == 0);
  614. MemSnap = (PMMPFN_MEMSNAP_INFORMATION)&MemSnapLocal;
  615. ActualBits = (PULONG)(MemSnap + 1);
  616. RtlInitializeBitMap (&BitMap, ActualBits, MI_MAXIMUM_PFNID_RUN);
  617. MemSnap->Count = 0;
  618. RtlClearAllBits (&BitMap);
  619. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  620. for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
  621. StartPage = MmPhysicalMemoryBlock->Run[i].BasePage;
  622. EndPage = StartPage + MmPhysicalMemoryBlock->Run[i].PageCount;
  623. FirstPfn = MI_PFN_ELEMENT (StartPage);
  624. LastPfn = MI_PFN_ELEMENT (EndPage);
  625. //
  626. // Find the first valid PFN and start the run there.
  627. //
  628. for (Pfn1 = FirstPfn; Pfn1 < LastPfn; Pfn1 += 1) {
  629. if (Pfn1->u3.e1.PageLocation == ActiveAndValid) {
  630. break;
  631. }
  632. }
  633. if (Pfn1 == LastPfn) {
  634. //
  635. // No valid PFNs in this block, move on to the next block.
  636. //
  637. continue;
  638. }
  639. MaxPfn = LastPfn;
  640. InitialPfn = NULL;
  641. do {
  642. if (Pfn1->u3.e1.PageLocation == ActiveAndValid) {
  643. if (InitialPfn == NULL) {
  644. MemSnap->InitialPageFrameIndex = Pfn1 - MmPfnDatabase;
  645. InitialPfn = Pfn1;
  646. MaxPfn = InitialPfn + MI_MAXIMUM_PFNID_RUN;
  647. }
  648. RtlSetBit (&BitMap, (ULONG) (Pfn1 - InitialPfn));
  649. }
  650. Pfn1 += 1;
  651. if ((Pfn1 >= MaxPfn) && (InitialPfn != NULL)) {
  652. //
  653. // Log the bitmap as we're at then end of it.
  654. //
  655. ASSERT ((Pfn1 - InitialPfn) == MI_MAXIMUM_PFNID_RUN);
  656. MemSnap->Count = MI_MAXIMUM_PFNID_RUN;
  657. PerfInfoLogBytes (PERFINFO_LOG_TYPE_MEMORYSNAPLITE,
  658. MemSnap,
  659. sizeof(MemSnapLocal));
  660. InitialPfn = NULL;
  661. MaxPfn = LastPfn;
  662. RtlClearAllBits (&BitMap);
  663. }
  664. } while (Pfn1 < LastPfn);
  665. //
  666. // Dump any straggling bitmap entries now as this range is finished.
  667. //
  668. if (InitialPfn != NULL) {
  669. ASSERT (Pfn1 == LastPfn);
  670. ASSERT (Pfn1 < MaxPfn);
  671. ASSERT (Pfn1 > InitialPfn);
  672. MemSnap->Count = Pfn1 - InitialPfn;
  673. PerfInfoLogBytes (PERFINFO_LOG_TYPE_MEMORYSNAPLITE,
  674. MemSnap,
  675. sizeof(MMPFN_MEMSNAP_INFORMATION) +
  676. (ULONG) ((MemSnap->Count + 8) / 8));
  677. RtlClearAllBits (&BitMap);
  678. }
  679. }
  680. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  681. return STATUS_SUCCESS;
  682. }
  683. #define PFN_ID_BUFFERS 128
  684. NTSTATUS
  685. MmIdentifyPhysicalMemory (
  686. VOID
  687. )
  688. /*++
  689. Routine Description:
  690. This routine calls the pfn id code for each page. Because
  691. the logging can't handle very large amounts of data in a burst
  692. (limited buffering), the data is broken into page size chunks.
  693. Arguments:
  694. None.
  695. Return Value:
  696. NTSTATUS.
  697. Environment:
  698. Kernel mode. PASSIVE level. No locks held.
  699. --*/
  700. {
  701. ULONG i;
  702. KIRQL OldIrql;
  703. PMMPFN Pfn1;
  704. PMMPFN EndPfn;
  705. PFN_NUMBER PageFrameIndex;
  706. MMPFN_IDENTITY PfnIdBuffer[PFN_ID_BUFFERS];
  707. PMMPFN_IDENTITY BufferPointer;
  708. PMMPFN_IDENTITY BufferLast;
  709. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  710. BufferPointer = &PfnIdBuffer[0];
  711. BufferLast = BufferPointer + PFN_ID_BUFFERS;
  712. RtlZeroMemory (PfnIdBuffer, sizeof(PfnIdBuffer));
  713. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  714. //
  715. // Walk through the ranges and identify pages until
  716. // the buffer is full or we've run out of pages.
  717. //
  718. for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
  719. PageFrameIndex = MmPhysicalMemoryBlock->Run[i].BasePage;
  720. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  721. EndPfn = Pfn1 + MmPhysicalMemoryBlock->Run[i].PageCount;
  722. LOCK_PFN (OldIrql);
  723. while (Pfn1 < EndPfn) {
  724. MiIdentifyPfn (Pfn1, BufferPointer);
  725. BufferPointer += 1;
  726. if (BufferPointer == BufferLast) {
  727. //
  728. // Release and reacquire the PFN lock so it's not held so long.
  729. //
  730. UNLOCK_PFN (OldIrql);
  731. //
  732. // Log the buffered entries.
  733. //
  734. BufferPointer = &PfnIdBuffer[0];
  735. do {
  736. PerfInfoLogBytes (PERFINFO_LOG_TYPE_PAGEINMEMORY,
  737. BufferPointer,
  738. sizeof(PfnIdBuffer[0]));
  739. BufferPointer += 1;
  740. } while (BufferPointer < BufferLast);
  741. //
  742. // Reset the buffer to the beginning and zero it.
  743. //
  744. BufferPointer = &PfnIdBuffer[0];
  745. RtlZeroMemory (PfnIdBuffer, sizeof(PfnIdBuffer));
  746. LOCK_PFN (OldIrql);
  747. }
  748. Pfn1 += 1;
  749. }
  750. UNLOCK_PFN (OldIrql);
  751. }
  752. //
  753. // Note that releasing this mutex here means the last entry can be
  754. // inserted out of order if we are preempted and another thread starts
  755. // the same operation (or if we're on an MP machine). The PERF module
  756. // must handle this properly as any synchronization provided by this
  757. // routine is purely a side effect not deliberate.
  758. //
  759. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  760. if (BufferPointer != &PfnIdBuffer[0]) {
  761. BufferLast = BufferPointer;
  762. BufferPointer = &PfnIdBuffer[0];
  763. do {
  764. PerfInfoLogBytes (PERFINFO_LOG_TYPE_PAGEINMEMORY,
  765. BufferPointer,
  766. sizeof(PfnIdBuffer[0]));
  767. BufferPointer += 1;
  768. } while (BufferPointer < BufferLast);
  769. }
  770. return STATUS_SUCCESS;
  771. }
  772. VOID
  773. FASTCALL
  774. MiIdentifyPfn (
  775. IN PMMPFN Pfn1,
  776. OUT PMMPFN_IDENTITY PfnIdentity
  777. )
  778. /*++
  779. Routine Description:
  780. This routine captures relevant information for the argument page frame.
  781. Arguments:
  782. Pfn1 - Supplies the PFN element of the page frame number being queried.
  783. PfnIdentity - Receives the structure to fill in with the information.
  784. Return Value:
  785. None.
  786. Environment:
  787. Kernel mode. PFN lock held.
  788. --*/
  789. {
  790. ULONG i;
  791. PMMPTE PteAddress;
  792. PSUBSECTION Subsection;
  793. PCONTROL_AREA ControlArea;
  794. PVOID VirtualAddress;
  795. PFILE_OBJECT FilePointer;
  796. PFN_NUMBER PageFrameIndex;
  797. MI_INCREMENT_IDENTIFY_COUNTER (8);
  798. ASSERT (PfnIdentity->u2.VirtualAddress == 0);
  799. ASSERT (PfnIdentity->u1.e1.ListDescription == 0);
  800. ASSERT (PfnIdentity->u1.e1.UseDescription == 0);
  801. ASSERT (PfnIdentity->u1.e1.Pinned == 0);
  802. ASSERT (PfnIdentity->u1.e2.Offset == 0);
  803. MM_PFN_LOCK_ASSERT();
  804. PageFrameIndex = Pfn1 - MmPfnDatabase;
  805. PfnIdentity->PageFrameIndex = PageFrameIndex;
  806. PfnIdentity->u1.e1.ListDescription = Pfn1->u3.e1.PageLocation;
  807. #if DBG
  808. if (PageFrameIndex == MiIdentifyFrame) {
  809. DbgPrint ("MmIdentifyPfn: requested PFN %p\n", PageFrameIndex);
  810. DbgBreakPoint ();
  811. }
  812. #endif
  813. MI_INCREMENT_IDENTIFY_COUNTER (Pfn1->u3.e1.PageLocation);
  814. switch (Pfn1->u3.e1.PageLocation) {
  815. case ZeroedPageList:
  816. case FreePageList:
  817. case BadPageList:
  818. return;
  819. case ActiveAndValid:
  820. //
  821. // It's too much work to determine if the page is locked
  822. // in a working set due to cross-process WSL references, etc.
  823. // So don't bother for now.
  824. //
  825. ASSERT (PfnIdentity->u1.e1.ListDescription == MMPFNLIST_ACTIVE);
  826. if (Pfn1->u1.WsIndex == 0) {
  827. MI_INCREMENT_IDENTIFY_COUNTER (9);
  828. PfnIdentity->u1.e1.Pinned = 1;
  829. }
  830. else if (Pfn1->u3.e2.ReferenceCount > 1) {
  831. //
  832. // This page is pinned, presumably for an ongoing I/O.
  833. //
  834. PfnIdentity->u1.e1.Pinned = 1;
  835. MI_INCREMENT_IDENTIFY_COUNTER (10);
  836. }
  837. break;
  838. case StandbyPageList:
  839. case ModifiedPageList:
  840. case ModifiedNoWritePageList:
  841. if (Pfn1->u3.e2.ReferenceCount >= 1) {
  842. //
  843. // This page is pinned, presumably for an ongoing I/O.
  844. //
  845. PfnIdentity->u1.e1.Pinned = 1;
  846. MI_INCREMENT_IDENTIFY_COUNTER (11);
  847. }
  848. if ((Pfn1->u3.e1.PageLocation == ModifiedPageList) &&
  849. (MI_IS_PFN_DELETED (Pfn1)) &&
  850. (Pfn1->u2.ShareCount == 0)) {
  851. //
  852. // This page may be a modified write completing in the
  853. // context of the modified writer thread. If the
  854. // address space was deleted while the I/O was in
  855. // progress, the frame will be released now. More
  856. // importantly, the frame's containing frame is
  857. // meaningless as it may have already been freed
  858. // and reused.
  859. //
  860. // We can't tell what this page was being used for
  861. // since its address space is gone, so just call it
  862. // process private for now.
  863. //
  864. MI_INCREMENT_IDENTIFY_COUNTER (40);
  865. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE;
  866. return;
  867. }
  868. break;
  869. case TransitionPage:
  870. //
  871. // This page is pinned due to a straggling I/O - the virtual
  872. // address has been deleted but an I/O referencing it has not
  873. // completed.
  874. //
  875. PfnIdentity->u1.e1.Pinned = 1;
  876. MI_INCREMENT_IDENTIFY_COUNTER (11);
  877. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE;
  878. return;
  879. default:
  880. #if DBG
  881. DbgPrint ("MmIdentifyPfn: unknown PFN %p %x\n",
  882. Pfn1, Pfn1->u3.e1.PageLocation);
  883. DbgBreakPoint ();
  884. #endif
  885. break;
  886. }
  887. //
  888. // Capture differing information based on the type of page being examined.
  889. //
  890. //
  891. // General purpose stress shows 40% of the pages are prototypes so
  892. // for speed, check for these first.
  893. //
  894. if (Pfn1->u3.e1.PrototypePte == 1) {
  895. MI_INCREMENT_IDENTIFY_COUNTER (12);
  896. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  897. //
  898. // Demand zero or (equivalently) pagefile backed.
  899. //
  900. // There are some hard problems here preventing more indepth
  901. // identification of these pages:
  902. //
  903. // 1. The PFN contains a backpointer to the prototype PTE - but
  904. // there is no definitive way to get to the SEGMENT or
  905. // CONTROL_AREA from this.
  906. //
  907. // 2. The prototype PTE pointer itself may be paged out and
  908. // the PFN lock is held right now.
  909. //
  910. MI_INCREMENT_IDENTIFY_COUNTER (13);
  911. #if 0
  912. PfnIdentity->u2.FileObject = (PVOID) ControlArea->Segment->u1.CreatingProcess;
  913. PfnIdentity->u1.e2.Offset = (((ULONG_PTR)ControlArea->Segment->u2.FirstMappedVa) >> MMSECTOR_SHIFT);
  914. #endif
  915. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PAGEFILEMAPPED;
  916. return;
  917. }
  918. MI_INCREMENT_IDENTIFY_COUNTER (14);
  919. //
  920. // Backed by a mapped file.
  921. //
  922. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  923. ControlArea = Subsection->ControlArea;
  924. ASSERT (ControlArea->u.Flags.File == 1);
  925. FilePointer = ControlArea->FilePointer;
  926. ASSERT (FilePointer != NULL);
  927. PfnIdentity->u2.FileObject = FilePointer;
  928. if (Subsection->SubsectionBase != NULL) {
  929. PfnIdentity->u1.e2.Offset = (MiStartingOffset (Subsection, Pfn1->PteAddress) >> MMSECTOR_SHIFT);
  930. }
  931. else {
  932. //
  933. // The only time we should be here (a valid PFN with no subsection)
  934. // is if we are the segment dereference thread putting pages into
  935. // the freelist. At this point the PFN lock is held and the
  936. // control area/subsection/PFN structures are not yet consistent
  937. // so just treat this as an offset of 0 as it should be rare.
  938. //
  939. ASSERT (PsGetCurrentThread()->StartAddress == (PVOID)(ULONG_PTR)MiDereferenceSegmentThread);
  940. }
  941. //
  942. // Check for nomodwrite sections - typically this is filesystem
  943. // metadata although it could also be registry data (which is named).
  944. //
  945. if (ControlArea->u.Flags.NoModifiedWriting) {
  946. MI_INCREMENT_IDENTIFY_COUNTER (15);
  947. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_METAFILE;
  948. return;
  949. }
  950. if (FilePointer->FileName.Length != 0) {
  951. //
  952. // This mapped file has a name.
  953. //
  954. MI_INCREMENT_IDENTIFY_COUNTER (16);
  955. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_FILE;
  956. return;
  957. }
  958. //
  959. // No name - this file must be in the midst of a purge, but it
  960. // still *was* a mapped file of some sort.
  961. //
  962. MI_INCREMENT_IDENTIFY_COUNTER (17);
  963. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_FILE;
  964. return;
  965. }
  966. if ((PageFrameIndex >= MiStartOfInitialPoolFrame) &&
  967. (PageFrameIndex <= MiEndOfInitialPoolFrame)) {
  968. //
  969. // This is initial nonpaged pool.
  970. //
  971. MI_INCREMENT_IDENTIFY_COUNTER (18);
  972. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_NONPAGEDPOOL;
  973. VirtualAddress = (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
  974. ((PageFrameIndex - MiStartOfInitialPoolFrame) << PAGE_SHIFT));
  975. PfnIdentity->u2.VirtualAddress = PAGE_ALIGN(VirtualAddress);
  976. return;
  977. }
  978. PteAddress = Pfn1->PteAddress;
  979. VirtualAddress = MiGetVirtualAddressMappedByPte (PteAddress);
  980. PfnIdentity->u2.VirtualAddress = PAGE_ALIGN(VirtualAddress);
  981. if (MI_IS_SESSION_ADDRESS(VirtualAddress)) {
  982. //
  983. // Note session addresses that map images (or views) that haven't
  984. // undergone a copy-on-write split were already treated as prototype
  985. // PTEs above. This clause handles session pool and copy-on-written
  986. // pages.
  987. //
  988. MI_INCREMENT_IDENTIFY_COUNTER (19);
  989. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_SESSIONPRIVATE;
  990. return;
  991. }
  992. if ((VirtualAddress >= MmPagedPoolStart) &&
  993. (VirtualAddress <= MmPagedPoolEnd)) {
  994. //
  995. // This is paged pool.
  996. //
  997. MI_INCREMENT_IDENTIFY_COUNTER (20);
  998. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PAGEDPOOL;
  999. return;
  1000. }
  1001. if ((VirtualAddress >= MmNonPagedPoolExpansionStart) &&
  1002. (VirtualAddress < MmNonPagedPoolEnd)) {
  1003. //
  1004. // This is expansion nonpaged pool.
  1005. //
  1006. MI_INCREMENT_IDENTIFY_COUNTER (21);
  1007. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_NONPAGEDPOOL;
  1008. return;
  1009. }
  1010. if ((VirtualAddress >= MmNonPagedSystemStart) &&
  1011. (PteAddress <= MmSystemPtesEnd[SystemPteSpace])) {
  1012. //
  1013. // This is driver space, kernel stack, special pool or other
  1014. // system PTE mappings.
  1015. //
  1016. MI_INCREMENT_IDENTIFY_COUNTER (22);
  1017. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_SYSTEMPTE;
  1018. return;
  1019. }
  1020. #if defined (_X86_)
  1021. //
  1022. // 2 other ranges of system PTEs can exist on x86.
  1023. //
  1024. if (((MiNumberOfExtraSystemPdes != 0) &&
  1025. (VirtualAddress >= (PVOID)MiExtraResourceStart) &&
  1026. (VirtualAddress < (PVOID)MiExtraResourceEnd)) ||
  1027. ((MiUseMaximumSystemSpace != 0) &&
  1028. (VirtualAddress >= (PVOID)MiUseMaximumSystemSpace) &&
  1029. (VirtualAddress < (PVOID)MiUseMaximumSystemSpaceEnd)))
  1030. {
  1031. //
  1032. // This is driver space, kernel stack, special pool or other
  1033. // system PTE mappings.
  1034. //
  1035. MI_INCREMENT_IDENTIFY_COUNTER (23);
  1036. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_SYSTEMPTE;
  1037. return;
  1038. }
  1039. #endif
  1040. if (Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME) {
  1041. MI_INCREMENT_IDENTIFY_COUNTER (24);
  1042. //
  1043. // Carefully check here as this could be a legitimate frame as well.
  1044. //
  1045. if ((Pfn1->u3.e1.StartOfAllocation == 1) &&
  1046. (Pfn1->u3.e1.EndOfAllocation == 1) &&
  1047. (Pfn1->u3.e1.PageLocation == ActiveAndValid)) {
  1048. if (MI_IS_PFN_DELETED (Pfn1)) {
  1049. MI_INCREMENT_IDENTIFY_COUNTER (25);
  1050. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_DRIVERLOCKPAGE;
  1051. }
  1052. else {
  1053. MI_INCREMENT_IDENTIFY_COUNTER (26);
  1054. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_AWEPAGE;
  1055. }
  1056. return;
  1057. }
  1058. }
  1059. #if DBG
  1060. //
  1061. // In checked kernels, AWE frames get their containing frame decremented
  1062. // when the AWE frame is freed.
  1063. //
  1064. if (Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME - 1) {
  1065. MI_INCREMENT_IDENTIFY_COUNTER (24);
  1066. //
  1067. // Carefully check here as this could be a legitimate frame as well.
  1068. //
  1069. if ((Pfn1->u3.e1.StartOfAllocation == 0) &&
  1070. (Pfn1->u3.e1.EndOfAllocation == 0) &&
  1071. (Pfn1->u3.e1.PageLocation == StandbyPageList)) {
  1072. MI_INCREMENT_IDENTIFY_COUNTER (26);
  1073. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_AWEPAGE;
  1074. return;
  1075. }
  1076. }
  1077. #endif
  1078. //
  1079. // Check the PFN working set index carefully here. This must be done
  1080. // before walking back through the containing frames because if this page
  1081. // is not in a working set, the containing frame may not be meaningful and
  1082. // dereferencing it can crash the system and/or yield incorrect walks.
  1083. // This is because if a page will never be trimmable there is no need to
  1084. // have a containing frame initialized. This also covers the case of
  1085. // data pages mapped via large page directory entries as these have no
  1086. // containing page table frame.
  1087. //
  1088. if (Pfn1->u3.e1.PageLocation == ActiveAndValid) {
  1089. if (Pfn1->u1.WsIndex == 0) {
  1090. //
  1091. // Default to calling these allocations nonpaged pool because even
  1092. // when they technically are not, from a usage standpoint they are.
  1093. // Note the default is overridden for specific cases where the usage
  1094. // is not in fact nonpaged.
  1095. //
  1096. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_NONPAGEDPOOL;
  1097. ASSERT (PfnIdentity->u1.e1.Pinned == 1);
  1098. MI_INCREMENT_IDENTIFY_COUNTER (27);
  1099. return;
  1100. }
  1101. }
  1102. //
  1103. // Must be a process private page
  1104. //
  1105. // OR
  1106. //
  1107. // a page table, page directory, parent or extended parent.
  1108. //
  1109. i = 0;
  1110. while (Pfn1->u4.PteFrame != PageFrameIndex) {
  1111. //
  1112. // The only way the PTE address will go out of bounds is if this is
  1113. // a top level page directory page for a process that has been
  1114. // swapped out but is still waiting for the transition/modified
  1115. // page table pages to be reclaimed. ie: until that happens, the
  1116. // page directory is marked Active, but the PteAddress & containing
  1117. // page are pointing at the EPROCESS pool page.
  1118. //
  1119. #if defined(_IA64_)
  1120. if (((Pfn1->PteAddress >= (PMMPTE) PTE_BASE) &&
  1121. (Pfn1->PteAddress <= (PMMPTE) PTE_TOP)) ||
  1122. ((Pfn1->PteAddress >= (PMMPTE) PTE_KBASE) &&
  1123. (Pfn1->PteAddress <= (PMMPTE) PTE_KTOP)) ||
  1124. ((Pfn1->PteAddress >= (PMMPTE) PTE_SBASE) &&
  1125. (Pfn1->PteAddress <= (PMMPTE) PTE_STOP)))
  1126. #else
  1127. if ((Pfn1->PteAddress >= (PMMPTE) PTE_BASE) &&
  1128. (Pfn1->PteAddress <= (PMMPTE) PTE_TOP))
  1129. #endif
  1130. {
  1131. PageFrameIndex = Pfn1->u4.PteFrame;
  1132. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1133. i += 1;
  1134. }
  1135. else {
  1136. MI_INCREMENT_IDENTIFY_COUNTER (41);
  1137. break;
  1138. }
  1139. }
  1140. MI_INCREMENT_IDENTIFY_COUNTER (31+i);
  1141. PfnIdentity->u1.e3.PageDirectoryBase = PageFrameIndex;
  1142. #if defined(_X86PAE_)
  1143. //
  1144. // PAE is unique because the 3rd level is not defined as only a mini
  1145. // 4 entry 3rd level is in use. Check for that explicitly, noting that
  1146. // it takes one extra walk to get to the top. Top level PAE pages (the
  1147. // ones that contain only the 4 PDPTE pointers) are treated above as
  1148. // active pinned pages, not as pagetable pages because each one is shared
  1149. // across 127 processes and resides in the system global space.
  1150. //
  1151. if (i == _MI_PAGING_LEVELS + 1) {
  1152. //
  1153. // Had to walk all the way to the top. Must be a data page.
  1154. //
  1155. MI_INCREMENT_IDENTIFY_COUNTER (29);
  1156. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE;
  1157. return;
  1158. }
  1159. #else
  1160. if (i == _MI_PAGING_LEVELS) {
  1161. //
  1162. // Had to walk all the way to the top. Must be a data page.
  1163. //
  1164. MI_INCREMENT_IDENTIFY_COUNTER (29);
  1165. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE;
  1166. return;
  1167. }
  1168. #endif
  1169. //
  1170. // Must have been a page in the hierarchy (not a data page) as we arrived
  1171. // at the top early.
  1172. //
  1173. MI_INCREMENT_IDENTIFY_COUNTER (30);
  1174. PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PAGETABLE;
  1175. return;
  1176. }