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