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.

2278 lines
62 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. dynmem.c
  5. Abstract:
  6. This module contains the routines which implement dynamically adding
  7. and removing physical memory from the system.
  8. Author:
  9. Landy Wang (landyw) 05-Feb-1999
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. FAST_MUTEX MmDynamicMemoryMutex;
  14. LOGICAL MiTrimRemovalPagesOnly = FALSE;
  15. #if DBG
  16. ULONG MiShowStuckPages;
  17. ULONG MiDynmemData[9];
  18. #endif
  19. #if defined (_MI_COMPRESSION)
  20. extern PMM_SET_COMPRESSION_THRESHOLD MiSetCompressionThreshold;
  21. #endif
  22. //
  23. // Leave the low 3 bits clear as this will be inserted into the PFN PteAddress.
  24. //
  25. #define PFN_REMOVED ((PMMPTE)(INT_PTR)(int)0x99887768)
  26. PFN_COUNT
  27. MiRemovePhysicalPages (
  28. IN PFN_NUMBER StartPage,
  29. IN PFN_NUMBER EndPage
  30. );
  31. NTSTATUS
  32. MiRemovePhysicalMemory (
  33. IN PPHYSICAL_ADDRESS StartAddress,
  34. IN OUT PLARGE_INTEGER NumberOfBytes,
  35. IN LOGICAL PermanentRemoval,
  36. IN ULONG Flags
  37. );
  38. #ifdef ALLOC_PRAGMA
  39. #pragma alloc_text(PAGE,MmRemovePhysicalMemory)
  40. #pragma alloc_text(PAGE,MmMarkPhysicalMemoryAsBad)
  41. #pragma alloc_text(PAGELK,MmAddPhysicalMemory)
  42. #pragma alloc_text(PAGELK,MmAddPhysicalMemoryEx)
  43. #pragma alloc_text(PAGELK,MiRemovePhysicalMemory)
  44. #pragma alloc_text(PAGELK,MmMarkPhysicalMemoryAsGood)
  45. #pragma alloc_text(PAGELK,MmGetPhysicalMemoryRanges)
  46. #pragma alloc_text(PAGELK,MiRemovePhysicalPages)
  47. #endif
  48. NTSTATUS
  49. MmAddPhysicalMemory (
  50. IN PPHYSICAL_ADDRESS StartAddress,
  51. IN OUT PLARGE_INTEGER NumberOfBytes
  52. )
  53. /*++
  54. Routine Description:
  55. A wrapper for MmAddPhysicalMemoryEx.
  56. Arguments:
  57. StartAddress - Supplies the starting physical address.
  58. NumberOfBytes - Supplies a pointer to the number of bytes being added.
  59. If any bytes were added (ie: STATUS_SUCCESS is being
  60. returned), the actual amount is returned here.
  61. Return Value:
  62. NTSTATUS.
  63. Environment:
  64. Kernel mode. PASSIVE level. No locks held.
  65. --*/
  66. {
  67. return MmAddPhysicalMemoryEx (StartAddress, NumberOfBytes, 0);
  68. }
  69. NTSTATUS
  70. MmAddPhysicalMemoryEx (
  71. IN PPHYSICAL_ADDRESS StartAddress,
  72. IN OUT PLARGE_INTEGER NumberOfBytes,
  73. IN ULONG Flags
  74. )
  75. /*++
  76. Routine Description:
  77. This routine adds the specified physical address range to the system.
  78. This includes initializing PFN database entries and adding it to the
  79. freelists.
  80. Arguments:
  81. StartAddress - Supplies the starting physical address.
  82. NumberOfBytes - Supplies a pointer to the number of bytes being added.
  83. If any bytes were added (ie: STATUS_SUCCESS is being
  84. returned), the actual amount is returned here.
  85. Flags - Supplies relevant flags describing the memory range.
  86. Return Value:
  87. NTSTATUS.
  88. Environment:
  89. Kernel mode. PASSIVE level. No locks held.
  90. --*/
  91. {
  92. ULONG i;
  93. PMMPFN Pfn1;
  94. KIRQL OldIrql;
  95. LOGICAL Inserted;
  96. LOGICAL Updated;
  97. MMPTE TempPte;
  98. PMMPTE PointerPte;
  99. PMMPTE LastPte;
  100. PFN_NUMBER NumberOfPages;
  101. PFN_NUMBER start;
  102. PFN_NUMBER count;
  103. PFN_NUMBER StartPage;
  104. PFN_NUMBER EndPage;
  105. PFN_NUMBER PageFrameIndex;
  106. PFN_NUMBER Page;
  107. PFN_NUMBER LastPage;
  108. PFN_NUMBER TotalPagesAllowed;
  109. PFN_COUNT PagesNeeded;
  110. PPHYSICAL_MEMORY_DESCRIPTOR OldPhysicalMemoryBlock;
  111. PPHYSICAL_MEMORY_DESCRIPTOR NewPhysicalMemoryBlock;
  112. PPHYSICAL_MEMORY_RUN NewRun;
  113. LOGICAL PfnDatabaseIsPhysical;
  114. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  115. #ifdef _MI_MESSAGE_SERVER
  116. if (NumberOfBytes->LowPart & 0x1) {
  117. MI_INSTRUMENT_QUEUE((PVOID)StartAddress);
  118. return STATUS_NOT_SUPPORTED;
  119. }
  120. else if (NumberOfBytes->LowPart & 0x2) {
  121. #if defined(_WIN64)
  122. StartAddress->QuadPart = (ULONG_PTR)(MI_INSTRUMENTR_QUEUE());
  123. #else
  124. StartAddress->LowPart = PtrToUlong(MI_INSTRUMENTR_QUEUE());
  125. #endif
  126. return STATUS_NOT_SUPPORTED;
  127. }
  128. #endif
  129. if (BYTE_OFFSET(StartAddress->LowPart) != 0) {
  130. return STATUS_INVALID_PARAMETER_1;
  131. }
  132. if (BYTE_OFFSET(NumberOfBytes->LowPart) != 0) {
  133. return STATUS_INVALID_PARAMETER_2;
  134. }
  135. #if defined (_MI_COMPRESSION)
  136. if (Flags & ~MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) {
  137. return STATUS_INVALID_PARAMETER_3;
  138. }
  139. #else
  140. if (Flags != 0) {
  141. return STATUS_INVALID_PARAMETER_3;
  142. }
  143. #endif
  144. //
  145. // The system must be configured for dynamic memory addition. This is
  146. // critical as only then is the database guaranteed to be non-sparse.
  147. //
  148. if (MmDynamicPfn == 0) {
  149. return STATUS_NOT_SUPPORTED;
  150. }
  151. if (MI_IS_PHYSICAL_ADDRESS(MmPfnDatabase)) {
  152. PfnDatabaseIsPhysical = TRUE;
  153. }
  154. else {
  155. PfnDatabaseIsPhysical = FALSE;
  156. }
  157. StartPage = (PFN_NUMBER)(StartAddress->QuadPart >> PAGE_SHIFT);
  158. NumberOfPages = (PFN_NUMBER)(NumberOfBytes->QuadPart >> PAGE_SHIFT);
  159. EndPage = StartPage + NumberOfPages;
  160. if (EndPage - 1 > MmHighestPossiblePhysicalPage) {
  161. //
  162. // Truncate the request into something that can be mapped by the PFN
  163. // database.
  164. //
  165. EndPage = MmHighestPossiblePhysicalPage + 1;
  166. NumberOfPages = EndPage - StartPage;
  167. }
  168. //
  169. // Ensure that the memory being added does not exceed the license
  170. // restrictions.
  171. //
  172. if (ExVerifySuite(DataCenter) == TRUE) {
  173. TotalPagesAllowed = MI_DTC_MAX_PAGES;
  174. }
  175. else if ((MmProductType != 0x00690057) &&
  176. (ExVerifySuite(Enterprise) == TRUE)) {
  177. TotalPagesAllowed = MI_ADS_MAX_PAGES;
  178. }
  179. else {
  180. TotalPagesAllowed = MI_DEFAULT_MAX_PAGES;
  181. }
  182. if (MmNumberOfPhysicalPages + NumberOfPages > TotalPagesAllowed) {
  183. //
  184. // Truncate the request appropriately.
  185. //
  186. NumberOfPages = TotalPagesAllowed - MmNumberOfPhysicalPages;
  187. EndPage = StartPage + NumberOfPages;
  188. }
  189. //
  190. // The range cannot wrap.
  191. //
  192. if (StartPage >= EndPage) {
  193. return STATUS_INVALID_PARAMETER_1;
  194. }
  195. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  196. OldPhysicalMemoryBlock = MmPhysicalMemoryBlock;
  197. i = (sizeof(PHYSICAL_MEMORY_DESCRIPTOR) +
  198. (sizeof(PHYSICAL_MEMORY_RUN) * (MmPhysicalMemoryBlock->NumberOfRuns + 1)));
  199. NewPhysicalMemoryBlock = ExAllocatePoolWithTag (NonPagedPool,
  200. i,
  201. ' mM');
  202. if (NewPhysicalMemoryBlock == NULL) {
  203. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  204. return STATUS_INSUFFICIENT_RESOURCES;
  205. }
  206. //
  207. // The range cannot overlap any ranges that are already present.
  208. //
  209. start = 0;
  210. MmLockPagableSectionByHandle (ExPageLockHandle);
  211. LOCK_PFN (OldIrql);
  212. #if defined (_MI_COMPRESSION)
  213. //
  214. // Adding compression-generated ranges can only be done if the hardware
  215. // has already successfully announced itself.
  216. //
  217. if (Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) {
  218. if (MiSetCompressionThreshold == NULL) {
  219. UNLOCK_PFN (OldIrql);
  220. MmUnlockPagableImageSection(ExPageLockHandle);
  221. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  222. ExFreePool (NewPhysicalMemoryBlock);
  223. return STATUS_NOT_SUPPORTED;
  224. }
  225. }
  226. #endif
  227. do {
  228. count = MmPhysicalMemoryBlock->Run[start].PageCount;
  229. Page = MmPhysicalMemoryBlock->Run[start].BasePage;
  230. if (count != 0) {
  231. LastPage = Page + count;
  232. if ((StartPage < Page) && (EndPage > Page)) {
  233. UNLOCK_PFN (OldIrql);
  234. MmUnlockPagableImageSection(ExPageLockHandle);
  235. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  236. ExFreePool (NewPhysicalMemoryBlock);
  237. return STATUS_CONFLICTING_ADDRESSES;
  238. }
  239. if ((StartPage >= Page) && (StartPage < LastPage)) {
  240. UNLOCK_PFN (OldIrql);
  241. MmUnlockPagableImageSection(ExPageLockHandle);
  242. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  243. ExFreePool (NewPhysicalMemoryBlock);
  244. return STATUS_CONFLICTING_ADDRESSES;
  245. }
  246. }
  247. start += 1;
  248. } while (start != MmPhysicalMemoryBlock->NumberOfRuns);
  249. //
  250. // Fill any gaps in the (sparse) PFN database needed for these pages,
  251. // unless the PFN database was physically allocated and completely
  252. // committed up front.
  253. //
  254. PagesNeeded = 0;
  255. if (PfnDatabaseIsPhysical == FALSE) {
  256. PointerPte = MiGetPteAddress (MI_PFN_ELEMENT(StartPage));
  257. LastPte = MiGetPteAddress ((PCHAR)(MI_PFN_ELEMENT(EndPage)) - 1);
  258. while (PointerPte <= LastPte) {
  259. if (PointerPte->u.Hard.Valid == 0) {
  260. PagesNeeded += 1;
  261. }
  262. PointerPte += 1;
  263. }
  264. if (MmAvailablePages < PagesNeeded) {
  265. UNLOCK_PFN (OldIrql);
  266. MmUnlockPagableImageSection(ExPageLockHandle);
  267. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  268. ExFreePool (NewPhysicalMemoryBlock);
  269. return STATUS_INSUFFICIENT_RESOURCES;
  270. }
  271. TempPte = ValidKernelPte;
  272. PointerPte = MiGetPteAddress (MI_PFN_ELEMENT(StartPage));
  273. while (PointerPte <= LastPte) {
  274. if (PointerPte->u.Hard.Valid == 0) {
  275. PageFrameIndex = MiRemoveZeroPage(MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  276. MiInitializePfn (PageFrameIndex, PointerPte, 0);
  277. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  278. *PointerPte = TempPte;
  279. }
  280. PointerPte += 1;
  281. }
  282. MmResidentAvailablePages -= PagesNeeded;
  283. }
  284. //
  285. // If the new range is adjacent to an existing range, just merge it into
  286. // the old block. Otherwise use the new block as a new entry will have to
  287. // be used.
  288. //
  289. NewPhysicalMemoryBlock->NumberOfRuns = MmPhysicalMemoryBlock->NumberOfRuns + 1;
  290. NewPhysicalMemoryBlock->NumberOfPages = MmPhysicalMemoryBlock->NumberOfPages + NumberOfPages;
  291. NewRun = &NewPhysicalMemoryBlock->Run[0];
  292. start = 0;
  293. Inserted = FALSE;
  294. Updated = FALSE;
  295. do {
  296. Page = MmPhysicalMemoryBlock->Run[start].BasePage;
  297. count = MmPhysicalMemoryBlock->Run[start].PageCount;
  298. if (Inserted == FALSE) {
  299. //
  300. // Note overlaps into adjacent ranges were already checked above.
  301. //
  302. if (StartPage == Page + count) {
  303. MmPhysicalMemoryBlock->Run[start].PageCount += NumberOfPages;
  304. OldPhysicalMemoryBlock = NewPhysicalMemoryBlock;
  305. MmPhysicalMemoryBlock->NumberOfPages += NumberOfPages;
  306. //
  307. // Coalesce below and above to avoid leaving zero length gaps
  308. // as these gaps would prevent callers from removing ranges
  309. // the span them.
  310. //
  311. if (start + 1 < MmPhysicalMemoryBlock->NumberOfRuns) {
  312. start += 1;
  313. Page = MmPhysicalMemoryBlock->Run[start].BasePage;
  314. count = MmPhysicalMemoryBlock->Run[start].PageCount;
  315. if (StartPage + NumberOfPages == Page) {
  316. MmPhysicalMemoryBlock->Run[start - 1].PageCount +=
  317. count;
  318. MmPhysicalMemoryBlock->NumberOfRuns -= 1;
  319. //
  320. // Copy any remaining entries.
  321. //
  322. if (start != MmPhysicalMemoryBlock->NumberOfRuns) {
  323. RtlMoveMemory (&MmPhysicalMemoryBlock->Run[start],
  324. &MmPhysicalMemoryBlock->Run[start + 1],
  325. (MmPhysicalMemoryBlock->NumberOfRuns - start) * sizeof (PHYSICAL_MEMORY_RUN));
  326. }
  327. }
  328. }
  329. Updated = TRUE;
  330. break;
  331. }
  332. if (StartPage + NumberOfPages == Page) {
  333. MmPhysicalMemoryBlock->Run[start].BasePage = StartPage;
  334. MmPhysicalMemoryBlock->Run[start].PageCount += NumberOfPages;
  335. OldPhysicalMemoryBlock = NewPhysicalMemoryBlock;
  336. MmPhysicalMemoryBlock->NumberOfPages += NumberOfPages;
  337. Updated = TRUE;
  338. break;
  339. }
  340. if (StartPage + NumberOfPages <= Page) {
  341. if (start + 1 < MmPhysicalMemoryBlock->NumberOfRuns) {
  342. if (StartPage + NumberOfPages <= MmPhysicalMemoryBlock->Run[start + 1].BasePage) {
  343. //
  344. // Don't insert here - the new entry really belongs
  345. // (at least) one entry further down.
  346. //
  347. continue;
  348. }
  349. }
  350. NewRun->BasePage = StartPage;
  351. NewRun->PageCount = NumberOfPages;
  352. NewRun += 1;
  353. Inserted = TRUE;
  354. Updated = TRUE;
  355. }
  356. }
  357. *NewRun = MmPhysicalMemoryBlock->Run[start];
  358. NewRun += 1;
  359. start += 1;
  360. } while (start != MmPhysicalMemoryBlock->NumberOfRuns);
  361. //
  362. // If the memory block has not been updated, then the new entry must
  363. // be added at the very end.
  364. //
  365. if (Updated == FALSE) {
  366. ASSERT (Inserted == FALSE);
  367. NewRun->BasePage = StartPage;
  368. NewRun->PageCount = NumberOfPages;
  369. Inserted = TRUE;
  370. }
  371. //
  372. // Repoint the MmPhysicalMemoryBlock at the new chunk, free the old after
  373. // releasing the PFN lock.
  374. //
  375. if (Inserted == TRUE) {
  376. OldPhysicalMemoryBlock = MmPhysicalMemoryBlock;
  377. MmPhysicalMemoryBlock = NewPhysicalMemoryBlock;
  378. }
  379. //
  380. // Note that the page directory (page parent entries on Win64) must be
  381. // filled in at system boot so that already-created processes do not fault
  382. // when referencing the new PFNs.
  383. //
  384. //
  385. // Walk through the memory descriptors and add pages to the
  386. // free list in the PFN database.
  387. //
  388. PageFrameIndex = StartPage;
  389. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  390. if (EndPage - 1 > MmHighestPhysicalPage) {
  391. MmHighestPhysicalPage = EndPage - 1;
  392. }
  393. while (PageFrameIndex < EndPage) {
  394. ASSERT (Pfn1->u2.ShareCount == 0);
  395. ASSERT (Pfn1->u3.e2.ShortFlags == 0);
  396. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  397. ASSERT64 (Pfn1->UsedPageTableEntries == 0);
  398. ASSERT (Pfn1->OriginalPte.u.Long == ZeroKernelPte.u.Long);
  399. ASSERT (Pfn1->u4.PteFrame == 0);
  400. ASSERT ((Pfn1->PteAddress == PFN_REMOVED) ||
  401. (Pfn1->PteAddress == (PMMPTE)(UINT_PTR)0));
  402. //
  403. // Initialize the color for NUMA purposes.
  404. //
  405. MiDetermineNode (PageFrameIndex, Pfn1);
  406. //
  407. // Set the PTE address to the physical page for
  408. // virtual address alignment checking.
  409. //
  410. Pfn1->PteAddress = (PMMPTE)(PageFrameIndex << PTE_SHIFT);
  411. MiInsertPageInFreeList (PageFrameIndex);
  412. PageFrameIndex += 1;
  413. Pfn1 += 1;
  414. }
  415. MmNumberOfPhysicalPages += (PFN_COUNT)NumberOfPages;
  416. //
  417. // Only non-compression ranges get to contribute to ResidentAvailable as
  418. // adding compression ranges to this could crash the system.
  419. //
  420. // For the same reason, compression range additions also need to subtract
  421. // from AvailablePages the amount the above MiInsertPageInFreeList added.
  422. //
  423. #if defined (_MI_COMPRESSION)
  424. if (Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) {
  425. MmAvailablePages -= (PFN_COUNT) NumberOfPages;
  426. MiNumberOfCompressionPages += NumberOfPages;
  427. }
  428. else {
  429. MmResidentAvailablePages += NumberOfPages;
  430. //
  431. // Since real (noncompression-generated) physical memory was added,
  432. // rearm the interrupt to occur at a higher threshold.
  433. //
  434. MiArmCompressionInterrupt ();
  435. }
  436. #else
  437. MmResidentAvailablePages += NumberOfPages;
  438. #endif
  439. UNLOCK_PFN (OldIrql);
  440. InterlockedExchangeAdd ((PLONG)&SharedUserData->NumberOfPhysicalPages,
  441. (LONG) NumberOfPages);
  442. //
  443. // Carefully increase all commit limits to reflect the additional memory -
  444. // notice the current usage must be done first so no one else cuts the
  445. // line.
  446. //
  447. InterlockedExchangeAddSizeT (&MmTotalCommittedPages, PagesNeeded);
  448. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, NumberOfPages);
  449. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, NumberOfPages);
  450. MmUnlockPagableImageSection(ExPageLockHandle);
  451. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  452. ExFreePool (OldPhysicalMemoryBlock);
  453. //
  454. // Indicate number of bytes actually added to our caller.
  455. //
  456. NumberOfBytes->QuadPart = (ULONGLONG)NumberOfPages * PAGE_SIZE;
  457. return STATUS_SUCCESS;
  458. }
  459. NTSTATUS
  460. MiRemovePhysicalMemory (
  461. IN PPHYSICAL_ADDRESS StartAddress,
  462. IN OUT PLARGE_INTEGER NumberOfBytes,
  463. IN LOGICAL PermanentRemoval,
  464. IN ULONG Flags
  465. )
  466. /*++
  467. Routine Description:
  468. This routine attempts to remove the specified physical address range
  469. from the system.
  470. Arguments:
  471. StartAddress - Supplies the starting physical address.
  472. NumberOfBytes - Supplies a pointer to the number of bytes being removed.
  473. PermanentRemoval - Supplies TRUE if the memory is being permanently
  474. (ie: physically) removed. FALSE if not (ie: just a
  475. bad page detected via ECC which is being marked
  476. "don't-use".
  477. Return Value:
  478. NTSTATUS.
  479. Environment:
  480. Kernel mode. PASSIVE level. No locks held.
  481. --*/
  482. {
  483. ULONG i;
  484. ULONG Additional;
  485. PFN_NUMBER Page;
  486. PFN_NUMBER LastPage;
  487. PFN_NUMBER OriginalLastPage;
  488. PFN_NUMBER start;
  489. PFN_NUMBER PagesReleased;
  490. PMMPFN Pfn1;
  491. PMMPFN StartPfn;
  492. PMMPFN EndPfn;
  493. KIRQL OldIrql;
  494. PFN_NUMBER StartPage;
  495. PFN_NUMBER EndPage;
  496. PFN_COUNT NumberOfPages;
  497. PFN_COUNT ParityPages;
  498. SPFN_NUMBER MaxPages;
  499. PFN_NUMBER PageFrameIndex;
  500. PFN_NUMBER RemovedPages;
  501. PFN_NUMBER RemovedPagesThisPass;
  502. LOGICAL Inserted;
  503. NTSTATUS Status;
  504. PMMPTE PointerPte;
  505. PMMPTE EndPte;
  506. PVOID VirtualAddress;
  507. PPHYSICAL_MEMORY_DESCRIPTOR OldPhysicalMemoryBlock;
  508. PPHYSICAL_MEMORY_DESCRIPTOR NewPhysicalMemoryBlock;
  509. PPHYSICAL_MEMORY_RUN NewRun;
  510. LOGICAL PfnDatabaseIsPhysical;
  511. PFN_NUMBER HighestPossiblePhysicalPage;
  512. PFN_COUNT FluidPages;
  513. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  514. ASSERT (BYTE_OFFSET(NumberOfBytes->LowPart) == 0);
  515. ASSERT (BYTE_OFFSET(StartAddress->LowPart) == 0);
  516. if (MI_IS_PHYSICAL_ADDRESS(MmPfnDatabase)) {
  517. if (PermanentRemoval == TRUE) {
  518. //
  519. // The system must be configured for dynamic memory addition. This
  520. // is not strictly required to remove the memory, but it's better
  521. // to check for it now under the assumption that the administrator
  522. // is probably going to want to add this range of memory back in -
  523. // better to give the error now and refuse the removal than to
  524. // refuse the addition later.
  525. //
  526. if (MmDynamicPfn == 0) {
  527. return STATUS_NOT_SUPPORTED;
  528. }
  529. }
  530. PfnDatabaseIsPhysical = TRUE;
  531. }
  532. else {
  533. PfnDatabaseIsPhysical = FALSE;
  534. }
  535. if (PermanentRemoval == TRUE) {
  536. HighestPossiblePhysicalPage = MmHighestPossiblePhysicalPage;
  537. FluidPages = 100;
  538. }
  539. else {
  540. HighestPossiblePhysicalPage = MmHighestPhysicalPage;
  541. FluidPages = 0;
  542. }
  543. StartPage = (PFN_NUMBER)(StartAddress->QuadPart >> PAGE_SHIFT);
  544. NumberOfPages = (PFN_COUNT)(NumberOfBytes->QuadPart >> PAGE_SHIFT);
  545. EndPage = StartPage + NumberOfPages;
  546. if (EndPage - 1 > HighestPossiblePhysicalPage) {
  547. //
  548. // Truncate the request into something that can be mapped by the PFN
  549. // database.
  550. //
  551. EndPage = MmHighestPossiblePhysicalPage + 1;
  552. NumberOfPages = (PFN_COUNT)(EndPage - StartPage);
  553. }
  554. //
  555. // The range cannot wrap.
  556. //
  557. if (StartPage >= EndPage) {
  558. return STATUS_INVALID_PARAMETER_1;
  559. }
  560. #if !defined (_MI_COMPRESSION)
  561. if (Flags != 0) {
  562. return STATUS_INVALID_PARAMETER_4;
  563. }
  564. #endif
  565. StartPfn = MI_PFN_ELEMENT (StartPage);
  566. EndPfn = MI_PFN_ELEMENT (EndPage);
  567. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  568. #if DBG
  569. MiDynmemData[0] += 1;
  570. #endif
  571. //
  572. // Attempt to decrease all commit limits to reflect the removed memory.
  573. //
  574. if (MiChargeTemporaryCommitmentForReduction (NumberOfPages + FluidPages) == FALSE) {
  575. #if DBG
  576. MiDynmemData[1] += 1;
  577. #endif
  578. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  579. return STATUS_INSUFFICIENT_RESOURCES;
  580. }
  581. //
  582. // Reduce the systemwide commit limit - note this is carefully done
  583. // *PRIOR* to returning this commitment so no one else (including a DPC
  584. // in this very thread) can consume past the limit.
  585. //
  586. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, 0 - NumberOfPages);
  587. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, 0 - NumberOfPages);
  588. //
  589. // Now that the systemwide commit limit has been lowered, the amount
  590. // we have removed can be safely returned.
  591. //
  592. MiReturnCommitment (NumberOfPages + FluidPages);
  593. MmLockPagableSectionByHandle (ExPageLockHandle);
  594. //
  595. // Check for outstanding promises that cannot be broken.
  596. //
  597. LOCK_PFN (OldIrql);
  598. if (PermanentRemoval == FALSE) {
  599. //
  600. // If it's just the removal of ECC-marked bad pages, then don't
  601. // allow the caller to remove any pages that have already been
  602. // ECC-removed. This is to prevent recursive erroneous charges.
  603. //
  604. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  605. if (Pfn1->u3.e1.ParityError == 1) {
  606. UNLOCK_PFN (OldIrql);
  607. Status = STATUS_INVALID_PARAMETER_2;
  608. goto giveup2;
  609. }
  610. }
  611. }
  612. MaxPages = MI_NONPAGABLE_MEMORY_AVAILABLE() - FluidPages;
  613. if ((SPFN_NUMBER)NumberOfPages > MaxPages) {
  614. #if DBG
  615. MiDynmemData[2] += 1;
  616. #endif
  617. UNLOCK_PFN (OldIrql);
  618. Status = STATUS_INSUFFICIENT_RESOURCES;
  619. goto giveup2;
  620. }
  621. //
  622. // The range must be contained in a single entry. It is
  623. // permissible for it to be part of a single entry, but it
  624. // must not cross multiple entries.
  625. //
  626. Additional = (ULONG)-2;
  627. start = 0;
  628. do {
  629. Page = MmPhysicalMemoryBlock->Run[start].BasePage;
  630. LastPage = Page + MmPhysicalMemoryBlock->Run[start].PageCount;
  631. if ((StartPage >= Page) && (EndPage <= LastPage)) {
  632. if ((StartPage == Page) && (EndPage == LastPage)) {
  633. Additional = (ULONG)-1;
  634. }
  635. else if ((StartPage == Page) || (EndPage == LastPage)) {
  636. Additional = 0;
  637. }
  638. else {
  639. Additional = 1;
  640. }
  641. break;
  642. }
  643. start += 1;
  644. } while (start != MmPhysicalMemoryBlock->NumberOfRuns);
  645. if (Additional == (ULONG)-2) {
  646. #if DBG
  647. MiDynmemData[3] += 1;
  648. #endif
  649. UNLOCK_PFN (OldIrql);
  650. Status = STATUS_CONFLICTING_ADDRESSES;
  651. goto giveup2;
  652. }
  653. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  654. Pfn1->u3.e1.RemovalRequested = 1;
  655. }
  656. if (PermanentRemoval == TRUE) {
  657. MmNumberOfPhysicalPages -= NumberOfPages;
  658. InterlockedExchangeAdd ((PLONG)&SharedUserData->NumberOfPhysicalPages,
  659. 0 - NumberOfPages);
  660. }
  661. #if defined (_MI_COMPRESSION)
  662. //
  663. // Only removal of non-compression ranges decrement ResidentAvailable as
  664. // only those ranges actually incremented this when they were added.
  665. //
  666. if ((Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) == 0) {
  667. MmResidentAvailablePages -= NumberOfPages;
  668. //
  669. // Since real (noncompression-generated) physical memory is being
  670. // removed, rearm the interrupt to occur at a lower threshold.
  671. //
  672. if (PermanentRemoval == TRUE) {
  673. MiArmCompressionInterrupt ();
  674. }
  675. }
  676. #else
  677. MmResidentAvailablePages -= NumberOfPages;
  678. #endif
  679. //
  680. // The free and zero lists must be pruned now before releasing the PFN
  681. // lock otherwise if another thread allocates the page from these lists,
  682. // the allocation will clear the RemovalRequested flag forever.
  683. //
  684. RemovedPages = MiRemovePhysicalPages (StartPage, EndPage);
  685. #if defined (_MI_COMPRESSION)
  686. //
  687. // Compression range removals add back into AvailablePages the same
  688. // amount that MiUnlinkPageFromList removes (as the original addition
  689. // of these ranges never bumps this counter).
  690. //
  691. if (Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) {
  692. MmAvailablePages += (PFN_COUNT) RemovedPages;
  693. MiNumberOfCompressionPages -= RemovedPages;
  694. }
  695. #endif
  696. if (RemovedPages != NumberOfPages) {
  697. #if DBG
  698. retry:
  699. #endif
  700. Pfn1 = StartPfn;
  701. InterlockedIncrement (&MiDelayPageFaults);
  702. for (i = 0; i < 5; i += 1) {
  703. UNLOCK_PFN (OldIrql);
  704. //
  705. // Attempt to move pages to the standby list. Note that only the
  706. // pages with RemovalRequested set are moved.
  707. //
  708. MiTrimRemovalPagesOnly = TRUE;
  709. MmEmptyAllWorkingSets ();
  710. MiTrimRemovalPagesOnly = FALSE;
  711. MiFlushAllPages ();
  712. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmHalfSecond);
  713. LOCK_PFN (OldIrql);
  714. RemovedPagesThisPass = MiRemovePhysicalPages (StartPage, EndPage);
  715. RemovedPages += RemovedPagesThisPass;
  716. #if defined (_MI_COMPRESSION)
  717. //
  718. // Compression range removals add back into AvailablePages the same
  719. // amount that MiUnlinkPageFromList removes (as the original
  720. // addition of these ranges never bumps this counter).
  721. //
  722. if (Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) {
  723. MmAvailablePages += (PFN_COUNT) RemovedPagesThisPass;
  724. MiNumberOfCompressionPages -= RemovedPagesThisPass;
  725. }
  726. #endif
  727. if (RemovedPages == NumberOfPages) {
  728. break;
  729. }
  730. //
  731. // RemovedPages doesn't include pages that were freed directly
  732. // to the bad page list via MiDecrementReferenceCount or by
  733. // ECC marking. So use the above check purely as an optimization -
  734. // and walk here before ever giving up.
  735. //
  736. for ( ; Pfn1 < EndPfn; Pfn1 += 1) {
  737. if (Pfn1->u3.e1.PageLocation != BadPageList) {
  738. break;
  739. }
  740. }
  741. if (Pfn1 == EndPfn) {
  742. RemovedPages = NumberOfPages;
  743. break;
  744. }
  745. }
  746. InterlockedDecrement (&MiDelayPageFaults);
  747. }
  748. if (RemovedPages != NumberOfPages) {
  749. #if DBG
  750. MiDynmemData[4] += 1;
  751. if (MiShowStuckPages != 0) {
  752. RemovedPages = 0;
  753. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  754. if (Pfn1->u3.e1.PageLocation != BadPageList) {
  755. RemovedPages += 1;
  756. }
  757. }
  758. ASSERT (RemovedPages != 0);
  759. DbgPrint("MiRemovePhysicalMemory : could not get %d of %d pages\n",
  760. RemovedPages, NumberOfPages);
  761. if (MiShowStuckPages & 0x2) {
  762. ULONG PfnsPrinted;
  763. ULONG EnoughShown;
  764. PMMPFN FirstPfn;
  765. PFN_COUNT PfnCount;
  766. PfnCount = 0;
  767. PfnsPrinted = 0;
  768. EnoughShown = 100;
  769. //
  770. // Initializing FirstPfn is not needed for correctness
  771. // but without it the compiler cannot compile this code
  772. // W4 to check for use of uninitialized variables.
  773. //
  774. FirstPfn = NULL;
  775. if (MiShowStuckPages & 0x4) {
  776. EnoughShown = (ULONG)-1;
  777. }
  778. DbgPrint("Stuck PFN list: ");
  779. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  780. if (Pfn1->u3.e1.PageLocation != BadPageList) {
  781. if (PfnCount == 0) {
  782. FirstPfn = Pfn1;
  783. }
  784. PfnCount += 1;
  785. }
  786. else {
  787. if (PfnCount != 0) {
  788. DbgPrint("%x -> %x ; ", FirstPfn - MmPfnDatabase,
  789. (FirstPfn - MmPfnDatabase) + PfnCount - 1);
  790. PfnsPrinted += 1;
  791. if (PfnsPrinted == EnoughShown) {
  792. break;
  793. }
  794. PfnCount = 0;
  795. }
  796. }
  797. }
  798. if (PfnCount != 0) {
  799. DbgPrint("%x -> %x ; ", FirstPfn - MmPfnDatabase,
  800. (FirstPfn - MmPfnDatabase) + PfnCount - 1);
  801. }
  802. DbgPrint("\n");
  803. }
  804. if (MiShowStuckPages & 0x8) {
  805. DbgBreakPoint ();
  806. }
  807. if (MiShowStuckPages & 0x10) {
  808. goto retry;
  809. }
  810. }
  811. #endif
  812. UNLOCK_PFN (OldIrql);
  813. Status = STATUS_NO_MEMORY;
  814. goto giveup;
  815. }
  816. #if DBG
  817. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  818. ASSERT (Pfn1->u3.e1.PageLocation == BadPageList);
  819. }
  820. #endif
  821. //
  822. // All the pages in the range have been removed.
  823. //
  824. if (PermanentRemoval == FALSE) {
  825. //
  826. // If it's just the removal of ECC-marked bad pages, then no
  827. // adjustment to the physical memory block ranges or PFN database
  828. // trimming is needed. Exit now.
  829. //
  830. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  831. ASSERT (Pfn1->u3.e1.ParityError == 0);
  832. Pfn1->u3.e1.ParityError = 1;
  833. }
  834. UNLOCK_PFN (OldIrql);
  835. MmUnlockPagableImageSection(ExPageLockHandle);
  836. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  837. NumberOfBytes->QuadPart = (ULONGLONG)NumberOfPages * PAGE_SIZE;
  838. return STATUS_SUCCESS;
  839. }
  840. //
  841. // Update the physical memory blocks and other associated housekeeping.
  842. //
  843. if (Additional == 0) {
  844. //
  845. // The range can be split off from an end of an existing chunk so no
  846. // pool growth or shrinkage is required.
  847. //
  848. NewPhysicalMemoryBlock = MmPhysicalMemoryBlock;
  849. OldPhysicalMemoryBlock = NULL;
  850. }
  851. else {
  852. //
  853. // The range cannot be split off from an end of an existing chunk so
  854. // pool growth or shrinkage is required.
  855. //
  856. UNLOCK_PFN (OldIrql);
  857. i = (sizeof(PHYSICAL_MEMORY_DESCRIPTOR) +
  858. (sizeof(PHYSICAL_MEMORY_RUN) * (MmPhysicalMemoryBlock->NumberOfRuns + Additional)));
  859. NewPhysicalMemoryBlock = ExAllocatePoolWithTag (NonPagedPool,
  860. i,
  861. ' mM');
  862. if (NewPhysicalMemoryBlock == NULL) {
  863. Status = STATUS_INSUFFICIENT_RESOURCES;
  864. #if DBG
  865. MiDynmemData[5] += 1;
  866. #endif
  867. goto giveup;
  868. }
  869. OldPhysicalMemoryBlock = MmPhysicalMemoryBlock;
  870. RtlZeroMemory (NewPhysicalMemoryBlock, i);
  871. LOCK_PFN (OldIrql);
  872. }
  873. //
  874. // Remove or split the requested range from the existing memory block.
  875. //
  876. NewPhysicalMemoryBlock->NumberOfRuns = MmPhysicalMemoryBlock->NumberOfRuns + Additional;
  877. NewPhysicalMemoryBlock->NumberOfPages = MmPhysicalMemoryBlock->NumberOfPages - NumberOfPages;
  878. NewRun = &NewPhysicalMemoryBlock->Run[0];
  879. start = 0;
  880. Inserted = FALSE;
  881. do {
  882. Page = MmPhysicalMemoryBlock->Run[start].BasePage;
  883. LastPage = Page + MmPhysicalMemoryBlock->Run[start].PageCount;
  884. if (Inserted == FALSE) {
  885. if ((StartPage >= Page) && (EndPage <= LastPage)) {
  886. if ((StartPage == Page) && (EndPage == LastPage)) {
  887. ASSERT (Additional == -1);
  888. start += 1;
  889. continue;
  890. }
  891. else if ((StartPage == Page) || (EndPage == LastPage)) {
  892. ASSERT (Additional == 0);
  893. if (StartPage == Page) {
  894. MmPhysicalMemoryBlock->Run[start].BasePage += NumberOfPages;
  895. }
  896. MmPhysicalMemoryBlock->Run[start].PageCount -= NumberOfPages;
  897. }
  898. else {
  899. ASSERT (Additional == 1);
  900. OriginalLastPage = LastPage;
  901. MmPhysicalMemoryBlock->Run[start].PageCount =
  902. StartPage - MmPhysicalMemoryBlock->Run[start].BasePage;
  903. *NewRun = MmPhysicalMemoryBlock->Run[start];
  904. NewRun += 1;
  905. NewRun->BasePage = EndPage;
  906. NewRun->PageCount = OriginalLastPage - EndPage;
  907. NewRun += 1;
  908. start += 1;
  909. continue;
  910. }
  911. Inserted = TRUE;
  912. }
  913. }
  914. *NewRun = MmPhysicalMemoryBlock->Run[start];
  915. NewRun += 1;
  916. start += 1;
  917. } while (start != MmPhysicalMemoryBlock->NumberOfRuns);
  918. //
  919. // Repoint the MmPhysicalMemoryBlock at the new chunk.
  920. // Free the old block after releasing the PFN lock.
  921. //
  922. MmPhysicalMemoryBlock = NewPhysicalMemoryBlock;
  923. if (EndPage - 1 == MmHighestPhysicalPage) {
  924. MmHighestPhysicalPage = StartPage - 1;
  925. }
  926. //
  927. // Throw away all the removed pages that are currently enqueued.
  928. //
  929. ParityPages = 0;
  930. for (Pfn1 = StartPfn; Pfn1 < EndPfn; Pfn1 += 1) {
  931. ASSERT (Pfn1->u3.e1.PageLocation == BadPageList);
  932. ASSERT (Pfn1->u3.e1.RemovalRequested == 1);
  933. //
  934. // Some pages may have already been ECC-removed. For these pages,
  935. // the commit limits and resident available pages have already been
  936. // adjusted - tally them here so we can undo the extraneous charge
  937. // just applied.
  938. //
  939. if (Pfn1->u3.e1.ParityError == 1) {
  940. ParityPages += 1;
  941. }
  942. MiUnlinkPageFromList (Pfn1);
  943. ASSERT (Pfn1->u1.Flink == 0);
  944. ASSERT (Pfn1->u2.Blink == 0);
  945. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  946. ASSERT64 (Pfn1->UsedPageTableEntries == 0);
  947. Pfn1->PteAddress = PFN_REMOVED;
  948. //
  949. // Note this clears ParityError among other flags...
  950. //
  951. Pfn1->u3.e2.ShortFlags = 0;
  952. Pfn1->OriginalPte.u.Long = ZeroKernelPte.u.Long;
  953. Pfn1->u4.PteFrame = 0;
  954. }
  955. //
  956. // Now that the removed pages have been discarded, eliminate the PFN
  957. // entries that mapped them. Straddling entries left over from an
  958. // adjacent earlier removal are not collapsed at this point.
  959. //
  960. //
  961. PagesReleased = 0;
  962. if (PfnDatabaseIsPhysical == FALSE) {
  963. VirtualAddress = (PVOID)ROUND_TO_PAGES(MI_PFN_ELEMENT(StartPage));
  964. PointerPte = MiGetPteAddress (VirtualAddress);
  965. EndPte = MiGetPteAddress (PAGE_ALIGN(MI_PFN_ELEMENT(EndPage)));
  966. while (PointerPte < EndPte) {
  967. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  968. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  969. ASSERT (Pfn1->u2.ShareCount == 1);
  970. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  971. Pfn1->u2.ShareCount = 0;
  972. MI_SET_PFN_DELETED (Pfn1);
  973. #if DBG
  974. Pfn1->u3.e1.PageLocation = StandbyPageList;
  975. #endif //DBG
  976. MiDecrementReferenceCount (PageFrameIndex);
  977. KeFlushSingleTb (VirtualAddress,
  978. TRUE,
  979. TRUE,
  980. (PHARDWARE_PTE)PointerPte,
  981. ZeroKernelPte.u.Flush);
  982. PagesReleased += 1;
  983. PointerPte += 1;
  984. VirtualAddress = (PVOID)((PCHAR)VirtualAddress + PAGE_SIZE);
  985. }
  986. MmResidentAvailablePages += PagesReleased;
  987. }
  988. #if DBG
  989. MiDynmemData[6] += 1;
  990. #endif
  991. //
  992. // Give back anything that has been double-charged.
  993. //
  994. if (ParityPages != 0) {
  995. MmResidentAvailablePages += ParityPages;
  996. }
  997. UNLOCK_PFN (OldIrql);
  998. //
  999. // Give back anything that has been double-charged.
  1000. //
  1001. if (ParityPages != 0) {
  1002. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, ParityPages);
  1003. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, ParityPages);
  1004. }
  1005. if (PagesReleased != 0) {
  1006. MiReturnCommitment (PagesReleased);
  1007. }
  1008. MmUnlockPagableImageSection(ExPageLockHandle);
  1009. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1010. if (OldPhysicalMemoryBlock != NULL) {
  1011. ExFreePool (OldPhysicalMemoryBlock);
  1012. }
  1013. NumberOfBytes->QuadPart = (ULONGLONG)NumberOfPages * PAGE_SIZE;
  1014. return STATUS_SUCCESS;
  1015. giveup:
  1016. //
  1017. // All the pages in the range were not obtained. Back everything out.
  1018. //
  1019. PageFrameIndex = StartPage;
  1020. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1021. LOCK_PFN (OldIrql);
  1022. while (PageFrameIndex < EndPage) {
  1023. ASSERT (Pfn1->u3.e1.RemovalRequested == 1);
  1024. Pfn1->u3.e1.RemovalRequested = 0;
  1025. if (Pfn1->u3.e1.PageLocation == BadPageList) {
  1026. MiUnlinkPageFromList (Pfn1);
  1027. MiInsertPageInFreeList (PageFrameIndex);
  1028. }
  1029. Pfn1 += 1;
  1030. PageFrameIndex += 1;
  1031. }
  1032. #if defined (_MI_COMPRESSION)
  1033. //
  1034. // Only removal of non-compression ranges decrement ResidentAvailable as
  1035. // only those ranges actually incremented this when they were added.
  1036. //
  1037. if ((Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) == 0) {
  1038. MmResidentAvailablePages += NumberOfPages;
  1039. }
  1040. else {
  1041. //
  1042. // Compression range removals add back into AvailablePages the same
  1043. // amount that MiUnlinkPageFromList removes (as the original
  1044. // addition of these ranges never bumps this counter).
  1045. //
  1046. MmAvailablePages -= (PFN_COUNT) RemovedPages;
  1047. MiNumberOfCompressionPages += RemovedPages;
  1048. }
  1049. #else
  1050. MmResidentAvailablePages += NumberOfPages;
  1051. #endif
  1052. if (PermanentRemoval == TRUE) {
  1053. MmNumberOfPhysicalPages += NumberOfPages;
  1054. InterlockedExchangeAdd ((PLONG)&SharedUserData->NumberOfPhysicalPages,
  1055. NumberOfPages);
  1056. #if defined (_MI_COMPRESSION)
  1057. //
  1058. // Rearm the interrupt to occur at the original threshold.
  1059. //
  1060. if ((Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) == 0) {
  1061. MiArmCompressionInterrupt ();
  1062. }
  1063. #endif
  1064. }
  1065. UNLOCK_PFN (OldIrql);
  1066. giveup2:
  1067. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, NumberOfPages);
  1068. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, NumberOfPages);
  1069. MmUnlockPagableImageSection(ExPageLockHandle);
  1070. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1071. return Status;
  1072. }
  1073. NTSTATUS
  1074. MmRemovePhysicalMemory (
  1075. IN PPHYSICAL_ADDRESS StartAddress,
  1076. IN OUT PLARGE_INTEGER NumberOfBytes
  1077. )
  1078. /*++
  1079. Routine Description:
  1080. A wrapper for MmRemovePhysicalMemoryEx.
  1081. Arguments:
  1082. StartAddress - Supplies the starting physical address.
  1083. NumberOfBytes - Supplies a pointer to the number of bytes being removed.
  1084. Return Value:
  1085. NTSTATUS.
  1086. Environment:
  1087. Kernel mode. PASSIVE level. No locks held.
  1088. --*/
  1089. {
  1090. return MmRemovePhysicalMemoryEx (StartAddress, NumberOfBytes, 0);
  1091. }
  1092. NTSTATUS
  1093. MmRemovePhysicalMemoryEx (
  1094. IN PPHYSICAL_ADDRESS StartAddress,
  1095. IN OUT PLARGE_INTEGER NumberOfBytes,
  1096. IN ULONG Flags
  1097. )
  1098. /*++
  1099. Routine Description:
  1100. This routine attempts to remove the specified physical address range
  1101. from the system.
  1102. Arguments:
  1103. StartAddress - Supplies the starting physical address.
  1104. NumberOfBytes - Supplies a pointer to the number of bytes being removed.
  1105. Flags - Supplies relevant flags describing the memory range.
  1106. Return Value:
  1107. NTSTATUS.
  1108. Environment:
  1109. Kernel mode. PASSIVE level. No locks held.
  1110. --*/
  1111. {
  1112. NTSTATUS Status;
  1113. #if defined (_X86_)
  1114. BOOLEAN CachesFlushed;
  1115. #endif
  1116. #if defined(_IA64_)
  1117. PVOID VirtualAddress;
  1118. PVOID SingleVirtualAddress;
  1119. SIZE_T SizeInBytes;
  1120. SIZE_T MapSizeInBytes;
  1121. PFN_COUNT NumberOfPages;
  1122. PFN_COUNT i;
  1123. PFN_NUMBER StartPage;
  1124. #endif
  1125. PAGED_CODE();
  1126. #if defined (_MI_COMPRESSION_SUPPORTED_)
  1127. if (Flags & MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION) {
  1128. return STATUS_NOT_SUPPORTED;
  1129. }
  1130. #else
  1131. if (Flags != 0) {
  1132. return STATUS_INVALID_PARAMETER_3;
  1133. }
  1134. #endif
  1135. #if defined (_X86_)
  1136. //
  1137. // Issue a cache invalidation here just as a test to make sure the
  1138. // machine can support it. If not, then don't bother trying to remove
  1139. // any memory.
  1140. //
  1141. CachesFlushed = KeInvalidateAllCaches (TRUE);
  1142. if (CachesFlushed == FALSE) {
  1143. return STATUS_NOT_SUPPORTED;
  1144. }
  1145. #endif
  1146. #if defined(_IA64_)
  1147. //
  1148. // Pick up at least a single PTE mapping now as we do not want to fail this
  1149. // call if no PTEs are available after a successful remove. Resorting to
  1150. // actually using this PTE should be a very rare case indeed.
  1151. //
  1152. SingleVirtualAddress = (PMMPTE)MiMapSinglePage (NULL,
  1153. 0,
  1154. MmCached,
  1155. HighPagePriority);
  1156. if (SingleVirtualAddress == NULL) {
  1157. return STATUS_INSUFFICIENT_RESOURCES;
  1158. }
  1159. #endif
  1160. Status = MiRemovePhysicalMemory (StartAddress, NumberOfBytes, TRUE, Flags);
  1161. if (NT_SUCCESS (Status)) {
  1162. #if defined (_X86_)
  1163. CachesFlushed = KeInvalidateAllCaches (TRUE);
  1164. ASSERT (CachesFlushed == TRUE);
  1165. #endif
  1166. #if defined(_IA64_)
  1167. SizeInBytes = (SIZE_T)NumberOfBytes->QuadPart;
  1168. //
  1169. // Flush the entire TB to remove any KSEG translations that may map the
  1170. // pages being removed. Otherwise hardware or software speculation
  1171. // can reference the memory speculatively which would crash the machine.
  1172. //
  1173. KeFlushEntireTb (TRUE, TRUE);
  1174. //
  1175. // Establish an uncached mapping to the pages being removed.
  1176. //
  1177. MapSizeInBytes = SizeInBytes;
  1178. //
  1179. // Initializing VirtualAddress is not needed for correctness
  1180. // but without it the compiler cannot compile this code
  1181. // W4 to check for use of uninitialized variables.
  1182. //
  1183. VirtualAddress = NULL;
  1184. while (MapSizeInBytes > PAGE_SIZE) {
  1185. VirtualAddress = MmMapIoSpace (*StartAddress,
  1186. MapSizeInBytes,
  1187. MmNonCached);
  1188. if (VirtualAddress != NULL) {
  1189. break;
  1190. }
  1191. MapSizeInBytes = MapSizeInBytes >> 1;
  1192. }
  1193. if (MapSizeInBytes <= PAGE_SIZE) {
  1194. StartPage = (PFN_NUMBER)(StartAddress->QuadPart >> PAGE_SHIFT);
  1195. NumberOfPages = (PFN_COUNT)(NumberOfBytes->QuadPart >> PAGE_SHIFT);
  1196. for (i = 0; i < NumberOfPages; i += 1) {
  1197. SingleVirtualAddress = (PMMPTE)MiMapSinglePage (SingleVirtualAddress,
  1198. StartPage,
  1199. MmCached,
  1200. HighPagePriority);
  1201. KeSweepCacheRangeWithDrain (TRUE,
  1202. SingleVirtualAddress,
  1203. PAGE_SIZE);
  1204. StartPage += 1;
  1205. }
  1206. }
  1207. else {
  1208. //
  1209. // Drain all pending transactions and prefetches and perform cache
  1210. // evictions. Only drain 4gb max at a time as this API takes a
  1211. // ULONG.
  1212. //
  1213. while (SizeInBytes > _4gb) {
  1214. KeSweepCacheRangeWithDrain (TRUE, VirtualAddress, _4gb - 1);
  1215. SizeInBytes -= (_4gb - 1);
  1216. }
  1217. KeSweepCacheRangeWithDrain (TRUE,
  1218. VirtualAddress,
  1219. (ULONG)SizeInBytes);
  1220. MmUnmapIoSpace (VirtualAddress, NumberOfBytes->QuadPart);
  1221. }
  1222. #endif
  1223. }
  1224. #if defined(_IA64_)
  1225. MiUnmapSinglePage (SingleVirtualAddress);
  1226. #endif
  1227. return Status;
  1228. }
  1229. NTSTATUS
  1230. MmMarkPhysicalMemoryAsBad (
  1231. IN PPHYSICAL_ADDRESS StartAddress,
  1232. IN OUT PLARGE_INTEGER NumberOfBytes
  1233. )
  1234. /*++
  1235. Routine Description:
  1236. This routine attempts to mark the specified physical address range
  1237. as bad so the system will not use it. This is generally done for pages
  1238. which contain ECC errors.
  1239. Note that this is different from removing pages permanently (ie: physically
  1240. removing the memory board) which should be done via the
  1241. MmRemovePhysicalMemory API.
  1242. The caller is responsible for maintaining a global table so that subsequent
  1243. boots can examine it and remove the ECC pages before loading the kernel.
  1244. Arguments:
  1245. StartAddress - Supplies the starting physical address.
  1246. NumberOfBytes - Supplies a pointer to the number of bytes being removed.
  1247. Return Value:
  1248. NTSTATUS.
  1249. Environment:
  1250. Kernel mode. PASSIVE level. No locks held.
  1251. --*/
  1252. {
  1253. PAGED_CODE();
  1254. return MiRemovePhysicalMemory (StartAddress, NumberOfBytes, FALSE, 0);
  1255. }
  1256. NTSTATUS
  1257. MmMarkPhysicalMemoryAsGood (
  1258. IN PPHYSICAL_ADDRESS StartAddress,
  1259. IN OUT PLARGE_INTEGER NumberOfBytes
  1260. )
  1261. /*++
  1262. Routine Description:
  1263. This routine attempts to mark the specified physical address range
  1264. as good so the system will use it. This is generally done for pages
  1265. which used to (but presumably no longer do) contain ECC errors.
  1266. Note that this is different from adding pages permanently (ie: physically
  1267. inserting a new memory board) which should be done via the
  1268. MmAddPhysicalMemory API.
  1269. The caller is responsible for removing these entries from a global table
  1270. so that subsequent boots will use the pages.
  1271. Arguments:
  1272. StartAddress - Supplies the starting physical address.
  1273. NumberOfBytes - Supplies a pointer to the number of bytes being removed.
  1274. Return Value:
  1275. NTSTATUS.
  1276. Environment:
  1277. Kernel mode. PASSIVE level. No locks held.
  1278. --*/
  1279. {
  1280. PMMPFN Pfn1;
  1281. KIRQL OldIrql;
  1282. PFN_NUMBER NumberOfPages;
  1283. PFN_NUMBER start;
  1284. PFN_NUMBER count;
  1285. PFN_NUMBER StartPage;
  1286. PFN_NUMBER EndPage;
  1287. PFN_NUMBER PageFrameIndex;
  1288. PFN_NUMBER Page;
  1289. PFN_NUMBER LastPage;
  1290. LOGICAL PfnDatabaseIsPhysical;
  1291. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  1292. ASSERT (BYTE_OFFSET(NumberOfBytes->LowPart) == 0);
  1293. ASSERT (BYTE_OFFSET(StartAddress->LowPart) == 0);
  1294. if (MI_IS_PHYSICAL_ADDRESS(MmPfnDatabase)) {
  1295. PfnDatabaseIsPhysical = TRUE;
  1296. }
  1297. else {
  1298. PfnDatabaseIsPhysical = FALSE;
  1299. }
  1300. StartPage = (PFN_NUMBER)(StartAddress->QuadPart >> PAGE_SHIFT);
  1301. NumberOfPages = (PFN_NUMBER)(NumberOfBytes->QuadPart >> PAGE_SHIFT);
  1302. EndPage = StartPage + NumberOfPages;
  1303. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  1304. if (EndPage - 1 > MmHighestPhysicalPage) {
  1305. //
  1306. // Truncate the request into something that can be mapped by the PFN
  1307. // database.
  1308. //
  1309. EndPage = MmHighestPhysicalPage + 1;
  1310. NumberOfPages = EndPage - StartPage;
  1311. }
  1312. //
  1313. // The range cannot wrap.
  1314. //
  1315. if (StartPage >= EndPage) {
  1316. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1317. return STATUS_INVALID_PARAMETER_1;
  1318. }
  1319. //
  1320. // The request must lie within an already present range.
  1321. //
  1322. start = 0;
  1323. MmLockPagableSectionByHandle (ExPageLockHandle);
  1324. LOCK_PFN (OldIrql);
  1325. do {
  1326. count = MmPhysicalMemoryBlock->Run[start].PageCount;
  1327. Page = MmPhysicalMemoryBlock->Run[start].BasePage;
  1328. if (count != 0) {
  1329. LastPage = Page + count;
  1330. if ((StartPage >= Page) && (EndPage <= LastPage)) {
  1331. break;
  1332. }
  1333. }
  1334. start += 1;
  1335. } while (start != MmPhysicalMemoryBlock->NumberOfRuns);
  1336. if (start == MmPhysicalMemoryBlock->NumberOfRuns) {
  1337. UNLOCK_PFN (OldIrql);
  1338. MmUnlockPagableImageSection(ExPageLockHandle);
  1339. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1340. return STATUS_CONFLICTING_ADDRESSES;
  1341. }
  1342. //
  1343. // Walk through the range and add only pages previously removed to the
  1344. // free list in the PFN database.
  1345. //
  1346. PageFrameIndex = StartPage;
  1347. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1348. NumberOfPages = 0;
  1349. while (PageFrameIndex < EndPage) {
  1350. if ((Pfn1->u3.e1.ParityError == 1) &&
  1351. (Pfn1->u3.e1.RemovalRequested == 1) &&
  1352. (Pfn1->u3.e1.PageLocation == BadPageList)) {
  1353. Pfn1->u3.e1.ParityError = 0;
  1354. Pfn1->u3.e1.RemovalRequested = 0;
  1355. MiUnlinkPageFromList (Pfn1);
  1356. MiInsertPageInFreeList (PageFrameIndex);
  1357. NumberOfPages += 1;
  1358. }
  1359. Pfn1 += 1;
  1360. PageFrameIndex += 1;
  1361. }
  1362. MmResidentAvailablePages += NumberOfPages;
  1363. UNLOCK_PFN (OldIrql);
  1364. //
  1365. // Increase all commit limits to reflect the additional memory.
  1366. //
  1367. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, NumberOfPages);
  1368. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, NumberOfPages);
  1369. MmUnlockPagableImageSection(ExPageLockHandle);
  1370. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1371. //
  1372. // Indicate number of bytes actually added to our caller.
  1373. //
  1374. NumberOfBytes->QuadPart = (ULONGLONG)NumberOfPages * PAGE_SIZE;
  1375. return STATUS_SUCCESS;
  1376. }
  1377. PPHYSICAL_MEMORY_RANGE
  1378. MmGetPhysicalMemoryRanges (
  1379. VOID
  1380. )
  1381. /*++
  1382. Routine Description:
  1383. This routine returns the virtual address of a nonpaged pool block which
  1384. contains the physical memory ranges in the system.
  1385. The returned block contains physical address and page count pairs.
  1386. The last entry contains zero for both.
  1387. The caller must understand that this block can change at any point before
  1388. or after this snapshot.
  1389. It is the caller's responsibility to free this block.
  1390. Arguments:
  1391. None.
  1392. Return Value:
  1393. NULL on failure.
  1394. Environment:
  1395. Kernel mode. PASSIVE level. No locks held.
  1396. --*/
  1397. {
  1398. ULONG i;
  1399. KIRQL OldIrql;
  1400. PPHYSICAL_MEMORY_RANGE p;
  1401. PPHYSICAL_MEMORY_RANGE PhysicalMemoryBlock;
  1402. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  1403. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  1404. i = sizeof(PHYSICAL_MEMORY_RANGE) * (MmPhysicalMemoryBlock->NumberOfRuns + 1);
  1405. PhysicalMemoryBlock = ExAllocatePoolWithTag (NonPagedPool,
  1406. i,
  1407. 'hPmM');
  1408. if (PhysicalMemoryBlock == NULL) {
  1409. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1410. return NULL;
  1411. }
  1412. p = PhysicalMemoryBlock;
  1413. MmLockPagableSectionByHandle (ExPageLockHandle);
  1414. LOCK_PFN (OldIrql);
  1415. ASSERT (i == (sizeof(PHYSICAL_MEMORY_RANGE) * (MmPhysicalMemoryBlock->NumberOfRuns + 1)));
  1416. for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
  1417. p->BaseAddress.QuadPart = (LONGLONG)MmPhysicalMemoryBlock->Run[i].BasePage * PAGE_SIZE;
  1418. p->NumberOfBytes.QuadPart = (LONGLONG)MmPhysicalMemoryBlock->Run[i].PageCount * PAGE_SIZE;
  1419. p += 1;
  1420. }
  1421. p->BaseAddress.QuadPart = 0;
  1422. p->NumberOfBytes.QuadPart = 0;
  1423. UNLOCK_PFN (OldIrql);
  1424. MmUnlockPagableImageSection(ExPageLockHandle);
  1425. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  1426. return PhysicalMemoryBlock;
  1427. }
  1428. PFN_COUNT
  1429. MiRemovePhysicalPages (
  1430. IN PFN_NUMBER StartPage,
  1431. IN PFN_NUMBER EndPage
  1432. )
  1433. /*++
  1434. Routine Description:
  1435. This routine searches the PFN database for free, zeroed or standby pages
  1436. that are marked for removal.
  1437. Arguments:
  1438. StartPage - Supplies the low physical frame number to remove.
  1439. EndPage - Supplies the last physical frame number to remove.
  1440. Return Value:
  1441. Returns the number of pages removed from the free, zeroed and standby lists.
  1442. Environment:
  1443. Kernel mode, PFN lock held. Since this routine is PAGELK, the caller is
  1444. responsible for locking it down and unlocking it on return.
  1445. --*/
  1446. {
  1447. PMMPFN Pfn1;
  1448. PMMPFN Pfn2;
  1449. PMMPFN PfnNextColored;
  1450. PMMPFN PfnNextFlink;
  1451. PMMPFN PfnLastColored;
  1452. PFN_NUMBER Page;
  1453. LOGICAL RemovePage;
  1454. ULONG Color;
  1455. PMMCOLOR_TABLES ColorHead;
  1456. PFN_NUMBER MovedPage;
  1457. MMLISTS MemoryList;
  1458. PFN_NUMBER PageNextColored;
  1459. PFN_NUMBER PageNextFlink;
  1460. PFN_NUMBER PageLastColored;
  1461. PFN_COUNT NumberOfPages;
  1462. PMMPFNLIST ListHead;
  1463. LOGICAL RescanNeeded;
  1464. MM_PFN_LOCK_ASSERT();
  1465. NumberOfPages = 0;
  1466. rescan:
  1467. //
  1468. // Grab all zeroed (and then free) pages first directly from the
  1469. // colored lists to avoid multiple walks down these singly linked lists.
  1470. // Handle transition pages last.
  1471. //
  1472. for (MemoryList = ZeroedPageList; MemoryList <= FreePageList; MemoryList += 1) {
  1473. ListHead = MmPageLocationList[MemoryList];
  1474. for (Color = 0; Color < MmSecondaryColors; Color += 1) {
  1475. ColorHead = &MmFreePagesByColor[MemoryList][Color];
  1476. MovedPage = (PFN_NUMBER) MM_EMPTY_LIST;
  1477. while (ColorHead->Flink != MM_EMPTY_LIST) {
  1478. Page = ColorHead->Flink;
  1479. Pfn1 = MI_PFN_ELEMENT(Page);
  1480. ASSERT ((MMLISTS)Pfn1->u3.e1.PageLocation == MemoryList);
  1481. //
  1482. // The Flink and Blink must be nonzero here for the page
  1483. // to be on the listhead. Only code that scans the
  1484. // MmPhysicalMemoryBlock has to check for the zero case.
  1485. //
  1486. ASSERT (Pfn1->u1.Flink != 0);
  1487. ASSERT (Pfn1->u2.Blink != 0);
  1488. //
  1489. // See if the page is desired by the caller.
  1490. //
  1491. // Systems utilizing memory compression may have more
  1492. // pages on the zero, free and standby lists than we
  1493. // want to give out. Explicitly check MmAvailablePages
  1494. // instead (and recheck whenever the PFN lock is
  1495. // released and reacquired).
  1496. //
  1497. if ((Pfn1->u3.e1.RemovalRequested == 1) &&
  1498. (MmAvailablePages != 0)) {
  1499. ASSERT (Pfn1->u3.e1.ReadInProgress == 0);
  1500. MiUnlinkFreeOrZeroedPage (Page);
  1501. MiInsertPageInList (&MmBadPageListHead, Page);
  1502. NumberOfPages += 1;
  1503. }
  1504. else {
  1505. //
  1506. // Unwanted so put the page on the end of list.
  1507. // If first time, save pfn.
  1508. //
  1509. if (MovedPage == MM_EMPTY_LIST) {
  1510. MovedPage = Page;
  1511. }
  1512. else if (Page == MovedPage) {
  1513. //
  1514. // No more pages available in this colored chain.
  1515. //
  1516. break;
  1517. }
  1518. //
  1519. // If the colored chain has more than one entry then
  1520. // put this page on the end.
  1521. //
  1522. PageNextColored = (PFN_NUMBER)Pfn1->OriginalPte.u.Long;
  1523. if (PageNextColored == MM_EMPTY_LIST) {
  1524. //
  1525. // No more pages available in this colored chain.
  1526. //
  1527. break;
  1528. }
  1529. ASSERT (Pfn1->u1.Flink != 0);
  1530. ASSERT (Pfn1->u1.Flink != MM_EMPTY_LIST);
  1531. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1532. PfnNextColored = MI_PFN_ELEMENT(PageNextColored);
  1533. ASSERT ((MMLISTS)PfnNextColored->u3.e1.PageLocation == MemoryList);
  1534. ASSERT (PfnNextColored->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1535. //
  1536. // Adjust the free page list so Page
  1537. // follows PageNextFlink.
  1538. //
  1539. PageNextFlink = Pfn1->u1.Flink;
  1540. PfnNextFlink = MI_PFN_ELEMENT(PageNextFlink);
  1541. ASSERT ((MMLISTS)PfnNextFlink->u3.e1.PageLocation == MemoryList);
  1542. ASSERT (PfnNextFlink->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1543. PfnLastColored = ColorHead->Blink;
  1544. ASSERT (PfnLastColored != (PMMPFN)MM_EMPTY_LIST);
  1545. ASSERT (PfnLastColored->OriginalPte.u.Long == MM_EMPTY_LIST);
  1546. ASSERT (PfnLastColored->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1547. ASSERT (PfnLastColored->u2.Blink != MM_EMPTY_LIST);
  1548. ASSERT ((MMLISTS)PfnLastColored->u3.e1.PageLocation == MemoryList);
  1549. PageLastColored = PfnLastColored - MmPfnDatabase;
  1550. if (ListHead->Flink == Page) {
  1551. ASSERT (Pfn1->u2.Blink == MM_EMPTY_LIST);
  1552. ASSERT (ListHead->Blink != Page);
  1553. ListHead->Flink = PageNextFlink;
  1554. PfnNextFlink->u2.Blink = MM_EMPTY_LIST;
  1555. }
  1556. else {
  1557. ASSERT (Pfn1->u2.Blink != MM_EMPTY_LIST);
  1558. ASSERT ((MMLISTS)(MI_PFN_ELEMENT((MI_PFN_ELEMENT(Pfn1->u2.Blink)->u1.Flink)))->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1559. ASSERT ((MMLISTS)(MI_PFN_ELEMENT((MI_PFN_ELEMENT(Pfn1->u2.Blink)->u1.Flink)))->u3.e1.PageLocation == MemoryList);
  1560. MI_PFN_ELEMENT(Pfn1->u2.Blink)->u1.Flink = PageNextFlink;
  1561. PfnNextFlink->u2.Blink = Pfn1->u2.Blink;
  1562. }
  1563. #if DBG
  1564. if (PfnLastColored->u1.Flink == MM_EMPTY_LIST) {
  1565. ASSERT (ListHead->Blink == PageLastColored);
  1566. }
  1567. #endif
  1568. Pfn1->u1.Flink = PfnLastColored->u1.Flink;
  1569. Pfn1->u2.Blink = PageLastColored;
  1570. if (ListHead->Blink == PageLastColored) {
  1571. ListHead->Blink = Page;
  1572. }
  1573. //
  1574. // Adjust the colored chains.
  1575. //
  1576. if (PfnLastColored->u1.Flink != MM_EMPTY_LIST) {
  1577. ASSERT (MI_PFN_ELEMENT(PfnLastColored->u1.Flink)->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1578. ASSERT ((MMLISTS)(MI_PFN_ELEMENT(PfnLastColored->u1.Flink)->u3.e1.PageLocation) == MemoryList);
  1579. MI_PFN_ELEMENT(PfnLastColored->u1.Flink)->u2.Blink = Page;
  1580. }
  1581. PfnLastColored->u1.Flink = Page;
  1582. ColorHead->Flink = PageNextColored;
  1583. Pfn1->OriginalPte.u.Long = MM_EMPTY_LIST;
  1584. ASSERT (PfnLastColored->OriginalPte.u.Long == MM_EMPTY_LIST);
  1585. PfnLastColored->OriginalPte.u.Long = Page;
  1586. ColorHead->Blink = Pfn1;
  1587. }
  1588. }
  1589. }
  1590. }
  1591. RescanNeeded = FALSE;
  1592. Pfn1 = MI_PFN_ELEMENT (StartPage);
  1593. do {
  1594. if ((Pfn1->u3.e1.PageLocation == StandbyPageList) &&
  1595. (Pfn1->u1.Flink != 0) &&
  1596. (Pfn1->u2.Blink != 0) &&
  1597. (Pfn1->u3.e2.ReferenceCount == 0) &&
  1598. (MmAvailablePages != 0)) {
  1599. //
  1600. // Systems utilizing memory compression may have more
  1601. // pages on the zero, free and standby lists than we
  1602. // want to give out. Explicitly check MmAvailablePages
  1603. // above instead (and recheck whenever the PFN lock is
  1604. // released and reacquired).
  1605. //
  1606. ASSERT (Pfn1->u3.e1.ReadInProgress == 0);
  1607. RemovePage = TRUE;
  1608. if (Pfn1->u3.e1.RemovalRequested == 0) {
  1609. //
  1610. // This page is not directly needed for a hot remove - but if
  1611. // it contains a chunk of prototype PTEs (and this chunk is
  1612. // in a page that needs to be removed), then any pages
  1613. // referenced by transition prototype PTEs must also be removed
  1614. // before the desired page can be removed.
  1615. //
  1616. // The same analogy holds for page table, directory, parent
  1617. // and extended parent pages.
  1618. //
  1619. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  1620. if (Pfn2->u3.e1.RemovalRequested == 0) {
  1621. #if (_MI_PAGING_LEVELS >= 3)
  1622. Pfn2 = MI_PFN_ELEMENT (Pfn2->u4.PteFrame);
  1623. if (Pfn2->u3.e1.RemovalRequested == 0) {
  1624. RemovePage = FALSE;
  1625. }
  1626. else if (Pfn2->u2.ShareCount == 1) {
  1627. RescanNeeded = TRUE;
  1628. }
  1629. #if (_MI_PAGING_LEVELS >= 4)
  1630. Pfn2 = MI_PFN_ELEMENT (Pfn2->u4.PteFrame);
  1631. if (Pfn2->u3.e1.RemovalRequested == 0) {
  1632. RemovePage = FALSE;
  1633. }
  1634. else if (Pfn2->u2.ShareCount == 1) {
  1635. RescanNeeded = TRUE;
  1636. }
  1637. #endif
  1638. #else
  1639. RemovePage = FALSE;
  1640. #endif
  1641. }
  1642. else if (Pfn2->u2.ShareCount == 1) {
  1643. RescanNeeded = TRUE;
  1644. }
  1645. }
  1646. if (RemovePage == TRUE) {
  1647. //
  1648. // This page is in the desired range - grab it.
  1649. //
  1650. MiUnlinkPageFromList (Pfn1);
  1651. MiRestoreTransitionPte (StartPage);
  1652. MiInsertPageInList (&MmBadPageListHead, StartPage);
  1653. NumberOfPages += 1;
  1654. }
  1655. }
  1656. StartPage += 1;
  1657. Pfn1 += 1;
  1658. } while (StartPage < EndPage);
  1659. if (RescanNeeded == TRUE) {
  1660. //
  1661. // A page table, directory or parent was freed by removing a transition
  1662. // page from the cache. Rescan from the top to pick it up.
  1663. //
  1664. #if DBG
  1665. MiDynmemData[7] += 1;
  1666. #endif
  1667. goto rescan;
  1668. }
  1669. #if DBG
  1670. else {
  1671. MiDynmemData[8] += 1;
  1672. }
  1673. #endif
  1674. return NumberOfPages;
  1675. }