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