xref: /reactos/ntoskrnl/mm/ARM3/mminit.c (revision 2b91b296)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/mminit.c
5  * PURPOSE:         ARM Memory Manager Initialization
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "miarm.h"
17 #undef MmSystemRangeStart
18 
19 /* GLOBALS ********************************************************************/
20 
21 //
22 // These are all registry-configurable, but by default, the memory manager will
23 // figure out the most appropriate values.
24 //
25 ULONG MmMaximumNonPagedPoolPercent;
26 SIZE_T MmSizeOfNonPagedPoolInBytes;
27 SIZE_T MmMaximumNonPagedPoolInBytes;
28 
29 /* Some of the same values, in pages */
30 PFN_NUMBER MmMaximumNonPagedPoolInPages;
31 
32 //
33 // These numbers describe the discrete equation components of the nonpaged
34 // pool sizing algorithm.
35 //
36 // They are described on http://support.microsoft.com/default.aspx/kb/126402/ja
37 // along with the algorithm that uses them, which is implemented later below.
38 //
39 SIZE_T MmMinimumNonPagedPoolSize = 256 * 1024;
40 ULONG MmMinAdditionNonPagedPoolPerMb = 32 * 1024;
41 SIZE_T MmDefaultMaximumNonPagedPool = 1024 * 1024;
42 ULONG MmMaxAdditionNonPagedPoolPerMb = 400 * 1024;
43 
44 //
45 // The memory layout (and especially variable names) of the NT kernel mode
46 // components can be a bit hard to twig, especially when it comes to the non
47 // paged area.
48 //
49 // There are really two components to the non-paged pool:
50 //
51 // - The initial nonpaged pool, sized dynamically up to a maximum.
52 // - The expansion nonpaged pool, sized dynamically up to a maximum.
53 //
54 // The initial nonpaged pool is physically continuous for performance, and
55 // immediately follows the PFN database, typically sharing the same PDE. It is
56 // a very small resource (32MB on a 1GB system), and capped at 128MB.
57 //
58 // Right now we call this the "ARM³ Nonpaged Pool" and it begins somewhere after
59 // the PFN database (which starts at 0xB0000000).
60 //
61 // The expansion nonpaged pool, on the other hand, can grow much bigger (400MB
62 // for a 1GB system). On ARM³ however, it is currently capped at 128MB.
63 //
64 // The address where the initial nonpaged pool starts is aptly named
65 // MmNonPagedPoolStart, and it describes a range of MmSizeOfNonPagedPoolInBytes
66 // bytes.
67 //
68 // Expansion nonpaged pool starts at an address described by the variable called
69 // MmNonPagedPoolExpansionStart, and it goes on for MmMaximumNonPagedPoolInBytes
70 // minus MmSizeOfNonPagedPoolInBytes bytes, always reaching MmNonPagedPoolEnd
71 // (because of the way it's calculated) at 0xFFBE0000.
72 //
73 // Initial nonpaged pool is allocated and mapped early-on during boot, but what
74 // about the expansion nonpaged pool? It is instead composed of special pages
75 // which belong to what are called System PTEs. These PTEs are the matter of a
76 // later discussion, but they are also considered part of the "nonpaged" OS, due
77 // to the fact that they are never paged out -- once an address is described by
78 // a System PTE, it is always valid, until the System PTE is torn down.
79 //
80 // System PTEs are actually composed of two "spaces", the system space proper,
81 // and the nonpaged pool expansion space. The latter, as we've already seen,
82 // begins at MmNonPagedPoolExpansionStart. Based on the number of System PTEs
83 // that the system will support, the remaining address space below this address
84 // is used to hold the system space PTEs. This address, in turn, is held in the
85 // variable named MmNonPagedSystemStart, which itself is never allowed to go
86 // below 0xEB000000 (thus creating an upper bound on the number of System PTEs).
87 //
88 // This means that 330MB are reserved for total nonpaged system VA, on top of
89 // whatever the initial nonpaged pool allocation is.
90 //
91 // The following URLs, valid as of April 23rd, 2008, support this evidence:
92 //
93 // http://www.cs.miami.edu/~burt/journal/NT/memory.html
94 // http://www.ditii.com/2007/09/28/windows-memory-management-x86-virtual-address-space/
95 //
96 PVOID MmNonPagedSystemStart;
97 PVOID MmNonPagedPoolStart;
98 PVOID MmNonPagedPoolExpansionStart;
99 PVOID MmNonPagedPoolEnd = MI_NONPAGED_POOL_END;
100 
101 //
102 // This is where paged pool starts by default
103 //
104 PVOID MmPagedPoolStart = MI_PAGED_POOL_START;
105 PVOID MmPagedPoolEnd;
106 
107 //
108 // And this is its default size
109 //
110 SIZE_T MmSizeOfPagedPoolInBytes = MI_MIN_INIT_PAGED_POOLSIZE;
111 PFN_NUMBER MmSizeOfPagedPoolInPages = MI_MIN_INIT_PAGED_POOLSIZE / PAGE_SIZE;
112 
113 //
114 // Session space starts at 0xBFFFFFFF and grows downwards
115 // By default, it includes an 8MB image area where we map win32k and video card
116 // drivers, followed by a 4MB area containing the session's working set. This is
117 // then followed by a 20MB mapped view area and finally by the session's paged
118 // pool, by default 16MB.
119 //
120 // On a normal system, this results in session space occupying the region from
121 // 0xBD000000 to 0xC0000000
122 //
123 // See miarm.h for the defines that determine the sizing of this region. On an
124 // NT system, some of these can be configured through the registry, but we don't
125 // support that yet.
126 //
127 PVOID MiSessionSpaceEnd;    // 0xC0000000
128 PVOID MiSessionImageEnd;    // 0xC0000000
129 PVOID MiSessionImageStart;  // 0xBF800000
130 PVOID MiSessionSpaceWs;
131 PVOID MiSessionViewStart;   // 0xBE000000
132 PVOID MiSessionPoolEnd;     // 0xBE000000
133 PVOID MiSessionPoolStart;   // 0xBD000000
134 PVOID MmSessionBase;        // 0xBD000000
135 SIZE_T MmSessionSize;
136 SIZE_T MmSessionViewSize;
137 SIZE_T MmSessionPoolSize;
138 SIZE_T MmSessionImageSize;
139 
140 /*
141  * These are the PTE addresses of the boundaries carved out above
142  */
143 PMMPTE MiSessionImagePteStart;
144 PMMPTE MiSessionImagePteEnd;
145 PMMPTE MiSessionBasePte;
146 PMMPTE MiSessionLastPte;
147 
148 //
149 // The system view space, on the other hand, is where sections that are memory
150 // mapped into "system space" end up.
151 //
152 // By default, it is a 16MB region, but we hack it to be 32MB for ReactOS
153 //
154 PVOID MiSystemViewStart;
155 SIZE_T MmSystemViewSize;
156 
157 #if (_MI_PAGING_LEVELS == 2)
158 //
159 // A copy of the system page directory (the page directory associated with the
160 // System process) is kept (double-mapped) by the manager in order to lazily
161 // map paged pool PDEs into external processes when they fault on a paged pool
162 // address.
163 //
164 PFN_NUMBER MmSystemPageDirectory[PD_COUNT];
165 PMMPDE MmSystemPagePtes;
166 #endif
167 
168 //
169 // The system cache starts right after hyperspace. The first few pages are for
170 // keeping track of the system working set list.
171 //
172 // This should be 0xC0C00000 -- the cache itself starts at 0xC1000000
173 //
174 PMMWSL MmSystemCacheWorkingSetList = (PVOID)MI_SYSTEM_CACHE_WS_START;
175 
176 //
177 // Windows NT seems to choose between 7000, 11000 and 50000
178 // On systems with more than 32MB, this number is then doubled, and further
179 // aligned up to a PDE boundary (4MB).
180 //
181 PFN_COUNT MmNumberOfSystemPtes;
182 
183 //
184 // This is how many pages the PFN database will take up
185 // In Windows, this includes the Quark Color Table, but not in ARM³
186 //
187 PFN_NUMBER MxPfnAllocation;
188 
189 //
190 // Unlike the old ReactOS Memory Manager, ARM³ (and Windows) does not keep track
191 // of pages that are not actually valid physical memory, such as ACPI reserved
192 // regions, BIOS address ranges, or holes in physical memory address space which
193 // could indicate device-mapped I/O memory.
194 //
195 // In fact, the lack of a PFN entry for a page usually indicates that this is
196 // I/O space instead.
197 //
198 // A bitmap, called the PFN bitmap, keeps track of all page frames by assigning
199 // a bit to each. If the bit is set, then the page is valid physical RAM.
200 //
201 RTL_BITMAP MiPfnBitMap;
202 
203 //
204 // This structure describes the different pieces of RAM-backed address space
205 //
206 PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
207 
208 //
209 // This is where we keep track of the most basic physical layout markers
210 //
211 PFN_NUMBER MmHighestPhysicalPage, MmLowestPhysicalPage = -1;
212 PFN_COUNT MmNumberOfPhysicalPages;
213 
214 //
215 // The total number of pages mapped by the boot loader, which include the kernel
216 // HAL, boot drivers, registry, NLS files and other loader data structures is
217 // kept track of here. This depends on "LoaderPagesSpanned" being correct when
218 // coming from the loader.
219 //
220 // This number is later aligned up to a PDE boundary.
221 //
222 SIZE_T MmBootImageSize;
223 
224 //
225 // These three variables keep track of the core separation of address space that
226 // exists between kernel mode and user mode.
227 //
228 ULONG_PTR MmUserProbeAddress;
229 PVOID MmHighestUserAddress;
230 PVOID MmSystemRangeStart;
231 
232 /* And these store the respective highest PTE/PDE address */
233 PMMPTE MiHighestUserPte;
234 PMMPDE MiHighestUserPde;
235 #if (_MI_PAGING_LEVELS >= 3)
236 PMMPTE MiHighestUserPpe;
237 #if (_MI_PAGING_LEVELS >= 4)
238 PMMPTE MiHighestUserPxe;
239 #endif
240 #endif
241 
242 /* These variables define the system cache address space */
243 PVOID MmSystemCacheStart;
244 PVOID MmSystemCacheEnd;
245 MMSUPPORT MmSystemCacheWs;
246 
247 //
248 // This is where hyperspace ends (followed by the system cache working set)
249 //
250 PVOID MmHyperSpaceEnd;
251 
252 //
253 // Page coloring algorithm data
254 //
255 ULONG MmSecondaryColors;
256 ULONG MmSecondaryColorMask;
257 
258 //
259 // Actual (registry-configurable) size of a GUI thread's stack
260 //
261 ULONG MmLargeStackSize = KERNEL_LARGE_STACK_SIZE;
262 
263 //
264 // Before we have a PFN database, memory comes straight from our physical memory
265 // blocks, which is nice because it's guaranteed contiguous and also because once
266 // we take a page from here, the system doesn't see it anymore.
267 // However, once the fun is over, those pages must be re-integrated back into
268 // PFN society life, and that requires us keeping a copy of the original layout
269 // so that we can parse it later.
270 //
271 PMEMORY_ALLOCATION_DESCRIPTOR MxFreeDescriptor;
272 MEMORY_ALLOCATION_DESCRIPTOR MxOldFreeDescriptor;
273 
274 /*
275  * For each page's worth bytes of L2 cache in a given set/way line, the zero and
276  * free lists are organized in what is called a "color".
277  *
278  * This array points to the two lists, so it can be thought of as a multi-dimensional
279  * array of MmFreePagesByColor[2][MmSecondaryColors]. Since the number is dynamic,
280  * we describe the array in pointer form instead.
281  *
282  * On a final note, the color tables themselves are right after the PFN database.
283  */
284 C_ASSERT(FreePageList == 1);
285 PMMCOLOR_TABLES MmFreePagesByColor[FreePageList + 1];
286 
287 /* An event used in Phase 0 before the rest of the system is ready to go */
288 KEVENT MiTempEvent;
289 
290 /* All the events used for memory threshold notifications */
291 PKEVENT MiLowMemoryEvent;
292 PKEVENT MiHighMemoryEvent;
293 PKEVENT MiLowPagedPoolEvent;
294 PKEVENT MiHighPagedPoolEvent;
295 PKEVENT MiLowNonPagedPoolEvent;
296 PKEVENT MiHighNonPagedPoolEvent;
297 
298 /* The actual thresholds themselves, in page numbers */
299 PFN_NUMBER MmLowMemoryThreshold;
300 PFN_NUMBER MmHighMemoryThreshold;
301 PFN_NUMBER MiLowPagedPoolThreshold;
302 PFN_NUMBER MiHighPagedPoolThreshold;
303 PFN_NUMBER MiLowNonPagedPoolThreshold;
304 PFN_NUMBER MiHighNonPagedPoolThreshold;
305 
306 /*
307  * This number determines how many free pages must exist, at minimum, until we
308  * start trimming working sets and flushing modified pages to obtain more free
309  * pages.
310  *
311  * This number changes if the system detects that this is a server product
312  */
313 PFN_NUMBER MmMinimumFreePages = 26;
314 
315 /*
316  * This number indicates how many pages we consider to be a low limit of having
317  * "plenty" of free memory.
318  *
319  * It is doubled on systems that have more than 63MB of memory
320  */
321 PFN_NUMBER MmPlentyFreePages = 400;
322 
323 /* These values store the type of system this is (small, med, large) and if server */
324 ULONG MmProductType;
325 MM_SYSTEMSIZE MmSystemSize;
326 
327 /*
328  * These values store the cache working set minimums and maximums, in pages
329  *
330  * The minimum value is boosted on systems with more than 24MB of RAM, and cut
331  * down to only 32 pages on embedded (<24MB RAM) systems.
332  *
333  * An extra boost of 2MB is given on systems with more than 33MB of RAM.
334  */
335 PFN_NUMBER MmSystemCacheWsMinimum = 288;
336 PFN_NUMBER MmSystemCacheWsMaximum = 350;
337 
338 /* FIXME: Move to cache/working set code later */
339 BOOLEAN MmLargeSystemCache;
340 
341 /*
342  * This value determines in how many fragments/chunks the subsection prototype
343  * PTEs should be allocated when mapping a section object. It is configurable in
344  * the registry through the MapAllocationFragment parameter.
345  *
346  * The default is 64KB on systems with more than 1GB of RAM, 32KB on systems with
347  * more than 256MB of RAM, and 16KB on systems with less than 256MB of RAM.
348  *
349  * The maximum it can be set to is 2MB, and the minimum is 4KB.
350  */
351 SIZE_T MmAllocationFragment;
352 
353 /*
354  * These two values track how much virtual memory can be committed, and when
355  * expansion should happen.
356  */
357  // FIXME: They should be moved elsewhere since it's not an "init" setting?
358 SIZE_T MmTotalCommitLimit;
359 SIZE_T MmTotalCommitLimitMaximum;
360 
361 /*
362  * These values tune certain user parameters. They have default values set here,
363  * as well as in the code, and can be overwritten by registry settings.
364  */
365 SIZE_T MmHeapSegmentReserve = 1 * _1MB;
366 SIZE_T MmHeapSegmentCommit = 2 * PAGE_SIZE;
367 SIZE_T MmHeapDeCommitTotalFreeThreshold = 64 * _1KB;
368 SIZE_T MmHeapDeCommitFreeBlockThreshold = PAGE_SIZE;
369 SIZE_T MmMinimumStackCommitInBytes = 0;
370 
371 /* Internal setting used for debugging memory descriptors */
372 BOOLEAN MiDbgEnableMdDump =
373 #ifdef _ARM_
374 TRUE;
375 #else
376 FALSE;
377 #endif
378 
379 /* Number of memory descriptors in the loader block */
380 ULONG MiNumberDescriptors = 0;
381 
382 /* Number of free pages in the loader block */
383 PFN_NUMBER MiNumberOfFreePages = 0;
384 
385 /* Timeout value for critical sections (2.5 minutes) */
386 ULONG MmCritsectTimeoutSeconds = 150; // NT value: 720 * 60 * 60; (30 days)
387 LARGE_INTEGER MmCriticalSectionTimeout;
388 
389 /* PRIVATE FUNCTIONS **********************************************************/
390 
391 VOID
392 NTAPI
393 MiScanMemoryDescriptors(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
394 {
395     PLIST_ENTRY ListEntry;
396     PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
397     PFN_NUMBER PageFrameIndex, FreePages = 0;
398 
399     /* Loop the memory descriptors */
400     for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
401          ListEntry != &LoaderBlock->MemoryDescriptorListHead;
402          ListEntry = ListEntry->Flink)
403     {
404         /* Get the descriptor */
405         Descriptor = CONTAINING_RECORD(ListEntry,
406                                        MEMORY_ALLOCATION_DESCRIPTOR,
407                                        ListEntry);
408         DPRINT("MD Type: %lx Base: %lx Count: %lx\n",
409             Descriptor->MemoryType, Descriptor->BasePage, Descriptor->PageCount);
410 
411         /* Count this descriptor */
412         MiNumberDescriptors++;
413 
414         /* Check if this is invisible memory */
415         if ((Descriptor->MemoryType == LoaderFirmwarePermanent) ||
416             (Descriptor->MemoryType == LoaderSpecialMemory) ||
417             (Descriptor->MemoryType == LoaderHALCachedMemory) ||
418             (Descriptor->MemoryType == LoaderBBTMemory))
419         {
420             /* Skip this descriptor */
421             continue;
422         }
423 
424         /* Check if this is bad memory */
425         if (Descriptor->MemoryType != LoaderBad)
426         {
427             /* Count this in the total of pages */
428             MmNumberOfPhysicalPages += (PFN_COUNT)Descriptor->PageCount;
429         }
430 
431         /* Check if this is the new lowest page */
432         if (Descriptor->BasePage < MmLowestPhysicalPage)
433         {
434             /* Update the lowest page */
435             MmLowestPhysicalPage = Descriptor->BasePage;
436         }
437 
438         /* Check if this is the new highest page */
439         PageFrameIndex = Descriptor->BasePage + Descriptor->PageCount;
440         if (PageFrameIndex > MmHighestPhysicalPage)
441         {
442             /* Update the highest page */
443             MmHighestPhysicalPage = PageFrameIndex - 1;
444         }
445 
446         /* Check if this is free memory */
447         if ((Descriptor->MemoryType == LoaderFree) ||
448             (Descriptor->MemoryType == LoaderLoadedProgram) ||
449             (Descriptor->MemoryType == LoaderFirmwareTemporary) ||
450             (Descriptor->MemoryType == LoaderOsloaderStack))
451         {
452             /* Count it too free pages */
453             MiNumberOfFreePages += Descriptor->PageCount;
454 
455             /* Check if this is the largest memory descriptor */
456             if (Descriptor->PageCount > FreePages)
457             {
458                 /* Remember it */
459                 MxFreeDescriptor = Descriptor;
460                 FreePages = Descriptor->PageCount;
461             }
462         }
463     }
464 
465     /* Save original values of the free descriptor, since it'll be
466      * altered by early allocations */
467     MxOldFreeDescriptor = *MxFreeDescriptor;
468 }
469 
470 PFN_NUMBER
471 NTAPI
472 INIT_FUNCTION
473 MxGetNextPage(IN PFN_NUMBER PageCount)
474 {
475     PFN_NUMBER Pfn;
476 
477     /* Make sure we have enough pages */
478     if (PageCount > MxFreeDescriptor->PageCount)
479     {
480         /* Crash the system */
481         KeBugCheckEx(INSTALL_MORE_MEMORY,
482                      MmNumberOfPhysicalPages,
483                      MxFreeDescriptor->PageCount,
484                      MxOldFreeDescriptor.PageCount,
485                      PageCount);
486     }
487 
488     /* Use our lowest usable free pages */
489     Pfn = MxFreeDescriptor->BasePage;
490     MxFreeDescriptor->BasePage += PageCount;
491     MxFreeDescriptor->PageCount -= PageCount;
492     return Pfn;
493 }
494 
495 VOID
496 NTAPI
497 INIT_FUNCTION
498 MiComputeColorInformation(VOID)
499 {
500     ULONG L2Associativity;
501 
502     /* Check if no setting was provided already */
503     if (!MmSecondaryColors)
504     {
505         /* Get L2 cache information */
506         L2Associativity = KeGetPcr()->SecondLevelCacheAssociativity;
507 
508         /* The number of colors is the number of cache bytes by set/way */
509         MmSecondaryColors = KeGetPcr()->SecondLevelCacheSize;
510         if (L2Associativity) MmSecondaryColors /= L2Associativity;
511     }
512 
513     /* Now convert cache bytes into pages */
514     MmSecondaryColors >>= PAGE_SHIFT;
515     if (!MmSecondaryColors)
516     {
517         /* If there was no cache data from the KPCR, use the default colors */
518         MmSecondaryColors = MI_SECONDARY_COLORS;
519     }
520     else
521     {
522         /* Otherwise, make sure there aren't too many colors */
523         if (MmSecondaryColors > MI_MAX_SECONDARY_COLORS)
524         {
525             /* Set the maximum */
526             MmSecondaryColors = MI_MAX_SECONDARY_COLORS;
527         }
528 
529         /* Make sure there aren't too little colors */
530         if (MmSecondaryColors < MI_MIN_SECONDARY_COLORS)
531         {
532             /* Set the default */
533             MmSecondaryColors = MI_SECONDARY_COLORS;
534         }
535 
536         /* Finally make sure the colors are a power of two */
537         if (MmSecondaryColors & (MmSecondaryColors - 1))
538         {
539             /* Set the default */
540             MmSecondaryColors = MI_SECONDARY_COLORS;
541         }
542     }
543 
544     /* Compute the mask and store it */
545     MmSecondaryColorMask = MmSecondaryColors - 1;
546     KeGetCurrentPrcb()->SecondaryColorMask = MmSecondaryColorMask;
547 }
548 
549 VOID
550 NTAPI
551 INIT_FUNCTION
552 MiInitializeColorTables(VOID)
553 {
554     ULONG i;
555     PMMPTE PointerPte, LastPte;
556     MMPTE TempPte = ValidKernelPte;
557 
558     /* The color table starts after the ARM3 PFN database */
559     MmFreePagesByColor[0] = (PMMCOLOR_TABLES)&MmPfnDatabase[MmHighestPhysicalPage + 1];
560 
561     /* Loop the PTEs. We have two color tables for each secondary color */
562     PointerPte = MiAddressToPte(&MmFreePagesByColor[0][0]);
563     LastPte = MiAddressToPte((ULONG_PTR)MmFreePagesByColor[0] +
564                              (2 * MmSecondaryColors * sizeof(MMCOLOR_TABLES))
565                              - 1);
566     while (PointerPte <= LastPte)
567     {
568         /* Check for valid PTE */
569         if (PointerPte->u.Hard.Valid == 0)
570         {
571             /* Get a page and map it */
572             TempPte.u.Hard.PageFrameNumber = MxGetNextPage(1);
573             MI_WRITE_VALID_PTE(PointerPte, TempPte);
574 
575             /* Zero out the page */
576             RtlZeroMemory(MiPteToAddress(PointerPte), PAGE_SIZE);
577         }
578 
579         /* Next */
580         PointerPte++;
581     }
582 
583     /* Now set the address of the next list, right after this one */
584     MmFreePagesByColor[1] = &MmFreePagesByColor[0][MmSecondaryColors];
585 
586     /* Now loop the lists to set them up */
587     for (i = 0; i < MmSecondaryColors; i++)
588     {
589         /* Set both free and zero lists for each color */
590         MmFreePagesByColor[ZeroedPageList][i].Flink = LIST_HEAD;
591         MmFreePagesByColor[ZeroedPageList][i].Blink = (PVOID)LIST_HEAD;
592         MmFreePagesByColor[ZeroedPageList][i].Count = 0;
593         MmFreePagesByColor[FreePageList][i].Flink = LIST_HEAD;
594         MmFreePagesByColor[FreePageList][i].Blink = (PVOID)LIST_HEAD;
595         MmFreePagesByColor[FreePageList][i].Count = 0;
596     }
597 }
598 
599 #ifndef _M_AMD64
600 BOOLEAN
601 NTAPI
602 INIT_FUNCTION
603 MiIsRegularMemory(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
604                   IN PFN_NUMBER Pfn)
605 {
606     PLIST_ENTRY NextEntry;
607     PMEMORY_ALLOCATION_DESCRIPTOR MdBlock;
608 
609     /* Loop the memory descriptors */
610     NextEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
611     while (NextEntry != &LoaderBlock->MemoryDescriptorListHead)
612     {
613         /* Get the memory descriptor */
614         MdBlock = CONTAINING_RECORD(NextEntry,
615                                     MEMORY_ALLOCATION_DESCRIPTOR,
616                                     ListEntry);
617 
618         /* Check if this PFN could be part of the block */
619         if (Pfn >= (MdBlock->BasePage))
620         {
621             /* Check if it really is part of the block */
622             if (Pfn < (MdBlock->BasePage + MdBlock->PageCount))
623             {
624                 /* Check if the block is actually memory we don't map */
625                 if ((MdBlock->MemoryType == LoaderFirmwarePermanent) ||
626                     (MdBlock->MemoryType == LoaderBBTMemory) ||
627                     (MdBlock->MemoryType == LoaderSpecialMemory))
628                 {
629                     /* We don't need PFN database entries for this memory */
630                     break;
631                 }
632 
633                 /* This is memory we want to map */
634                 return TRUE;
635             }
636         }
637         else
638         {
639             /* Blocks are ordered, so if it's not here, it doesn't exist */
640             break;
641         }
642 
643         /* Get to the next descriptor */
644         NextEntry = MdBlock->ListEntry.Flink;
645     }
646 
647     /* Check if this PFN is actually from our free memory descriptor */
648     if ((Pfn >= MxOldFreeDescriptor.BasePage) &&
649         (Pfn < MxOldFreeDescriptor.BasePage + MxOldFreeDescriptor.PageCount))
650     {
651         /* We use these pages for initial mappings, so we do want to count them */
652         return TRUE;
653     }
654 
655     /* Otherwise this isn't memory that we describe or care about */
656     return FALSE;
657 }
658 
659 VOID
660 NTAPI
661 INIT_FUNCTION
662 MiMapPfnDatabase(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
663 {
664     PFN_NUMBER FreePage, FreePageCount, PagesLeft, BasePage, PageCount;
665     PLIST_ENTRY NextEntry;
666     PMEMORY_ALLOCATION_DESCRIPTOR MdBlock;
667     PMMPTE PointerPte, LastPte;
668     MMPTE TempPte = ValidKernelPte;
669 
670     /* Get current page data, since we won't be using MxGetNextPage as it would corrupt our state */
671     FreePage = MxFreeDescriptor->BasePage;
672     FreePageCount = MxFreeDescriptor->PageCount;
673     PagesLeft = 0;
674 
675     /* Loop the memory descriptors */
676     NextEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
677     while (NextEntry != &LoaderBlock->MemoryDescriptorListHead)
678     {
679         /* Get the descriptor */
680         MdBlock = CONTAINING_RECORD(NextEntry,
681                                     MEMORY_ALLOCATION_DESCRIPTOR,
682                                     ListEntry);
683         if ((MdBlock->MemoryType == LoaderFirmwarePermanent) ||
684             (MdBlock->MemoryType == LoaderBBTMemory) ||
685             (MdBlock->MemoryType == LoaderSpecialMemory))
686         {
687             /* These pages are not part of the PFN database */
688             NextEntry = MdBlock->ListEntry.Flink;
689             continue;
690         }
691 
692         /* Next, check if this is our special free descriptor we've found */
693         if (MdBlock == MxFreeDescriptor)
694         {
695             /* Use the real numbers instead */
696             BasePage = MxOldFreeDescriptor.BasePage;
697             PageCount = MxOldFreeDescriptor.PageCount;
698         }
699         else
700         {
701             /* Use the descriptor's numbers */
702             BasePage = MdBlock->BasePage;
703             PageCount = MdBlock->PageCount;
704         }
705 
706         /* Get the PTEs for this range */
707         PointerPte = MiAddressToPte(&MmPfnDatabase[BasePage]);
708         LastPte = MiAddressToPte(((ULONG_PTR)&MmPfnDatabase[BasePage + PageCount]) - 1);
709         DPRINT("MD Type: %lx Base: %lx Count: %lx\n", MdBlock->MemoryType, BasePage, PageCount);
710 
711         /* Loop them */
712         while (PointerPte <= LastPte)
713         {
714             /* We'll only touch PTEs that aren't already valid */
715             if (PointerPte->u.Hard.Valid == 0)
716             {
717                 /* Use the next free page */
718                 TempPte.u.Hard.PageFrameNumber = FreePage;
719                 ASSERT(FreePageCount != 0);
720 
721                 /* Consume free pages */
722                 FreePage++;
723                 FreePageCount--;
724                 if (!FreePageCount)
725                 {
726                     /* Out of memory */
727                     KeBugCheckEx(INSTALL_MORE_MEMORY,
728                                  MmNumberOfPhysicalPages,
729                                  FreePageCount,
730                                  MxOldFreeDescriptor.PageCount,
731                                  1);
732                 }
733 
734                 /* Write out this PTE */
735                 PagesLeft++;
736                 MI_WRITE_VALID_PTE(PointerPte, TempPte);
737 
738                 /* Zero this page */
739                 RtlZeroMemory(MiPteToAddress(PointerPte), PAGE_SIZE);
740             }
741 
742             /* Next! */
743             PointerPte++;
744         }
745 
746         /* Do the next address range */
747         NextEntry = MdBlock->ListEntry.Flink;
748     }
749 
750     /* Now update the free descriptors to consume the pages we used up during the PFN allocation loop */
751     MxFreeDescriptor->BasePage = FreePage;
752     MxFreeDescriptor->PageCount = FreePageCount;
753 }
754 
755 VOID
756 NTAPI
757 INIT_FUNCTION
758 MiBuildPfnDatabaseFromPages(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
759 {
760     PMMPDE PointerPde;
761     PMMPTE PointerPte;
762     ULONG i, Count, j;
763     PFN_NUMBER PageFrameIndex, StartupPdIndex, PtePageIndex;
764     PMMPFN Pfn1, Pfn2;
765     ULONG_PTR BaseAddress = 0;
766 
767     /* PFN of the startup page directory */
768     StartupPdIndex = PFN_FROM_PTE(MiAddressToPde(PDE_BASE));
769 
770     /* Start with the first PDE and scan them all */
771     PointerPde = MiAddressToPde(NULL);
772     Count = PD_COUNT * PDE_COUNT;
773     for (i = 0; i < Count; i++)
774     {
775         /* Check for valid PDE */
776         if (PointerPde->u.Hard.Valid == 1)
777         {
778             /* Get the PFN from it */
779             PageFrameIndex = PFN_FROM_PTE(PointerPde);
780 
781             /* Do we want a PFN entry for this page? */
782             if (MiIsRegularMemory(LoaderBlock, PageFrameIndex))
783             {
784                 /* Yes we do, set it up */
785                 Pfn1 = MiGetPfnEntry(PageFrameIndex);
786                 Pfn1->u4.PteFrame = StartupPdIndex;
787                 Pfn1->PteAddress = (PMMPTE)PointerPde;
788                 Pfn1->u2.ShareCount++;
789                 Pfn1->u3.e2.ReferenceCount = 1;
790                 Pfn1->u3.e1.PageLocation = ActiveAndValid;
791                 Pfn1->u3.e1.CacheAttribute = MiNonCached;
792 #if MI_TRACE_PFNS
793                 Pfn1->PfnUsage = MI_USAGE_INIT_MEMORY;
794                 memcpy(Pfn1->ProcessName, "Initial PDE", 16);
795 #endif
796             }
797             else
798             {
799                 /* No PFN entry */
800                 Pfn1 = NULL;
801             }
802 
803             /* Now get the PTE and scan the pages */
804             PointerPte = MiAddressToPte(BaseAddress);
805             for (j = 0; j < PTE_COUNT; j++)
806             {
807                 /* Check for a valid PTE */
808                 if (PointerPte->u.Hard.Valid == 1)
809                 {
810                     /* Increase the shared count of the PFN entry for the PDE */
811                     ASSERT(Pfn1 != NULL);
812                     Pfn1->u2.ShareCount++;
813 
814                     /* Now check if the PTE is valid memory too */
815                     PtePageIndex = PFN_FROM_PTE(PointerPte);
816                     if (MiIsRegularMemory(LoaderBlock, PtePageIndex))
817                     {
818                         /*
819                          * Only add pages above the end of system code or pages
820                          * that are part of nonpaged pool
821                          */
822                         if ((BaseAddress >= 0xA0000000) ||
823                             ((BaseAddress >= (ULONG_PTR)MmNonPagedPoolStart) &&
824                              (BaseAddress < (ULONG_PTR)MmNonPagedPoolStart +
825                                             MmSizeOfNonPagedPoolInBytes)))
826                         {
827                             /* Get the PFN entry and make sure it too is valid */
828                             Pfn2 = MiGetPfnEntry(PtePageIndex);
829                             if ((MmIsAddressValid(Pfn2)) &&
830                                 (MmIsAddressValid(Pfn2 + 1)))
831                             {
832                                 /* Setup the PFN entry */
833                                 Pfn2->u4.PteFrame = PageFrameIndex;
834                                 Pfn2->PteAddress = PointerPte;
835                                 Pfn2->u2.ShareCount++;
836                                 Pfn2->u3.e2.ReferenceCount = 1;
837                                 Pfn2->u3.e1.PageLocation = ActiveAndValid;
838                                 Pfn2->u3.e1.CacheAttribute = MiNonCached;
839 #if MI_TRACE_PFNS
840                                 Pfn2->PfnUsage = MI_USAGE_INIT_MEMORY;
841                                 memcpy(Pfn1->ProcessName, "Initial PTE", 16);
842 #endif
843                             }
844                         }
845                     }
846                 }
847 
848                 /* Next PTE */
849                 PointerPte++;
850                 BaseAddress += PAGE_SIZE;
851             }
852         }
853         else
854         {
855             /* Next PDE mapped address */
856             BaseAddress += PDE_MAPPED_VA;
857         }
858 
859         /* Next PTE */
860         PointerPde++;
861     }
862 }
863 
864 VOID
865 NTAPI
866 INIT_FUNCTION
867 MiBuildPfnDatabaseZeroPage(VOID)
868 {
869     PMMPFN Pfn1;
870     PMMPDE PointerPde;
871 
872     /* Grab the lowest page and check if it has no real references */
873     Pfn1 = MiGetPfnEntry(MmLowestPhysicalPage);
874     if (!(MmLowestPhysicalPage) && !(Pfn1->u3.e2.ReferenceCount))
875     {
876         /* Make it a bogus page to catch errors */
877         PointerPde = MiAddressToPde(0xFFFFFFFF);
878         Pfn1->u4.PteFrame = PFN_FROM_PTE(PointerPde);
879         Pfn1->PteAddress = (PMMPTE)PointerPde;
880         Pfn1->u2.ShareCount++;
881         Pfn1->u3.e2.ReferenceCount = 0xFFF0;
882         Pfn1->u3.e1.PageLocation = ActiveAndValid;
883         Pfn1->u3.e1.CacheAttribute = MiNonCached;
884     }
885 }
886 
887 VOID
888 NTAPI
889 INIT_FUNCTION
890 MiBuildPfnDatabaseFromLoaderBlock(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
891 {
892     PLIST_ENTRY NextEntry;
893     PFN_NUMBER PageCount = 0;
894     PMEMORY_ALLOCATION_DESCRIPTOR MdBlock;
895     PFN_NUMBER PageFrameIndex;
896     PMMPFN Pfn1;
897     PMMPTE PointerPte;
898     PMMPDE PointerPde;
899     KIRQL OldIrql;
900 
901     /* Now loop through the descriptors */
902     NextEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
903     while (NextEntry != &LoaderBlock->MemoryDescriptorListHead)
904     {
905         /* Get the current descriptor */
906         MdBlock = CONTAINING_RECORD(NextEntry,
907                                     MEMORY_ALLOCATION_DESCRIPTOR,
908                                     ListEntry);
909 
910         /* Read its data */
911         PageCount = MdBlock->PageCount;
912         PageFrameIndex = MdBlock->BasePage;
913 
914         /* Don't allow memory above what the PFN database is mapping */
915         if (PageFrameIndex > MmHighestPhysicalPage)
916         {
917             /* Since they are ordered, everything past here will be larger */
918             break;
919         }
920 
921         /* On the other hand, the end page might be higher up... */
922         if ((PageFrameIndex + PageCount) > (MmHighestPhysicalPage + 1))
923         {
924             /* In which case we'll trim the descriptor to go as high as we can */
925             PageCount = MmHighestPhysicalPage + 1 - PageFrameIndex;
926             MdBlock->PageCount = PageCount;
927 
928             /* But if there's nothing left to trim, we got too high, so quit */
929             if (!PageCount) break;
930         }
931 
932         /* Now check the descriptor type */
933         switch (MdBlock->MemoryType)
934         {
935             /* Check for bad RAM */
936             case LoaderBad:
937 
938                 DPRINT1("You either have specified /BURNMEMORY or damaged RAM modules.\n");
939                 break;
940 
941             /* Check for free RAM */
942             case LoaderFree:
943             case LoaderLoadedProgram:
944             case LoaderFirmwareTemporary:
945             case LoaderOsloaderStack:
946 
947                 /* Get the last page of this descriptor. Note we loop backwards */
948                 PageFrameIndex += PageCount - 1;
949                 Pfn1 = MiGetPfnEntry(PageFrameIndex);
950 
951                 /* Lock the PFN Database */
952                 OldIrql = MiAcquirePfnLock();
953                 while (PageCount--)
954                 {
955                     /* If the page really has no references, mark it as free */
956                     if (!Pfn1->u3.e2.ReferenceCount)
957                     {
958                         /* Add it to the free list */
959                         Pfn1->u3.e1.CacheAttribute = MiNonCached;
960                         MiInsertPageInFreeList(PageFrameIndex);
961                     }
962 
963                     /* Go to the next page */
964                     Pfn1--;
965                     PageFrameIndex--;
966                 }
967 
968                 /* Release PFN database */
969                 MiReleasePfnLock(OldIrql);
970 
971                 /* Done with this block */
972                 break;
973 
974             /* Check for pages that are invisible to us */
975             case LoaderFirmwarePermanent:
976             case LoaderSpecialMemory:
977             case LoaderBBTMemory:
978 
979                 /* And skip them */
980                 break;
981 
982             default:
983 
984                 /* Map these pages with the KSEG0 mapping that adds 0x80000000 */
985                 PointerPte = MiAddressToPte(KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
986                 Pfn1 = MiGetPfnEntry(PageFrameIndex);
987                 while (PageCount--)
988                 {
989                     /* Check if the page is really unused */
990                     PointerPde = MiAddressToPde(KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
991                     if (!Pfn1->u3.e2.ReferenceCount)
992                     {
993                         /* Mark it as being in-use */
994                         Pfn1->u4.PteFrame = PFN_FROM_PTE(PointerPde);
995                         Pfn1->PteAddress = PointerPte;
996                         Pfn1->u2.ShareCount++;
997                         Pfn1->u3.e2.ReferenceCount = 1;
998                         Pfn1->u3.e1.PageLocation = ActiveAndValid;
999                         Pfn1->u3.e1.CacheAttribute = MiNonCached;
1000 #if MI_TRACE_PFNS
1001                         Pfn1->PfnUsage = MI_USAGE_BOOT_DRIVER;
1002 #endif
1003 
1004                         /* Check for RAM disk page */
1005                         if (MdBlock->MemoryType == LoaderXIPRom)
1006                         {
1007                             /* Make it a pseudo-I/O ROM mapping */
1008                             Pfn1->u1.Flink = 0;
1009                             Pfn1->u2.ShareCount = 0;
1010                             Pfn1->u3.e2.ReferenceCount = 0;
1011                             Pfn1->u3.e1.PageLocation = 0;
1012                             Pfn1->u3.e1.Rom = 1;
1013                             Pfn1->u4.InPageError = 0;
1014                             Pfn1->u3.e1.PrototypePte = 1;
1015                         }
1016                     }
1017 
1018                     /* Advance page structures */
1019                     Pfn1++;
1020                     PageFrameIndex++;
1021                     PointerPte++;
1022                 }
1023                 break;
1024         }
1025 
1026         /* Next descriptor entry */
1027         NextEntry = MdBlock->ListEntry.Flink;
1028     }
1029 }
1030 
1031 VOID
1032 NTAPI
1033 INIT_FUNCTION
1034 MiBuildPfnDatabaseSelf(VOID)
1035 {
1036     PMMPTE PointerPte, LastPte;
1037     PMMPFN Pfn1;
1038 
1039     /* Loop the PFN database page */
1040     PointerPte = MiAddressToPte(MiGetPfnEntry(MmLowestPhysicalPage));
1041     LastPte = MiAddressToPte(MiGetPfnEntry(MmHighestPhysicalPage));
1042     while (PointerPte <= LastPte)
1043     {
1044         /* Make sure the page is valid */
1045         if (PointerPte->u.Hard.Valid == 1)
1046         {
1047             /* Get the PFN entry and just mark it referenced */
1048             Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
1049             Pfn1->u2.ShareCount = 1;
1050             Pfn1->u3.e2.ReferenceCount = 1;
1051 #if MI_TRACE_PFNS
1052             Pfn1->PfnUsage = MI_USAGE_PFN_DATABASE;
1053 #endif
1054         }
1055 
1056         /* Next */
1057         PointerPte++;
1058     }
1059 }
1060 
1061 VOID
1062 NTAPI
1063 INIT_FUNCTION
1064 MiInitializePfnDatabase(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1065 {
1066     /* Scan memory and start setting up PFN entries */
1067     MiBuildPfnDatabaseFromPages(LoaderBlock);
1068 
1069     /* Add the zero page */
1070     MiBuildPfnDatabaseZeroPage();
1071 
1072     /* Scan the loader block and build the rest of the PFN database */
1073     MiBuildPfnDatabaseFromLoaderBlock(LoaderBlock);
1074 
1075     /* Finally add the pages for the PFN database itself */
1076     MiBuildPfnDatabaseSelf();
1077 }
1078 #endif /* !_M_AMD64 */
1079 
1080 VOID
1081 NTAPI
1082 INIT_FUNCTION
1083 MmFreeLoaderBlock(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1084 {
1085     PLIST_ENTRY NextMd;
1086     PMEMORY_ALLOCATION_DESCRIPTOR MdBlock;
1087     ULONG_PTR i;
1088     PFN_NUMBER BasePage, LoaderPages;
1089     PMMPFN Pfn1;
1090     KIRQL OldIrql;
1091     PPHYSICAL_MEMORY_RUN Buffer, Entry;
1092 
1093     /* Loop the descriptors in order to count them */
1094     i = 0;
1095     NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
1096     while (NextMd != &LoaderBlock->MemoryDescriptorListHead)
1097     {
1098         MdBlock = CONTAINING_RECORD(NextMd,
1099                                     MEMORY_ALLOCATION_DESCRIPTOR,
1100                                     ListEntry);
1101         i++;
1102         NextMd = MdBlock->ListEntry.Flink;
1103     }
1104 
1105     /* Allocate a structure to hold the physical runs */
1106     Buffer = ExAllocatePoolWithTag(NonPagedPool,
1107                                    i * sizeof(PHYSICAL_MEMORY_RUN),
1108                                    'lMmM');
1109     ASSERT(Buffer != NULL);
1110     Entry = Buffer;
1111 
1112     /* Loop the descriptors again */
1113     NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
1114     while (NextMd != &LoaderBlock->MemoryDescriptorListHead)
1115     {
1116         /* Check what kind this was */
1117         MdBlock = CONTAINING_RECORD(NextMd,
1118                                     MEMORY_ALLOCATION_DESCRIPTOR,
1119                                     ListEntry);
1120         switch (MdBlock->MemoryType)
1121         {
1122             /* Registry, NLS, and heap data */
1123             case LoaderRegistryData:
1124             case LoaderOsloaderHeap:
1125             case LoaderNlsData:
1126                 /* Are all a candidate for deletion */
1127                 Entry->BasePage = MdBlock->BasePage;
1128                 Entry->PageCount = MdBlock->PageCount;
1129                 Entry++;
1130 
1131             /* We keep the rest */
1132             default:
1133                 break;
1134         }
1135 
1136         /* Move to the next descriptor */
1137         NextMd = MdBlock->ListEntry.Flink;
1138     }
1139 
1140     /* Acquire the PFN lock */
1141     OldIrql = MiAcquirePfnLock();
1142 
1143     /* Loop the runs */
1144     LoaderPages = 0;
1145     while (--Entry >= Buffer)
1146     {
1147         /* See how many pages are in this run */
1148         i = Entry->PageCount;
1149         BasePage = Entry->BasePage;
1150 
1151         /* Loop each page */
1152         Pfn1 = MiGetPfnEntry(BasePage);
1153         while (i--)
1154         {
1155             /* Check if it has references or is in any kind of list */
1156             if (!(Pfn1->u3.e2.ReferenceCount) && (!Pfn1->u1.Flink))
1157             {
1158                 /* Set the new PTE address and put this page into the free list */
1159                 Pfn1->PteAddress = (PMMPTE)(BasePage << PAGE_SHIFT);
1160                 MiInsertPageInFreeList(BasePage);
1161                 LoaderPages++;
1162             }
1163             else if (BasePage)
1164             {
1165                 /* It has a reference, so simply drop it */
1166                 ASSERT(MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(Pfn1->PteAddress)) == FALSE);
1167 
1168                 /* Drop a dereference on this page, which should delete it */
1169                 Pfn1->PteAddress->u.Long = 0;
1170                 MI_SET_PFN_DELETED(Pfn1);
1171                 MiDecrementShareCount(Pfn1, BasePage);
1172                 LoaderPages++;
1173             }
1174 
1175             /* Move to the next page */
1176             Pfn1++;
1177             BasePage++;
1178         }
1179     }
1180 
1181     /* Release the PFN lock and flush the TLB */
1182     DPRINT("Loader pages freed: %lx\n", LoaderPages);
1183     MiReleasePfnLock(OldIrql);
1184     KeFlushCurrentTb();
1185 
1186     /* Free our run structure */
1187     ExFreePoolWithTag(Buffer, 'lMmM');
1188 }
1189 
1190 VOID
1191 NTAPI
1192 INIT_FUNCTION
1193 MiAdjustWorkingSetManagerParameters(IN BOOLEAN Client)
1194 {
1195     /* This function needs to do more work, for now, we tune page minimums */
1196 
1197     /* Check for a system with around 64MB RAM or more */
1198     if (MmNumberOfPhysicalPages >= (63 * _1MB) / PAGE_SIZE)
1199     {
1200         /* Double the minimum amount of pages we consider for a "plenty free" scenario */
1201         MmPlentyFreePages *= 2;
1202     }
1203 }
1204 
1205 VOID
1206 NTAPI
1207 INIT_FUNCTION
1208 MiNotifyMemoryEvents(VOID)
1209 {
1210     /* Are we in a low-memory situation? */
1211     if (MmAvailablePages < MmLowMemoryThreshold)
1212     {
1213         /* Clear high, set low  */
1214         if (KeReadStateEvent(MiHighMemoryEvent)) KeClearEvent(MiHighMemoryEvent);
1215         if (!KeReadStateEvent(MiLowMemoryEvent)) KeSetEvent(MiLowMemoryEvent, 0, FALSE);
1216     }
1217     else if (MmAvailablePages < MmHighMemoryThreshold)
1218     {
1219         /* We are in between, clear both */
1220         if (KeReadStateEvent(MiHighMemoryEvent)) KeClearEvent(MiHighMemoryEvent);
1221         if (KeReadStateEvent(MiLowMemoryEvent)) KeClearEvent(MiLowMemoryEvent);
1222     }
1223     else
1224     {
1225         /* Clear low, set high  */
1226         if (KeReadStateEvent(MiLowMemoryEvent)) KeClearEvent(MiLowMemoryEvent);
1227         if (!KeReadStateEvent(MiHighMemoryEvent)) KeSetEvent(MiHighMemoryEvent, 0, FALSE);
1228     }
1229 }
1230 
1231 NTSTATUS
1232 NTAPI
1233 INIT_FUNCTION
1234 MiCreateMemoryEvent(IN PUNICODE_STRING Name,
1235                     OUT PKEVENT *Event)
1236 {
1237     PACL Dacl;
1238     HANDLE EventHandle;
1239     ULONG DaclLength;
1240     NTSTATUS Status;
1241     OBJECT_ATTRIBUTES ObjectAttributes;
1242     SECURITY_DESCRIPTOR SecurityDescriptor;
1243 
1244     /* Create the SD */
1245     Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
1246                                          SECURITY_DESCRIPTOR_REVISION);
1247     if (!NT_SUCCESS(Status)) return Status;
1248 
1249     /* One ACL with 3 ACEs, containing each one SID */
1250     DaclLength = sizeof(ACL) +
1251                  3 * sizeof(ACCESS_ALLOWED_ACE) +
1252                  RtlLengthSid(SeLocalSystemSid) +
1253                  RtlLengthSid(SeAliasAdminsSid) +
1254                  RtlLengthSid(SeWorldSid);
1255 
1256     /* Allocate space for the DACL */
1257     Dacl = ExAllocatePoolWithTag(PagedPool, DaclLength, 'lcaD');
1258     if (!Dacl) return STATUS_INSUFFICIENT_RESOURCES;
1259 
1260     /* Setup the ACL inside it */
1261     Status = RtlCreateAcl(Dacl, DaclLength, ACL_REVISION);
1262     if (!NT_SUCCESS(Status)) goto CleanUp;
1263 
1264     /* Add query rights for everyone */
1265     Status = RtlAddAccessAllowedAce(Dacl,
1266                                     ACL_REVISION,
1267                                     SYNCHRONIZE | EVENT_QUERY_STATE | READ_CONTROL,
1268                                     SeWorldSid);
1269     if (!NT_SUCCESS(Status)) goto CleanUp;
1270 
1271     /* Full rights for the admin */
1272     Status = RtlAddAccessAllowedAce(Dacl,
1273                                     ACL_REVISION,
1274                                     EVENT_ALL_ACCESS,
1275                                     SeAliasAdminsSid);
1276     if (!NT_SUCCESS(Status)) goto CleanUp;
1277 
1278     /* As well as full rights for the system */
1279     Status = RtlAddAccessAllowedAce(Dacl,
1280                                     ACL_REVISION,
1281                                     EVENT_ALL_ACCESS,
1282                                     SeLocalSystemSid);
1283     if (!NT_SUCCESS(Status)) goto CleanUp;
1284 
1285     /* Set this DACL inside the SD */
1286     Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor,
1287                                           TRUE,
1288                                           Dacl,
1289                                           FALSE);
1290     if (!NT_SUCCESS(Status)) goto CleanUp;
1291 
1292     /* Setup the event attributes, making sure it's a permanent one */
1293     InitializeObjectAttributes(&ObjectAttributes,
1294                                Name,
1295                                OBJ_KERNEL_HANDLE | OBJ_PERMANENT,
1296                                NULL,
1297                                &SecurityDescriptor);
1298 
1299     /* Create the event */
1300     Status = ZwCreateEvent(&EventHandle,
1301                            EVENT_ALL_ACCESS,
1302                            &ObjectAttributes,
1303                            NotificationEvent,
1304                            FALSE);
1305 CleanUp:
1306     /* Free the DACL */
1307     ExFreePoolWithTag(Dacl, 'lcaD');
1308 
1309     /* Check if this is the success path */
1310     if (NT_SUCCESS(Status))
1311     {
1312         /* Add a reference to the object, then close the handle we had */
1313         Status = ObReferenceObjectByHandle(EventHandle,
1314                                            EVENT_MODIFY_STATE,
1315                                            ExEventObjectType,
1316                                            KernelMode,
1317                                            (PVOID*)Event,
1318                                            NULL);
1319         ZwClose (EventHandle);
1320     }
1321 
1322     /* Return status */
1323     return Status;
1324 }
1325 
1326 BOOLEAN
1327 NTAPI
1328 INIT_FUNCTION
1329 MiInitializeMemoryEvents(VOID)
1330 {
1331     UNICODE_STRING LowString = RTL_CONSTANT_STRING(L"\\KernelObjects\\LowMemoryCondition");
1332     UNICODE_STRING HighString = RTL_CONSTANT_STRING(L"\\KernelObjects\\HighMemoryCondition");
1333     UNICODE_STRING LowPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\LowPagedPoolCondition");
1334     UNICODE_STRING HighPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\HighPagedPoolCondition");
1335     UNICODE_STRING LowNonPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\LowNonPagedPoolCondition");
1336     UNICODE_STRING HighNonPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\HighNonPagedPoolCondition");
1337     NTSTATUS Status;
1338 
1339     /* Check if we have a registry setting */
1340     if (MmLowMemoryThreshold)
1341     {
1342         /* Convert it to pages */
1343         MmLowMemoryThreshold *= (_1MB / PAGE_SIZE);
1344     }
1345     else
1346     {
1347         /* The low memory threshold is hit when we don't consider that we have "plenty" of free pages anymore */
1348         MmLowMemoryThreshold = MmPlentyFreePages;
1349 
1350         /* More than one GB of memory? */
1351         if (MmNumberOfPhysicalPages > 0x40000)
1352         {
1353             /* Start at 32MB, and add another 16MB for each GB */
1354             MmLowMemoryThreshold = (32 * _1MB) / PAGE_SIZE;
1355             MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x40000) >> 7);
1356         }
1357         else if (MmNumberOfPhysicalPages > 0x8000)
1358         {
1359             /* For systems with > 128MB RAM, add another 4MB for each 128MB */
1360             MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x8000) >> 5);
1361         }
1362 
1363         /* Don't let the minimum threshold go past 64MB */
1364         MmLowMemoryThreshold = min(MmLowMemoryThreshold, (64 * _1MB) / PAGE_SIZE);
1365     }
1366 
1367     /* Check if we have a registry setting */
1368     if (MmHighMemoryThreshold)
1369     {
1370         /* Convert it into pages */
1371         MmHighMemoryThreshold *= (_1MB / PAGE_SIZE);
1372     }
1373     else
1374     {
1375         /* Otherwise, the default is three times the low memory threshold */
1376         MmHighMemoryThreshold = 3 * MmLowMemoryThreshold;
1377         ASSERT(MmHighMemoryThreshold > MmLowMemoryThreshold);
1378     }
1379 
1380     /* Make sure high threshold is actually higher than the low */
1381     MmHighMemoryThreshold = max(MmHighMemoryThreshold, MmLowMemoryThreshold);
1382 
1383     /* Create the memory events for all the thresholds */
1384     Status = MiCreateMemoryEvent(&LowString, &MiLowMemoryEvent);
1385     if (!NT_SUCCESS(Status)) return FALSE;
1386     Status = MiCreateMemoryEvent(&HighString, &MiHighMemoryEvent);
1387     if (!NT_SUCCESS(Status)) return FALSE;
1388     Status = MiCreateMemoryEvent(&LowPagedPoolString, &MiLowPagedPoolEvent);
1389     if (!NT_SUCCESS(Status)) return FALSE;
1390     Status = MiCreateMemoryEvent(&HighPagedPoolString, &MiHighPagedPoolEvent);
1391     if (!NT_SUCCESS(Status)) return FALSE;
1392     Status = MiCreateMemoryEvent(&LowNonPagedPoolString, &MiLowNonPagedPoolEvent);
1393     if (!NT_SUCCESS(Status)) return FALSE;
1394     Status = MiCreateMemoryEvent(&HighNonPagedPoolString, &MiHighNonPagedPoolEvent);
1395     if (!NT_SUCCESS(Status)) return FALSE;
1396 
1397     /* Now setup the pool events */
1398     MiInitializePoolEvents();
1399 
1400     /* Set the initial event state */
1401     MiNotifyMemoryEvents();
1402     return TRUE;
1403 }
1404 
1405 VOID
1406 NTAPI
1407 INIT_FUNCTION
1408 MiAddHalIoMappings(VOID)
1409 {
1410     PVOID BaseAddress;
1411     PMMPDE PointerPde, LastPde;
1412     PMMPTE PointerPte;
1413     ULONG j;
1414     PFN_NUMBER PageFrameIndex;
1415 
1416     /* HAL Heap address -- should be on a PDE boundary */
1417     BaseAddress = (PVOID)MM_HAL_VA_START;
1418     ASSERT(MiAddressToPteOffset(BaseAddress) == 0);
1419 
1420     /* Check how many PDEs the heap has */
1421     PointerPde = MiAddressToPde(BaseAddress);
1422     LastPde = MiAddressToPde((PVOID)MM_HAL_VA_END);
1423 
1424     while (PointerPde <= LastPde)
1425     {
1426         /* Does the HAL own this mapping? */
1427         if ((PointerPde->u.Hard.Valid == 1) &&
1428             (MI_IS_PAGE_LARGE(PointerPde) == FALSE))
1429         {
1430             /* Get the PTE for it and scan each page */
1431             PointerPte = MiAddressToPte(BaseAddress);
1432             for (j = 0 ; j < PTE_COUNT; j++)
1433             {
1434                 /* Does the HAL own this page? */
1435                 if (PointerPte->u.Hard.Valid == 1)
1436                 {
1437                     /* Is the HAL using it for device or I/O mapped memory? */
1438                     PageFrameIndex = PFN_FROM_PTE(PointerPte);
1439                     if (!MiGetPfnEntry(PageFrameIndex))
1440                     {
1441                         /* FIXME: For PAT, we need to track I/O cache attributes for coherency */
1442                         DPRINT1("HAL I/O Mapping at %p is unsafe\n", BaseAddress);
1443                     }
1444                 }
1445 
1446                 /* Move to the next page */
1447                 BaseAddress = (PVOID)((ULONG_PTR)BaseAddress + PAGE_SIZE);
1448                 PointerPte++;
1449             }
1450         }
1451         else
1452         {
1453             /* Move to the next address */
1454             BaseAddress = (PVOID)((ULONG_PTR)BaseAddress + PDE_MAPPED_VA);
1455         }
1456 
1457         /* Move to the next PDE */
1458         PointerPde++;
1459     }
1460 }
1461 
1462 VOID
1463 NTAPI
1464 MmDumpArmPfnDatabase(IN BOOLEAN StatusOnly)
1465 {
1466     ULONG i;
1467     PMMPFN Pfn1;
1468     PCHAR Consumer = "Unknown";
1469     KIRQL OldIrql;
1470     ULONG ActivePages = 0, FreePages = 0, OtherPages = 0;
1471 #if MI_TRACE_PFNS
1472     ULONG UsageBucket[MI_USAGE_FREE_PAGE + 1] = {0};
1473     PCHAR MI_USAGE_TEXT[MI_USAGE_FREE_PAGE + 1] =
1474     {
1475         "Not set",
1476         "Paged Pool",
1477         "Nonpaged Pool",
1478         "Nonpaged Pool Ex",
1479         "Kernel Stack",
1480         "Kernel Stack Ex",
1481         "System PTE",
1482         "VAD",
1483         "PEB/TEB",
1484         "Section",
1485         "Page Table",
1486         "Page Directory",
1487         "Old Page Table",
1488         "Driver Page",
1489         "Contiguous Alloc",
1490         "MDL",
1491         "Demand Zero",
1492         "Zero Loop",
1493         "Cache",
1494         "PFN Database",
1495         "Boot Driver",
1496         "Initial Memory",
1497         "Free Page"
1498     };
1499 #endif
1500     //
1501     // Loop the PFN database
1502     //
1503     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1504     for (i = 0; i <= MmHighestPhysicalPage; i++)
1505     {
1506         Pfn1 = MiGetPfnEntry(i);
1507         if (!Pfn1) continue;
1508 #if MI_TRACE_PFNS
1509         ASSERT(Pfn1->PfnUsage <= MI_USAGE_FREE_PAGE);
1510 #endif
1511         //
1512         // Get the page location
1513         //
1514         switch (Pfn1->u3.e1.PageLocation)
1515         {
1516             case ActiveAndValid:
1517 
1518                 Consumer = "Active and Valid";
1519                 ActivePages++;
1520                 break;
1521 
1522             case ZeroedPageList:
1523 
1524                 Consumer = "Zero Page List";
1525                 FreePages++;
1526                 break;//continue;
1527 
1528             case FreePageList:
1529 
1530                 Consumer = "Free Page List";
1531                 FreePages++;
1532                 break;//continue;
1533 
1534             default:
1535 
1536                 Consumer = "Other (ASSERT!)";
1537                 OtherPages++;
1538                 break;
1539         }
1540 
1541 #if MI_TRACE_PFNS
1542         /* Add into bucket */
1543         UsageBucket[Pfn1->PfnUsage]++;
1544 #endif
1545 
1546         //
1547         // Pretty-print the page
1548         //
1549         if (!StatusOnly)
1550         DbgPrint("0x%08p:\t%20s\t(%04d.%04d)\t[%16s - %16s])\n",
1551                  i << PAGE_SHIFT,
1552                  Consumer,
1553                  Pfn1->u3.e2.ReferenceCount,
1554                  Pfn1->u2.ShareCount == LIST_HEAD ? 0xFFFF : Pfn1->u2.ShareCount,
1555 #if MI_TRACE_PFNS
1556                  MI_USAGE_TEXT[Pfn1->PfnUsage],
1557                  Pfn1->ProcessName);
1558 #else
1559                  "Page tracking",
1560                  "is disabled");
1561 #endif
1562     }
1563 
1564     DbgPrint("Active:               %5d pages\t[%6d KB]\n", ActivePages,  (ActivePages    << PAGE_SHIFT) / 1024);
1565     DbgPrint("Free:                 %5d pages\t[%6d KB]\n", FreePages,    (FreePages      << PAGE_SHIFT) / 1024);
1566     DbgPrint("Other:                %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1567     DbgPrint("-----------------------------------------\n");
1568 #if MI_TRACE_PFNS
1569     OtherPages = UsageBucket[MI_USAGE_BOOT_DRIVER];
1570     DbgPrint("Boot Images:          %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1571     OtherPages = UsageBucket[MI_USAGE_DRIVER_PAGE];
1572     DbgPrint("System Drivers:       %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1573     OtherPages = UsageBucket[MI_USAGE_PFN_DATABASE];
1574     DbgPrint("PFN Database:         %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1575     OtherPages = UsageBucket[MI_USAGE_PAGE_TABLE] + UsageBucket[MI_USAGE_PAGE_DIRECTORY] + UsageBucket[MI_USAGE_LEGACY_PAGE_DIRECTORY];
1576     DbgPrint("Page Tables:          %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1577     OtherPages = UsageBucket[MI_USAGE_SYSTEM_PTE];
1578     DbgPrint("System PTEs:          %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1579     OtherPages = UsageBucket[MI_USAGE_VAD];
1580     DbgPrint("VADs:                 %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1581     OtherPages = UsageBucket[MI_USAGE_CONTINOUS_ALLOCATION];
1582     DbgPrint("Continuous Allocs:    %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1583     OtherPages = UsageBucket[MI_USAGE_MDL];
1584     DbgPrint("MDLs:                 %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1585     OtherPages = UsageBucket[MI_USAGE_NONPAGED_POOL] + UsageBucket[MI_USAGE_NONPAGED_POOL_EXPANSION];
1586     DbgPrint("NonPaged Pool:        %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1587     OtherPages = UsageBucket[MI_USAGE_PAGED_POOL];
1588     DbgPrint("Paged Pool:           %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1589     OtherPages = UsageBucket[MI_USAGE_DEMAND_ZERO];
1590     DbgPrint("Demand Zero:          %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1591     OtherPages = UsageBucket[MI_USAGE_ZERO_LOOP];
1592     DbgPrint("Zero Loop:            %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1593     OtherPages = UsageBucket[MI_USAGE_PEB_TEB];
1594     DbgPrint("PEB/TEB:              %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1595     OtherPages = UsageBucket[MI_USAGE_KERNEL_STACK] + UsageBucket[MI_USAGE_KERNEL_STACK_EXPANSION];
1596     DbgPrint("Kernel Stack:         %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1597     OtherPages = UsageBucket[MI_USAGE_INIT_MEMORY];
1598     DbgPrint("Init Memory:          %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1599     OtherPages = UsageBucket[MI_USAGE_SECTION];
1600     DbgPrint("Sections:             %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1601     OtherPages = UsageBucket[MI_USAGE_CACHE];
1602     DbgPrint("Cache:                %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1603     OtherPages = UsageBucket[MI_USAGE_FREE_PAGE];
1604     DbgPrint("Free:                 %5d pages\t[%6d KB]\n", OtherPages,   (OtherPages     << PAGE_SHIFT) / 1024);
1605 #endif
1606     KeLowerIrql(OldIrql);
1607 }
1608 
1609 PPHYSICAL_MEMORY_DESCRIPTOR
1610 NTAPI
1611 INIT_FUNCTION
1612 MmInitializeMemoryLimits(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
1613                          IN PBOOLEAN IncludeType)
1614 {
1615     PLIST_ENTRY NextEntry;
1616     ULONG Run = 0, InitialRuns;
1617     PFN_NUMBER NextPage = -1, PageCount = 0;
1618     PPHYSICAL_MEMORY_DESCRIPTOR Buffer, NewBuffer;
1619     PMEMORY_ALLOCATION_DESCRIPTOR MdBlock;
1620 
1621     //
1622     // Start with the maximum we might need
1623     //
1624     InitialRuns = MiNumberDescriptors;
1625 
1626     //
1627     // Allocate the maximum we'll ever need
1628     //
1629     Buffer = ExAllocatePoolWithTag(NonPagedPool,
1630                                    sizeof(PHYSICAL_MEMORY_DESCRIPTOR) +
1631                                    sizeof(PHYSICAL_MEMORY_RUN) *
1632                                    (InitialRuns - 1),
1633                                    'lMmM');
1634     if (!Buffer) return NULL;
1635 
1636     //
1637     // For now that's how many runs we have
1638     //
1639     Buffer->NumberOfRuns = InitialRuns;
1640 
1641     //
1642     // Now loop through the descriptors again
1643     //
1644     NextEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
1645     while (NextEntry != &LoaderBlock->MemoryDescriptorListHead)
1646     {
1647         //
1648         // Grab each one, and check if it's one we should include
1649         //
1650         MdBlock = CONTAINING_RECORD(NextEntry,
1651                                     MEMORY_ALLOCATION_DESCRIPTOR,
1652                                     ListEntry);
1653         if ((MdBlock->MemoryType < LoaderMaximum) &&
1654             (IncludeType[MdBlock->MemoryType]))
1655         {
1656             //
1657             // Add this to our running total
1658             //
1659             PageCount += MdBlock->PageCount;
1660 
1661             //
1662             // Check if the next page is described by the next descriptor
1663             //
1664             if (MdBlock->BasePage == NextPage)
1665             {
1666                 //
1667                 // Combine it into the same physical run
1668                 //
1669                 ASSERT(MdBlock->PageCount != 0);
1670                 Buffer->Run[Run - 1].PageCount += MdBlock->PageCount;
1671                 NextPage += MdBlock->PageCount;
1672             }
1673             else
1674             {
1675                 //
1676                 // Otherwise just duplicate the descriptor's contents
1677                 //
1678                 Buffer->Run[Run].BasePage = MdBlock->BasePage;
1679                 Buffer->Run[Run].PageCount = MdBlock->PageCount;
1680                 NextPage = Buffer->Run[Run].BasePage + Buffer->Run[Run].PageCount;
1681 
1682                 //
1683                 // And in this case, increase the number of runs
1684                 //
1685                 Run++;
1686             }
1687         }
1688 
1689         //
1690         // Try the next descriptor
1691         //
1692         NextEntry = MdBlock->ListEntry.Flink;
1693     }
1694 
1695     //
1696     // We should not have been able to go past our initial estimate
1697     //
1698     ASSERT(Run <= Buffer->NumberOfRuns);
1699 
1700     //
1701     // Our guess was probably exaggerated...
1702     //
1703     if (InitialRuns > Run)
1704     {
1705         //
1706         // Allocate a more accurately sized buffer
1707         //
1708         NewBuffer = ExAllocatePoolWithTag(NonPagedPool,
1709                                           sizeof(PHYSICAL_MEMORY_DESCRIPTOR) +
1710                                           sizeof(PHYSICAL_MEMORY_RUN) *
1711                                           (Run - 1),
1712                                           'lMmM');
1713         if (NewBuffer)
1714         {
1715             //
1716             // Copy the old buffer into the new, then free it
1717             //
1718             RtlCopyMemory(NewBuffer->Run,
1719                           Buffer->Run,
1720                           sizeof(PHYSICAL_MEMORY_RUN) * Run);
1721             ExFreePoolWithTag(Buffer, 'lMmM');
1722 
1723             //
1724             // Now use the new buffer
1725             //
1726             Buffer = NewBuffer;
1727         }
1728     }
1729 
1730     //
1731     // Write the final numbers, and return it
1732     //
1733     Buffer->NumberOfRuns = Run;
1734     Buffer->NumberOfPages = PageCount;
1735     return Buffer;
1736 }
1737 
1738 VOID
1739 NTAPI
1740 INIT_FUNCTION
1741 MiBuildPagedPool(VOID)
1742 {
1743     PMMPTE PointerPte;
1744     PMMPDE PointerPde;
1745     MMPDE TempPde = ValidKernelPde;
1746     PFN_NUMBER PageFrameIndex;
1747     KIRQL OldIrql;
1748     SIZE_T Size;
1749     ULONG BitMapSize;
1750 #if (_MI_PAGING_LEVELS >= 3)
1751     MMPPE TempPpe = ValidKernelPpe;
1752     PMMPPE PointerPpe;
1753 #elif (_MI_PAGING_LEVELS == 2)
1754     MMPTE TempPte = ValidKernelPte;
1755 
1756     //
1757     // Get the page frame number for the system page directory
1758     //
1759     PointerPte = MiAddressToPte(PDE_BASE);
1760     ASSERT(PD_COUNT == 1);
1761     MmSystemPageDirectory[0] = PFN_FROM_PTE(PointerPte);
1762 
1763     //
1764     // Allocate a system PTE which will hold a copy of the page directory
1765     //
1766     PointerPte = MiReserveSystemPtes(1, SystemPteSpace);
1767     ASSERT(PointerPte);
1768     MmSystemPagePtes = MiPteToAddress(PointerPte);
1769 
1770     //
1771     // Make this system PTE point to the system page directory.
1772     // It is now essentially double-mapped. This will be used later for lazy
1773     // evaluation of PDEs accross process switches, similarly to how the Global
1774     // page directory array in the old ReactOS Mm is used (but in a less hacky
1775     // way).
1776     //
1777     TempPte = ValidKernelPte;
1778     ASSERT(PD_COUNT == 1);
1779     TempPte.u.Hard.PageFrameNumber = MmSystemPageDirectory[0];
1780     MI_WRITE_VALID_PTE(PointerPte, TempPte);
1781 #endif
1782     //
1783     // Let's get back to paged pool work: size it up.
1784     // By default, it should be twice as big as nonpaged pool.
1785     //
1786     MmSizeOfPagedPoolInBytes = 2 * MmMaximumNonPagedPoolInBytes;
1787     if (MmSizeOfPagedPoolInBytes > ((ULONG_PTR)MmNonPagedSystemStart -
1788                                     (ULONG_PTR)MmPagedPoolStart))
1789     {
1790         //
1791         // On the other hand, we have limited VA space, so make sure that the VA
1792         // for paged pool doesn't overflow into nonpaged pool VA. Otherwise, set
1793         // whatever maximum is possible.
1794         //
1795         MmSizeOfPagedPoolInBytes = (ULONG_PTR)MmNonPagedSystemStart -
1796                                    (ULONG_PTR)MmPagedPoolStart;
1797     }
1798 
1799     //
1800     // Get the size in pages and make sure paged pool is at least 32MB.
1801     //
1802     Size = MmSizeOfPagedPoolInBytes;
1803     if (Size < MI_MIN_INIT_PAGED_POOLSIZE) Size = MI_MIN_INIT_PAGED_POOLSIZE;
1804     Size = BYTES_TO_PAGES(Size);
1805 
1806     //
1807     // Now check how many PTEs will be required for these many pages.
1808     //
1809     Size = (Size + (1024 - 1)) / 1024;
1810 
1811     //
1812     // Recompute the page-aligned size of the paged pool, in bytes and pages.
1813     //
1814     MmSizeOfPagedPoolInBytes = Size * PAGE_SIZE * 1024;
1815     MmSizeOfPagedPoolInPages = MmSizeOfPagedPoolInBytes >> PAGE_SHIFT;
1816 
1817     //
1818     // Let's be really sure this doesn't overflow into nonpaged system VA
1819     //
1820     ASSERT((MmSizeOfPagedPoolInBytes + (ULONG_PTR)MmPagedPoolStart) <=
1821            (ULONG_PTR)MmNonPagedSystemStart);
1822 
1823     //
1824     // This is where paged pool ends
1825     //
1826     MmPagedPoolEnd = (PVOID)(((ULONG_PTR)MmPagedPoolStart +
1827                               MmSizeOfPagedPoolInBytes) - 1);
1828 
1829     //
1830     // Lock the PFN database
1831     //
1832     OldIrql = MiAcquirePfnLock();
1833 
1834 #if (_MI_PAGING_LEVELS >= 3)
1835     /* On these systems, there's no double-mapping, so instead, the PPEs
1836      * are setup to span the entire paged pool area, so there's no need for the
1837      * system PD */
1838     for (PointerPpe = MiAddressToPpe(MmPagedPoolStart);
1839          PointerPpe <= MiAddressToPpe(MmPagedPoolEnd);
1840          PointerPpe++)
1841     {
1842         /* Check if the PPE is already valid */
1843         if (!PointerPpe->u.Hard.Valid)
1844         {
1845             /* It is not, so map a fresh zeroed page */
1846             TempPpe.u.Hard.PageFrameNumber = MiRemoveZeroPage(0);
1847             MI_WRITE_VALID_PPE(PointerPpe, TempPpe);
1848         }
1849     }
1850 #endif
1851 
1852     //
1853     // So now get the PDE for paged pool and zero it out
1854     //
1855     PointerPde = MiAddressToPde(MmPagedPoolStart);
1856     RtlZeroMemory(PointerPde,
1857                   (1 + MiAddressToPde(MmPagedPoolEnd) - PointerPde) * sizeof(MMPDE));
1858 
1859     //
1860     // Next, get the first and last PTE
1861     //
1862     PointerPte = MiAddressToPte(MmPagedPoolStart);
1863     MmPagedPoolInfo.FirstPteForPagedPool = PointerPte;
1864     MmPagedPoolInfo.LastPteForPagedPool = MiAddressToPte(MmPagedPoolEnd);
1865 
1866     /* Allocate a page and map the first paged pool PDE */
1867     MI_SET_USAGE(MI_USAGE_PAGED_POOL);
1868     MI_SET_PROCESS2("Kernel");
1869     PageFrameIndex = MiRemoveZeroPage(0);
1870     TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
1871     MI_WRITE_VALID_PDE(PointerPde, TempPde);
1872 #if (_MI_PAGING_LEVELS >= 3)
1873     /* Use the PPE of MmPagedPoolStart that was setup above */
1874 //    Bla = PFN_FROM_PTE(PpeAddress(MmPagedPool...));
1875 
1876     /* Initialize the PFN entry for it */
1877     MiInitializePfnForOtherProcess(PageFrameIndex,
1878                                    (PMMPTE)PointerPde,
1879                                    PFN_FROM_PTE(MiAddressToPpe(MmPagedPoolStart)));
1880 #else
1881     /* Do it this way */
1882 //    Bla = MmSystemPageDirectory[(PointerPde - (PMMPTE)PDE_BASE) / PDE_COUNT]
1883 
1884     /* Initialize the PFN entry for it */
1885     MiInitializePfnForOtherProcess(PageFrameIndex,
1886                                    (PMMPTE)PointerPde,
1887                                    MmSystemPageDirectory[(PointerPde - (PMMPDE)PDE_BASE) / PDE_COUNT]);
1888 #endif
1889 
1890     //
1891     // Release the PFN database lock
1892     //
1893     MiReleasePfnLock(OldIrql);
1894 
1895     //
1896     // We only have one PDE mapped for now... at fault time, additional PDEs
1897     // will be allocated to handle paged pool growth. This is where they'll have
1898     // to start.
1899     //
1900     MmPagedPoolInfo.NextPdeForPagedPoolExpansion = PointerPde + 1;
1901 
1902     //
1903     // We keep track of each page via a bit, so check how big the bitmap will
1904     // have to be (make sure to align our page count such that it fits nicely
1905     // into a 4-byte aligned bitmap.
1906     //
1907     // We'll also allocate the bitmap header itself part of the same buffer.
1908     //
1909     Size = Size * 1024;
1910     ASSERT(Size == MmSizeOfPagedPoolInPages);
1911     BitMapSize = (ULONG)Size;
1912     Size = sizeof(RTL_BITMAP) + (((Size + 31) / 32) * sizeof(ULONG));
1913 
1914     //
1915     // Allocate the allocation bitmap, which tells us which regions have not yet
1916     // been mapped into memory
1917     //
1918     MmPagedPoolInfo.PagedPoolAllocationMap = ExAllocatePoolWithTag(NonPagedPool,
1919                                                                    Size,
1920                                                                    TAG_MM);
1921     ASSERT(MmPagedPoolInfo.PagedPoolAllocationMap);
1922 
1923     //
1924     // Initialize it such that at first, only the first page's worth of PTEs is
1925     // marked as allocated (incidentially, the first PDE we allocated earlier).
1926     //
1927     RtlInitializeBitMap(MmPagedPoolInfo.PagedPoolAllocationMap,
1928                         (PULONG)(MmPagedPoolInfo.PagedPoolAllocationMap + 1),
1929                         BitMapSize);
1930     RtlSetAllBits(MmPagedPoolInfo.PagedPoolAllocationMap);
1931     RtlClearBits(MmPagedPoolInfo.PagedPoolAllocationMap, 0, 1024);
1932 
1933     //
1934     // We have a second bitmap, which keeps track of where allocations end.
1935     // Given the allocation bitmap and a base address, we can therefore figure
1936     // out which page is the last page of that allocation, and thus how big the
1937     // entire allocation is.
1938     //
1939     MmPagedPoolInfo.EndOfPagedPoolBitmap = ExAllocatePoolWithTag(NonPagedPool,
1940                                                                  Size,
1941                                                                  TAG_MM);
1942     ASSERT(MmPagedPoolInfo.EndOfPagedPoolBitmap);
1943     RtlInitializeBitMap(MmPagedPoolInfo.EndOfPagedPoolBitmap,
1944                         (PULONG)(MmPagedPoolInfo.EndOfPagedPoolBitmap + 1),
1945                         BitMapSize);
1946 
1947     //
1948     // Since no allocations have been made yet, there are no bits set as the end
1949     //
1950     RtlClearAllBits(MmPagedPoolInfo.EndOfPagedPoolBitmap);
1951 
1952     //
1953     // Initialize paged pool.
1954     //
1955     InitializePool(PagedPool, 0);
1956 
1957     /* Initialize special pool */
1958     MiInitializeSpecialPool();
1959 
1960     /* Default low threshold of 30MB or one fifth of paged pool */
1961     MiLowPagedPoolThreshold = (30 * _1MB) >> PAGE_SHIFT;
1962     MiLowPagedPoolThreshold = min(MiLowPagedPoolThreshold, Size / 5);
1963 
1964     /* Default high threshold of 60MB or 25% */
1965     MiHighPagedPoolThreshold = (60 * _1MB) >> PAGE_SHIFT;
1966     MiHighPagedPoolThreshold = min(MiHighPagedPoolThreshold, (Size * 2) / 5);
1967     ASSERT(MiLowPagedPoolThreshold < MiHighPagedPoolThreshold);
1968 
1969     /* Setup the global session space */
1970     MiInitializeSystemSpaceMap(NULL);
1971 }
1972 
1973 VOID
1974 NTAPI
1975 INIT_FUNCTION
1976 MiDbgDumpMemoryDescriptors(VOID)
1977 {
1978     PLIST_ENTRY NextEntry;
1979     PMEMORY_ALLOCATION_DESCRIPTOR Md;
1980     PFN_NUMBER TotalPages = 0;
1981     PCHAR
1982     MemType[] =
1983     {
1984         "ExceptionBlock    ",
1985         "SystemBlock       ",
1986         "Free              ",
1987         "Bad               ",
1988         "LoadedProgram     ",
1989         "FirmwareTemporary ",
1990         "FirmwarePermanent ",
1991         "OsloaderHeap      ",
1992         "OsloaderStack     ",
1993         "SystemCode        ",
1994         "HalCode           ",
1995         "BootDriver        ",
1996         "ConsoleInDriver   ",
1997         "ConsoleOutDriver  ",
1998         "StartupDpcStack   ",
1999         "StartupKernelStack",
2000         "StartupPanicStack ",
2001         "StartupPcrPage    ",
2002         "StartupPdrPage    ",
2003         "RegistryData      ",
2004         "MemoryData        ",
2005         "NlsData           ",
2006         "SpecialMemory     ",
2007         "BBTMemory         ",
2008         "LoaderReserve     ",
2009         "LoaderXIPRom      "
2010     };
2011 
2012     DPRINT1("Base\t\tLength\t\tType\n");
2013     for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
2014          NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
2015          NextEntry = NextEntry->Flink)
2016     {
2017         Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
2018         DPRINT1("%08lX\t%08lX\t%s\n", Md->BasePage, Md->PageCount, MemType[Md->MemoryType]);
2019         TotalPages += Md->PageCount;
2020     }
2021 
2022     DPRINT1("Total: %08lX (%lu MB)\n", (ULONG)TotalPages, (ULONG)(TotalPages * PAGE_SIZE) / 1024 / 1024);
2023 }
2024 
2025 BOOLEAN
2026 NTAPI
2027 INIT_FUNCTION
2028 MmArmInitSystem(IN ULONG Phase,
2029                 IN PLOADER_PARAMETER_BLOCK LoaderBlock)
2030 {
2031     ULONG i;
2032     BOOLEAN IncludeType[LoaderMaximum];
2033     PVOID Bitmap;
2034     PPHYSICAL_MEMORY_RUN Run;
2035     PFN_NUMBER PageCount;
2036 #if DBG
2037     ULONG j;
2038     PMMPTE PointerPte, TestPte;
2039     MMPTE TempPte;
2040 #endif
2041 
2042     /* Dump memory descriptors */
2043     if (MiDbgEnableMdDump) MiDbgDumpMemoryDescriptors();
2044 
2045     //
2046     // Instantiate memory that we don't consider RAM/usable
2047     // We use the same exclusions that Windows does, in order to try to be
2048     // compatible with WinLDR-style booting
2049     //
2050     for (i = 0; i < LoaderMaximum; i++) IncludeType[i] = TRUE;
2051     IncludeType[LoaderBad] = FALSE;
2052     IncludeType[LoaderFirmwarePermanent] = FALSE;
2053     IncludeType[LoaderSpecialMemory] = FALSE;
2054     IncludeType[LoaderBBTMemory] = FALSE;
2055     if (Phase == 0)
2056     {
2057         /* Count physical pages on the system */
2058         MiScanMemoryDescriptors(LoaderBlock);
2059 
2060         /* Initialize the phase 0 temporary event */
2061         KeInitializeEvent(&MiTempEvent, NotificationEvent, FALSE);
2062 
2063         /* Set all the events to use the temporary event for now */
2064         MiLowMemoryEvent = &MiTempEvent;
2065         MiHighMemoryEvent = &MiTempEvent;
2066         MiLowPagedPoolEvent = &MiTempEvent;
2067         MiHighPagedPoolEvent = &MiTempEvent;
2068         MiLowNonPagedPoolEvent = &MiTempEvent;
2069         MiHighNonPagedPoolEvent = &MiTempEvent;
2070 
2071         //
2072         // Define the basic user vs. kernel address space separation
2073         //
2074         MmSystemRangeStart = (PVOID)MI_DEFAULT_SYSTEM_RANGE_START;
2075         MmUserProbeAddress = (ULONG_PTR)MI_USER_PROBE_ADDRESS;
2076         MmHighestUserAddress = (PVOID)MI_HIGHEST_USER_ADDRESS;
2077 
2078         /* Highest PTE and PDE based on the addresses above */
2079         MiHighestUserPte = MiAddressToPte(MmHighestUserAddress);
2080         MiHighestUserPde = MiAddressToPde(MmHighestUserAddress);
2081 #if (_MI_PAGING_LEVELS >= 3)
2082         MiHighestUserPpe = MiAddressToPpe(MmHighestUserAddress);
2083 #if (_MI_PAGING_LEVELS >= 4)
2084         MiHighestUserPxe = MiAddressToPxe(MmHighestUserAddress);
2085 #endif
2086 #endif
2087         //
2088         // Get the size of the boot loader's image allocations and then round
2089         // that region up to a PDE size, so that any PDEs we might create for
2090         // whatever follows are separate from the PDEs that boot loader might've
2091         // already created (and later, we can blow all that away if we want to).
2092         //
2093         MmBootImageSize = KeLoaderBlock->Extension->LoaderPagesSpanned;
2094         MmBootImageSize *= PAGE_SIZE;
2095         MmBootImageSize = (MmBootImageSize + PDE_MAPPED_VA - 1) & ~(PDE_MAPPED_VA - 1);
2096         ASSERT((MmBootImageSize % PDE_MAPPED_VA) == 0);
2097 
2098         /* Initialize session space address layout */
2099         MiInitializeSessionSpaceLayout();
2100 
2101         /* Set the based section highest address */
2102         MmHighSectionBase = (PVOID)((ULONG_PTR)MmHighestUserAddress - 0x800000);
2103 
2104 #if DBG
2105         /* The subection PTE format depends on things being 8-byte aligned */
2106         ASSERT((sizeof(CONTROL_AREA) % 8) == 0);
2107         ASSERT((sizeof(SUBSECTION) % 8) == 0);
2108 
2109         /* Prototype PTEs are assumed to be in paged pool, so check if the math works */
2110         PointerPte = (PMMPTE)MmPagedPoolStart;
2111         MI_MAKE_PROTOTYPE_PTE(&TempPte, PointerPte);
2112         TestPte = MiProtoPteToPte(&TempPte);
2113         ASSERT(PointerPte == TestPte);
2114 
2115         /* Try the last nonpaged pool address */
2116         PointerPte = (PMMPTE)MI_NONPAGED_POOL_END;
2117         MI_MAKE_PROTOTYPE_PTE(&TempPte, PointerPte);
2118         TestPte = MiProtoPteToPte(&TempPte);
2119         ASSERT(PointerPte == TestPte);
2120 
2121         /* Try a bunch of random addresses near the end of the address space */
2122         PointerPte = (PMMPTE)((ULONG_PTR)MI_HIGHEST_SYSTEM_ADDRESS - 0x37FFF);
2123         for (j = 0; j < 20; j += 1)
2124         {
2125             MI_MAKE_PROTOTYPE_PTE(&TempPte, PointerPte);
2126             TestPte = MiProtoPteToPte(&TempPte);
2127             ASSERT(PointerPte == TestPte);
2128             PointerPte++;
2129         }
2130 
2131         /* Subsection PTEs are always in nonpaged pool, pick a random address to try */
2132         PointerPte = (PMMPTE)((ULONG_PTR)MmNonPagedPoolStart + (MmSizeOfNonPagedPoolInBytes / 2));
2133         MI_MAKE_SUBSECTION_PTE(&TempPte, PointerPte);
2134         TestPte = MiSubsectionPteToSubsection(&TempPte);
2135         ASSERT(PointerPte == TestPte);
2136 #endif
2137 
2138         /* Loop all 8 standby lists */
2139         for (i = 0; i < 8; i++)
2140         {
2141             /* Initialize them */
2142             MmStandbyPageListByPriority[i].Total = 0;
2143             MmStandbyPageListByPriority[i].ListName = StandbyPageList;
2144             MmStandbyPageListByPriority[i].Flink = MM_EMPTY_LIST;
2145             MmStandbyPageListByPriority[i].Blink = MM_EMPTY_LIST;
2146         }
2147 
2148         /* Initialize the user mode image list */
2149         InitializeListHead(&MmLoadedUserImageList);
2150 
2151         /* Initialize critical section timeout value (relative time is negative) */
2152         MmCriticalSectionTimeout.QuadPart = MmCritsectTimeoutSeconds * (-10000000LL);
2153 
2154         /* Initialize the paged pool mutex and the section commit mutex */
2155         KeInitializeGuardedMutex(&MmPagedPoolMutex);
2156         KeInitializeGuardedMutex(&MmSectionCommitMutex);
2157         KeInitializeGuardedMutex(&MmSectionBasedMutex);
2158 
2159         /* Initialize the Loader Lock */
2160         KeInitializeMutant(&MmSystemLoadLock, FALSE);
2161 
2162         /* Set the zero page event */
2163         KeInitializeEvent(&MmZeroingPageEvent, SynchronizationEvent, FALSE);
2164         MmZeroingPageThreadActive = FALSE;
2165 
2166         /* Initialize the dead stack S-LIST */
2167         InitializeSListHead(&MmDeadStackSListHead);
2168 
2169         //
2170         // Check if this is a machine with less than 19MB of RAM
2171         //
2172         PageCount = MmNumberOfPhysicalPages;
2173         if (PageCount < MI_MIN_PAGES_FOR_SYSPTE_TUNING)
2174         {
2175             //
2176             // Use the very minimum of system PTEs
2177             //
2178             MmNumberOfSystemPtes = 7000;
2179         }
2180         else
2181         {
2182             //
2183             // Use the default
2184             //
2185             MmNumberOfSystemPtes = 11000;
2186             if (PageCount > MI_MIN_PAGES_FOR_SYSPTE_BOOST)
2187             {
2188                 //
2189                 // Double the amount of system PTEs
2190                 //
2191                 MmNumberOfSystemPtes <<= 1;
2192             }
2193             if (PageCount > MI_MIN_PAGES_FOR_SYSPTE_BOOST_BOOST)
2194             {
2195                 //
2196                 // Double the amount of system PTEs
2197                 //
2198                 MmNumberOfSystemPtes <<= 1;
2199             }
2200             if (MmSpecialPoolTag != 0 && MmSpecialPoolTag != -1)
2201             {
2202                 //
2203                 // Add some extra PTEs for special pool
2204                 //
2205                 MmNumberOfSystemPtes += 0x6000;
2206             }
2207         }
2208 
2209         DPRINT("System PTE count has been tuned to %lu (%lu bytes)\n",
2210                MmNumberOfSystemPtes, MmNumberOfSystemPtes * PAGE_SIZE);
2211 
2212         /* Check if no values are set for the heap limits */
2213         if (MmHeapSegmentReserve == 0)
2214         {
2215             MmHeapSegmentReserve = 2 * _1MB;
2216         }
2217 
2218         if (MmHeapSegmentCommit == 0)
2219         {
2220             MmHeapSegmentCommit = 2 * PAGE_SIZE;
2221         }
2222 
2223         if (MmHeapDeCommitTotalFreeThreshold == 0)
2224         {
2225             MmHeapDeCommitTotalFreeThreshold = 64 * _1KB;
2226         }
2227 
2228         if (MmHeapDeCommitFreeBlockThreshold == 0)
2229         {
2230             MmHeapDeCommitFreeBlockThreshold = PAGE_SIZE;
2231         }
2232 
2233         /* Initialize the working set lock */
2234         ExInitializePushLock(&MmSystemCacheWs.WorkingSetMutex);
2235 
2236         /* Set commit limit */
2237         MmTotalCommitLimit = 2 * _1GB;
2238         MmTotalCommitLimitMaximum = MmTotalCommitLimit;
2239 
2240         /* Has the allocation fragment been setup? */
2241         if (!MmAllocationFragment)
2242         {
2243             /* Use the default value */
2244             MmAllocationFragment = MI_ALLOCATION_FRAGMENT;
2245             if (PageCount < ((256 * _1MB) / PAGE_SIZE))
2246             {
2247                 /* On memory systems with less than 256MB, divide by 4 */
2248                 MmAllocationFragment = MI_ALLOCATION_FRAGMENT / 4;
2249             }
2250             else if (PageCount < (_1GB / PAGE_SIZE))
2251             {
2252                 /* On systems with less than 1GB, divide by 2 */
2253                 MmAllocationFragment = MI_ALLOCATION_FRAGMENT / 2;
2254             }
2255         }
2256         else
2257         {
2258             /* Convert from 1KB fragments to pages */
2259             MmAllocationFragment *= _1KB;
2260             MmAllocationFragment = ROUND_TO_PAGES(MmAllocationFragment);
2261 
2262             /* Don't let it past the maximum */
2263             MmAllocationFragment = min(MmAllocationFragment,
2264                                        MI_MAX_ALLOCATION_FRAGMENT);
2265 
2266             /* Don't let it too small either */
2267             MmAllocationFragment = max(MmAllocationFragment,
2268                                        MI_MIN_ALLOCATION_FRAGMENT);
2269         }
2270 
2271         /* Check for kernel stack size that's too big */
2272         if (MmLargeStackSize > (KERNEL_LARGE_STACK_SIZE / _1KB))
2273         {
2274             /* Sanitize to default value */
2275             MmLargeStackSize = KERNEL_LARGE_STACK_SIZE;
2276         }
2277         else
2278         {
2279             /* Take the registry setting, and convert it into bytes */
2280             MmLargeStackSize *= _1KB;
2281 
2282             /* Now align it to a page boundary */
2283             MmLargeStackSize = PAGE_ROUND_UP(MmLargeStackSize);
2284 
2285             /* Sanity checks */
2286             ASSERT(MmLargeStackSize <= KERNEL_LARGE_STACK_SIZE);
2287             ASSERT((MmLargeStackSize & (PAGE_SIZE - 1)) == 0);
2288 
2289             /* Make sure it's not too low */
2290             if (MmLargeStackSize < KERNEL_STACK_SIZE) MmLargeStackSize = KERNEL_STACK_SIZE;
2291         }
2292 
2293         /* Compute color information (L2 cache-separated paging lists) */
2294         MiComputeColorInformation();
2295 
2296         // Calculate the number of bytes for the PFN database
2297         // then add the color tables and convert to pages
2298         MxPfnAllocation = (MmHighestPhysicalPage + 1) * sizeof(MMPFN);
2299         MxPfnAllocation += (MmSecondaryColors * sizeof(MMCOLOR_TABLES) * 2);
2300         MxPfnAllocation >>= PAGE_SHIFT;
2301 
2302         // We have to add one to the count here, because in the process of
2303         // shifting down to the page size, we actually ended up getting the
2304         // lower aligned size (so say, 0x5FFFF bytes is now 0x5F pages).
2305         // Later on, we'll shift this number back into bytes, which would cause
2306         // us to end up with only 0x5F000 bytes -- when we actually want to have
2307         // 0x60000 bytes.
2308         MxPfnAllocation++;
2309 
2310         /* Initialize the platform-specific parts */
2311         MiInitMachineDependent(LoaderBlock);
2312 
2313         //
2314         // Build the physical memory block
2315         //
2316         MmPhysicalMemoryBlock = MmInitializeMemoryLimits(LoaderBlock,
2317                                                          IncludeType);
2318 
2319         //
2320         // Allocate enough buffer for the PFN bitmap
2321         // Align it up to a 32-bit boundary
2322         //
2323         Bitmap = ExAllocatePoolWithTag(NonPagedPool,
2324                                        (((MmHighestPhysicalPage + 1) + 31) / 32) * 4,
2325                                        TAG_MM);
2326         if (!Bitmap)
2327         {
2328             //
2329             // This is critical
2330             //
2331             KeBugCheckEx(INSTALL_MORE_MEMORY,
2332                          MmNumberOfPhysicalPages,
2333                          MmLowestPhysicalPage,
2334                          MmHighestPhysicalPage,
2335                          0x101);
2336         }
2337 
2338         //
2339         // Initialize it and clear all the bits to begin with
2340         //
2341         RtlInitializeBitMap(&MiPfnBitMap,
2342                             Bitmap,
2343                             (ULONG)MmHighestPhysicalPage + 1);
2344         RtlClearAllBits(&MiPfnBitMap);
2345 
2346         //
2347         // Loop physical memory runs
2348         //
2349         for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i++)
2350         {
2351             //
2352             // Get the run
2353             //
2354             Run = &MmPhysicalMemoryBlock->Run[i];
2355             DPRINT("PHYSICAL RAM [0x%08p to 0x%08p]\n",
2356                    Run->BasePage << PAGE_SHIFT,
2357                    (Run->BasePage + Run->PageCount) << PAGE_SHIFT);
2358 
2359             //
2360             // Make sure it has pages inside it
2361             //
2362             if (Run->PageCount)
2363             {
2364                 //
2365                 // Set the bits in the PFN bitmap
2366                 //
2367                 RtlSetBits(&MiPfnBitMap, (ULONG)Run->BasePage, (ULONG)Run->PageCount);
2368             }
2369         }
2370 
2371         /* Look for large page cache entries that need caching */
2372         MiSyncCachedRanges();
2373 
2374         /* Loop for HAL Heap I/O device mappings that need coherency tracking */
2375         MiAddHalIoMappings();
2376 
2377         /* Set the initial resident page count */
2378         MmResidentAvailablePages = MmAvailablePages - 32;
2379 
2380         /* Initialize large page structures on PAE/x64, and MmProcessList on x86 */
2381         MiInitializeLargePageSupport();
2382 
2383         /* Check if the registry says any drivers should be loaded with large pages */
2384         MiInitializeDriverLargePageList();
2385 
2386         /* Relocate the boot drivers into system PTE space and fixup their PFNs */
2387         MiReloadBootLoadedDrivers(LoaderBlock);
2388 
2389         /* FIXME: Call out into Driver Verifier for initialization  */
2390 
2391         /* Check how many pages the system has */
2392         if (MmNumberOfPhysicalPages <= ((13 * _1MB) / PAGE_SIZE))
2393         {
2394             /* Set small system */
2395             MmSystemSize = MmSmallSystem;
2396             MmMaximumDeadKernelStacks = 0;
2397         }
2398         else if (MmNumberOfPhysicalPages <= ((19 * _1MB) / PAGE_SIZE))
2399         {
2400             /* Set small system and add 100 pages for the cache */
2401             MmSystemSize = MmSmallSystem;
2402             MmSystemCacheWsMinimum += 100;
2403             MmMaximumDeadKernelStacks = 2;
2404         }
2405         else
2406         {
2407             /* Set medium system and add 400 pages for the cache */
2408             MmSystemSize = MmMediumSystem;
2409             MmSystemCacheWsMinimum += 400;
2410             MmMaximumDeadKernelStacks = 5;
2411         }
2412 
2413         /* Check for less than 24MB */
2414         if (MmNumberOfPhysicalPages < ((24 * _1MB) / PAGE_SIZE))
2415         {
2416             /* No more than 32 pages */
2417             MmSystemCacheWsMinimum = 32;
2418         }
2419 
2420         /* Check for more than 32MB */
2421         if (MmNumberOfPhysicalPages >= ((32 * _1MB) / PAGE_SIZE))
2422         {
2423             /* Check for product type being "Wi" for WinNT */
2424             if (MmProductType == '\0i\0W')
2425             {
2426                 /* Then this is a large system */
2427                 MmSystemSize = MmLargeSystem;
2428             }
2429             else
2430             {
2431                 /* For servers, we need 64MB to consider this as being large */
2432                 if (MmNumberOfPhysicalPages >= ((64 * _1MB) / PAGE_SIZE))
2433                 {
2434                     /* Set it as large */
2435                     MmSystemSize = MmLargeSystem;
2436                 }
2437             }
2438         }
2439 
2440         /* Check for more than 33 MB */
2441         if (MmNumberOfPhysicalPages > ((33 * _1MB) / PAGE_SIZE))
2442         {
2443             /* Add another 500 pages to the cache */
2444             MmSystemCacheWsMinimum += 500;
2445         }
2446 
2447         /* Now setup the shared user data fields */
2448         ASSERT(SharedUserData->NumberOfPhysicalPages == 0);
2449         SharedUserData->NumberOfPhysicalPages = MmNumberOfPhysicalPages;
2450         SharedUserData->LargePageMinimum = 0;
2451 
2452         /* Check for workstation (Wi for WinNT) */
2453         if (MmProductType == '\0i\0W')
2454         {
2455             /* Set Windows NT Workstation product type */
2456             SharedUserData->NtProductType = NtProductWinNt;
2457             MmProductType = 0;
2458         }
2459         else
2460         {
2461             /* Check for LanMan server (La for LanmanNT) */
2462             if (MmProductType == '\0a\0L')
2463             {
2464                 /* This is a domain controller */
2465                 SharedUserData->NtProductType = NtProductLanManNt;
2466             }
2467             else
2468             {
2469                 /* Otherwise it must be a normal server (Se for ServerNT) */
2470                 SharedUserData->NtProductType = NtProductServer;
2471             }
2472 
2473             /* Set the product type, and make the system more aggressive with low memory */
2474             MmProductType = 1;
2475             MmMinimumFreePages = 81;
2476         }
2477 
2478         /* Update working set tuning parameters */
2479         MiAdjustWorkingSetManagerParameters(!MmProductType);
2480 
2481         /* Finetune the page count by removing working set and NP expansion */
2482         MmResidentAvailablePages -= MiExpansionPoolPagesInitialCharge;
2483         MmResidentAvailablePages -= MmSystemCacheWsMinimum;
2484         MmResidentAvailableAtInit = MmResidentAvailablePages;
2485         if (MmResidentAvailablePages <= 0)
2486         {
2487             /* This should not happen */
2488             DPRINT1("System cache working set too big\n");
2489             return FALSE;
2490         }
2491 
2492         /* Initialize the system cache */
2493         //MiInitializeSystemCache(MmSystemCacheWsMinimum, MmAvailablePages);
2494 
2495         /* Update the commit limit */
2496         MmTotalCommitLimit = MmAvailablePages;
2497         if (MmTotalCommitLimit > 1024) MmTotalCommitLimit -= 1024;
2498         MmTotalCommitLimitMaximum = MmTotalCommitLimit;
2499 
2500         /* Size up paged pool and build the shadow system page directory */
2501         MiBuildPagedPool();
2502 
2503         /* Debugger physical memory support is now ready to be used */
2504         MmDebugPte = MiAddressToPte(MiDebugMapping);
2505 
2506         /* Initialize the loaded module list */
2507         MiInitializeLoadedModuleList(LoaderBlock);
2508     }
2509 
2510     //
2511     // Always return success for now
2512     //
2513     return TRUE;
2514 }
2515 
2516 /* EOF */
2517