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.

3228 lines
100 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. init386.c
  5. Abstract:
  6. This module contains the machine dependent initialization for the
  7. memory management component. It is specifically tailored to the
  8. INTEL x86 and PAE machines.
  9. Author:
  10. Lou Perazzoli (loup) 6-Jan-1990
  11. Landy Wang (landyw) 2-Jun-1997
  12. Revision History:
  13. --*/
  14. #include "mi.h"
  15. PFN_NUMBER
  16. MxGetNextPage (
  17. IN PFN_NUMBER PagesNeeded
  18. );
  19. PFN_NUMBER
  20. MxPagesAvailable (
  21. VOID
  22. );
  23. VOID
  24. MxConvertToLargePage (
  25. IN PVOID VirtualAddress,
  26. IN PVOID EndVirtualAddress
  27. );
  28. #ifdef ALLOC_PRAGMA
  29. #pragma alloc_text(INIT,MiInitMachineDependent)
  30. #pragma alloc_text(INIT,MxGetNextPage)
  31. #pragma alloc_text(INIT,MxPagesAvailable)
  32. #pragma alloc_text(INIT,MxConvertToLargePage)
  33. #pragma alloc_text(INIT,MiReportPhysicalMemory)
  34. #endif
  35. #define MM_LARGE_PAGE_MINIMUM ((255*1024*1024) >> PAGE_SHIFT)
  36. extern ULONG MmLargeSystemCache;
  37. extern ULONG MmLargeStackSize;
  38. extern LOGICAL MmMakeLowMemory;
  39. extern LOGICAL MmPagedPoolMaximumDesired;
  40. #if defined(_X86PAE_)
  41. LOGICAL MiUseGlobalBitInLargePdes;
  42. #endif
  43. extern KEVENT MiImageMappingPteEvent;
  44. //
  45. // Local data.
  46. //
  47. #ifdef ALLOC_DATA_PRAGMA
  48. #pragma data_seg("INITDATA")
  49. #endif
  50. ULONG MxPfnAllocation;
  51. PMEMORY_ALLOCATION_DESCRIPTOR MxFreeDescriptor;
  52. #ifdef ALLOC_DATA_PRAGMA
  53. #pragma data_seg()
  54. #endif
  55. MEMORY_ALLOCATION_DESCRIPTOR MxOldFreeDescriptor;
  56. typedef struct _MI_LARGE_VA_RANGES {
  57. PVOID VirtualAddress;
  58. PVOID EndVirtualAddress;
  59. } MI_LARGE_VA_RANGES, *PMI_LARGE_VA_RANGES;
  60. //
  61. // There are potentially 4 large page ranges:
  62. //
  63. // 1. PFN database
  64. // 2. Initial nonpaged pool
  65. // 3. Kernel code/data
  66. // 4. HAL code/data
  67. //
  68. #define MI_LARGE_PFN_DATABASE 0x1
  69. #define MI_LARGE_NONPAGED_POOL 0x2
  70. #define MI_LARGE_KERNEL_HAL 0x4
  71. #define MI_LARGE_ALL 0x7
  72. ULONG MxMapLargePages = MI_LARGE_ALL;
  73. #define MI_MAX_LARGE_PAGE_RANGES 4
  74. ULONG MiLargeVaRangeIndex;
  75. MI_LARGE_VA_RANGES MiLargeVaRanges[MI_MAX_LARGE_PAGE_RANGES];
  76. ULONG MiLargePageRangeIndex;
  77. MI_LARGE_PAGE_RANGES MiLargePageRanges[MI_MAX_LARGE_PAGE_RANGES];
  78. #define MM_PFN_MAPPED_BY_PDE (MM_VA_MAPPED_BY_PDE >> PAGE_SHIFT)
  79. PFN_NUMBER
  80. MxGetNextPage (
  81. IN PFN_NUMBER PagesNeeded
  82. )
  83. /*++
  84. Routine Description:
  85. This function returns the next physical page number from the largest
  86. largest free descriptor. If there are not enough physical pages left
  87. to satisfy the request then a bugcheck is executed since the system
  88. cannot be initialized.
  89. Arguments:
  90. PagesNeeded - Supplies the number of pages needed.
  91. Return Value:
  92. The base of the range of physically contiguous pages.
  93. Environment:
  94. Kernel mode, Phase 0 only.
  95. --*/
  96. {
  97. PFN_NUMBER PageFrameIndex;
  98. //
  99. // Examine the free descriptor to see if enough usable memory is available.
  100. //
  101. if (PagesNeeded > MxFreeDescriptor->PageCount) {
  102. KeBugCheckEx (INSTALL_MORE_MEMORY,
  103. MmNumberOfPhysicalPages,
  104. MxFreeDescriptor->PageCount,
  105. MxOldFreeDescriptor.PageCount,
  106. PagesNeeded);
  107. }
  108. PageFrameIndex = MxFreeDescriptor->BasePage;
  109. MxFreeDescriptor->BasePage += PagesNeeded;
  110. MxFreeDescriptor->PageCount -= PagesNeeded;
  111. return PageFrameIndex;
  112. }
  113. PFN_NUMBER
  114. MxPagesAvailable (
  115. VOID
  116. )
  117. /*++
  118. Routine Description:
  119. This function returns the number of pages available.
  120. Arguments:
  121. None.
  122. Return Value:
  123. The number of physically contiguous pages currently available.
  124. Environment:
  125. Kernel mode, Phase 0 only.
  126. --*/
  127. {
  128. return MxFreeDescriptor->PageCount;
  129. }
  130. VOID
  131. MxConvertToLargePage (
  132. IN PVOID VirtualAddress,
  133. IN PVOID EndVirtualAddress
  134. )
  135. /*++
  136. Routine Description:
  137. This function converts the backing for the supplied virtual address range
  138. to a large page mapping.
  139. Arguments:
  140. VirtualAddress - Supplies the virtual address to convert to a large page.
  141. EndVirtualAddress - Supplies the end virtual address to convert to a
  142. large page.
  143. Return Value:
  144. None.
  145. Environment:
  146. Kernel mode, Phase 1 only.
  147. --*/
  148. {
  149. ULONG i;
  150. MMPTE TempPde;
  151. PMMPTE PointerPde;
  152. PMMPTE LastPde;
  153. PMMPTE PointerPte;
  154. KIRQL OldIrql;
  155. PMMPFN Pfn1;
  156. PFN_NUMBER PageFrameIndex;
  157. LOGICAL ValidPteFound;
  158. PFN_NUMBER LargePageBaseFrame;
  159. ASSERT (MxMapLargePages != 0);
  160. PointerPde = MiGetPdeAddress (VirtualAddress);
  161. LastPde = MiGetPdeAddress (EndVirtualAddress);
  162. TempPde = ValidKernelPde;
  163. TempPde.u.Hard.LargePage = 1;
  164. #if defined(_X86PAE_)
  165. if (MiUseGlobalBitInLargePdes == TRUE) {
  166. TempPde.u.Hard.Global = 1;
  167. }
  168. #endif
  169. LOCK_PFN (OldIrql);
  170. do {
  171. ASSERT (PointerPde->u.Hard.Valid == 1);
  172. if (PointerPde->u.Hard.LargePage == 1) {
  173. goto skip;
  174. }
  175. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  176. //
  177. // Here's a nasty little hack - the page table page mapping the kernel
  178. // and HAL (built by the loader) does not necessarily fill all the
  179. // page table entries (ie: any number of leading entries may be zero).
  180. //
  181. // To deal with this, walk forward until a nonzero entry is found
  182. // and re-index the large page based on this.
  183. //
  184. ValidPteFound = FALSE;
  185. LargePageBaseFrame = (ULONG)-1;
  186. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  187. ASSERT ((PageFrameIndex & (MM_PFN_MAPPED_BY_PDE - 1)) == 0);
  188. for (i = 0; i < PTE_PER_PAGE; i += 1) {
  189. ASSERT ((PointerPte->u.Long == ZeroKernelPte.u.Long) ||
  190. (ValidPteFound == FALSE) ||
  191. (PageFrameIndex == MI_GET_PAGE_FRAME_FROM_PTE (PointerPte)));
  192. if (PointerPte->u.Hard.Valid == 1) {
  193. if (ValidPteFound == FALSE) {
  194. ValidPteFound = TRUE;
  195. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  196. LargePageBaseFrame = PageFrameIndex - i;
  197. }
  198. }
  199. PointerPte += 1;
  200. PageFrameIndex += 1;
  201. }
  202. if (ValidPteFound == FALSE) {
  203. goto skip;
  204. }
  205. TempPde.u.Hard.PageFrameNumber = LargePageBaseFrame;
  206. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
  207. KeFlushSingleTb (MiGetVirtualAddressMappedByPte (PointerPde),
  208. TRUE,
  209. TRUE,
  210. (PHARDWARE_PTE)PointerPde,
  211. TempPde.u.Flush);
  212. //
  213. // P6 errata requires a TB flush here...
  214. //
  215. KeFlushEntireTb (TRUE, TRUE);
  216. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  217. Pfn1->u2.ShareCount = 0;
  218. Pfn1->u3.e2.ReferenceCount = 1;
  219. Pfn1->u3.e1.PageLocation = StandbyPageList;
  220. MI_SET_PFN_DELETED (Pfn1);
  221. MiDecrementReferenceCount (PageFrameIndex);
  222. skip:
  223. PointerPde += 1;
  224. } while (PointerPde <= LastPde);
  225. UNLOCK_PFN (OldIrql);
  226. }
  227. VOID
  228. MiReportPhysicalMemory (
  229. VOID
  230. )
  231. /*++
  232. Routine Description:
  233. This routine is called during Phase 0 initialization once the
  234. MmPhysicalMemoryBlock has been constructed. It's job is to decide
  235. which large page ranges to enable later and also to construct a
  236. large page comparison list so any requests which are not fully cached
  237. can check this list in order to refuse conflicting requests.
  238. Arguments:
  239. None.
  240. Return Value:
  241. None.
  242. Environment:
  243. Kernel mode. Phase 0 only.
  244. This is called before any non-MmCached allocations are made.
  245. --*/
  246. {
  247. ULONG i, j;
  248. PMMPTE PointerPte;
  249. LOGICAL EntryFound;
  250. PFN_NUMBER count;
  251. PFN_NUMBER Page;
  252. PFN_NUMBER LastPage;
  253. PFN_NUMBER PageFrameIndex;
  254. PFN_NUMBER LastPageFrameIndex;
  255. PFN_NUMBER PageFrameIndex2;
  256. //
  257. // Examine the physical memory block to see whether large pages should
  258. // be enabled. The key point is that all the physical pages within a
  259. // given large page range must have the same cache attributes (MmCached)
  260. // in order to maintain TB coherency. This can be done provided all
  261. // the pages within the large page range represent real RAM (as described
  262. // by the loader) so that memory management can control it. If any
  263. // portion of the large page range is not RAM, it is possible that it
  264. // may get used as noncached or writecombined device memory and
  265. // therefore large pages cannot be used.
  266. //
  267. if (MxMapLargePages == 0) {
  268. return;
  269. }
  270. for (i = 0; i < MiLargeVaRangeIndex; i += 1) {
  271. PointerPte = MiGetPteAddress (MiLargeVaRanges[i].VirtualAddress);
  272. ASSERT (PointerPte->u.Hard.Valid == 1);
  273. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  274. PointerPte = MiGetPteAddress (MiLargeVaRanges[i].EndVirtualAddress);
  275. ASSERT (PointerPte->u.Hard.Valid == 1);
  276. LastPageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  277. //
  278. // Round the start down to a page directory boundary and the end to
  279. // the last page directory entry before the next boundary.
  280. //
  281. PageFrameIndex &= ~(MM_PFN_MAPPED_BY_PDE - 1);
  282. LastPageFrameIndex |= (MM_PFN_MAPPED_BY_PDE - 1);
  283. EntryFound = FALSE;
  284. j = 0;
  285. do {
  286. count = MmPhysicalMemoryBlock->Run[j].PageCount;
  287. Page = MmPhysicalMemoryBlock->Run[j].BasePage;
  288. LastPage = Page + count;
  289. if ((PageFrameIndex >= Page) && (LastPageFrameIndex < LastPage)) {
  290. EntryFound = TRUE;
  291. break;
  292. }
  293. j += 1;
  294. } while (j != MmPhysicalMemoryBlock->NumberOfRuns);
  295. if (EntryFound == FALSE) {
  296. //
  297. // No entry was found that completely spans this large page range.
  298. // Zero it so this range will not be converted into large pages
  299. // later.
  300. //
  301. DbgPrint ("MM: Loader/HAL memory block indicates large pages cannot be used for %p->%p\n",
  302. MiLargeVaRanges[i].VirtualAddress,
  303. MiLargeVaRanges[i].EndVirtualAddress);
  304. MiLargeVaRanges[i].VirtualAddress = NULL;
  305. //
  306. // Don't use large pages for anything if this chunk overlaps any
  307. // others in the request list. This is because 2 separate ranges
  308. // may share a straddling large page. If the first range was unable
  309. // to use large pages, but the second one does ... then only part
  310. // of the first range will get large pages if we enable large
  311. // pages for the second range. This would be vey bad as we use
  312. // the MI_IS_PHYSICAL macro everywhere and assume the entire
  313. // range is in or out, so disable all large pages here instead.
  314. //
  315. for (j = 0; j < MiLargeVaRangeIndex; j += 1) {
  316. //
  317. // Skip the range that is already being rejected.
  318. //
  319. if (i == j) {
  320. continue;
  321. }
  322. //
  323. // Skip any range which has already been removed.
  324. //
  325. if (MiLargeVaRanges[j].VirtualAddress == NULL) {
  326. continue;
  327. }
  328. PointerPte = MiGetPteAddress (MiLargeVaRanges[j].VirtualAddress);
  329. ASSERT (PointerPte->u.Hard.Valid == 1);
  330. PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  331. if ((PageFrameIndex2 >= PageFrameIndex) &&
  332. (PageFrameIndex2 <= LastPageFrameIndex)) {
  333. MiLargeVaRangeIndex = 0;
  334. MiLargePageRangeIndex = 0;
  335. DbgPrint ("MM: Disabling large pages for all ranges due to overlap\n");
  336. return;
  337. }
  338. //
  339. // Since it is not possible for any request chunk to completely
  340. // encompass another one, checking only the start and end
  341. // addresses is sufficient.
  342. //
  343. PointerPte = MiGetPteAddress (MiLargeVaRanges[j].EndVirtualAddress);
  344. ASSERT (PointerPte->u.Hard.Valid == 1);
  345. PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  346. if ((PageFrameIndex2 >= PageFrameIndex) &&
  347. (PageFrameIndex2 <= LastPageFrameIndex)) {
  348. MiLargeVaRangeIndex = 0;
  349. MiLargePageRangeIndex = 0;
  350. DbgPrint ("MM: Disabling large pages for all ranges due to overlap\n");
  351. return;
  352. }
  353. }
  354. //
  355. // No other ranges overlapped with this one, it is sufficient to
  356. // just disable this range and continue to attempt to use large
  357. // pages for any others.
  358. //
  359. continue;
  360. }
  361. //
  362. // Someday get clever and merge and sort ranges.
  363. //
  364. MiLargePageRanges[MiLargePageRangeIndex].StartFrame = PageFrameIndex;
  365. MiLargePageRanges[MiLargePageRangeIndex].LastFrame = LastPageFrameIndex;
  366. MiLargePageRangeIndex += 1;
  367. }
  368. }
  369. LOGICAL
  370. MiMustFrameBeCached (
  371. IN PFN_NUMBER PageFrameIndex
  372. )
  373. /*++
  374. Routine Description:
  375. This routine checks whether the specified page frame must be mapped
  376. fully cached because it is already part of a large page which is fully
  377. cached. This must be detected otherwise we would be creating an
  378. incoherent overlapping TB entry as the same physical page would be
  379. mapped by 2 different TB entries with different cache attributes.
  380. Arguments:
  381. PageFrameIndex - Supplies the page frame index in question.
  382. Return Value:
  383. TRUE if the page must be mapped as fully cachable, FALSE if not.
  384. Environment:
  385. Kernel mode. IRQL of DISPATCH_LEVEL or below, PFN lock may be held.
  386. --*/
  387. {
  388. ULONG i;
  389. PMI_LARGE_PAGE_RANGES Range;
  390. Range = MiLargePageRanges;
  391. for (i = 0; i < MiLargePageRangeIndex; i += 1, Range += 1) {
  392. if ((PageFrameIndex >= Range->StartFrame) && (PageFrameIndex <= Range->StartFrame)) {
  393. return TRUE;
  394. }
  395. }
  396. return FALSE;
  397. }
  398. LONG MiAddPtesCount;
  399. ULONG MiExtraPtes1;
  400. ULONG MiExtraPtes2;
  401. LOGICAL
  402. MiRecoverExtraPtes (
  403. VOID
  404. )
  405. /*++
  406. Routine Description:
  407. This routine is called to recover extra PTEs for the system PTE pool.
  408. These are not just added in earlier in Phase 0 because the system PTE
  409. allocator uses the low addresses first which would fragment these
  410. bigger ranges.
  411. Arguments:
  412. None.
  413. Return Value:
  414. TRUE if any PTEs were added, FALSE if not.
  415. Environment:
  416. Kernel mode.
  417. --*/
  418. {
  419. LOGICAL PtesAdded;
  420. PMMPTE PointerPte;
  421. ULONG OriginalAddPtesCount;
  422. //
  423. // Make sure the add is only done once as this is called multiple times.
  424. //
  425. OriginalAddPtesCount = InterlockedCompareExchange (&MiAddPtesCount, 1, 0);
  426. if (OriginalAddPtesCount != 0) {
  427. return FALSE;
  428. }
  429. PtesAdded = FALSE;
  430. if (MiExtraPtes1 != 0) {
  431. //
  432. // Add extra system PTEs to the pool.
  433. //
  434. PointerPte = MiGetPteAddress (MiExtraResourceStart);
  435. MiAddSystemPtes (PointerPte, MiExtraPtes1, SystemPteSpace);
  436. PtesAdded = TRUE;
  437. }
  438. if (MiExtraPtes2 != 0) {
  439. //
  440. // Add extra system PTEs to the pool.
  441. //
  442. if (MM_SHARED_USER_DATA_VA > MiUseMaximumSystemSpace) {
  443. if (MiUseMaximumSystemSpaceEnd > MM_SHARED_USER_DATA_VA) {
  444. MiExtraPtes2 = BYTES_TO_PAGES(MM_SHARED_USER_DATA_VA - MiUseMaximumSystemSpace);
  445. }
  446. }
  447. if (MiExtraPtes2 != 0) {
  448. PointerPte = MiGetPteAddress (MiUseMaximumSystemSpace);
  449. MiAddSystemPtes (PointerPte, MiExtraPtes2, SystemPteSpace);
  450. }
  451. PtesAdded = TRUE;
  452. }
  453. return PtesAdded;
  454. }
  455. VOID
  456. MiInitMachineDependent (
  457. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  458. )
  459. /*++
  460. Routine Description:
  461. This routine performs the necessary operations to enable virtual
  462. memory. This includes building the page directory page, building
  463. page table pages to map the code section, the data section, the
  464. stack section and the trap handler.
  465. It also initializes the PFN database and populates the free list.
  466. Arguments:
  467. LoaderBlock - Supplies a pointer to the firmware setup loader block.
  468. Return Value:
  469. None.
  470. Environment:
  471. Kernel mode.
  472. N.B. This routine uses memory from the loader block descriptors, but
  473. the descriptors themselves must be restored prior to return as our caller
  474. walks them to create the MmPhysicalMemoryBlock.
  475. --*/
  476. {
  477. LOGICAL InitialNonPagedPoolSetViaRegistry;
  478. PHYSICAL_ADDRESS MaxHotPlugMemoryAddress;
  479. ULONG Bias;
  480. PMMPTE BasePte;
  481. PMMPFN BasePfn;
  482. PMMPFN BottomPfn;
  483. PMMPFN TopPfn;
  484. PFN_NUMBER FirstNonPagedPoolPage;
  485. PFN_NUMBER FirstPfnDatabasePage;
  486. LOGICAL PfnInLargePages;
  487. ULONG BasePage;
  488. ULONG PagesLeft;
  489. ULONG Range;
  490. ULONG PageCount;
  491. ULONG i, j;
  492. ULONG PdePageNumber;
  493. ULONG PdePage;
  494. ULONG PageFrameIndex;
  495. ULONG MaxPool;
  496. PEPROCESS CurrentProcess;
  497. ULONG DirBase;
  498. ULONG MostFreePage;
  499. ULONG MostFreeLowMem;
  500. PFN_NUMBER PagesNeeded;
  501. PLIST_ENTRY NextMd;
  502. PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
  503. MMPTE TempPde;
  504. MMPTE TempPte;
  505. PMMPTE PointerPde;
  506. PMMPTE PointerPte;
  507. PMMPTE LastPte;
  508. PMMPTE Pde;
  509. PMMPTE StartPde;
  510. PMMPTE EndPde;
  511. PMMPTE SystemPteStart;
  512. PMMPFN Pfn1;
  513. PMMPFN Pfn2;
  514. ULONG PdeCount;
  515. ULONG va;
  516. ULONG SavedSize;
  517. KIRQL OldIrql;
  518. PVOID VirtualAddress;
  519. PVOID NonPagedPoolStartVirtual;
  520. ULONG LargestFreePfnCount;
  521. ULONG LargestFreePfnStart;
  522. ULONG FreePfnCount;
  523. PVOID NonPagedPoolStartLow;
  524. LOGICAL ExtraSystemCacheViews;
  525. SIZE_T MaximumNonPagedPoolInBytesLimit;
  526. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  527. PLIST_ENTRY NextEntry;
  528. ULONG ReturnedLength;
  529. NTSTATUS status;
  530. UCHAR Associativity;
  531. ULONG InitialPagedPoolSize;
  532. ULONG NonPagedSystemStart;
  533. LOGICAL PagedPoolMaximumDesired;
  534. if (InitializationPhase == 1) {
  535. //
  536. // If the kernel image has not been biased to allow for 3gb of user
  537. // space, the host processor supports large pages, and the number of
  538. // physical pages is greater than the threshold, then map the kernel
  539. // image, HAL, PFN database and initial nonpaged pool with large pages.
  540. //
  541. if (MxMapLargePages != 0) {
  542. for (i = 0; i < MiLargeVaRangeIndex; i += 1) {
  543. if (MiLargeVaRanges[i].VirtualAddress != NULL) {
  544. MxConvertToLargePage (MiLargeVaRanges[i].VirtualAddress,
  545. MiLargeVaRanges[i].EndVirtualAddress);
  546. }
  547. }
  548. }
  549. return;
  550. }
  551. ASSERT (InitializationPhase == 0);
  552. ASSERT (MxMapLargePages == MI_LARGE_ALL);
  553. PfnInLargePages = FALSE;
  554. ExtraSystemCacheViews = FALSE;
  555. MostFreePage = 0;
  556. MostFreeLowMem = 0;
  557. LargestFreePfnCount = 0;
  558. NonPagedPoolStartLow = NULL;
  559. PagedPoolMaximumDesired = FALSE;
  560. //
  561. // Initializing these is not needed for correctness, but without it
  562. // the compiler cannot compile this code W4 to check for use of
  563. // uninitialized variables.
  564. //
  565. LargestFreePfnStart = 0;
  566. FirstPfnDatabasePage = 0;
  567. MaximumNonPagedPoolInBytesLimit = 0;
  568. //
  569. // If the chip doesn't support large pages or the system is booted /3GB,
  570. // then disable large page support.
  571. //
  572. if (((KeFeatureBits & KF_LARGE_PAGE) == 0) || (MmVirtualBias != 0)) {
  573. MxMapLargePages = 0;
  574. }
  575. //
  576. // This flag is registry-settable so check before overriding.
  577. //
  578. if (MmProtectFreedNonPagedPool == TRUE) {
  579. MxMapLargePages &= ~(MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL);
  580. }
  581. //
  582. // Sanitize this registry-specifiable large stack size. Note the registry
  583. // size is in 1K chunks, ie: 32 means 32k. Note also that the registry
  584. // setting does not include the guard page and we don't want to burden
  585. // administrators with knowing about it so we automatically subtract one
  586. // page from their request.
  587. //
  588. if (MmLargeStackSize > (KERNEL_LARGE_STACK_SIZE / 1024)) {
  589. //
  590. // No registry override or the override is too high.
  591. // Set it to the default.
  592. //
  593. MmLargeStackSize = KERNEL_LARGE_STACK_SIZE;
  594. }
  595. else {
  596. //
  597. // Convert registry override from 1K units to bytes. Note intelligent
  598. // choices are 16k or 32k because we bin those sizes in sysptes.
  599. //
  600. MmLargeStackSize *= 1024;
  601. MmLargeStackSize = MI_ROUND_TO_SIZE (MmLargeStackSize, PAGE_SIZE);
  602. MmLargeStackSize -= PAGE_SIZE;
  603. ASSERT (MmLargeStackSize <= KERNEL_LARGE_STACK_SIZE);
  604. ASSERT ((MmLargeStackSize & (PAGE_SIZE-1)) == 0);
  605. //
  606. // Don't allow a value that is too low either.
  607. //
  608. if (MmLargeStackSize < KERNEL_STACK_SIZE) {
  609. MmLargeStackSize = KERNEL_STACK_SIZE;
  610. }
  611. }
  612. //
  613. // If the host processor supports global bits, then set the global
  614. // bit in the template kernel PTE and PDE entries.
  615. //
  616. if (KeFeatureBits & KF_GLOBAL_PAGE) {
  617. ValidKernelPte.u.Long |= MM_PTE_GLOBAL_MASK;
  618. #if defined(_X86PAE_)
  619. //
  620. // Note that the PAE mode of the processor does not support the
  621. // global bit in PDEs which map 4K page table pages.
  622. //
  623. MiUseGlobalBitInLargePdes = TRUE;
  624. #else
  625. ValidKernelPde.u.Long |= MM_PTE_GLOBAL_MASK;
  626. #endif
  627. MmPteGlobal.u.Long = MM_PTE_GLOBAL_MASK;
  628. }
  629. TempPte = ValidKernelPte;
  630. TempPde = ValidKernelPde;
  631. //
  632. // Set the directory base for the system process.
  633. //
  634. PointerPte = MiGetPdeAddress (PDE_BASE);
  635. PdePageNumber = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
  636. CurrentProcess = PsGetCurrentProcess ();
  637. #if defined(_X86PAE_)
  638. PrototypePte.u.Soft.PageFileHigh = MI_PTE_LOOKUP_NEEDED;
  639. _asm {
  640. mov eax, cr3
  641. mov DirBase, eax
  642. }
  643. //
  644. // Note cr3 must be 32-byte aligned.
  645. //
  646. ASSERT ((DirBase & 0x1f) == 0);
  647. #else
  648. DirBase = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte) << PAGE_SHIFT;
  649. #endif
  650. CurrentProcess->Pcb.DirectoryTableBase[0] = DirBase;
  651. KeSweepDcache (FALSE);
  652. //
  653. // Unmap the low 2Gb of memory.
  654. //
  655. PointerPde = MiGetPdeAddress (0);
  656. LastPte = MiGetPdeAddress (KSEG0_BASE);
  657. MiFillMemoryPte (PointerPde,
  658. (LastPte - PointerPde) * sizeof(MMPTE),
  659. ZeroKernelPte.u.Long);
  660. //
  661. // Get the lower bound of the free physical memory and the
  662. // number of physical pages by walking the memory descriptor lists.
  663. //
  664. MxFreeDescriptor = NULL;
  665. NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
  666. while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
  667. MemoryDescriptor = CONTAINING_RECORD(NextMd,
  668. MEMORY_ALLOCATION_DESCRIPTOR,
  669. ListEntry);
  670. if ((MemoryDescriptor->MemoryType != LoaderFirmwarePermanent) &&
  671. (MemoryDescriptor->MemoryType != LoaderBBTMemory) &&
  672. (MemoryDescriptor->MemoryType != LoaderHALCachedMemory) &&
  673. (MemoryDescriptor->MemoryType != LoaderSpecialMemory)) {
  674. //
  675. // This check results in /BURNMEMORY chunks not being counted.
  676. //
  677. if (MemoryDescriptor->MemoryType != LoaderBad) {
  678. MmNumberOfPhysicalPages += MemoryDescriptor->PageCount;
  679. }
  680. if (MemoryDescriptor->BasePage < MmLowestPhysicalPage) {
  681. MmLowestPhysicalPage = MemoryDescriptor->BasePage;
  682. }
  683. if ((MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) >
  684. MmHighestPhysicalPage) {
  685. MmHighestPhysicalPage =
  686. MemoryDescriptor->BasePage + MemoryDescriptor->PageCount - 1;
  687. }
  688. //
  689. // Locate the largest free descriptor.
  690. //
  691. if ((MemoryDescriptor->MemoryType == LoaderFree) ||
  692. (MemoryDescriptor->MemoryType == LoaderLoadedProgram) ||
  693. (MemoryDescriptor->MemoryType == LoaderFirmwareTemporary) ||
  694. (MemoryDescriptor->MemoryType == LoaderOsloaderStack)) {
  695. if (MemoryDescriptor->PageCount > MostFreePage) {
  696. MostFreePage = MemoryDescriptor->PageCount;
  697. MxFreeDescriptor = MemoryDescriptor;
  698. }
  699. }
  700. }
  701. NextMd = MemoryDescriptor->ListEntry.Flink;
  702. }
  703. if (MmLargeSystemCache != 0) {
  704. ExtraSystemCacheViews = TRUE;
  705. }
  706. //
  707. // This flag is registry-settable so check before overriding.
  708. //
  709. // Enabling special IRQL automatically disables mapping the kernel with
  710. // large pages so we can catch kernel and HAL code.
  711. //
  712. if (MmVerifyDriverBufferLength != (ULONG)-1) {
  713. MmLargePageMinimum = (ULONG)-2;
  714. }
  715. else if (MmLargePageMinimum == 0) {
  716. MmLargePageMinimum = MM_LARGE_PAGE_MINIMUM;
  717. }
  718. //
  719. // Capture the registry-specified initial nonpaged pool setting as we
  720. // will modify the variable later.
  721. //
  722. if ((MmSizeOfNonPagedPoolInBytes != 0) ||
  723. (MmMaximumNonPagedPoolPercent != 0)) {
  724. InitialNonPagedPoolSetViaRegistry = TRUE;
  725. }
  726. else {
  727. InitialNonPagedPoolSetViaRegistry = FALSE;
  728. }
  729. if (MmNumberOfPhysicalPages <= MmLargePageMinimum) {
  730. MxMapLargePages = 0;
  731. //
  732. // Reduce the size of the initial nonpaged pool on small configurations
  733. // as RAM is precious (unless the registry has overridden it).
  734. //
  735. if ((MmNumberOfPhysicalPages <= MM_LARGE_PAGE_MINIMUM) &&
  736. (MmSizeOfNonPagedPoolInBytes == 0)) {
  737. MmSizeOfNonPagedPoolInBytes = 2*1024*1024;
  738. }
  739. }
  740. //
  741. // MmDynamicPfn may have been initialized based on the registry to
  742. // a value representing the highest physical address in gigabytes.
  743. //
  744. MmDynamicPfn *= ((1024 * 1024 * 1024) / PAGE_SIZE);
  745. //
  746. // Retrieve highest hot plug memory range from the HAL if
  747. // available and not otherwise retrieved from the registry.
  748. //
  749. if (MmDynamicPfn == 0) {
  750. status = HalQuerySystemInformation(
  751. HalQueryMaxHotPlugMemoryAddress,
  752. sizeof(PHYSICAL_ADDRESS),
  753. (PPHYSICAL_ADDRESS) &MaxHotPlugMemoryAddress,
  754. &ReturnedLength);
  755. if (NT_SUCCESS(status)) {
  756. ASSERT (ReturnedLength == sizeof(PHYSICAL_ADDRESS));
  757. MmDynamicPfn = (PFN_NUMBER) (MaxHotPlugMemoryAddress.QuadPart / PAGE_SIZE);
  758. }
  759. }
  760. if (MmDynamicPfn != 0) {
  761. if (MmVirtualBias != 0) {
  762. MmDynamicPfn = 0;
  763. }
  764. }
  765. if (MmDynamicPfn != 0) {
  766. #if defined(_X86PAE_)
  767. MmHighestPossiblePhysicalPage = MI_DTC_MAX_PAGES - 1;
  768. #else
  769. MmHighestPossiblePhysicalPage = MI_DEFAULT_MAX_PAGES - 1;
  770. #endif
  771. if (MmDynamicPfn - 1 < MmHighestPossiblePhysicalPage) {
  772. if (MmDynamicPfn - 1 < MmHighestPhysicalPage) {
  773. MmDynamicPfn = MmHighestPhysicalPage + 1;
  774. }
  775. MmHighestPossiblePhysicalPage = MmDynamicPfn - 1;
  776. }
  777. }
  778. else {
  779. MmHighestPossiblePhysicalPage = MmHighestPhysicalPage;
  780. }
  781. if (MmHighestPossiblePhysicalPage > 0x400000 - 1) {
  782. //
  783. // The PFN database is more than 112mb. Force it to come from the
  784. // 2GB->3GB virtual address range. Note the administrator cannot be
  785. // booting /3GB as when he does, the loader throws away memory
  786. // above the physical 16GB line, so this must be a hot-add
  787. // configuration. Since the loader has already put the system at
  788. // 3GB, the highest possible hot add page must be reduced now.
  789. //
  790. if (MmVirtualBias != 0) {
  791. MmHighestPossiblePhysicalPage = 0x400000 - 1;
  792. if (MmHighestPhysicalPage > MmHighestPossiblePhysicalPage) {
  793. MmHighestPhysicalPage = MmHighestPossiblePhysicalPage;
  794. }
  795. }
  796. else {
  797. //
  798. // The virtual space between 2 and 3GB virtual is best used
  799. // for system PTEs when this much physical memory is present.
  800. //
  801. ExtraSystemCacheViews = FALSE;
  802. }
  803. }
  804. //
  805. // Don't enable extra system cache views as virtual addresses are limited.
  806. // Only a kernel-verifier special case can trigger this.
  807. //
  808. if ((KernelVerifier == TRUE) &&
  809. (MmVirtualBias == 0) &&
  810. (MmNumberOfPhysicalPages <= MmLargePageMinimum) &&
  811. (MmHighestPossiblePhysicalPage > 0x100000)) {
  812. ExtraSystemCacheViews = FALSE;
  813. }
  814. #if defined(_X86PAE_)
  815. //
  816. // Only PAE machines with at least 5GB of physical memory get to use this
  817. // and then only if they are NOT booted /3GB.
  818. //
  819. if (strstr(LoaderBlock->LoadOptions, "NOLOWMEM")) {
  820. if ((MmVirtualBias == 0) &&
  821. (MmNumberOfPhysicalPages >= 5 * 1024 * 1024 / 4)) {
  822. MiNoLowMemory = (PFN_NUMBER)((ULONGLONG)_4gb / PAGE_SIZE);
  823. }
  824. }
  825. if (MiNoLowMemory != 0) {
  826. MmMakeLowMemory = TRUE;
  827. }
  828. #endif
  829. //
  830. // Save the original descriptor value as everything must be restored
  831. // prior to this function returning.
  832. //
  833. *(PMEMORY_ALLOCATION_DESCRIPTOR)&MxOldFreeDescriptor = *MxFreeDescriptor;
  834. if (MmNumberOfPhysicalPages < 1100) {
  835. KeBugCheckEx (INSTALL_MORE_MEMORY,
  836. MmNumberOfPhysicalPages,
  837. MmLowestPhysicalPage,
  838. MmHighestPhysicalPage,
  839. 0);
  840. }
  841. //
  842. // Build non-paged pool using the physical pages following the
  843. // data page in which to build the pool from. Non-paged pool grows
  844. // from the high range of the virtual address space and expands
  845. // downward.
  846. //
  847. // At this time non-paged pool is constructed so virtual addresses
  848. // are also physically contiguous.
  849. //
  850. if ((MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT) >
  851. (7 * (MmNumberOfPhysicalPages >> 3))) {
  852. //
  853. // More than 7/8 of memory is allocated to nonpagedpool, reset to 0.
  854. //
  855. MmSizeOfNonPagedPoolInBytes = 0;
  856. }
  857. if (MmSizeOfNonPagedPoolInBytes < MmMinimumNonPagedPoolSize) {
  858. //
  859. // Calculate the size of nonpaged pool.
  860. // Use the minimum size, then for every MB above 4mb add extra
  861. // pages.
  862. //
  863. MmSizeOfNonPagedPoolInBytes = MmMinimumNonPagedPoolSize;
  864. MmSizeOfNonPagedPoolInBytes +=
  865. ((MmNumberOfPhysicalPages - 1024)/256) *
  866. MmMinAdditionNonPagedPoolPerMb;
  867. }
  868. if (MmSizeOfNonPagedPoolInBytes > MM_MAX_INITIAL_NONPAGED_POOL) {
  869. MmSizeOfNonPagedPoolInBytes = MM_MAX_INITIAL_NONPAGED_POOL;
  870. }
  871. //
  872. // If the registry specifies a total nonpaged pool percentage cap, enforce
  873. // it here.
  874. //
  875. if (MmMaximumNonPagedPoolPercent != 0) {
  876. if (MmMaximumNonPagedPoolPercent < 5) {
  877. MmMaximumNonPagedPoolPercent = 5;
  878. }
  879. else if (MmMaximumNonPagedPoolPercent > 80) {
  880. MmMaximumNonPagedPoolPercent = 80;
  881. }
  882. //
  883. // Use the registry-expressed percentage value.
  884. //
  885. MaximumNonPagedPoolInBytesLimit =
  886. ((MmNumberOfPhysicalPages * MmMaximumNonPagedPoolPercent) / 100);
  887. //
  888. // Carefully set the maximum keeping in mind that maximum PAE
  889. // machines can have 16*1024*1024 pages so care must be taken
  890. // that multiplying by PAGE_SIZE doesn't overflow here.
  891. //
  892. if (MaximumNonPagedPoolInBytesLimit > ((MM_MAX_INITIAL_NONPAGED_POOL + MM_MAX_ADDITIONAL_NONPAGED_POOL) / PAGE_SIZE)) {
  893. MaximumNonPagedPoolInBytesLimit = MM_MAX_INITIAL_NONPAGED_POOL + MM_MAX_ADDITIONAL_NONPAGED_POOL;
  894. }
  895. else {
  896. MaximumNonPagedPoolInBytesLimit *= PAGE_SIZE;
  897. }
  898. if (MaximumNonPagedPoolInBytesLimit < 6 * 1024 * 1024) {
  899. MaximumNonPagedPoolInBytesLimit = 6 * 1024 * 1024;
  900. }
  901. if (MmSizeOfNonPagedPoolInBytes > MaximumNonPagedPoolInBytesLimit) {
  902. MmSizeOfNonPagedPoolInBytes = MaximumNonPagedPoolInBytesLimit;
  903. }
  904. }
  905. //
  906. // Align to page size boundary.
  907. //
  908. MmSizeOfNonPagedPoolInBytes &= ~(PAGE_SIZE - 1);
  909. //
  910. // Calculate the maximum size of pool.
  911. //
  912. if (MmMaximumNonPagedPoolInBytes == 0) {
  913. //
  914. // Calculate the size of nonpaged pool. If 4mb or less use
  915. // the minimum size, then for every MB above 4mb add extra
  916. // pages.
  917. //
  918. MmMaximumNonPagedPoolInBytes = MmDefaultMaximumNonPagedPool;
  919. //
  920. // Make sure enough expansion for the PFN database exists.
  921. //
  922. MmMaximumNonPagedPoolInBytes += (ULONG)PAGE_ALIGN (
  923. (MmHighestPossiblePhysicalPage + 1) * sizeof(MMPFN));
  924. //
  925. // Only use the new formula for autosizing nonpaged pool on machines
  926. // with at least 512MB. The new formula allocates 1/2 as much nonpaged
  927. // pool per MB but scales much higher - machines with ~1.2GB or more
  928. // get 256MB of nonpaged pool. Note that the old formula gave machines
  929. // with 512MB of RAM 128MB of nonpaged pool so this behavior is
  930. // preserved with the new formula as well.
  931. //
  932. if (MmNumberOfPhysicalPages >= 0x1f000) {
  933. MmMaximumNonPagedPoolInBytes +=
  934. ((MmNumberOfPhysicalPages - 1024)/256) *
  935. (MmMaxAdditionNonPagedPoolPerMb / 2);
  936. if (MmMaximumNonPagedPoolInBytes < MM_MAX_ADDITIONAL_NONPAGED_POOL) {
  937. MmMaximumNonPagedPoolInBytes = MM_MAX_ADDITIONAL_NONPAGED_POOL;
  938. }
  939. }
  940. else {
  941. MmMaximumNonPagedPoolInBytes +=
  942. ((MmNumberOfPhysicalPages - 1024)/256) *
  943. MmMaxAdditionNonPagedPoolPerMb;
  944. }
  945. if ((MmMaximumNonPagedPoolPercent != 0) &&
  946. (MmMaximumNonPagedPoolInBytes > MaximumNonPagedPoolInBytesLimit)) {
  947. MmMaximumNonPagedPoolInBytes = MaximumNonPagedPoolInBytesLimit;
  948. }
  949. }
  950. MaxPool = MmSizeOfNonPagedPoolInBytes + PAGE_SIZE * 16 +
  951. (ULONG)PAGE_ALIGN (
  952. (MmHighestPossiblePhysicalPage + 1) * sizeof(MMPFN));
  953. if (MmMaximumNonPagedPoolInBytes < MaxPool) {
  954. MmMaximumNonPagedPoolInBytes = MaxPool;
  955. }
  956. //
  957. // Systems that are booted /3GB have a 128MB nonpaged pool maximum,
  958. //
  959. // Systems that have a full 2GB system virtual address space can enjoy an
  960. // extra 128MB of nonpaged pool in the upper GB of the address space.
  961. //
  962. MaxPool = MM_MAX_INITIAL_NONPAGED_POOL;
  963. if (MmVirtualBias == 0) {
  964. MaxPool += MM_MAX_ADDITIONAL_NONPAGED_POOL;
  965. }
  966. if (InitialNonPagedPoolSetViaRegistry == TRUE) {
  967. MaxPool = MmSizeOfNonPagedPoolInBytes + MM_MAX_ADDITIONAL_NONPAGED_POOL;
  968. }
  969. if (MmMaximumNonPagedPoolInBytes > MaxPool) {
  970. MmMaximumNonPagedPoolInBytes = MaxPool;
  971. }
  972. //
  973. // Grow the initial nonpaged pool if necessary so that the overall pool
  974. // will aggregate to the right size.
  975. //
  976. if ((MmMaximumNonPagedPoolInBytes > MM_MAX_INITIAL_NONPAGED_POOL) &&
  977. (InitialNonPagedPoolSetViaRegistry == FALSE)) {
  978. if (MmSizeOfNonPagedPoolInBytes < MmMaximumNonPagedPoolInBytes - MM_MAX_ADDITIONAL_NONPAGED_POOL) {
  979. MmSizeOfNonPagedPoolInBytes = MmMaximumNonPagedPoolInBytes - MM_MAX_ADDITIONAL_NONPAGED_POOL;
  980. }
  981. }
  982. //
  983. // Get secondary color value from:
  984. //
  985. // (a) from the registry (already filled in) or
  986. // (b) from the PCR or
  987. // (c) default value.
  988. //
  989. if (MmSecondaryColors == 0) {
  990. Associativity = KeGetPcr()->SecondLevelCacheAssociativity;
  991. MmSecondaryColors = KeGetPcr()->SecondLevelCacheSize;
  992. if (Associativity != 0) {
  993. MmSecondaryColors /= Associativity;
  994. }
  995. }
  996. MmSecondaryColors = MmSecondaryColors >> PAGE_SHIFT;
  997. if (MmSecondaryColors == 0) {
  998. MmSecondaryColors = MM_SECONDARY_COLORS_DEFAULT;
  999. }
  1000. else {
  1001. //
  1002. // Make sure the value is a power of two and within limits.
  1003. //
  1004. if (((MmSecondaryColors & (MmSecondaryColors - 1)) != 0) ||
  1005. (MmSecondaryColors < MM_SECONDARY_COLORS_MIN) ||
  1006. (MmSecondaryColors > MM_SECONDARY_COLORS_MAX)) {
  1007. MmSecondaryColors = MM_SECONDARY_COLORS_DEFAULT;
  1008. }
  1009. }
  1010. MmSecondaryColorMask = MmSecondaryColors - 1;
  1011. #if defined(MI_MULTINODE)
  1012. //
  1013. // Determine the number of bits in MmSecondaryColorMask. This
  1014. // is the number of bits the Node color must be shifted
  1015. // by before it is included in colors.
  1016. //
  1017. i = MmSecondaryColorMask;
  1018. MmSecondaryColorNodeShift = 0;
  1019. while (i != 0) {
  1020. i >>= 1;
  1021. MmSecondaryColorNodeShift += 1;
  1022. }
  1023. //
  1024. // Adjust the number of secondary colors by the number of nodes
  1025. // in the machine. The secondary color mask is NOT adjusted
  1026. // as it is used to control coloring within a node. The node
  1027. // color is added to the color AFTER normal color calculations
  1028. // are performed.
  1029. //
  1030. MmSecondaryColors *= KeNumberNodes;
  1031. for (i = 0; i < KeNumberNodes; i += 1) {
  1032. KeNodeBlock[i]->Color = i;
  1033. KeNodeBlock[i]->MmShiftedColor = i << MmSecondaryColorNodeShift;
  1034. InitializeSListHead(&KeNodeBlock[i]->DeadStackList);
  1035. }
  1036. #endif
  1037. //
  1038. // Add in the PFN database size (based on the number of pages required
  1039. // from page zero to the highest page).
  1040. //
  1041. // Get the number of secondary colors and add the array for tracking
  1042. // secondary colors to the end of the PFN database.
  1043. //
  1044. MxPfnAllocation = 1 + ((((MmHighestPossiblePhysicalPage + 1) * sizeof(MMPFN)) +
  1045. (MmSecondaryColors * sizeof(MMCOLOR_TABLES)*2))
  1046. >> PAGE_SHIFT);
  1047. if (MmVirtualBias != 0) {
  1048. MmMaximumNonPagedPoolInBytes += MxPfnAllocation << PAGE_SHIFT;
  1049. }
  1050. MmNonPagedPoolStart = (PVOID)((ULONG)MmNonPagedPoolEnd
  1051. - MmMaximumNonPagedPoolInBytes);
  1052. MmNonPagedPoolStart = (PVOID)PAGE_ALIGN(MmNonPagedPoolStart);
  1053. MmPageAlignedPoolBase[NonPagedPool] = MmNonPagedPoolStart;
  1054. //
  1055. // Allocate additional paged pool provided it can fit and either the
  1056. // user asked for it or we decide 460MB of PTE space is sufficient.
  1057. //
  1058. //
  1059. // The total PTE allocation is also reduced if we are booted /USERVA=1GB
  1060. // _AND_ large kernel stacks are at most half the normal size. This is
  1061. // because typical Hydra configurations use approximately 10x PTEs compared
  1062. // to paged pool per session. If the stack size is halved, then 5x
  1063. // becomes the ratio. To optimize this in light of the limited 32-bit
  1064. // virtual address space, the system autoconfigures to 1GB+464MB of system
  1065. // PTEs and expands paged pool to approximately 340MB.
  1066. //
  1067. if (MmVirtualBias == 0) {
  1068. if (((MmLargeStackSize <= (32 * 1024 - PAGE_SIZE)) && (MiUseMaximumSystemSpace != 0)) ||
  1069. ((MmSizeOfPagedPoolInBytes == (SIZE_T)-1) ||
  1070. ((MmSizeOfPagedPoolInBytes == 0) &&
  1071. (MmNumberOfPhysicalPages >= (1 * 1024 * 1024 * 1024 / PAGE_SIZE)) &&
  1072. (ExpMultiUserTS == FALSE) &&
  1073. (MiRequestedSystemPtes != (ULONG)-1)))) {
  1074. ExtraSystemCacheViews = FALSE;
  1075. MmNumberOfSystemPtes = 3000;
  1076. PagedPoolMaximumDesired = TRUE;
  1077. MmPagedPoolMaximumDesired = TRUE;
  1078. //
  1079. // Make sure we always allocate extra PTEs later as we have crimped
  1080. // the initial allocation here.
  1081. //
  1082. if (ExpMultiUserTS == FALSE) {
  1083. if (MmNumberOfPhysicalPages <= 0x7F00) {
  1084. MiRequestedSystemPtes = (ULONG)-1;
  1085. }
  1086. }
  1087. }
  1088. }
  1089. //
  1090. // Calculate the starting PDE for the system PTE pool which is
  1091. // right below the nonpaged pool.
  1092. //
  1093. MmNonPagedSystemStart = (PVOID)(((ULONG)MmNonPagedPoolStart -
  1094. ((MmNumberOfSystemPtes + 1) * PAGE_SIZE)) &
  1095. (~PAGE_DIRECTORY_MASK));
  1096. if (MmNonPagedSystemStart < MM_LOWEST_NONPAGED_SYSTEM_START) {
  1097. MmNonPagedSystemStart = MM_LOWEST_NONPAGED_SYSTEM_START;
  1098. MmNumberOfSystemPtes = (((ULONG)MmNonPagedPoolStart -
  1099. (ULONG)MmNonPagedSystemStart) >> PAGE_SHIFT)-1;
  1100. ASSERT (MmNumberOfSystemPtes > 1000);
  1101. }
  1102. InitialPagedPoolSize = (ULONG) MmNonPagedSystemStart - (ULONG) MM_PAGED_POOL_START;
  1103. if ((MmVirtualBias == 0) &&
  1104. (MmPagedPoolMaximumDesired == FALSE) &&
  1105. (MmSizeOfPagedPoolInBytes > InitialPagedPoolSize)) {
  1106. ULONG OldNonPagedSystemStart;
  1107. ULONG ExtraPtesNeeded;
  1108. MmSizeOfPagedPoolInBytes = MI_ROUND_TO_SIZE (MmSizeOfPagedPoolInBytes, MM_VA_MAPPED_BY_PDE);
  1109. //
  1110. // Recalculate the starting PDE for the system PTE pool which is
  1111. // right below the nonpaged pool. Leave at least 3000 high system PTEs.
  1112. //
  1113. OldNonPagedSystemStart = (ULONG) MmNonPagedSystemStart;
  1114. NonPagedSystemStart = ((ULONG)MmNonPagedPoolStart -
  1115. ((3000 + 1) * PAGE_SIZE)) &
  1116. ~PAGE_DIRECTORY_MASK;
  1117. if (NonPagedSystemStart < (ULONG) MM_LOWEST_NONPAGED_SYSTEM_START) {
  1118. NonPagedSystemStart = (ULONG) MM_LOWEST_NONPAGED_SYSTEM_START;
  1119. }
  1120. InitialPagedPoolSize = NonPagedSystemStart - (ULONG) MM_PAGED_POOL_START;
  1121. if (MmSizeOfPagedPoolInBytes > InitialPagedPoolSize) {
  1122. MmSizeOfPagedPoolInBytes = InitialPagedPoolSize;
  1123. }
  1124. else {
  1125. NonPagedSystemStart = ((ULONG) MM_PAGED_POOL_START +
  1126. MmSizeOfPagedPoolInBytes);
  1127. ASSERT ((NonPagedSystemStart & PAGE_DIRECTORY_MASK) == 0);
  1128. ASSERT (NonPagedSystemStart >= (ULONG) MM_LOWEST_NONPAGED_SYSTEM_START);
  1129. }
  1130. ASSERT (NonPagedSystemStart >= OldNonPagedSystemStart);
  1131. ExtraPtesNeeded = (NonPagedSystemStart - OldNonPagedSystemStart) >> PAGE_SHIFT;
  1132. //
  1133. // Note the PagedPoolMaximumDesired local is deliberately not set
  1134. // because we don't want or need to delete PDEs later in this routine.
  1135. // The exact amount has been allocated here.
  1136. // The global MmPagedPoolMaximumDesired is set because other parts
  1137. // of memory management use it to finish sizing properly.
  1138. //
  1139. MmPagedPoolMaximumDesired = TRUE;
  1140. if (ExtraSystemCacheViews == TRUE) {
  1141. if (ExtraPtesNeeded >
  1142. (MmNumberOfSystemPtes + (MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT)) / 5) {
  1143. //
  1144. // Only reclaim extra system cache views for PTE use if
  1145. // more than 20% of the high PTEs were dipped into. Remember
  1146. // that the initial nonpaged pool is going to be moved to low
  1147. // virtual address space and the high alias addresses will be
  1148. // reused for system PTEs as well.
  1149. //
  1150. ExtraSystemCacheViews = FALSE;
  1151. }
  1152. }
  1153. //
  1154. // Make sure we always allocate extra PTEs later as we have crimped
  1155. // the initial allocation here.
  1156. //
  1157. if ((ExtraSystemCacheViews == FALSE) &&
  1158. (ExpMultiUserTS == FALSE) &&
  1159. (MmNumberOfPhysicalPages <= 0x7F00)) {
  1160. MiRequestedSystemPtes = (ULONG)-1;
  1161. }
  1162. MmNonPagedSystemStart = (PVOID) NonPagedSystemStart;
  1163. MmNumberOfSystemPtes = (((ULONG)MmNonPagedPoolStart -
  1164. (ULONG)NonPagedSystemStart) >> PAGE_SHIFT)-1;
  1165. }
  1166. //
  1167. // Set up page table pages to map nonpaged pool and system PTEs.
  1168. //
  1169. StartPde = MiGetPdeAddress (MmNonPagedSystemStart);
  1170. EndPde = MiGetPdeAddress ((PVOID)((PCHAR)MmNonPagedPoolEnd - 1));
  1171. while (StartPde <= EndPde) {
  1172. ASSERT(StartPde->u.Hard.Valid == 0);
  1173. //
  1174. // Map in a page table page.
  1175. //
  1176. TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
  1177. *StartPde = TempPde;
  1178. PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
  1179. RtlZeroMemory (PointerPte, PAGE_SIZE);
  1180. StartPde += 1;
  1181. }
  1182. MiMaximumSystemCacheSizeExtra = 0;
  1183. if (MmVirtualBias == 0) {
  1184. //
  1185. // If the kernel image has not been biased to allow for 3gb of user
  1186. // space, the host processor supports large pages, and the number of
  1187. // physical pages is greater than the threshold, then map the kernel
  1188. // image and HAL into a large page.
  1189. //
  1190. if (MxMapLargePages & MI_LARGE_KERNEL_HAL) {
  1191. //
  1192. // Add the kernel and HAL ranges to the large page ranges.
  1193. //
  1194. i = 0;
  1195. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  1196. for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
  1197. DataTableEntry = CONTAINING_RECORD (NextEntry,
  1198. KLDR_DATA_TABLE_ENTRY,
  1199. InLoadOrderLinks);
  1200. MiLargeVaRanges[MiLargeVaRangeIndex].VirtualAddress = DataTableEntry->DllBase;
  1201. MiLargeVaRanges[MiLargeVaRangeIndex].EndVirtualAddress =
  1202. (PVOID)((ULONG_PTR)DataTableEntry->DllBase + DataTableEntry->SizeOfImage - 1);
  1203. MiLargeVaRangeIndex += 1;
  1204. i += 1;
  1205. if (i == 2) {
  1206. break;
  1207. }
  1208. }
  1209. }
  1210. //
  1211. // If the processor supports large pages and the descriptor has
  1212. // enough contiguous pages for the entire PFN database then use
  1213. // large pages to map it. Regardless of large page support, put
  1214. // the PFN database in low virtual memory just above the loaded images.
  1215. //
  1216. PagesLeft = MxPagesAvailable ();
  1217. if ((MxMapLargePages & (MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL))&&
  1218. (PagesLeft > MxPfnAllocation)) {
  1219. //
  1220. // Allocate the PFN database using large pages as there is enough
  1221. // physically contiguous and decently aligned memory available.
  1222. //
  1223. PfnInLargePages = TRUE;
  1224. FirstPfnDatabasePage = MxGetNextPage (MxPfnAllocation);
  1225. MmPfnDatabase = (PMMPFN)(MM_KSEG0_BASE | MmBootImageSize);
  1226. ASSERT (((ULONG_PTR)MmPfnDatabase & (MM_VA_MAPPED_BY_PDE - 1)) == 0);
  1227. MmPfnDatabase = (PMMPFN) ((ULONG_PTR)MmPfnDatabase + (((FirstPfnDatabasePage & (MM_PFN_MAPPED_BY_PDE - 1))) << PAGE_SHIFT));
  1228. //
  1229. // Add the PFN database range to the large page ranges.
  1230. //
  1231. MiLargeVaRanges[MiLargeVaRangeIndex].VirtualAddress = MmPfnDatabase;
  1232. MiLargeVaRanges[MiLargeVaRangeIndex].EndVirtualAddress =
  1233. (PVOID) (((ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT)) - 1);
  1234. MiLargeVaRangeIndex += 1;
  1235. }
  1236. else {
  1237. MxMapLargePages &= ~(MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL);
  1238. MmPfnDatabase = (PMMPFN)(MM_KSEG0_BASE | MmBootImageSize);
  1239. }
  1240. //
  1241. // The system is booted 2GB, initial nonpaged pool immediately
  1242. // follows the PFN database.
  1243. //
  1244. // Since the PFN database and the initial nonpaged pool are physically
  1245. // adjacent, a single PDE is shared, thus reducing the number of pages
  1246. // that would otherwise might need to be marked as must-be-cachable.
  1247. //
  1248. // Calculate the correct initial nonpaged pool virtual address and
  1249. // maximum size now. Don't allocate pages for any other use at this
  1250. // point to guarantee that the PFN database and nonpaged pool are
  1251. // physically contiguous so large pages can be enabled.
  1252. //
  1253. NonPagedPoolStartLow = (PVOID)((ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT));
  1254. }
  1255. else {
  1256. if ((MxPfnAllocation + 500) * PAGE_SIZE > MmMaximumNonPagedPoolInBytes - MmSizeOfNonPagedPoolInBytes) {
  1257. //
  1258. // Recarve portions of the initial and expansion nonpaged pools
  1259. // so enough expansion PTEs will be available to map the PFN
  1260. // database on large memory systems that are booted /3GB.
  1261. //
  1262. if ((MxPfnAllocation + 500) * PAGE_SIZE < MmSizeOfNonPagedPoolInBytes) {
  1263. MmSizeOfNonPagedPoolInBytes -= ((MxPfnAllocation + 500) * PAGE_SIZE);
  1264. }
  1265. }
  1266. }
  1267. NonPagedPoolStartVirtual = MmNonPagedPoolStart;
  1268. //
  1269. // Systems with extremely large PFN databases (ie: spanning 64GB of RAM)
  1270. // and bumped session space sizes may require a reduction in the initial
  1271. // nonpaged pool size in order to fit.
  1272. //
  1273. if (MmVirtualBias == 0) {
  1274. if (MmSizeOfNonPagedPoolInBytes > MiSystemViewStart - (ULONG_PTR)NonPagedPoolStartLow) {
  1275. NonPagedPoolStartVirtual = (PVOID)((ULONG_PTR)NonPagedPoolStartVirtual + (MmSizeOfNonPagedPoolInBytes - (MiSystemViewStart - (ULONG_PTR)NonPagedPoolStartLow)));
  1276. MmMaximumNonPagedPoolInBytes -= (MmSizeOfNonPagedPoolInBytes - (MiSystemViewStart - (ULONG_PTR)NonPagedPoolStartLow));
  1277. MmSizeOfNonPagedPoolInBytes = MiSystemViewStart - (ULONG_PTR)NonPagedPoolStartLow;
  1278. }
  1279. }
  1280. //
  1281. // Allocate pages and fill in the PTEs for the initial nonpaged pool.
  1282. //
  1283. SavedSize = MmSizeOfNonPagedPoolInBytes;
  1284. PagesNeeded = MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT;
  1285. //
  1286. // Don't ask for more than is reasonable both in terms of physical pages
  1287. // left and virtual space available.
  1288. //
  1289. PagesLeft = MxPagesAvailable ();
  1290. if (PagesNeeded > PagesLeft) {
  1291. PagesNeeded = PagesLeft;
  1292. }
  1293. if (MxMapLargePages & MI_LARGE_NONPAGED_POOL) {
  1294. ASSERT (MmVirtualBias == 0);
  1295. //
  1296. // The PFN database has already been allocated (but not mapped).
  1297. // Shortly we will transition from the descriptors to the real PFN
  1298. // database so eat up the slush now.
  1299. //
  1300. VirtualAddress = (PVOID) ((ULONG_PTR)NonPagedPoolStartLow + (PagesNeeded << PAGE_SHIFT));
  1301. if (((ULONG_PTR)VirtualAddress & (MM_VA_MAPPED_BY_PDE - 1)) &&
  1302. (PagesLeft - PagesNeeded > MM_PFN_MAPPED_BY_PDE) &&
  1303. (MmSizeOfNonPagedPoolInBytes + MM_VA_MAPPED_BY_PDE < MM_MAX_INITIAL_NONPAGED_POOL)) {
  1304. //
  1305. // Expand the initial nonpaged pool to use the slush efficiently.
  1306. //
  1307. VirtualAddress = (PVOID) MI_ROUND_TO_SIZE ((ULONG_PTR)VirtualAddress, MM_VA_MAPPED_BY_PDE);
  1308. PagesNeeded = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)NonPagedPoolStartLow) >> PAGE_SHIFT;
  1309. SavedSize = PagesNeeded << PAGE_SHIFT;
  1310. }
  1311. }
  1312. //
  1313. // Update various globals since the size of initial pool may have
  1314. // changed.
  1315. //
  1316. MmSizeOfNonPagedPoolInBytes = PagesNeeded << PAGE_SHIFT;
  1317. //
  1318. // Allocate the actual pages for the initial nonpaged pool and map them in.
  1319. //
  1320. PageFrameIndex = MxGetNextPage (PagesNeeded);
  1321. FirstNonPagedPoolPage = PageFrameIndex;
  1322. if (MmVirtualBias == 0) {
  1323. if (MxMapLargePages & MI_LARGE_PFN_DATABASE) {
  1324. ASSERT (FirstNonPagedPoolPage == FirstPfnDatabasePage + MxPfnAllocation);
  1325. }
  1326. //
  1327. // Allocate the page table pages to map the PFN database and the
  1328. // initial nonpaged pool now. If the system switches to large
  1329. // pages in Phase 1, these pages will be discarded then.
  1330. //
  1331. StartPde = MiGetPdeAddress (MmPfnDatabase);
  1332. VirtualAddress = (PVOID) ((ULONG_PTR)NonPagedPoolStartLow + MmSizeOfNonPagedPoolInBytes - 1);
  1333. EndPde = MiGetPdeAddress (VirtualAddress);
  1334. //
  1335. // Machines with 128MB or more get to use any extra virtual address
  1336. // space between the top of initial nonpaged pool and session space
  1337. // for additional system PTEs or caching.
  1338. //
  1339. if (MmNumberOfPhysicalPages > 0x7F00) {
  1340. PointerPde = EndPde + 1;
  1341. EndPde = MiGetPdeAddress (MiSystemViewStart - 1);
  1342. if (PointerPde <= EndPde) {
  1343. //
  1344. // There is available virtual space - consume everything up
  1345. // to the system view area (which is always rounded to a page
  1346. // directory boundary to avoid wasting valuable virtual
  1347. // address space.
  1348. //
  1349. MiExtraResourceStart = (ULONG) MiGetVirtualAddressMappedByPde (PointerPde);
  1350. MiExtraResourceEnd = MiSystemViewStart;
  1351. MiNumberOfExtraSystemPdes = EndPde - PointerPde + 1;
  1352. //
  1353. // Mark the new range as PTEs iff maximum PTEs are requested,
  1354. // TS in app server mode is selected or special pooling is
  1355. // enabled. Otherwise if large system caching was selected
  1356. // then use it for that. Finally default to PTEs if neither
  1357. // of the above.
  1358. //
  1359. if ((MiRequestedSystemPtes == (ULONG)-1) ||
  1360. (ExpMultiUserTS == TRUE) ||
  1361. (MmVerifyDriverBufferLength != (ULONG)-1) ||
  1362. ((MmSpecialPoolTag != 0) && (MmSpecialPoolTag != (ULONG)-1))) {
  1363. MiExtraPtes1 = BYTES_TO_PAGES(MiExtraResourceEnd - MiExtraResourceStart);
  1364. }
  1365. else if (ExtraSystemCacheViews == TRUE) {
  1366. //
  1367. // The system is configured to favor large system caching,
  1368. // use the remaining virtual address space for the
  1369. // system cache.
  1370. //
  1371. MiMaximumSystemCacheSizeExtra =
  1372. (MiExtraResourceEnd - MiExtraResourceStart) >> PAGE_SHIFT;
  1373. }
  1374. else {
  1375. MiExtraPtes1 = BYTES_TO_PAGES(MiExtraResourceEnd - MiExtraResourceStart);
  1376. }
  1377. }
  1378. }
  1379. //
  1380. // Allocate and initialize the page table pages.
  1381. //
  1382. while (StartPde <= EndPde) {
  1383. ASSERT (StartPde->u.Hard.Valid == 0);
  1384. if (StartPde->u.Hard.Valid == 0) {
  1385. //
  1386. // Map in a page directory page.
  1387. //
  1388. TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
  1389. *StartPde = TempPde;
  1390. PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
  1391. RtlZeroMemory (PointerPte, PAGE_SIZE);
  1392. }
  1393. StartPde += 1;
  1394. }
  1395. if (MiUseMaximumSystemSpace != 0) {
  1396. //
  1397. // Use the 1GB->2GB virtual range for even more system PTEs.
  1398. // Note the shared user data PTE (and PDE) must be left user
  1399. // accessible, but everything else is kernelmode only.
  1400. //
  1401. MiExtraPtes2 = BYTES_TO_PAGES(MiUseMaximumSystemSpaceEnd - MiUseMaximumSystemSpace);
  1402. StartPde = MiGetPdeAddress (MiUseMaximumSystemSpace);
  1403. EndPde = MiGetPdeAddress (MiUseMaximumSystemSpaceEnd);
  1404. while (StartPde < EndPde) {
  1405. ASSERT (StartPde->u.Hard.Valid == 0);
  1406. //
  1407. // Map in a page directory page.
  1408. //
  1409. TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
  1410. *StartPde = TempPde;
  1411. PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
  1412. RtlZeroMemory (PointerPte, PAGE_SIZE);
  1413. StartPde += 1;
  1414. MiMaximumSystemExtraSystemPdes += 1;
  1415. }
  1416. ASSERT (MiExtraPtes2 == MiMaximumSystemExtraSystemPdes * PTE_PER_PAGE);
  1417. }
  1418. NonPagedPoolStartVirtual = (PVOID)((PCHAR)NonPagedPoolStartVirtual +
  1419. MmSizeOfNonPagedPoolInBytes);
  1420. //
  1421. // The virtual address, length and page tables to map the initial
  1422. // nonpaged pool are already allocated - just fill in the mappings.
  1423. //
  1424. MmNonPagedPoolStart = NonPagedPoolStartLow;
  1425. MmSubsectionBase = (ULONG)MmNonPagedPoolStart;
  1426. PointerPte = MiGetPteAddress (MmNonPagedPoolStart);
  1427. LastPte = MiGetPteAddress ((ULONG)MmNonPagedPoolStart +
  1428. MmSizeOfNonPagedPoolInBytes);
  1429. if (MxMapLargePages & (MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL)) {
  1430. //
  1431. // Since every page table page needs to be filled, ensure PointerPte
  1432. // and LastPte span entire page table pages, and adjust
  1433. // PageFrameIndex to account for this.
  1434. //
  1435. if (!MiIsPteOnPdeBoundary(PointerPte)) {
  1436. PageFrameIndex -= (BYTE_OFFSET (PointerPte) / sizeof (MMPTE));
  1437. PointerPte = PAGE_ALIGN (PointerPte);
  1438. }
  1439. if (!MiIsPteOnPdeBoundary(LastPte)) {
  1440. LastPte = (PMMPTE) (PAGE_ALIGN (LastPte)) + PTE_PER_PAGE;
  1441. }
  1442. //
  1443. // Add the initial nonpaged pool range to the large page ranges.
  1444. //
  1445. MiLargeVaRanges[MiLargeVaRangeIndex].VirtualAddress = MmNonPagedPoolStart;
  1446. MiLargeVaRanges[MiLargeVaRangeIndex].EndVirtualAddress =
  1447. (PVOID) ((ULONG_PTR)MmNonPagedPoolStart + MmSizeOfNonPagedPoolInBytes - 1);
  1448. MiLargeVaRangeIndex += 1;
  1449. }
  1450. while (PointerPte < LastPte) {
  1451. ASSERT (PointerPte->u.Hard.Valid == 0);
  1452. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  1453. *PointerPte = TempPte;
  1454. PointerPte += 1;
  1455. PageFrameIndex += 1;
  1456. }
  1457. MmNonPagedPoolExpansionStart = (PVOID)((PCHAR)NonPagedPoolStartVirtual +
  1458. (SavedSize - MmSizeOfNonPagedPoolInBytes));
  1459. }
  1460. else {
  1461. PointerPte = MiGetPteAddress (MmNonPagedPoolStart);
  1462. LastPte = MiGetPteAddress((ULONG)MmNonPagedPoolStart +
  1463. MmSizeOfNonPagedPoolInBytes - 1);
  1464. ASSERT (PagesNeeded == (PFN_NUMBER)(LastPte - PointerPte + 1));
  1465. while (PointerPte <= LastPte) {
  1466. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  1467. *PointerPte = TempPte;
  1468. PointerPte += 1;
  1469. PageFrameIndex += 1;
  1470. }
  1471. SavedSize = MmSizeOfNonPagedPoolInBytes;
  1472. MmNonPagedPoolExpansionStart = (PVOID)((PCHAR)NonPagedPoolStartVirtual +
  1473. MmSizeOfNonPagedPoolInBytes);
  1474. //
  1475. // When booted /3GB, if /USERVA was specified then use any leftover
  1476. // virtual space between 2 and 3gb for extra system PTEs.
  1477. //
  1478. if (MiUseMaximumSystemSpace != 0) {
  1479. MiExtraPtes2 = BYTES_TO_PAGES(MiUseMaximumSystemSpaceEnd - MiUseMaximumSystemSpace);
  1480. StartPde = MiGetPdeAddress (MiUseMaximumSystemSpace);
  1481. EndPde = MiGetPdeAddress (MiUseMaximumSystemSpaceEnd);
  1482. while (StartPde < EndPde) {
  1483. ASSERT (StartPde->u.Hard.Valid == 0);
  1484. //
  1485. // Map in a page directory page.
  1486. //
  1487. TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
  1488. *StartPde = TempPde;
  1489. PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
  1490. RtlZeroMemory (PointerPte, PAGE_SIZE);
  1491. StartPde += 1;
  1492. MiMaximumSystemExtraSystemPdes += 1;
  1493. }
  1494. ASSERT (MiExtraPtes2 == MiMaximumSystemExtraSystemPdes * PTE_PER_PAGE);
  1495. }
  1496. }
  1497. //
  1498. // There must be at least one page of system PTEs before the expanded
  1499. // nonpaged pool.
  1500. //
  1501. ASSERT (MiGetPteAddress(MmNonPagedSystemStart) < MiGetPteAddress(MmNonPagedPoolExpansionStart));
  1502. //
  1503. // Non-paged pages now exist, build the pool structures.
  1504. //
  1505. MmPageAlignedPoolBase[NonPagedPool] = MmNonPagedPoolStart;
  1506. MmMaximumNonPagedPoolInBytes -= (SavedSize - MmSizeOfNonPagedPoolInBytes);
  1507. MiInitializeNonPagedPool ();
  1508. MmMaximumNonPagedPoolInBytes += (SavedSize - MmSizeOfNonPagedPoolInBytes);
  1509. //
  1510. // Before nonpaged pool can be used, the PFN database must
  1511. // be built. This is due to the fact that the start and end of
  1512. // allocation bits for nonpaged pool are maintained in the
  1513. // PFN elements for the corresponding pages.
  1514. //
  1515. if (MxMapLargePages & MI_LARGE_PFN_DATABASE) {
  1516. //
  1517. // The physical pages to be used for the PFN database have already
  1518. // been allocated. Initialize their mappings now.
  1519. //
  1520. //
  1521. // Initialize the page table mappings (the directory mappings are
  1522. // already initialized) for the PFN database until the switch to large
  1523. // pages occurs in Phase 1.
  1524. //
  1525. PointerPte = MiGetPteAddress (MmPfnDatabase);
  1526. BasePte = MiGetVirtualAddressMappedByPte (MiGetPdeAddress (MmPfnDatabase));
  1527. LastPte = MiGetPteAddress ((ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT));
  1528. if (!MiIsPteOnPdeBoundary(LastPte)) {
  1529. LastPte = MiGetVirtualAddressMappedByPte (MiGetPteAddress (LastPte) + 1);
  1530. }
  1531. PageFrameIndex = FirstPfnDatabasePage - (PointerPte - BasePte);
  1532. PointerPte = BasePte;
  1533. while (PointerPte < LastPte) {
  1534. ASSERT ((PointerPte->u.Hard.Valid == 0) ||
  1535. (PointerPte->u.Hard.PageFrameNumber == PageFrameIndex));
  1536. if (MiIsPteOnPdeBoundary(PointerPte)) {
  1537. ASSERT ((PageFrameIndex & (MM_PFN_MAPPED_BY_PDE - 1)) == 0);
  1538. }
  1539. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  1540. *PointerPte = TempPte;
  1541. PointerPte += 1;
  1542. PageFrameIndex += 1;
  1543. }
  1544. RtlZeroMemory (MmPfnDatabase, MxPfnAllocation << PAGE_SHIFT);
  1545. //
  1546. // The PFN database was allocated in large pages. Since space was left
  1547. // for it virtually (in the nonpaged pool expansion PTEs), remove this
  1548. // now unused space if it can cause PTE encoding to exceed the 27 bits.
  1549. //
  1550. if (MmTotalFreeSystemPtes[NonPagedPoolExpansion] >
  1551. (MM_MAX_ADDITIONAL_NONPAGED_POOL >> PAGE_SHIFT)) {
  1552. //
  1553. // Reserve the expanded pool PTEs so they cannot be used.
  1554. //
  1555. ULONG PfnDatabaseSpace;
  1556. PfnDatabaseSpace = MmTotalFreeSystemPtes[NonPagedPoolExpansion] -
  1557. (MM_MAX_ADDITIONAL_NONPAGED_POOL >> PAGE_SHIFT);
  1558. if (MiReserveSystemPtes (PfnDatabaseSpace, NonPagedPoolExpansion) == NULL) {
  1559. MiIssueNoPtesBugcheck (PfnDatabaseSpace, NonPagedPoolExpansion);
  1560. }
  1561. //
  1562. // Adjust the end of nonpaged pool to reflect this reservation.
  1563. // This is so the entire nonpaged pool expansion space is available
  1564. // not just for general purpose consumption, but also for subsection
  1565. // encoding into protoptes when subsections are allocated from the
  1566. // very end of the expansion range.
  1567. //
  1568. MmNonPagedPoolEnd = (PVOID)((PCHAR)MmNonPagedPoolEnd - PfnDatabaseSpace * PAGE_SIZE);
  1569. }
  1570. else {
  1571. //
  1572. // Allocate one more PTE just below the PFN database. This provides
  1573. // protection against the caller of the first real nonpaged
  1574. // expansion allocation in case he accidentally overruns his pool
  1575. // block. (We'll trap instead of corrupting the PFN database).
  1576. // This also allows us to freely increment in MiFreePoolPages
  1577. // without having to worry about a valid PTE just after the end of
  1578. // the highest nonpaged pool allocation.
  1579. //
  1580. if (MiReserveSystemPtes (1, NonPagedPoolExpansion) == NULL) {
  1581. MiIssueNoPtesBugcheck (1, NonPagedPoolExpansion);
  1582. }
  1583. }
  1584. }
  1585. else {
  1586. ULONG FreeNextPhysicalPage;
  1587. ULONG FreeNumberOfPages;
  1588. //
  1589. // Calculate the start of the PFN database (it starts at physical
  1590. // page zero, even if the lowest physical page is not zero).
  1591. //
  1592. if (MmVirtualBias == 0) {
  1593. ASSERT (MmPfnDatabase != NULL);
  1594. PointerPte = MiGetPteAddress (MmPfnDatabase);
  1595. }
  1596. else {
  1597. ASSERT (MxPagesAvailable () >= MxPfnAllocation);
  1598. PointerPte = MiReserveSystemPtes (MxPfnAllocation,
  1599. NonPagedPoolExpansion);
  1600. if (PointerPte == NULL) {
  1601. MiIssueNoPtesBugcheck (MxPfnAllocation, NonPagedPoolExpansion);
  1602. }
  1603. MmPfnDatabase = (PMMPFN)(MiGetVirtualAddressMappedByPte (PointerPte));
  1604. //
  1605. // Adjust the end of nonpaged pool to reflect the PFN database
  1606. // allocation. This is so the entire nonpaged pool expansion space
  1607. // is available not just for general purpose consumption, but also
  1608. // for subsection encoding into protoptes when subsections are
  1609. // allocated from the very beginning of the initial nonpaged pool
  1610. // range.
  1611. //
  1612. MmNonPagedPoolEnd = (PVOID)MmPfnDatabase;
  1613. //
  1614. // Allocate one more PTE just below the PFN database. This provides
  1615. // protection against the caller of the first real nonpaged
  1616. // expansion allocation in case he accidentally overruns his pool
  1617. // block. (We'll trap instead of corrupting the PFN database).
  1618. // This also allows us to freely increment in MiFreePoolPages
  1619. // without having to worry about a valid PTE just after the end of
  1620. // the highest nonpaged pool allocation.
  1621. //
  1622. if (MiReserveSystemPtes (1, NonPagedPoolExpansion) == NULL) {
  1623. MiIssueNoPtesBugcheck (1, NonPagedPoolExpansion);
  1624. }
  1625. }
  1626. //
  1627. // Go through the memory descriptors and for each physical page make
  1628. // sure the PFN database has a valid PTE to map it. This allows
  1629. // machines with sparse physical memory to have a minimal PFN database.
  1630. //
  1631. FreeNextPhysicalPage = MxFreeDescriptor->BasePage;
  1632. FreeNumberOfPages = MxFreeDescriptor->PageCount;
  1633. PagesLeft = 0;
  1634. NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
  1635. while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
  1636. MemoryDescriptor = CONTAINING_RECORD(NextMd,
  1637. MEMORY_ALLOCATION_DESCRIPTOR,
  1638. ListEntry);
  1639. if ((MemoryDescriptor->MemoryType == LoaderFirmwarePermanent) ||
  1640. (MemoryDescriptor->MemoryType == LoaderBBTMemory) ||
  1641. (MemoryDescriptor->MemoryType == LoaderSpecialMemory)) {
  1642. //
  1643. // Skip these ranges.
  1644. //
  1645. NextMd = MemoryDescriptor->ListEntry.Flink;
  1646. continue;
  1647. }
  1648. //
  1649. // Temporarily add back in the memory allocated since Phase 0
  1650. // began so PFN entries for it will be created and mapped.
  1651. //
  1652. // Note actual PFN entry allocations must be done carefully as
  1653. // memory from the descriptor itself could get used to map
  1654. // the PFNs for the descriptor !
  1655. //
  1656. if (MemoryDescriptor == MxFreeDescriptor) {
  1657. BasePage = MxOldFreeDescriptor.BasePage;
  1658. PageCount = MxOldFreeDescriptor.PageCount;
  1659. }
  1660. else {
  1661. BasePage = MemoryDescriptor->BasePage;
  1662. PageCount = MemoryDescriptor->PageCount;
  1663. }
  1664. PointerPte = MiGetPteAddress (MI_PFN_ELEMENT(BasePage));
  1665. LastPte = MiGetPteAddress (((PCHAR)(MI_PFN_ELEMENT(
  1666. BasePage + PageCount))) - 1);
  1667. while (PointerPte <= LastPte) {
  1668. if (PointerPte->u.Hard.Valid == 0) {
  1669. TempPte.u.Hard.PageFrameNumber = FreeNextPhysicalPage;
  1670. ASSERT (FreeNumberOfPages != 0);
  1671. FreeNextPhysicalPage += 1;
  1672. FreeNumberOfPages -= 1;
  1673. if (FreeNumberOfPages == 0) {
  1674. KeBugCheckEx (INSTALL_MORE_MEMORY,
  1675. MmNumberOfPhysicalPages,
  1676. FreeNumberOfPages,
  1677. MxOldFreeDescriptor.PageCount,
  1678. 1);
  1679. }
  1680. PagesLeft += 1;
  1681. *PointerPte = TempPte;
  1682. RtlZeroMemory (MiGetVirtualAddressMappedByPte (PointerPte),
  1683. PAGE_SIZE);
  1684. }
  1685. PointerPte += 1;
  1686. }
  1687. NextMd = MemoryDescriptor->ListEntry.Flink;
  1688. }
  1689. //
  1690. // Update the global counts - this would have been tricky to do while
  1691. // removing pages from them as we looped above.
  1692. //
  1693. // Later we will walk the memory descriptors and add pages to the free
  1694. // list in the PFN database.
  1695. //
  1696. // To do this correctly:
  1697. //
  1698. // The FreeDescriptor fields must be updated so the PFN database
  1699. // consumption isn't added to the freelist.
  1700. //
  1701. MxFreeDescriptor->BasePage = FreeNextPhysicalPage;
  1702. MxFreeDescriptor->PageCount = FreeNumberOfPages;
  1703. }
  1704. if (MmVirtualBias != 0) {
  1705. MmAllocatedNonPagedPool += MxPfnAllocation;
  1706. }
  1707. #if defined (_X86PAE_)
  1708. for (i = 0; i < 32; i += 1) {
  1709. j = i & 7;
  1710. switch (j) {
  1711. case MM_READONLY:
  1712. case MM_READWRITE:
  1713. case MM_WRITECOPY:
  1714. MmProtectToPteMask[i] |= MmPaeMask;
  1715. break;
  1716. default:
  1717. break;
  1718. }
  1719. }
  1720. #endif
  1721. //
  1722. // Initialize support for colored pages.
  1723. //
  1724. MmFreePagesByColor[0] = (PMMCOLOR_TABLES)
  1725. &MmPfnDatabase[MmHighestPossiblePhysicalPage + 1];
  1726. MmFreePagesByColor[1] = &MmFreePagesByColor[0][MmSecondaryColors];
  1727. //
  1728. // Make sure the PTEs are mapped.
  1729. //
  1730. PointerPte = MiGetPteAddress (&MmFreePagesByColor[0][0]);
  1731. LastPte = MiGetPteAddress (
  1732. (PVOID)((PCHAR)&MmFreePagesByColor[1][MmSecondaryColors] - 1));
  1733. while (PointerPte <= LastPte) {
  1734. if (PointerPte->u.Hard.Valid == 0) {
  1735. TempPte.u.Hard.PageFrameNumber = MxGetNextPage (1);
  1736. *PointerPte = TempPte;
  1737. RtlZeroMemory (MiGetVirtualAddressMappedByPte (PointerPte),
  1738. PAGE_SIZE);
  1739. }
  1740. PointerPte += 1;
  1741. }
  1742. for (i = 0; i < MmSecondaryColors; i += 1) {
  1743. MmFreePagesByColor[ZeroedPageList][i].Flink = MM_EMPTY_LIST;
  1744. MmFreePagesByColor[ZeroedPageList][i].Count = 0;
  1745. MmFreePagesByColor[FreePageList][i].Flink = MM_EMPTY_LIST;
  1746. MmFreePagesByColor[FreePageList][i].Count = 0;
  1747. }
  1748. //
  1749. // Add nonpaged pool to PFN database if mapped via large pages.
  1750. //
  1751. PointerPde = MiGetPdeAddress (PTE_BASE);
  1752. if ((MmNonPagedPoolStart < (PVOID)MM_SYSTEM_CACHE_END_EXTRA) &&
  1753. (MxMapLargePages & MI_LARGE_NONPAGED_POOL)) {
  1754. j = FirstNonPagedPoolPage;
  1755. Pfn1 = MI_PFN_ELEMENT (j);
  1756. i = MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT;
  1757. do {
  1758. PointerPde = MiGetPdeAddress ((ULONG_PTR)MmNonPagedPoolStart + ((j - FirstNonPagedPoolPage) << PAGE_SHIFT));
  1759. Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
  1760. Pfn1->PteAddress = (PMMPTE)(j << PAGE_SHIFT);
  1761. Pfn1->u2.ShareCount += 1;
  1762. Pfn1->u3.e2.ReferenceCount = 1;
  1763. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1764. Pfn1->u3.e1.CacheAttribute = MiCached;
  1765. MiDetermineNode (j, Pfn1);
  1766. j += 1;
  1767. Pfn1 += 1;
  1768. i -= 1;
  1769. } while (i != 0);
  1770. }
  1771. //
  1772. // Go through the page table entries and for any page which is valid,
  1773. // update the corresponding PFN database element.
  1774. //
  1775. Pde = MiGetPdeAddress (NULL);
  1776. va = 0;
  1777. PdeCount = PD_PER_SYSTEM * PDE_PER_PAGE;
  1778. for (i = 0; i < PdeCount; i += 1) {
  1779. //
  1780. // If the kernel image has been biased to allow for 3gb of user
  1781. // address space, then the first several mb of memory is
  1782. // double mapped to KSEG0_BASE and to ALTERNATE_BASE. Therefore,
  1783. // the KSEG0_BASE entries must be skipped.
  1784. //
  1785. if (MmVirtualBias != 0) {
  1786. if ((Pde >= MiGetPdeAddress(KSEG0_BASE)) &&
  1787. (Pde < MiGetPdeAddress(KSEG0_BASE + MmBootImageSize))) {
  1788. Pde += 1;
  1789. va += (ULONG)PDE_PER_PAGE * (ULONG)PAGE_SIZE;
  1790. continue;
  1791. }
  1792. }
  1793. if ((Pde->u.Hard.Valid == 1) && (Pde->u.Hard.LargePage == 0)) {
  1794. PdePage = MI_GET_PAGE_FRAME_FROM_PTE(Pde);
  1795. Pfn1 = MI_PFN_ELEMENT(PdePage);
  1796. Pfn1->u4.PteFrame = PdePageNumber;
  1797. Pfn1->PteAddress = Pde;
  1798. Pfn1->u2.ShareCount += 1;
  1799. Pfn1->u3.e2.ReferenceCount = 1;
  1800. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1801. Pfn1->u3.e1.CacheAttribute = MiCached;
  1802. MiDetermineNode (PdePage, Pfn1);
  1803. PointerPte = MiGetPteAddress (va);
  1804. //
  1805. // Set global bit.
  1806. //
  1807. Pde->u.Long |= MiDetermineUserGlobalPteMask (PointerPte) &
  1808. ~MM_PTE_ACCESS_MASK;
  1809. for (j = 0 ; j < PTE_PER_PAGE; j += 1) {
  1810. if (PointerPte->u.Hard.Valid == 1) {
  1811. PointerPte->u.Long |= MiDetermineUserGlobalPteMask (PointerPte) &
  1812. ~MM_PTE_ACCESS_MASK;
  1813. Pfn1->u2.ShareCount += 1;
  1814. if ((PointerPte->u.Hard.PageFrameNumber <= MmHighestPhysicalPage) &&
  1815. ((va >= MM_KSEG2_BASE) &&
  1816. ((va < KSEG0_BASE + MmVirtualBias) ||
  1817. (va >= (KSEG0_BASE + MmVirtualBias + MmBootImageSize)))) ||
  1818. ((MmVirtualBias == 0) &&
  1819. (va >= (ULONG)MmNonPagedPoolStart) &&
  1820. (va < (ULONG)MmNonPagedPoolStart + MmSizeOfNonPagedPoolInBytes))) {
  1821. Pfn2 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
  1822. if (MmIsAddressValid(Pfn2) &&
  1823. MmIsAddressValid((PUCHAR)(Pfn2+1)-1)) {
  1824. Pfn2->u4.PteFrame = PdePage;
  1825. Pfn2->PteAddress = PointerPte;
  1826. Pfn2->u2.ShareCount += 1;
  1827. Pfn2->u3.e2.ReferenceCount = 1;
  1828. Pfn2->u3.e1.PageLocation = ActiveAndValid;
  1829. Pfn2->u3.e1.CacheAttribute = MiCached;
  1830. MiDetermineNode(
  1831. (PFN_NUMBER)PointerPte->u.Hard.PageFrameNumber,
  1832. Pfn2);
  1833. }
  1834. }
  1835. }
  1836. va += PAGE_SIZE;
  1837. PointerPte += 1;
  1838. }
  1839. }
  1840. else {
  1841. va += (ULONG)PDE_PER_PAGE * (ULONG)PAGE_SIZE;
  1842. }
  1843. Pde += 1;
  1844. }
  1845. KeFlushCurrentTb ();
  1846. //
  1847. // If the lowest physical page is zero and the page is still unused, mark
  1848. // it as in use. This is temporary as we want to find bugs where a physical
  1849. // page is specified as zero.
  1850. //
  1851. Pfn1 = &MmPfnDatabase[MmLowestPhysicalPage];
  1852. if ((MmLowestPhysicalPage == 0) && (Pfn1->u3.e2.ReferenceCount == 0)) {
  1853. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1854. //
  1855. // Make the reference count non-zero and point it into a
  1856. // page directory.
  1857. //
  1858. Pde = MiGetPdeAddress (0xffffffff);
  1859. PdePage = MI_GET_PAGE_FRAME_FROM_PTE(Pde);
  1860. Pfn1->u4.PteFrame = PdePageNumber;
  1861. Pfn1->PteAddress = Pde;
  1862. Pfn1->u2.ShareCount += 1;
  1863. Pfn1->u3.e2.ReferenceCount = 0xfff0;
  1864. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1865. Pfn1->u3.e1.CacheAttribute = MiCached;
  1866. MiDetermineNode (0, Pfn1);
  1867. }
  1868. // end of temporary set to physical page zero.
  1869. //
  1870. // Walk through the memory descriptors and add pages to the
  1871. // free list in the PFN database. Before doing this, adjust the
  1872. // two descriptors we used so they only contain memory that can be
  1873. // freed now (ie: any memory we removed from them earlier in this routine
  1874. // without updating the descriptor for must be updated now).
  1875. //
  1876. //
  1877. // We may have taken memory out of the MxFreeDescriptor - but
  1878. // that's ok because we wouldn't want to free that memory right now
  1879. // (or ever) anyway.
  1880. //
  1881. //
  1882. // Since the LoaderBlock memory descriptors are ordered
  1883. // from low physical memory address to high, walk it backwards so the
  1884. // high physical pages go to the front of the freelists. The thinking
  1885. // is that pages initially allocated by the system are less likely to be
  1886. // freed so don't waste memory below 16mb (or 4gb) that may be needed
  1887. // by ISA drivers later.
  1888. //
  1889. NextMd = LoaderBlock->MemoryDescriptorListHead.Blink;
  1890. Bias = 0;
  1891. if (MmVirtualBias != 0) {
  1892. //
  1893. // This is nasty. You don't want to know. Cleanup needed.
  1894. //
  1895. Bias = ALTERNATE_BASE - KSEG0_BASE;
  1896. }
  1897. while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
  1898. MemoryDescriptor = CONTAINING_RECORD(NextMd,
  1899. MEMORY_ALLOCATION_DESCRIPTOR,
  1900. ListEntry);
  1901. i = MemoryDescriptor->PageCount;
  1902. PageFrameIndex = MemoryDescriptor->BasePage;
  1903. //
  1904. // Ensure no frames are inserted beyond the end of the PFN
  1905. // database. This can happen for example if the system
  1906. // has > 16GB of RAM and is booted /3GB - the top of this
  1907. // routine reduces the highest physical page and then
  1908. // creates the PFN database. But the loader block still
  1909. // contains descriptions of the pages above 16GB.
  1910. //
  1911. if (PageFrameIndex > MmHighestPhysicalPage) {
  1912. NextMd = MemoryDescriptor->ListEntry.Blink;
  1913. continue;
  1914. }
  1915. if (PageFrameIndex + i > MmHighestPhysicalPage + 1) {
  1916. i = MmHighestPhysicalPage + 1 - PageFrameIndex;
  1917. MemoryDescriptor->PageCount = i;
  1918. if (i == 0) {
  1919. NextMd = MemoryDescriptor->ListEntry.Blink;
  1920. continue;
  1921. }
  1922. }
  1923. switch (MemoryDescriptor->MemoryType) {
  1924. case LoaderBad:
  1925. while (i != 0) {
  1926. MiInsertPageInList (&MmBadPageListHead, PageFrameIndex);
  1927. i -= 1;
  1928. PageFrameIndex += 1;
  1929. }
  1930. break;
  1931. case LoaderFree:
  1932. case LoaderLoadedProgram:
  1933. case LoaderFirmwareTemporary:
  1934. case LoaderOsloaderStack:
  1935. FreePfnCount = 0;
  1936. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1937. while (i != 0) {
  1938. if (Pfn1->u3.e2.ReferenceCount == 0) {
  1939. //
  1940. // Set the PTE address to the physical page for
  1941. // virtual address alignment checking.
  1942. //
  1943. Pfn1->PteAddress =
  1944. (PMMPTE)(PageFrameIndex << PTE_SHIFT);
  1945. //
  1946. // No need to initialize Pfn1->u3.e1.CacheAttribute
  1947. // here as the freelist insertion will mark it as
  1948. // not-mapped.
  1949. //
  1950. MiDetermineNode (PageFrameIndex, Pfn1);
  1951. MiInsertPageInFreeList (PageFrameIndex);
  1952. FreePfnCount += 1;
  1953. }
  1954. else {
  1955. if (FreePfnCount > LargestFreePfnCount) {
  1956. LargestFreePfnCount = FreePfnCount;
  1957. LargestFreePfnStart = PageFrameIndex - FreePfnCount;
  1958. FreePfnCount = 0;
  1959. }
  1960. }
  1961. Pfn1 += 1;
  1962. i -= 1;
  1963. PageFrameIndex += 1;
  1964. }
  1965. if (FreePfnCount > LargestFreePfnCount) {
  1966. LargestFreePfnCount = FreePfnCount;
  1967. LargestFreePfnStart = PageFrameIndex - FreePfnCount;
  1968. }
  1969. break;
  1970. case LoaderFirmwarePermanent:
  1971. case LoaderSpecialMemory:
  1972. case LoaderBBTMemory:
  1973. //
  1974. // Skip these ranges.
  1975. //
  1976. break;
  1977. default:
  1978. PointerPte = MiGetPteAddress (KSEG0_BASE + Bias +
  1979. (PageFrameIndex << PAGE_SHIFT));
  1980. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1981. while (i != 0) {
  1982. //
  1983. // Set page as in use.
  1984. //
  1985. PointerPde = MiGetPdeAddress (KSEG0_BASE + Bias +
  1986. (PageFrameIndex << PAGE_SHIFT));
  1987. if (Pfn1->u3.e2.ReferenceCount == 0) {
  1988. Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
  1989. Pfn1->PteAddress = PointerPte;
  1990. Pfn1->u2.ShareCount += 1;
  1991. Pfn1->u3.e2.ReferenceCount = 1;
  1992. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1993. MiDetermineNode (PageFrameIndex, Pfn1);
  1994. if (MemoryDescriptor->MemoryType == LoaderXIPRom) {
  1995. Pfn1->u1.Flink = 0;
  1996. Pfn1->u2.ShareCount = 0;
  1997. Pfn1->u3.e2.ReferenceCount = 0;
  1998. Pfn1->u3.e1.PageLocation = 0;
  1999. Pfn1->u3.e1.Rom = 1;
  2000. Pfn1->u4.InPageError = 0;
  2001. Pfn1->u3.e1.PrototypePte = 1;
  2002. }
  2003. Pfn1->u3.e1.CacheAttribute = MiCached;
  2004. }
  2005. Pfn1 += 1;
  2006. i -= 1;
  2007. PageFrameIndex += 1;
  2008. PointerPte += 1;
  2009. }
  2010. break;
  2011. }
  2012. NextMd = MemoryDescriptor->ListEntry.Blink;
  2013. }
  2014. if (PfnInLargePages == FALSE) {
  2015. //
  2016. // Indicate that the PFN database is allocated in nonpaged pool.
  2017. //
  2018. PointerPte = MiGetPteAddress (&MmPfnDatabase[MmLowestPhysicalPage]);
  2019. Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
  2020. Pfn1->u3.e1.StartOfAllocation = 1;
  2021. if (MmVirtualBias == 0) {
  2022. LastPte = MiGetPteAddress (&MmPfnDatabase[MmHighestPossiblePhysicalPage]);
  2023. while (PointerPte <= LastPte) {
  2024. Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
  2025. Pfn1->u2.ShareCount = 1;
  2026. Pfn1->u3.e2.ReferenceCount = 1;
  2027. PointerPte += 1;
  2028. }
  2029. }
  2030. //
  2031. // Set the end of the allocation.
  2032. //
  2033. PointerPte = MiGetPteAddress (&MmPfnDatabase[MmHighestPossiblePhysicalPage]);
  2034. Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
  2035. Pfn1->u3.e1.EndOfAllocation = 1;
  2036. }
  2037. else {
  2038. //
  2039. // The PFN database is allocated using large pages.
  2040. //
  2041. // Mark all PFN entries for the PFN pages in use.
  2042. //
  2043. PointerPte = MiGetPteAddress (MmPfnDatabase);
  2044. PageFrameIndex = (PFN_NUMBER)PointerPte->u.Hard.PageFrameNumber;
  2045. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  2046. i = MxPfnAllocation;
  2047. do {
  2048. Pfn1->PteAddress = (PMMPTE)(PageFrameIndex << PTE_SHIFT);
  2049. Pfn1->u3.e1.CacheAttribute = MiCached;
  2050. MiDetermineNode (PageFrameIndex, Pfn1);
  2051. Pfn1->u3.e2.ReferenceCount += 1;
  2052. PageFrameIndex += 1;
  2053. Pfn1 += 1;
  2054. i -= 1;
  2055. } while (i != 0);
  2056. if (MmDynamicPfn == 0) {
  2057. //
  2058. // Scan the PFN database backward for pages that are completely zero.
  2059. // These pages are unused and can be added to the free list.
  2060. //
  2061. BottomPfn = MI_PFN_ELEMENT(MmHighestPhysicalPage);
  2062. do {
  2063. //
  2064. // Compute the address of the start of the page that is next
  2065. // lower in memory and scan backwards until that page address
  2066. // is reached or just crossed.
  2067. //
  2068. if (((ULONG)BottomPfn & (PAGE_SIZE - 1)) != 0) {
  2069. BasePfn = (PMMPFN)((ULONG)BottomPfn & ~(PAGE_SIZE - 1));
  2070. TopPfn = BottomPfn + 1;
  2071. }
  2072. else {
  2073. BasePfn = (PMMPFN)((ULONG)BottomPfn - PAGE_SIZE);
  2074. TopPfn = BottomPfn;
  2075. }
  2076. while (BottomPfn > BasePfn) {
  2077. BottomPfn -= 1;
  2078. }
  2079. //
  2080. // If the entire range over which the PFN entries span is
  2081. // completely zero and the PFN entry that maps the page is
  2082. // not in the range, then add the page to the appropriate
  2083. // free list.
  2084. //
  2085. Range = (ULONG)TopPfn - (ULONG)BottomPfn;
  2086. if (RtlCompareMemoryUlong((PVOID)BottomPfn, Range, 0) == Range) {
  2087. //
  2088. // Set the PTE address to the physical page for virtual
  2089. // address alignment checking.
  2090. //
  2091. PointerPte = MiGetPteAddress (BasePfn);
  2092. PageFrameIndex = (PFN_NUMBER)PointerPte->u.Hard.PageFrameNumber;
  2093. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  2094. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  2095. ASSERT (Pfn1->PteAddress == (PMMPTE)(PageFrameIndex << PTE_SHIFT));
  2096. Pfn1->u3.e2.ReferenceCount = 0;
  2097. Pfn1->PteAddress = (PMMPTE)(PageFrameIndex << PTE_SHIFT);
  2098. //
  2099. // No need to initialize Pfn1->u3.e1.CacheAttribute
  2100. // here as the freelist insertion will mark it as
  2101. // not-mapped.
  2102. //
  2103. MiDetermineNode (PageFrameIndex, Pfn1);
  2104. MiInsertPageInFreeList (PageFrameIndex);
  2105. }
  2106. } while (BottomPfn > MmPfnDatabase);
  2107. }
  2108. }
  2109. //
  2110. // Adjust the memory descriptor to indicate that free pool has
  2111. // been used for nonpaged pool creation.
  2112. //
  2113. // N.B. This is required because the descriptors are walked upon
  2114. // return from this routine to create the MmPhysicalMemoryBlock.
  2115. //
  2116. *MxFreeDescriptor = *(PMEMORY_ALLOCATION_DESCRIPTOR)&MxOldFreeDescriptor;
  2117. //
  2118. // Initialize the nonpaged available PTEs for mapping I/O space
  2119. // and kernel stacks. Note this expands the initial PTE allocation to use
  2120. // all possible available virtual space by reclaiming the initial nonpaged
  2121. // pool range (in non /3GB systems) because that range has already been
  2122. // moved into the 2GB virtual range.
  2123. //
  2124. if ((PagedPoolMaximumDesired == TRUE) &&
  2125. (MmNonPagedPoolStart == NonPagedPoolStartLow)) {
  2126. //
  2127. // Maximum paged pool was requested. This means slice away most of
  2128. // the system PTEs being used at the high end of the virtual address
  2129. // space and use that address range for more paged pool instead.
  2130. //
  2131. ASSERT (MmVirtualBias == 0);
  2132. ASSERT (MiIsVirtualAddressOnPdeBoundary (MmNonPagedSystemStart));
  2133. ASSERT (SavedSize == MmSizeOfNonPagedPoolInBytes);
  2134. PointerPde = MiGetPdeAddress (NonPagedPoolStartVirtual);
  2135. PointerPde -= 2;
  2136. if (PointerPde > MiGetPdeAddress (MmNonPagedSystemStart)) {
  2137. VirtualAddress = MiGetVirtualAddressMappedByPde (PointerPde + 1);
  2138. do {
  2139. ASSERT (RtlCompareMemoryUlong (MiGetVirtualAddressMappedByPte (PointerPde),
  2140. PAGE_SIZE,
  2141. 0) == PAGE_SIZE);
  2142. ASSERT (PointerPde->u.Hard.Valid == 1);
  2143. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
  2144. *PointerPde = ZeroKernelPte;
  2145. PointerPde -= 1;
  2146. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2147. LOCK_PFN (OldIrql);
  2148. MI_SET_PFN_DELETED (Pfn1);
  2149. #if 0
  2150. //
  2151. // This cannot be enabled until the HAL is fixed - it currently
  2152. // maps memory without removing it from the loader's memory
  2153. // descriptor list.
  2154. //
  2155. ASSERT (Pfn1->u2.ShareCount == 2);
  2156. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  2157. #else
  2158. ASSERT (Pfn1->u2.ShareCount >= 2);
  2159. ASSERT (Pfn1->u3.e2.ReferenceCount >= 1);
  2160. #endif
  2161. MiDecrementShareCountOnly (PageFrameIndex);
  2162. MiDecrementShareCountOnly (PageFrameIndex);
  2163. UNLOCK_PFN (OldIrql);
  2164. } while (PointerPde >= MiGetPdeAddress (MmNonPagedSystemStart));
  2165. KeFlushCurrentTb ();
  2166. MmNonPagedSystemStart = VirtualAddress;
  2167. }
  2168. }
  2169. PointerPte = MiGetPteAddress (MmNonPagedSystemStart);
  2170. ASSERT (((ULONG)PointerPte & (PAGE_SIZE - 1)) == 0);
  2171. MmNumberOfSystemPtes = MiGetPteAddress(NonPagedPoolStartVirtual) - PointerPte - 1;
  2172. SystemPteStart = PointerPte;
  2173. //
  2174. // Add pages to nonpaged pool if we could not allocate enough physically
  2175. // contiguous.
  2176. //
  2177. j = (SavedSize - MmSizeOfNonPagedPoolInBytes) >> PAGE_SHIFT;
  2178. if (j != 0) {
  2179. ULONG CountContiguous;
  2180. CountContiguous = LargestFreePfnCount;
  2181. PageFrameIndex = LargestFreePfnStart - 1;
  2182. PointerPte = MiGetPteAddress (NonPagedPoolStartVirtual);
  2183. while (j) {
  2184. if (CountContiguous) {
  2185. PageFrameIndex += 1;
  2186. MiUnlinkFreeOrZeroedPage (PageFrameIndex);
  2187. CountContiguous -= 1;
  2188. }
  2189. else {
  2190. PageFrameIndex = MiRemoveAnyPage (
  2191. MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  2192. }
  2193. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2194. Pfn1->u3.e2.ReferenceCount = 1;
  2195. Pfn1->u2.ShareCount = 1;
  2196. Pfn1->PteAddress = PointerPte;
  2197. Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
  2198. Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(MiGetPteAddress(PointerPte));
  2199. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  2200. Pfn1->u3.e1.CacheAttribute = MiCached;
  2201. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  2202. *PointerPte = TempPte;
  2203. PointerPte += 1;
  2204. j -= 1;
  2205. }
  2206. Pfn1->u3.e1.EndOfAllocation = 1;
  2207. Pfn1 = MI_PFN_ELEMENT (MiGetPteAddress(NonPagedPoolStartVirtual)->u.Hard.PageFrameNumber);
  2208. Pfn1->u3.e1.StartOfAllocation = 1;
  2209. Range = MmAllocatedNonPagedPool;
  2210. MiFreePoolPages (NonPagedPoolStartVirtual);
  2211. MmAllocatedNonPagedPool = Range;
  2212. }
  2213. //
  2214. // Initialize the nonpaged pool.
  2215. //
  2216. InitializePool (NonPagedPool, 0);
  2217. //
  2218. // Initialize the system PTE pool now that nonpaged pool exists.
  2219. //
  2220. MiInitializeSystemPtes (SystemPteStart, MmNumberOfSystemPtes, SystemPteSpace);
  2221. if (MiExtraPtes1 != 0) {
  2222. //
  2223. // Increment the system PTEs (for autoconfiguration purposes) but
  2224. // don't actually add the PTEs till later (to prevent fragmentation).
  2225. //
  2226. MiIncrementSystemPtes (MiExtraPtes1);
  2227. }
  2228. if (MiExtraPtes2 != 0) {
  2229. //
  2230. // Add extra system PTEs to the pool.
  2231. //
  2232. if (MM_SHARED_USER_DATA_VA > MiUseMaximumSystemSpace) {
  2233. if (MiUseMaximumSystemSpaceEnd > MM_SHARED_USER_DATA_VA) {
  2234. MiExtraPtes2 = BYTES_TO_PAGES(MM_SHARED_USER_DATA_VA - MiUseMaximumSystemSpace);
  2235. }
  2236. }
  2237. else {
  2238. ASSERT (MmVirtualBias != 0);
  2239. }
  2240. if (MiExtraPtes2 != 0) {
  2241. //
  2242. // Increment the system PTEs (for autoconfiguration purposes) but
  2243. // don't actually add the PTEs till later (to prevent
  2244. // fragmentation).
  2245. //
  2246. MiIncrementSystemPtes (MiExtraPtes2);
  2247. }
  2248. }
  2249. //
  2250. // Recover the extra PTE ranges immediately if special pool is enabled
  2251. // so the special pool range can be made as large as possible by consuming
  2252. // these.
  2253. //
  2254. if ((MmVerifyDriverBufferLength != (ULONG)-1) ||
  2255. ((MmSpecialPoolTag != 0) && (MmSpecialPoolTag != (ULONG)-1))) {
  2256. MiRecoverExtraPtes ();
  2257. }
  2258. //
  2259. // Initialize memory management structures for this process.
  2260. //
  2261. // Build the working set list. This requires the creation of a PDE
  2262. // to map hyperspace and the page table page pointed to
  2263. // by the PDE must be initialized.
  2264. //
  2265. // Note we can't remove a zeroed page as hyperspace does not
  2266. // exist and we map non-zeroed pages into hyperspace to zero them.
  2267. //
  2268. TempPde = ValidKernelPdeLocal;
  2269. PointerPde = MiGetPdeAddress (HYPER_SPACE);
  2270. LOCK_PFN (OldIrql);
  2271. PageFrameIndex = MiRemoveAnyPage (0);
  2272. TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
  2273. *PointerPde = TempPde;
  2274. #if defined (_X86PAE_)
  2275. PointerPde = MiGetPdeAddress((PVOID)((PCHAR)HYPER_SPACE + MM_VA_MAPPED_BY_PDE));
  2276. PageFrameIndex = MiRemoveAnyPage (0);
  2277. TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
  2278. *PointerPde = TempPde;
  2279. //
  2280. // Point to the page table page we just created and zero it.
  2281. //
  2282. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  2283. RtlZeroMemory (PointerPte, PAGE_SIZE);
  2284. #endif
  2285. KeFlushCurrentTb();
  2286. UNLOCK_PFN (OldIrql);
  2287. //
  2288. // Point to the page table page we just created and zero it.
  2289. //
  2290. PointerPte = MiGetPteAddress(HYPER_SPACE);
  2291. RtlZeroMemory ((PVOID)PointerPte, PAGE_SIZE);
  2292. //
  2293. // Hyper space now exists, set the necessary variables.
  2294. //
  2295. MmFirstReservedMappingPte = MiGetPteAddress (FIRST_MAPPING_PTE);
  2296. MmLastReservedMappingPte = MiGetPteAddress (LAST_MAPPING_PTE);
  2297. MmFirstReservedMappingPte->u.Hard.PageFrameNumber = NUMBER_OF_MAPPING_PTES;
  2298. MmWorkingSetList = (PMMWSL) ((ULONG_PTR)VAD_BITMAP_SPACE + PAGE_SIZE);
  2299. //
  2300. // Create zeroing PTEs for the zero page thread.
  2301. //
  2302. MiFirstReservedZeroingPte = MiReserveSystemPtes (NUMBER_OF_ZEROING_PTES + 1,
  2303. SystemPteSpace);
  2304. RtlZeroMemory (MiFirstReservedZeroingPte,
  2305. (NUMBER_OF_ZEROING_PTES + 1) * sizeof(MMPTE));
  2306. //
  2307. // Use the page frame number field of the first PTE as an
  2308. // offset into the available zeroing PTEs.
  2309. //
  2310. MiFirstReservedZeroingPte->u.Hard.PageFrameNumber = NUMBER_OF_ZEROING_PTES;
  2311. //
  2312. // Create the VAD bitmap for this process.
  2313. //
  2314. PointerPte = MiGetPteAddress (VAD_BITMAP_SPACE);
  2315. LOCK_PFN (OldIrql);
  2316. PageFrameIndex = MiRemoveAnyPage (0);
  2317. UNLOCK_PFN (OldIrql);
  2318. //
  2319. // Note the global bit must be off for the bitmap data.
  2320. //
  2321. TempPte = ValidKernelPteLocal;
  2322. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  2323. *PointerPte = TempPte;
  2324. //
  2325. // Point to the page we just created and zero it.
  2326. //
  2327. RtlZeroMemory (VAD_BITMAP_SPACE, PAGE_SIZE);
  2328. //
  2329. // If booted /3GB, then the bitmap needs to be 2K bigger, shift
  2330. // the working set accordingly as well.
  2331. //
  2332. // Note the 2K expansion portion of the bitmap is automatically
  2333. // carved out of the working set page allocated below.
  2334. //
  2335. if (MmVirtualBias != 0) {
  2336. MmWorkingSetList = (PMMWSL) ((ULONG_PTR)MmWorkingSetList + PAGE_SIZE / 2);
  2337. }
  2338. MiLastVadBit = (((ULONG_PTR) MI_64K_ALIGN (MM_HIGHEST_VAD_ADDRESS))) / X64K;
  2339. #if defined (_X86PAE_)
  2340. //
  2341. // Only bitmap the first 2GB of the PAE address space when booted /3GB.
  2342. // This is because PAE has twice as many pagetable pages as non-PAE which
  2343. // causes the MMWSL structure to be larger than 2K. If we bitmapped the
  2344. // entire user address space in this configuration then we'd need a 6K
  2345. // bitmap and this would cause the initial MMWSL structure to overflow
  2346. // into a second page. This would require a bunch of extra code throughout
  2347. // process support and other areas so just limit the bitmap for now.
  2348. //
  2349. if (MiLastVadBit > PAGE_SIZE * 8 - 1) {
  2350. ASSERT (MmVirtualBias != 0);
  2351. MiLastVadBit = PAGE_SIZE * 8 - 1;
  2352. MmWorkingSetList = (PMMWSL) ((ULONG_PTR)VAD_BITMAP_SPACE + PAGE_SIZE);
  2353. }
  2354. #endif
  2355. KeInitializeEvent (&MiImageMappingPteEvent,
  2356. NotificationEvent,
  2357. FALSE);
  2358. //
  2359. // Initialize this process's memory management structures including
  2360. // the working set list.
  2361. //
  2362. // The PFN element for the page directory has already been initialized,
  2363. // zero the reference count and the share count so they won't be
  2364. // wrong.
  2365. //
  2366. Pfn1 = MI_PFN_ELEMENT (PdePageNumber);
  2367. LOCK_PFN (OldIrql);
  2368. Pfn1->u2.ShareCount = 0;
  2369. Pfn1->u3.e2.ReferenceCount = 0;
  2370. #if defined (_X86PAE_)
  2371. PointerPte = MiGetPteAddress (PDE_BASE);
  2372. for (i = 0; i < PD_PER_SYSTEM; i += 1) {
  2373. PdePageNumber = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
  2374. Pfn1 = MI_PFN_ELEMENT (PdePageNumber);
  2375. Pfn1->u2.ShareCount = 0;
  2376. Pfn1->u3.e2.ReferenceCount = 0;
  2377. PointerPte += 1;
  2378. }
  2379. #endif
  2380. //
  2381. // Get a page for the working set list and zero it.
  2382. //
  2383. TempPte = ValidKernelPteLocal;
  2384. PageFrameIndex = MiRemoveAnyPage (0);
  2385. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  2386. PointerPte = MiGetPteAddress (MmWorkingSetList);
  2387. *PointerPte = TempPte;
  2388. //
  2389. // Note that when booted /3GB, MmWorkingSetList is not page aligned, so
  2390. // always start zeroing from the start of the page regardless.
  2391. //
  2392. RtlZeroMemory (MiGetVirtualAddressMappedByPte (PointerPte), PAGE_SIZE);
  2393. CurrentProcess->WorkingSetPage = PageFrameIndex;
  2394. #if defined (_X86PAE_)
  2395. MiPaeInitialize ();
  2396. #endif
  2397. KeFlushCurrentTb();
  2398. UNLOCK_PFN (OldIrql);
  2399. CurrentProcess->Vm.MaximumWorkingSetSize = MmSystemProcessWorkingSetMax;
  2400. CurrentProcess->Vm.MinimumWorkingSetSize = MmSystemProcessWorkingSetMin;
  2401. MmInitializeProcessAddressSpace (CurrentProcess, NULL, NULL, NULL);
  2402. //
  2403. // Ensure the secondary page structures are marked as in use.
  2404. //
  2405. if (MmVirtualBias == 0) {
  2406. ASSERT (MmFreePagesByColor[0] < (PMMCOLOR_TABLES)MM_SYSTEM_CACHE_END_EXTRA);
  2407. PointerPde = MiGetPdeAddress(MmFreePagesByColor[0]);
  2408. ASSERT (PointerPde->u.Hard.Valid == 1);
  2409. PointerPte = MiGetPteAddress(MmFreePagesByColor[0]);
  2410. ASSERT (PointerPte->u.Hard.Valid == 1);
  2411. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
  2412. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2413. LOCK_PFN (OldIrql);
  2414. if (Pfn1->u3.e2.ReferenceCount == 0) {
  2415. Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
  2416. Pfn1->PteAddress = PointerPte;
  2417. Pfn1->u2.ShareCount += 1;
  2418. Pfn1->u3.e2.ReferenceCount = 1;
  2419. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  2420. Pfn1->u3.e1.CacheAttribute = MiCached;
  2421. MiDetermineNode (PageFrameIndex, Pfn1);
  2422. }
  2423. UNLOCK_PFN (OldIrql);
  2424. }
  2425. else if ((((ULONG)MmFreePagesByColor[0] & (PAGE_SIZE - 1)) == 0) &&
  2426. ((MmSecondaryColors * 2 * sizeof(MMCOLOR_TABLES)) < PAGE_SIZE)) {
  2427. PMMCOLOR_TABLES c;
  2428. c = MmFreePagesByColor[0];
  2429. MmFreePagesByColor[0] = ExAllocatePoolWithTag (NonPagedPoolMustSucceed,
  2430. MmSecondaryColors * 2 * sizeof(MMCOLOR_TABLES),
  2431. ' mM');
  2432. MmFreePagesByColor[1] = &MmFreePagesByColor[0][MmSecondaryColors];
  2433. RtlCopyMemory (MmFreePagesByColor[0],
  2434. c,
  2435. MmSecondaryColors * 2 * sizeof(MMCOLOR_TABLES));
  2436. //
  2437. // Free the page.
  2438. //
  2439. PointerPte = MiGetPteAddress(c);
  2440. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
  2441. ASSERT (c > (PMMCOLOR_TABLES)MM_SYSTEM_CACHE_END_EXTRA);
  2442. *PointerPte = ZeroKernelPte;
  2443. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2444. LOCK_PFN (OldIrql);
  2445. ASSERT ((Pfn1->u2.ShareCount <= 1) && (Pfn1->u3.e2.ReferenceCount <= 1));
  2446. Pfn1->u2.ShareCount = 0;
  2447. Pfn1->u3.e2.ReferenceCount = 1;
  2448. MI_SET_PFN_DELETED (Pfn1);
  2449. #if DBG
  2450. Pfn1->u3.e1.PageLocation = StandbyPageList;
  2451. #endif
  2452. MiDecrementReferenceCount (PageFrameIndex);
  2453. UNLOCK_PFN (OldIrql);
  2454. }
  2455. return;
  2456. }