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