Leaked source code of windows server 2003
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.

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