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