xref: /reactos/ntoskrnl/mm/amd64/init.c (revision 5100859e)
1 /*
2  * COPYRIGHT:       GPL, See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/mm/amd64/init.c
5  * PURPOSE:         Memory Manager Initialization for amd64
6  *
7  * PROGRAMMERS:     Timo kreuzer (timo.kreuzer@reactos.org)
8  *                  ReactOS Portable Systems Group
9  */
10 
11 /* INCLUDES ***************************************************************/
12 
13 #include <ntoskrnl.h>
14 //#define NDEBUG
15 #include <debug.h>
16 
17 #include <mm/ARM3/miarm.h>
18 
19 #ifdef _WINKD_
20 extern PMMPTE MmDebugPte;
21 #endif
22 
23 /* Helper macros */
24 #define IS_ALIGNED(addr, align) (((ULONG64)(addr) & (align - 1)) == 0)
25 #define IS_PAGE_ALIGNED(addr) IS_ALIGNED(addr, PAGE_SIZE)
26 
27 /* GLOBALS *****************************************************************/
28 
29 /* Template PTE and PDE for a kernel page */
30 MMPTE ValidKernelPde = {{PTE_VALID|PTE_READWRITE|PTE_DIRTY|PTE_ACCESSED}};
31 MMPTE ValidKernelPte = {{PTE_VALID|PTE_READWRITE|PTE_DIRTY|PTE_ACCESSED}};
32 
33 /* The same, but for local pages */
34 MMPTE ValidKernelPdeLocal = {{PTE_VALID|PTE_READWRITE|PTE_DIRTY|PTE_ACCESSED}};
35 MMPTE ValidKernelPteLocal = {{PTE_VALID|PTE_READWRITE|PTE_DIRTY|PTE_ACCESSED}};
36 
37 /* Template PDE for a demand-zero page */
38 MMPDE DemandZeroPde  = {{MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS}};
39 MMPTE DemandZeroPte  = {{MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS}};
40 
41 /* Template PTE for prototype page */
42 MMPTE PrototypePte = {{(MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS) |
43                       PTE_PROTOTYPE | (MI_PTE_LOOKUP_NEEDED << 32)}};
44 
45 /* Template PTE for decommited page */
46 MMPTE MmDecommittedPte = {{MM_DECOMMIT << MM_PTE_SOFTWARE_PROTECTION_BITS}};
47 
48 /* Address ranges */
49 PVOID MiSessionViewEnd;
50 PVOID MiSystemPteSpaceStart;
51 PVOID MiSystemPteSpaceEnd;
52 
53 ULONG64 MxPfnSizeInBytes;
54 BOOLEAN MiIncludeType[LoaderMaximum];
55 PFN_NUMBER MxFreePageBase;
56 ULONG64 MxFreePageCount = 0;
57 
58 BOOLEAN MiPfnsInitialized = FALSE;
59 
60 /* FUNCTIONS *****************************************************************/
61 
62 VOID
63 NTAPI
64 INIT_FUNCTION
65 MiInitializeSessionSpaceLayout(VOID)
66 {
67     MmSessionSize = MI_SESSION_SIZE;
68     MmSessionViewSize = MI_SESSION_VIEW_SIZE;
69     MmSessionPoolSize = MI_SESSION_POOL_SIZE;
70     MmSessionImageSize = MI_SESSION_IMAGE_SIZE;
71     MmSystemViewSize = MI_SYSTEM_VIEW_SIZE;
72 
73     /* Set up session space */
74     MiSessionSpaceEnd = (PVOID)MI_SESSION_SPACE_END;
75 
76     /* This is where we will load Win32k.sys and the video driver */
77     MiSessionImageEnd = MiSessionSpaceEnd;
78     MiSessionImageStart = (PCHAR)MiSessionImageEnd - MmSessionImageSize;
79 
80     /* The view starts right below the session working set (itself below
81      * the image area) */
82     MiSessionViewEnd = (PVOID)MI_SESSION_VIEW_END;
83     MiSessionViewStart = (PCHAR)MiSessionViewEnd - MmSessionViewSize;
84     ASSERT(IS_PAGE_ALIGNED(MiSessionViewStart));
85 
86     /* Session pool follows */
87     MiSessionPoolEnd = MiSessionViewStart;
88     MiSessionPoolStart = (PCHAR)MiSessionPoolEnd - MmSessionPoolSize;
89     ASSERT(IS_PAGE_ALIGNED(MiSessionPoolStart));
90 
91     /* And it all begins here */
92     MmSessionBase = MiSessionPoolStart;
93 
94     /* System view space ends at session space, so now that we know where
95      * this is, we can compute the base address of system view space itself. */
96     MiSystemViewStart = (PCHAR)MmSessionBase - MmSystemViewSize;
97     ASSERT(IS_PAGE_ALIGNED(MiSystemViewStart));
98 
99     /* Sanity checks */
100     ASSERT(MiSessionViewEnd <= MiSessionImageStart);
101     ASSERT(MmSessionBase <= MiSessionPoolStart);
102 }
103 
104 VOID
105 NTAPI
106 MiMapPPEs(
107     PVOID StartAddress,
108     PVOID EndAddress)
109 {
110     PMMPDE PointerPpe;
111     MMPDE TmplPde = ValidKernelPde;
112 
113     /* Loop the PPEs */
114     for (PointerPpe = MiAddressToPpe(StartAddress);
115          PointerPpe <= MiAddressToPpe(EndAddress);
116          PointerPpe++)
117     {
118         /* Check if its already mapped */
119         if (!PointerPpe->u.Hard.Valid)
120         {
121             /* No, map it! */
122             TmplPde.u.Hard.PageFrameNumber = MxGetNextPage(1);
123             MI_WRITE_VALID_PTE(PointerPpe, TmplPde);
124 
125             /* Zero out the page table */
126             RtlZeroMemory(MiPteToAddress(PointerPpe), PAGE_SIZE);
127         }
128     }
129 }
130 
131 VOID
132 NTAPI
133 MiMapPDEs(
134     PVOID StartAddress,
135     PVOID EndAddress)
136 {
137     PMMPDE PointerPde;
138     MMPDE TmplPde = ValidKernelPde;
139 
140     /* Loop the PDEs */
141     for (PointerPde = MiAddressToPde(StartAddress);
142          PointerPde <= MiAddressToPde(EndAddress);
143          PointerPde++)
144     {
145         /* Check if its already mapped */
146         if (!PointerPde->u.Hard.Valid)
147         {
148             /* No, map it! */
149             TmplPde.u.Hard.PageFrameNumber = MxGetNextPage(1);
150             MI_WRITE_VALID_PTE(PointerPde, TmplPde);
151 
152             /* Zero out the page table */
153             RtlZeroMemory(MiPteToAddress(PointerPde), PAGE_SIZE);
154         }
155     }
156 }
157 
158 VOID
159 NTAPI
160 MiMapPTEs(
161     PVOID StartAddress,
162     PVOID EndAddress)
163 {
164     PMMPTE PointerPte;
165     MMPTE TmplPte = ValidKernelPte;
166 
167     /* Loop the PTEs */
168     for (PointerPte = MiAddressToPte(StartAddress);
169          PointerPte <= MiAddressToPte(EndAddress);
170          PointerPte++)
171     {
172         /* Check if its already mapped */
173         if (!PointerPte->u.Hard.Valid)
174         {
175             /* No, map it! */
176             TmplPte.u.Hard.PageFrameNumber = MxGetNextPage(1);
177             MI_WRITE_VALID_PTE(PointerPte, TmplPte);
178 
179             /* Zero out the page (FIXME: not always neccessary) */
180             RtlZeroMemory(MiPteToAddress(PointerPte), PAGE_SIZE);
181         }
182     }
183 }
184 
185 VOID
186 NTAPI
187 INIT_FUNCTION
188 MiInitializePageTable(VOID)
189 {
190     ULONG64 PxePhysicalAddress;
191     MMPTE TmplPte, *PointerPxe;
192     PFN_NUMBER PxePfn;
193 
194     /* Get current directory base */
195     PxePfn = ((PMMPTE)PXE_SELFMAP)->u.Hard.PageFrameNumber;
196     PxePhysicalAddress = PxePfn << PAGE_SHIFT;
197     ASSERT(PxePhysicalAddress == __readcr3());
198 
199     /* Set directory base for the system process */
200     PsGetCurrentProcess()->Pcb.DirectoryTableBase[0] = PxePhysicalAddress;
201 
202     /* Enable global pages */
203     __writecr4(__readcr4() | CR4_PGE);
204     ASSERT(__readcr4() & CR4_PGE);
205 
206     /* Loop the user mode PXEs */
207     for (PointerPxe = MiAddressToPxe(0);
208          PointerPxe <= MiAddressToPxe(MmHighestUserAddress);
209          PointerPxe++)
210     {
211         /* Zero the PXE, clear all mappings */
212         PointerPxe->u.Long = 0;
213     }
214 
215     /* Flush the TLB */
216     KeFlushCurrentTb();
217 
218     /* Set up a template PTE */
219     TmplPte.u.Long = 0;
220     TmplPte.u.Flush.Valid = 1;
221     TmplPte.u.Flush.Write = 1;
222     HyperTemplatePte = TmplPte;
223 
224     /* Create PDPTs (72 KB) for shared system address space,
225      * skip page tables TODO: use global pages. */
226 
227     /* Loop the PXEs */
228     for (PointerPxe = MiAddressToPxe((PVOID)HYPER_SPACE);
229          PointerPxe <= MiAddressToPxe(MI_HIGHEST_SYSTEM_ADDRESS);
230          PointerPxe++)
231     {
232         /* Is the PXE already valid? */
233         if (!PointerPxe->u.Hard.Valid)
234         {
235             /* It's not Initialize it */
236             TmplPte.u.Flush.PageFrameNumber = MxGetNextPage(1);
237             *PointerPxe = TmplPte;
238 
239             /* Zero the page. The PXE is the PTE for the PDPT. */
240             RtlZeroMemory(MiPteToAddress(PointerPxe), PAGE_SIZE);
241         }
242     }
243 
244     /* Map PPEs for paged pool */
245     MiMapPPEs(MmPagedPoolStart, MmPagedPoolEnd);
246 
247     /* Setup 1 PPE for hyper space */
248     MiMapPPEs((PVOID)HYPER_SPACE, (PVOID)HYPER_SPACE_END);
249 
250     /* Setup PPEs for system space view */
251     MiMapPPEs(MiSystemViewStart, (PCHAR)MiSystemViewStart + MmSystemViewSize);
252 
253     /* Setup the mapping PDEs */
254     MiMapPDEs((PVOID)MI_MAPPING_RANGE_START, (PVOID)MI_MAPPING_RANGE_END);
255 
256     /* Setup the mapping PTEs */
257     MmFirstReservedMappingPte = MiAddressToPte((PVOID)MI_MAPPING_RANGE_START);
258     MmLastReservedMappingPte = MiAddressToPte((PVOID)MI_MAPPING_RANGE_END);
259     MmFirstReservedMappingPte->u.Hard.PageFrameNumber = MI_HYPERSPACE_PTES;
260 
261 #ifdef _WINKD_
262     /* Setup debug mapping PTE */
263     MiMapPPEs((PVOID)MI_DEBUG_MAPPING, (PVOID)MI_DEBUG_MAPPING);
264     MiMapPDEs((PVOID)MI_DEBUG_MAPPING, (PVOID)MI_DEBUG_MAPPING);
265     MmDebugPte = MiAddressToPte((PVOID)MI_DEBUG_MAPPING);
266 #endif
267 
268     /* Setup PDE and PTEs for VAD bitmap and working set list */
269     MiMapPDEs((PVOID)MI_VAD_BITMAP, (PVOID)(MI_WORKING_SET_LIST + PAGE_SIZE - 1));
270     MiMapPTEs((PVOID)MI_VAD_BITMAP, (PVOID)(MI_WORKING_SET_LIST + PAGE_SIZE - 1));
271 }
272 
273 VOID
274 NTAPI
275 INIT_FUNCTION
276 MiBuildNonPagedPool(VOID)
277 {
278     /* Check if this is a machine with less than 256MB of RAM, and no overide */
279     if ((MmNumberOfPhysicalPages <= MI_MIN_PAGES_FOR_NONPAGED_POOL_TUNING) &&
280         !(MmSizeOfNonPagedPoolInBytes))
281     {
282         /* Force the non paged pool to be 2MB so we can reduce RAM usage */
283         MmSizeOfNonPagedPoolInBytes = 2 * 1024 * 1024;
284     }
285 
286     /* Check if the user gave a ridicuously large nonpaged pool RAM size */
287     if ((MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT) >
288         (MmNumberOfPhysicalPages * 7 / 8))
289     {
290         /* More than 7/8ths of RAM was dedicated to nonpaged pool, ignore! */
291         MmSizeOfNonPagedPoolInBytes = 0;
292     }
293 
294     /* Check if no registry setting was set, or if the setting was too low */
295     if (MmSizeOfNonPagedPoolInBytes < MmMinimumNonPagedPoolSize)
296     {
297         /* Start with the minimum (256 KB) and add 32 KB for each MB above 4 */
298         MmSizeOfNonPagedPoolInBytes = MmMinimumNonPagedPoolSize;
299         MmSizeOfNonPagedPoolInBytes += (MmNumberOfPhysicalPages - 1024) /
300                                        256 * MmMinAdditionNonPagedPoolPerMb;
301     }
302 
303     /* Check if the registy setting or our dynamic calculation was too high */
304     if (MmSizeOfNonPagedPoolInBytes > MI_MAX_INIT_NONPAGED_POOL_SIZE)
305     {
306         /* Set it to the maximum */
307         MmSizeOfNonPagedPoolInBytes = MI_MAX_INIT_NONPAGED_POOL_SIZE;
308     }
309 
310     /* Check if a percentage cap was set through the registry */
311     if (MmMaximumNonPagedPoolPercent)
312     {
313         /* Don't feel like supporting this right now */
314         UNIMPLEMENTED;
315     }
316 
317     /* Page-align the nonpaged pool size */
318     MmSizeOfNonPagedPoolInBytes &= ~(PAGE_SIZE - 1);
319 
320     /* Now, check if there was a registry size for the maximum size */
321     if (!MmMaximumNonPagedPoolInBytes)
322     {
323         /* Start with the default (1MB) and add 400 KB for each MB above 4 */
324         MmMaximumNonPagedPoolInBytes = MmDefaultMaximumNonPagedPool;
325         MmMaximumNonPagedPoolInBytes += (MmNumberOfPhysicalPages - 1024) /
326                                          256 * MmMaxAdditionNonPagedPoolPerMb;
327     }
328 
329     /* Don't let the maximum go too high */
330     if (MmMaximumNonPagedPoolInBytes > MI_MAX_NONPAGED_POOL_SIZE)
331     {
332         /* Set it to the upper limit */
333         MmMaximumNonPagedPoolInBytes = MI_MAX_NONPAGED_POOL_SIZE;
334     }
335 
336     /* Convert nonpaged pool size from bytes to pages */
337     MmMaximumNonPagedPoolInPages = MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT;
338 
339     /* Non paged pool starts after the PFN database */
340     MmNonPagedPoolStart = MmPfnDatabase + MxPfnAllocation * PAGE_SIZE;
341 
342     /* Calculate the nonpaged pool expansion start region */
343     MmNonPagedPoolExpansionStart = (PCHAR)MmNonPagedPoolStart +
344                                           MmSizeOfNonPagedPoolInBytes;
345     ASSERT(IS_PAGE_ALIGNED(MmNonPagedPoolExpansionStart));
346 
347     /* And this is where the none paged pool ends */
348     MmNonPagedPoolEnd = (PCHAR)MmNonPagedPoolStart + MmMaximumNonPagedPoolInBytes;
349     ASSERT(MmNonPagedPoolEnd < (PVOID)MM_HAL_VA_START);
350 
351     /* Map PPEs and PDEs for non paged pool (including expansion) */
352     MiMapPPEs(MmNonPagedPoolStart, MmNonPagedPoolEnd);
353     MiMapPDEs(MmNonPagedPoolStart, MmNonPagedPoolEnd);
354 
355     /* Map the nonpaged pool PTEs (without expansion) */
356     MiMapPTEs(MmNonPagedPoolStart, (PCHAR)MmNonPagedPoolExpansionStart - 1);
357 
358     /* Initialize the ARM3 nonpaged pool */
359     MiInitializeNonPagedPool();
360     MiInitializeNonPagedPoolThresholds();
361 
362 }
363 
364 VOID
365 NTAPI
366 INIT_FUNCTION
367 MiBuildSystemPteSpace(VOID)
368 {
369     PMMPTE PointerPte;
370     SIZE_T NonPagedSystemSize;
371 
372     /* Use the default number of system PTEs */
373     MmNumberOfSystemPtes = MI_NUMBER_SYSTEM_PTES;
374     NonPagedSystemSize = (MmNumberOfSystemPtes + 1) * PAGE_SIZE;
375 
376     /* Put system PTEs at the start of the system VA space */
377     MiSystemPteSpaceStart = MmNonPagedSystemStart;
378     MiSystemPteSpaceEnd = (PUCHAR)MiSystemPteSpaceStart + NonPagedSystemSize;
379 
380     /* Map the PPEs and PDEs for the system PTEs */
381     MiMapPPEs(MiSystemPteSpaceStart, MiSystemPteSpaceEnd);
382     MiMapPDEs(MiSystemPteSpaceStart, MiSystemPteSpaceEnd);
383 
384     /* Initialize the system PTE space */
385     PointerPte = MiAddressToPte(MiSystemPteSpaceStart);
386     MiInitializeSystemPtes(PointerPte, MmNumberOfSystemPtes, SystemPteSpace);
387 
388     /* Reserve system PTEs for zeroing PTEs and clear them */
389     MiFirstReservedZeroingPte = MiReserveSystemPtes(MI_ZERO_PTES, SystemPteSpace);
390     RtlZeroMemory(MiFirstReservedZeroingPte, MI_ZERO_PTES * sizeof(MMPTE));
391 
392     /* Set the counter to maximum */
393     MiFirstReservedZeroingPte->u.Hard.PageFrameNumber = MI_ZERO_PTES - 1;
394 }
395 
396 static
397 VOID
398 MiSetupPfnForPageTable(
399     PFN_NUMBER PageFrameIndex,
400     PMMPTE PointerPte)
401 {
402     PMMPFN Pfn;
403     PMMPDE PointerPde;
404 
405     /* Get the pfn entry for this page */
406     Pfn = MiGetPfnEntry(PageFrameIndex);
407 
408     /* Check if it's valid memory */
409     if ((PageFrameIndex <= MmHighestPhysicalPage) &&
410         (MmIsAddressValid(Pfn)) &&
411         (Pfn->u3.e1.PageLocation == ActiveAndValid))
412     {
413         /* Setup the PFN entry */
414         Pfn->u1.WsIndex = 0;
415         Pfn->u2.ShareCount++;
416         Pfn->PteAddress = PointerPte;
417         Pfn->OriginalPte = *PointerPte;
418         Pfn->u3.e1.PageLocation = ActiveAndValid;
419         Pfn->u3.e1.CacheAttribute = MiNonCached;
420         Pfn->u3.e2.ReferenceCount = 1;
421         Pfn->u4.PteFrame = PFN_FROM_PTE(MiAddressToPte(PointerPte));
422     }
423 
424     /* Increase the shared count of the PFN entry for the PDE */
425     PointerPde = MiAddressToPde(MiPteToAddress(PointerPte));
426     Pfn = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
427     Pfn->u2.ShareCount++;
428 }
429 
430 VOID
431 NTAPI
432 MiBuildPfnDatabaseFromPageTables(VOID)
433 {
434     PVOID Address = NULL;
435     PFN_NUMBER PageFrameIndex;
436     PMMPDE PointerPde;
437     PMMPTE PointerPte;
438     ULONG k, l;
439     PMMPFN Pfn;
440 #if (_MI_PAGING_LEVELS >= 3)
441     PMMPDE PointerPpe;
442     ULONG j;
443 #endif
444 #if (_MI_PAGING_LEVELS == 4)
445     PMMPDE PointerPxe;
446     ULONG i;
447 #endif
448 
449     /* Manual setup of the top level page directory */
450 #if (_MI_PAGING_LEVELS == 4)
451     PageFrameIndex = PFN_FROM_PTE(MiAddressToPte(PXE_BASE));
452 #elif (_MI_PAGING_LEVELS == 3)
453     PageFrameIndex = PFN_FROM_PTE(MiAddressToPte(PPE_BASE));
454 #else
455     PageFrameIndex = PFN_FROM_PTE(MiAddressToPte(PDE_BASE));
456 #endif
457     Pfn = MiGetPfnEntry(PageFrameIndex);
458     ASSERT(Pfn->u3.e1.PageLocation == ActiveAndValid);
459     Pfn->u1.WsIndex = 0;
460     Pfn->u2.ShareCount = 1;
461     Pfn->PteAddress = NULL;
462     Pfn->u3.e1.CacheAttribute = MiNonCached;
463     Pfn->u3.e2.ReferenceCount = 1;
464     Pfn->u4.PteFrame = 0;
465 
466 #if (_MI_PAGING_LEVELS == 4)
467     /* Loop all PXEs in the PML4 */
468     PointerPxe = MiAddressToPxe(Address);
469     for (i = 0; i < PXE_PER_PAGE; i++, PointerPxe++)
470     {
471         /* Skip invalid PXEs */
472         if (!PointerPxe->u.Hard.Valid) continue;
473 
474         /* Handle the PFN */
475         PageFrameIndex = PFN_FROM_PXE(PointerPxe);
476         MiSetupPfnForPageTable(PageFrameIndex, PointerPxe);
477 
478         /* Get starting VA for this PXE */
479         Address = MiPxeToAddress(PointerPxe);
480 #endif
481 #if (_MI_PAGING_LEVELS >= 3)
482         /* Loop all PPEs in this PDP */
483         PointerPpe = MiAddressToPpe(Address);
484         for (j = 0; j < PPE_PER_PAGE; j++, PointerPpe++)
485         {
486             /* Skip invalid PPEs */
487             if (!PointerPpe->u.Hard.Valid) continue;
488 
489             /* Handle the PFN */
490             PageFrameIndex = PFN_FROM_PPE(PointerPpe);
491             MiSetupPfnForPageTable(PageFrameIndex, PointerPpe);
492 
493             /* Get starting VA for this PPE */
494             Address = MiPpeToAddress(PointerPpe);
495 #endif
496             /* Loop all PDEs in this PD */
497             PointerPde = MiAddressToPde(Address);
498             for (k = 0; k < PDE_PER_PAGE; k++, PointerPde++)
499             {
500                 /* Skip invalid PDEs */
501                 if (!PointerPde->u.Hard.Valid) continue;
502 
503                 /* Handle the PFN */
504                 PageFrameIndex = PFN_FROM_PDE(PointerPde);
505                 MiSetupPfnForPageTable(PageFrameIndex, PointerPde);
506 
507                 /* Get starting VA for this PDE */
508                 Address = MiPdeToAddress(PointerPde);
509 
510                 /* Loop all PTEs in this PT */
511                 PointerPte = MiAddressToPte(Address);
512                 for (l = 0; l < PTE_PER_PAGE; l++, PointerPte++)
513                 {
514                     /* Skip invalid PTEs */
515                     if (!PointerPte->u.Hard.Valid) continue;
516 
517                     /* Handle the PFN */
518                     PageFrameIndex = PFN_FROM_PTE(PointerPte);
519                     MiSetupPfnForPageTable(PageFrameIndex, PointerPte);
520                 }
521             }
522 #if (_MI_PAGING_LEVELS >= 3)
523         }
524 #endif
525 #if (_MI_PAGING_LEVELS == 4)
526     }
527 #endif
528 }
529 
530 VOID
531 NTAPI
532 INIT_FUNCTION
533 MiAddDescriptorToDatabase(
534     PFN_NUMBER BasePage,
535     PFN_NUMBER PageCount,
536     TYPE_OF_MEMORY MemoryType)
537 {
538     PMMPFN Pfn;
539 
540     ASSERT(!MiIsMemoryTypeInvisible(MemoryType));
541 
542     /* Check if the memory is free */
543     if (MiIsMemoryTypeFree(MemoryType))
544     {
545         /* Get the last pfn of this descriptor. Note we loop backwards */
546         Pfn = &MmPfnDatabase[BasePage + PageCount - 1];
547 
548         /* Loop all pages */
549         while (PageCount--)
550         {
551             /* Add it to the free list */
552             Pfn->u3.e1.CacheAttribute = MiNonCached;
553             MiInsertPageInFreeList(BasePage + PageCount);
554 
555             /* Go to the previous page */
556             Pfn--;
557         }
558     }
559     else if (MemoryType == LoaderXIPRom)
560     {
561         Pfn = &MmPfnDatabase[BasePage];
562         while (PageCount--)
563         {
564             /* Make it a pseudo-I/O ROM mapping */
565             Pfn->PteAddress = 0;
566             Pfn->u1.Flink = 0;
567             Pfn->u2.ShareCount = 0;
568             Pfn->u3.e1.PageLocation = 0;
569             Pfn->u3.e1.CacheAttribute = MiNonCached;
570             Pfn->u3.e1.Rom = 1;
571             Pfn->u3.e1.PrototypePte = 1;
572             Pfn->u3.e2.ReferenceCount = 0;
573             Pfn->u4.InPageError = 0;
574             Pfn->u4.PteFrame = 0;
575 
576             /* Advance one */
577             Pfn++;
578         }
579     }
580     else if (MemoryType == LoaderBad)
581     {
582         // FIXME: later
583         ASSERT(FALSE);
584     }
585     else
586     {
587         /* For now skip it */
588         DbgPrint("Skipping BasePage=0x%lx, PageCount=0x%lx, MemoryType=%lx\n",
589                  BasePage, PageCount, MemoryType);
590         Pfn = &MmPfnDatabase[BasePage];
591         while (PageCount--)
592         {
593             /* Make an active PFN */
594             Pfn->u3.e1.PageLocation = ActiveAndValid;
595 
596             /* Advance one */
597             Pfn++;
598         }
599     }
600 }
601 
602 VOID
603 NTAPI
604 INIT_FUNCTION
605 MiBuildPfnDatabase(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
606 {
607     PLIST_ENTRY ListEntry;
608     PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
609     PFN_NUMBER BasePage, PageCount;
610 
611     /* Map the PDEs and PPEs for the pfn database (ignore holes) */
612 #if (_MI_PAGING_LEVELS >= 3)
613     MiMapPPEs(MmPfnDatabase, (PUCHAR)MmPfnDatabase + (MxPfnAllocation * PAGE_SIZE) - 1);
614 #endif
615     MiMapPDEs(MmPfnDatabase, (PUCHAR)MmPfnDatabase + (MxPfnAllocation * PAGE_SIZE) - 1);
616 
617     /* First initialize the color tables */
618     MiInitializeColorTables();
619 
620     /* Loop the memory descriptors */
621     for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
622          ListEntry != &LoaderBlock->MemoryDescriptorListHead;
623          ListEntry = ListEntry->Flink)
624     {
625         /* Get the descriptor */
626         Descriptor = CONTAINING_RECORD(ListEntry,
627                                        MEMORY_ALLOCATION_DESCRIPTOR,
628                                        ListEntry);
629 
630         /* Skip invisible memory */
631         if (MiIsMemoryTypeInvisible(Descriptor->MemoryType)) continue;
632 
633         /* If this is the free descriptor, use the copy instead */
634         if (Descriptor == MxFreeDescriptor) Descriptor = &MxOldFreeDescriptor;
635 
636         /* Get the range for this descriptor */
637         BasePage = Descriptor->BasePage;
638         PageCount = Descriptor->PageCount;
639 
640         /* Map the pages for the database */
641         MiMapPTEs(&MmPfnDatabase[BasePage],
642                   (PUCHAR)(&MmPfnDatabase[BasePage + PageCount]) - 1);
643 
644         /* If this was the free descriptor, skip the next step */
645         if (Descriptor == &MxOldFreeDescriptor) continue;
646 
647         /* Add this descriptor to the database */
648         MiAddDescriptorToDatabase(BasePage, PageCount, Descriptor->MemoryType);
649     }
650 
651     /* At this point the whole pfn database is mapped. We are about to add the
652        pages from the free descriptor to the database, so from now on we cannot
653        use it anymore. */
654 
655     /* Now add the free descriptor */
656     BasePage = MxFreeDescriptor->BasePage;
657     PageCount = MxFreeDescriptor->PageCount;
658     MiAddDescriptorToDatabase(BasePage, PageCount, LoaderFree);
659 
660     /* And finally the memory we used */
661     BasePage = MxOldFreeDescriptor.BasePage;
662     PageCount = MxFreeDescriptor->BasePage - BasePage;
663     MiAddDescriptorToDatabase(BasePage, PageCount, LoaderMemoryData);
664 
665     /* Reset the descriptor back so we can create the correct memory blocks */
666     *MxFreeDescriptor = MxOldFreeDescriptor;
667 }
668 
669 NTSTATUS
670 NTAPI
671 INIT_FUNCTION
672 MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
673 {
674     KIRQL OldIrql;
675 
676     ASSERT(MxPfnAllocation != 0);
677 
678     /* Set some hardcoded addresses */
679     MmHyperSpaceEnd = (PVOID)HYPER_SPACE_END;
680     MmNonPagedSystemStart = (PVOID)MM_SYSTEM_SPACE_START;
681     MmPfnDatabase = (PVOID)MI_PFN_DATABASE;
682     MmWorkingSetList = (PVOID)MI_WORKING_SET_LIST;
683 
684 
685 //    PrototypePte.u.Proto.Valid = 1
686 //    PrototypePte.u.ReadOnly
687 //    PrototypePte.u.Prototype
688 //    PrototypePte.u.Protection = MM_READWRITE;
689 //    PrototypePte.u.ProtoAddress
690     PrototypePte.u.Soft.PageFileHigh = MI_PTE_LOOKUP_NEEDED;
691 
692 
693     MiInitializePageTable();
694 
695     MiBuildNonPagedPool();
696 
697     MiBuildSystemPteSpace();
698 
699     /* Need to be at DISPATCH_LEVEL for MiInsertPageInFreeList */
700     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
701 
702     /* Map the PFN database pages */
703     MiBuildPfnDatabase(LoaderBlock);
704 
705     /* Now process the page tables */
706     MiBuildPfnDatabaseFromPageTables();
707 
708     /* PFNs are initialized now! */
709     MiPfnsInitialized = TRUE;
710 
711     //KeLowerIrql(OldIrql);
712 
713     /* Need to be at DISPATCH_LEVEL for InitializePool */
714     //KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
715 
716     /* Initialize the nonpaged pool */
717     InitializePool(NonPagedPool, 0);
718 
719     KeLowerIrql(OldIrql);
720 
721     /* Initialize the balancer */
722     MmInitializeBalancer((ULONG)MmAvailablePages, 0);
723 
724     /* Make sure we have everything we need */
725     ASSERT(MmPfnDatabase);
726     ASSERT(MmNonPagedSystemStart);
727     ASSERT(MmNonPagedPoolStart);
728     ASSERT(MmSizeOfNonPagedPoolInBytes);
729     ASSERT(MmMaximumNonPagedPoolInBytes);
730     ASSERT(MmNonPagedPoolExpansionStart);
731     ASSERT(MmHyperSpaceEnd);
732     ASSERT(MmNumberOfSystemPtes);
733     ASSERT(MiAddressToPde(MmNonPagedPoolStart)->u.Hard.Valid);
734     ASSERT(MiAddressToPte(MmNonPagedPoolStart)->u.Hard.Valid);
735 
736     return STATUS_SUCCESS;
737 }
738