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