xref: /reactos/boot/environ/lib/mm/i386/mmx86.c (revision 40462c92)
1 /*
2 * COPYRIGHT:       See COPYING.ARM in the top level directory
3 * PROJECT:         ReactOS UEFI Boot Library
4 * FILE:            boot/environ/lib/mm/i386/mmx86.c
5 * PURPOSE:         Boot Library Memory Manager x86-Specific Code
6 * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7 */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 #include "bcd.h"
13 
14 #define PTE_BASE                0xC0000000
15 
16 //
17 // Specific PDE/PTE macros to be used inside the boot library environment
18 //
19 #define MiAddressToPte(x)       ((PMMPTE)(((((ULONG)(x)) >> 12) << 2) + (ULONG_PTR)MmPteBase))
20 #define MiAddressToPde(x)       ((PMMPDE)(((((ULONG)(x)) >> 22) << 2) + (ULONG_PTR)MmPdeBase))
21 #define MiAddressToPteOffset(x) ((((ULONG)(x)) << 10) >> 22)
22 #define MiAddressToPdeOffset(x) (((ULONG)(x)) / (1024 * PAGE_SIZE))
23 
24 /* DATA VARIABLES ************************************************************/
25 
26 ULONG_PTR MmArchKsegBase;
27 ULONG_PTR MmArchKsegBias;
28 ULONG MmArchLargePageSize;
29 BL_ADDRESS_RANGE MmArchKsegAddressRange;
30 ULONG_PTR MmArchTopOfApplicationAddressSpace;
31 PHYSICAL_ADDRESS Mmx86SelfMapBase;
32 ULONG MmDeferredMappingCount;
33 PMMPTE MmPdpt;
34 PULONG MmArchReferencePage;
35 PVOID MmPteBase;
36 PVOID MmPdeBase;
37 ULONG MmArchReferencePageSize;
38 
39 PBL_MM_TRANSLATE_VIRTUAL_ADDRESS Mmx86TranslateVirtualAddress;
40 PBL_MM_MAP_PHYSICAL_ADDRESS Mmx86MapPhysicalAddress;
41 PBL_MM_REMAP_VIRTUAL_ADDRESS Mmx86RemapVirtualAddress;
42 PBL_MM_UNMAP_VIRTUAL_ADDRESS Mmx86UnmapVirtualAddress;
43 PBL_MM_FLUSH_TLB Mmx86FlushTlb;
44 PBL_MM_FLUSH_TLB_ENTRY Mmx86FlushTlbEntry;
45 PBL_MM_DESTROY_SELF_MAP Mmx86DestroySelfMap;
46 
47 PBL_MM_RELOCATE_SELF_MAP BlMmRelocateSelfMap;
48 PBL_MM_FLUSH_TLB BlMmFlushTlb;
49 PBL_MM_MOVE_VIRTUAL_ADDRESS_RANGE BlMmMoveVirtualAddressRange;
50 PBL_MM_ZERO_VIRTUAL_ADDRESS_RANGE BlMmZeroVirtualAddressRange;
51 
52 PBL_MM_FLUSH_TLB Mmx86FlushTlb;
53 
54 /* FUNCTIONS *****************************************************************/
55 
56 BOOLEAN
57 BlMmIsTranslationEnabled (
58     VOID
59     )
60 {
61     /* Return if paging is on */
62     return ((CurrentExecutionContext) &&
63             (CurrentExecutionContext->ContextFlags & BL_CONTEXT_PAGING_ON));
64 }
65 
66 VOID
67 MmArchNullFunction (
68     VOID
69     )
70 {
71     /* Nothing to do */
72     return;
73 }
74 
75 VOID
76 MmDefRelocateSelfMap (
77     VOID
78     )
79 {
80     if (MmPteBase != (PVOID)PTE_BASE)
81     {
82         EfiPrintf(L"Supposed to relocate CR3\r\n");
83     }
84 }
85 
86 NTSTATUS
87 MmDefMoveVirtualAddressRange (
88     _In_ PVOID DestinationAddress,
89     _In_ PVOID SourceAddress,
90     _In_ ULONGLONG Size
91     )
92 {
93     EfiPrintf(L"Supposed to move shit\r\n");
94     return STATUS_NOT_IMPLEMENTED;
95 }
96 
97 NTSTATUS
98 MmDefZeroVirtualAddressRange (
99     _In_ PVOID DestinationAddress,
100     _In_ ULONGLONG Size
101     )
102 {
103     EfiPrintf(L"Supposed to zero shit\r\n");
104     return STATUS_NOT_IMPLEMENTED;
105 }
106 
107 BOOLEAN
108 MmArchTranslateVirtualAddress (
109     _In_ PVOID VirtualAddress,
110     _Out_opt_ PPHYSICAL_ADDRESS PhysicalAddress,
111     _Out_opt_ PULONG CachingFlags
112     )
113 {
114     PBL_MEMORY_DESCRIPTOR Descriptor;
115 
116     /* Check if paging is on */
117     if ((CurrentExecutionContext) &&
118         (CurrentExecutionContext->ContextFlags & BL_CONTEXT_PAGING_ON))
119     {
120         /* Yes -- we have to translate this from virtual */
121         return Mmx86TranslateVirtualAddress(VirtualAddress,
122                                             PhysicalAddress,
123                                             CachingFlags);
124     }
125 
126     /* Look in all descriptors except truncated and firmware ones */
127     Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_NO_FIRMWARE_MEMORY &
128                                     ~BL_MM_INCLUDE_TRUNCATED_MEMORY,
129                                     BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
130                                     (ULONG_PTR)VirtualAddress >> PAGE_SHIFT);
131 
132     /* Return the virtual address as the physical address */
133     if (PhysicalAddress)
134     {
135         PhysicalAddress->HighPart = 0;
136         PhysicalAddress->LowPart = (ULONG_PTR)VirtualAddress;
137     }
138 
139     /* There's no caching on physical memory */
140     if (CachingFlags)
141     {
142         *CachingFlags = 0;
143     }
144 
145     /* Success is if we found a descriptor */
146     return Descriptor != NULL;
147 }
148 
149 VOID
150 MmDefpDestroySelfMap (
151     VOID
152     )
153 {
154     EfiPrintf(L"No destroy\r\n");
155 }
156 
157 VOID
158 MmDefpFlushTlbEntry (
159     _In_ PVOID VirtualAddress
160     )
161 {
162     /* Flush the TLB */
163     __invlpg(VirtualAddress);
164 }
165 
166 VOID
167 MmDefpFlushTlb (
168     VOID
169     )
170 {
171     /* Flush the TLB */
172     __writecr3(__readcr3());
173 }
174 
175 NTSTATUS
176 MmDefpUnmapVirtualAddress (
177     _In_ PVOID VirtualAddress,
178     _In_ ULONG Size
179     )
180 {
181     EfiPrintf(L"No unmap\r\n");
182     return STATUS_NOT_IMPLEMENTED;
183 }
184 
185 NTSTATUS
186 MmDefpRemapVirtualAddress (
187     _In_ PPHYSICAL_ADDRESS PhysicalAddress,
188     _Out_ PVOID VirtualAddress,
189     _In_ ULONG Size,
190     _In_ ULONG CacheAttributes
191     )
192 {
193     EfiPrintf(L"No remap\r\n");
194     return STATUS_NOT_IMPLEMENTED;
195 }
196 
197 NTSTATUS
198 MmDefpMapPhysicalAddress (
199     _In_ PHYSICAL_ADDRESS PhysicalAddress,
200     _In_ PVOID VirtualAddress,
201     _In_ ULONG Size,
202     _In_ ULONG CacheAttributes
203     )
204 {
205     BOOLEAN Enabled;
206     ULONG i, PageCount, PdeOffset;
207     ULONGLONG CurrentAddress;
208     PMMPDE Pde;
209     PMMPTE Pte;
210     PMMPTE PageTable;
211     PHYSICAL_ADDRESS PageTableAddress;
212     NTSTATUS Status;
213 
214     /* Check if paging is on yet */
215     Enabled = BlMmIsTranslationEnabled();
216 
217     /* Get the physical address aligned */
218     CurrentAddress = (PhysicalAddress.QuadPart >> PAGE_SHIFT) << PAGE_SHIFT;
219 
220     /* Get the number of pages and loop through each one */
221     PageCount = Size >> PAGE_SHIFT;
222     for (i = 0; i < PageCount; i++)
223     {
224         /* Check if translation already exists for this page */
225         if (Mmx86TranslateVirtualAddress(VirtualAddress, NULL, NULL))
226         {
227             /* Ignore it and move to the next one */
228             VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
229             CurrentAddress += PAGE_SIZE;
230             continue;
231         }
232 
233         /* Get the PDE offset */
234         PdeOffset = MiAddressToPdeOffset(VirtualAddress);
235 
236         /* Check if paging is actually turned on */
237         if (Enabled)
238         {
239             /* Get the PDE entry using the self-map */
240             Pde = MiAddressToPde(VirtualAddress);
241         }
242         else
243         {
244             /* Get it using our physical mappings */
245             Pde = &MmPdpt[PdeOffset];
246             PageTable = (PMMPDE)(Pde->u.Hard.PageFrameNumber << PAGE_SHIFT);
247         }
248 
249         /* Check if we don't yet have a PDE */
250         if (!Pde->u.Hard.Valid)
251         {
252             /* Allocate a page table */
253             Status = MmPapAllocatePhysicalPagesInRange(&PageTableAddress,
254                                                        BlLoaderPageDirectory,
255                                                        1,
256                                                        0,
257                                                        0,
258                                                        &MmMdlUnmappedAllocated,
259                                                        0,
260                                                        0);
261             if (!NT_SUCCESS(Status))
262             {
263                 EfiPrintf(L"PDE alloc failed!\r\n");
264                 EfiStall(1000000);
265                 return STATUS_NO_MEMORY;
266             }
267 
268             /* This is our page table */
269             PageTable = (PVOID)(ULONG_PTR)PageTableAddress.QuadPart;
270 
271             /* Build the PDE for it */
272             Pde->u.Hard.PageFrameNumber = PageTableAddress.QuadPart >> PAGE_SHIFT;
273             Pde->u.Hard.Write = 1;
274             Pde->u.Hard.CacheDisable = 1;
275             Pde->u.Hard.WriteThrough = 1;
276             Pde->u.Hard.Valid = 1;
277 
278             /* Check if paging is enabled */
279             if (Enabled)
280             {
281                 /* Then actually, get the page table's virtual address */
282                 PageTable = (PVOID)PAGE_ROUND_DOWN(MiAddressToPte(VirtualAddress));
283 
284                 /* Flush the TLB */
285                 Mmx86FlushTlb();
286             }
287 
288             /* Zero out the page table */
289             RtlZeroMemory(PageTable, PAGE_SIZE);
290 
291             /* Reset caching attributes now */
292             Pde->u.Hard.CacheDisable = 0;
293             Pde->u.Hard.WriteThrough = 0;
294 
295             /* Check for paging again */
296             if (Enabled)
297             {
298                 /* Flush the TLB entry for the page table only */
299                 Mmx86FlushTlbEntry(PageTable);
300             }
301         }
302 
303         /* Add a reference to this page table */
304         MmArchReferencePage[PdeOffset]++;
305 
306         /* Check if a physical address was given */
307         if (PhysicalAddress.QuadPart != -1)
308         {
309             /* Check if paging is turned on */
310             if (Enabled)
311             {
312                 /* Get the PTE using the self-map */
313                 Pte = MiAddressToPte(VirtualAddress);
314             }
315             else
316             {
317                 /* Get the PTE using physical addressing */
318                 Pte = &PageTable[MiAddressToPteOffset(VirtualAddress)];
319             }
320 
321             /* Build a valid PTE for it */
322             Pte->u.Hard.PageFrameNumber = CurrentAddress >> PAGE_SHIFT;
323             Pte->u.Hard.Write = 1;
324             Pte->u.Hard.Valid = 1;
325 
326             /* Check if this is uncached */
327             if (CacheAttributes == BlMemoryUncached)
328             {
329                 /* Set the flags */
330                 Pte->u.Hard.CacheDisable = 1;
331                 Pte->u.Hard.WriteThrough = 1;
332             }
333             else if (CacheAttributes == BlMemoryWriteThrough)
334             {
335                 /* It's write-through, set the flag */
336                 Pte->u.Hard.WriteThrough = 1;
337             }
338         }
339 
340         /* Move to the next physical/virtual address */
341         VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
342         CurrentAddress += PAGE_SIZE;
343     }
344 
345     /* All done! */
346     return STATUS_SUCCESS;
347 }
348 
349 BOOLEAN
350 MmDefpTranslateVirtualAddress (
351     _In_ PVOID VirtualAddress,
352     _Out_ PPHYSICAL_ADDRESS PhysicalAddress,
353     _Out_opt_ PULONG CacheAttributes
354     )
355 {
356     PMMPDE Pde;
357     PMMPTE Pte;
358     PMMPTE PageTable;
359     BOOLEAN Enabled;
360 
361     /* Is there no page directory yet? */
362     if (!MmPdpt)
363     {
364         return FALSE;
365     }
366 
367     /* Is paging enabled? */
368     Enabled = BlMmIsTranslationEnabled();
369 
370     /* Check if paging is actually turned on */
371     if (Enabled)
372     {
373         /* Get the PDE entry using the self-map */
374         Pde = MiAddressToPde(VirtualAddress);
375     }
376     else
377     {
378         /* Get it using our physical mappings */
379         Pde = &MmPdpt[MiAddressToPdeOffset(VirtualAddress)];
380     }
381 
382     /* Is the PDE valid? */
383     if (!Pde->u.Hard.Valid)
384     {
385         return FALSE;
386     }
387 
388     /* Check if paging is turned on */
389     if (Enabled)
390     {
391         /* Get the PTE using the self-map */
392         Pte = MiAddressToPte(VirtualAddress);
393     }
394     else
395     {
396         /* Get the PTE using physical addressing */
397         PageTable = (PMMPTE)(Pde->u.Hard.PageFrameNumber << PAGE_SHIFT);
398         Pte = &PageTable[MiAddressToPteOffset(VirtualAddress)];
399     }
400 
401     /* Is the PTE valid? */
402     if (!Pte->u.Hard.Valid)
403     {
404         return FALSE;
405     }
406 
407     /* Does caller want the physical address?  */
408     if (PhysicalAddress)
409     {
410         /* Return it */
411         PhysicalAddress->QuadPart = (Pte->u.Hard.PageFrameNumber << PAGE_SHIFT) +
412                                      BYTE_OFFSET(VirtualAddress);
413     }
414 
415     /* Does caller want cache attributes? */
416     if (CacheAttributes)
417     {
418         /* Not yet -- lie and say it's cached */
419         EfiPrintf(L"Cache checking not yet enabled\r\n");
420         *CacheAttributes = BlMemoryWriteBack;
421     }
422 
423     /* It exists! */
424     return TRUE;
425 }
426 
427 NTSTATUS
428 MmMapPhysicalAddress (
429     _Inout_ PPHYSICAL_ADDRESS PhysicalAddressPtr,
430     _Inout_ PVOID* VirtualAddressPtr,
431     _Inout_ PULONGLONG SizePtr,
432     _In_ ULONG CacheAttributes
433     )
434 {
435     ULONGLONG Size;
436     ULONGLONG PhysicalAddress;
437     PVOID VirtualAddress;
438     PHYSICAL_ADDRESS TranslatedAddress;
439     ULONG_PTR CurrentAddress, VirtualAddressEnd;
440     NTSTATUS Status;
441 
442     /* Fail if any parameters are missing */
443     if (!(PhysicalAddressPtr) || !(VirtualAddressPtr) || !(SizePtr))
444     {
445         return STATUS_INVALID_PARAMETER;
446     }
447 
448     /* Fail if the size is over 32-bits */
449     Size = *SizePtr;
450     if (Size > 0xFFFFFFFF)
451     {
452         return STATUS_INVALID_PARAMETER;
453     }
454 
455     /* Nothing to do if we're in physical mode */
456     if (MmTranslationType == BlNone)
457     {
458         return STATUS_SUCCESS;
459     }
460 
461     /* Can't use virtual memory in real mode */
462     if (CurrentExecutionContext->Mode == BlRealMode)
463     {
464         return STATUS_UNSUCCESSFUL;
465     }
466 
467     /* Capture the current virtual and physical addresses */
468     VirtualAddress = *VirtualAddressPtr;
469     PhysicalAddress = PhysicalAddressPtr->QuadPart;
470 
471     /* Check if a physical address was requested */
472     if (PhysicalAddress != 0xFFFFFFFF)
473     {
474         /* Round down the base addresses */
475         PhysicalAddress = PAGE_ROUND_DOWN(PhysicalAddress);
476         VirtualAddress = (PVOID)PAGE_ROUND_DOWN(VirtualAddress);
477 
478         /* Round up the size */
479         Size = ROUND_TO_PAGES(PhysicalAddressPtr->QuadPart -
480                               PhysicalAddress +
481                               Size);
482 
483         /* Loop every virtual page */
484         CurrentAddress = (ULONG_PTR)VirtualAddress;
485         VirtualAddressEnd = CurrentAddress + Size - 1;
486         while (CurrentAddress < VirtualAddressEnd)
487         {
488             /* Get the physical page of this virtual page */
489             if (MmArchTranslateVirtualAddress((PVOID)CurrentAddress,
490                                               &TranslatedAddress,
491                                               &CacheAttributes))
492             {
493                 /* Make sure the physical page of the virtual page, matches our page */
494                 if (TranslatedAddress.QuadPart !=
495                     (PhysicalAddress +
496                      (CurrentAddress - (ULONG_PTR)VirtualAddress)))
497                 {
498                     /* There is an existing virtual mapping for a different address */
499                     EfiPrintf(L"Existing mapping exists: %lx vs %lx\r\n",
500                               TranslatedAddress.QuadPart,
501                               PhysicalAddress + (CurrentAddress - (ULONG_PTR)VirtualAddress));
502                     EfiStall(10000000);
503                     return STATUS_INVALID_PARAMETER;
504                 }
505             }
506 
507             /* Try the next one */
508             CurrentAddress += PAGE_SIZE;
509         }
510     }
511 
512     /* Aactually do the mapping */
513     TranslatedAddress.QuadPart = PhysicalAddress;
514     Status = Mmx86MapPhysicalAddress(TranslatedAddress,
515                                      VirtualAddress,
516                                      Size,
517                                      CacheAttributes);
518     if (!NT_SUCCESS(Status))
519     {
520         EfiPrintf(L"Failed to map!: %lx\r\n", Status);
521         EfiStall(1000000);
522         return Status;
523     }
524 
525     /* Return aligned/fixed up output parameters */
526     PhysicalAddressPtr->QuadPart = PhysicalAddress;
527     *VirtualAddressPtr = VirtualAddress;
528     *SizePtr = Size;
529 
530     /* Flush the TLB if paging is enabled */
531     if (BlMmIsTranslationEnabled())
532     {
533         Mmx86FlushTlb();
534     }
535 
536     /* All good! */
537     return STATUS_SUCCESS;
538 }
539 
540 NTSTATUS
541 Mmx86MapInitStructure (
542     _In_ PVOID VirtualAddress,
543     _In_ ULONGLONG Size,
544     _In_ PHYSICAL_ADDRESS PhysicalAddress
545     )
546 {
547     NTSTATUS Status;
548 
549     /* Make a virtual mapping for this physical address */
550     Status = MmMapPhysicalAddress(&PhysicalAddress, &VirtualAddress, &Size, 0);
551     if (!NT_SUCCESS(Status))
552     {
553         return Status;
554     }
555 
556     /* Nothing else to do if we're not in paging mode */
557     if (MmTranslationType == BlNone)
558     {
559         return STATUS_SUCCESS;
560     }
561 
562     /* Otherwise, remove this region from the list of free virtual ranges */
563     Status = MmMdRemoveRegionFromMdlEx(&MmMdlFreeVirtual,
564                                        BL_MM_REMOVE_VIRTUAL_REGION_FLAG,
565                                        (ULONG_PTR)VirtualAddress >> PAGE_SHIFT,
566                                        Size >> PAGE_SHIFT,
567                                        0);
568     if (!NT_SUCCESS(Status))
569     {
570         /* Unmap the address if that failed */
571         MmUnmapVirtualAddress(&VirtualAddress, &Size);
572     }
573 
574     /* Return back to caller */
575     return Status;
576 }
577 
578 VOID
579 MmMdDbgDumpList (
580     _In_ PBL_MEMORY_DESCRIPTOR_LIST DescriptorList,
581     _In_opt_ ULONG MaxCount
582     )
583 {
584     ULONGLONG EndPage, VirtualEndPage;
585     PBL_MEMORY_DESCRIPTOR MemoryDescriptor;
586     PLIST_ENTRY NextEntry;
587 
588     /* If no maximum was provided, use essentially infinite */
589     if (MaxCount == 0)
590     {
591         MaxCount = 0xFFFFFFFF;
592     }
593 
594     /* Loop the list as long as there's entries and max isn't reached */
595     NextEntry = DescriptorList->First->Flink;
596     while ((NextEntry != DescriptorList->First) && (MaxCount--))
597     {
598         /* Get the descriptor */
599         MemoryDescriptor = CONTAINING_RECORD(NextEntry,
600                                              BL_MEMORY_DESCRIPTOR,
601                                              ListEntry);
602 
603         /* Get the descriptor end page, and see if it was virtually mapepd */
604         EndPage = MemoryDescriptor->BasePage + MemoryDescriptor->PageCount;
605         if (MemoryDescriptor->VirtualPage)
606         {
607             /* Get the virtual end page too, then */
608             VirtualEndPage = MemoryDescriptor->VirtualPage +
609                              MemoryDescriptor->PageCount;
610         }
611         else
612         {
613             VirtualEndPage = 0;
614         }
615 
616         /* Print out the descriptor, physical range, virtual range, and type */
617         EfiPrintf(L"%p - [%08llx-%08llx @ %08llx-%08llx]:%x\r\n",
618                     MemoryDescriptor,
619                     MemoryDescriptor->BasePage << PAGE_SHIFT,
620                     (EndPage << PAGE_SHIFT) - 1,
621                     MemoryDescriptor->VirtualPage << PAGE_SHIFT,
622                     VirtualEndPage ? (VirtualEndPage << PAGE_SHIFT) - 1 : 0,
623                     (ULONG)MemoryDescriptor->Type);
624 
625         /* Next entry */
626         NextEntry = NextEntry->Flink;
627     }
628 }
629 
630 NTSTATUS
631 Mmx86pMapMemoryRegions (
632     _In_ ULONG Phase,
633     _In_ PBL_MEMORY_DATA MemoryData
634     )
635 {
636     BOOLEAN DoDeferred;
637     ULONG DescriptorCount;
638     PBL_MEMORY_DESCRIPTOR Descriptor;
639     ULONG FinalOffset;
640     PHYSICAL_ADDRESS PhysicalAddress;
641     ULONGLONG Size;
642     NTSTATUS Status;
643     PVOID VirtualAddress;
644     BL_MEMORY_DESCRIPTOR_LIST FirmwareMdl;
645     PLIST_ENTRY Head, NextEntry;
646 
647     /* Check which phase this is */
648     if (Phase == 1)
649     {
650         /* In phase 1 we don't initialize deferred mappings */
651         DoDeferred = FALSE;
652     }
653     else
654     {
655         /* Don't do anything if there's nothing to initialize */
656         if (!MmDeferredMappingCount)
657         {
658             return STATUS_SUCCESS;
659         }
660 
661         /* We'll do deferred descriptors in phase 2 */
662         DoDeferred = TRUE;
663     }
664 
665     /*
666     * Because BL supports cross x86-x64 application launches and a LIST_ENTRY
667     * is of variable size, care must be taken here to ensure that we see a
668     * consistent view of descriptors. BL uses some offset magic to figure out
669     * where the data actually starts, since everything is ULONGLONG past the
670     * LIST_ENTRY itself
671     */
672     FinalOffset = MemoryData->MdListOffset + MemoryData->DescriptorOffset;
673     Descriptor = (PBL_MEMORY_DESCRIPTOR)((ULONG_PTR)MemoryData + FinalOffset -
674                                          FIELD_OFFSET(BL_MEMORY_DESCRIPTOR, BasePage));
675 
676     /* Scan all of them */
677     DescriptorCount = MemoryData->DescriptorCount;
678     while (DescriptorCount != 0)
679     {
680         /* Ignore application data */
681         if (Descriptor->Type != BlApplicationData)
682         {
683             /* If this is a ramdisk, do it in phase 2 */
684             if ((Descriptor->Type == BlLoaderRamDisk) == DoDeferred)
685             {
686                 /* Get the current physical address and size */
687                 PhysicalAddress.QuadPart = Descriptor->BasePage << PAGE_SHIFT;
688                 Size = Descriptor->PageCount << PAGE_SHIFT;
689 
690                 /* Check if it was already mapped */
691                 if (Descriptor->VirtualPage)
692                 {
693                     /* Use the existing address */
694                     VirtualAddress = (PVOID)(ULONG_PTR)(Descriptor->VirtualPage << PAGE_SHIFT);
695                 }
696                 else
697                 {
698                     /* Use the physical address */
699                     VirtualAddress = (PVOID)(ULONG_PTR)PhysicalAddress.QuadPart;
700                 }
701 
702                 /* Crete the mapping */
703                 Status = Mmx86MapInitStructure(VirtualAddress,
704                                                Size,
705                                                PhysicalAddress);
706                 if (!NT_SUCCESS(Status))
707                 {
708                     return Status;
709                 }
710             }
711 
712             /* Check if we're in phase 1 and deferring RAM disk */
713             if ((Phase == 1) && (Descriptor->Type == BlLoaderRamDisk))
714             {
715                 MmDeferredMappingCount++;
716             }
717         }
718 
719         /* Move on to the next descriptor */
720         DescriptorCount--;
721         Descriptor = (PBL_MEMORY_DESCRIPTOR)((ULONG_PTR)Descriptor + MemoryData->DescriptorSize);
722     }
723 
724     /* In phase 1, also do UEFI mappings */
725     if (Phase != 2)
726     {
727         /* Get the memory map */
728         MmMdInitializeListHead(&FirmwareMdl);
729         Status = MmFwGetMemoryMap(&FirmwareMdl, BL_MM_FLAG_REQUEST_COALESCING);
730         if (!NT_SUCCESS(Status))
731         {
732             return Status;
733         }
734 
735         /* Iterate over it */
736         Head = FirmwareMdl.First;
737         NextEntry = Head->Flink;
738         while (NextEntry != Head)
739         {
740             /* Check if this is a UEFI-related descriptor, unless it's the self-map page */
741             Descriptor = CONTAINING_RECORD(NextEntry, BL_MEMORY_DESCRIPTOR, ListEntry);
742             if (((Descriptor->Type == BlEfiBootMemory) ||
743                  (Descriptor->Type == BlEfiRuntimeCodeMemory) ||
744                  (Descriptor->Type == BlEfiRuntimeDataMemory) || // WINBUG?
745                  (Descriptor->Type == BlLoaderMemory)) &&
746                 ((Descriptor->BasePage << PAGE_SHIFT) != Mmx86SelfMapBase.QuadPart))
747             {
748                 /* Identity-map it */
749                 PhysicalAddress.QuadPart = Descriptor->BasePage << PAGE_SHIFT;
750                 Status = Mmx86MapInitStructure((PVOID)((ULONG_PTR)Descriptor->BasePage << PAGE_SHIFT),
751                                                Descriptor->PageCount << PAGE_SHIFT,
752                                                PhysicalAddress);
753                 if (!NT_SUCCESS(Status))
754                 {
755                     return Status;
756                 }
757             }
758 
759             /* Move to the next descriptor */
760             NextEntry = NextEntry->Flink;
761         }
762 
763         /* Reset */
764         NextEntry = Head->Flink;
765         while (NextEntry != Head)
766         {
767             /* Get the descriptor */
768             Descriptor = CONTAINING_RECORD(NextEntry, BL_MEMORY_DESCRIPTOR, ListEntry);
769 
770             /* Skip to the next entry before we free */
771             NextEntry = NextEntry->Flink;
772 
773             /* Remove and free it */
774             MmMdRemoveDescriptorFromList(&FirmwareMdl, Descriptor);
775             MmMdFreeDescriptor(Descriptor);
776         }
777     }
778 
779     /* All library mappings identity mapped now */
780     return STATUS_SUCCESS;
781 }
782 
783 NTSTATUS
784 Mmx86InitializeMemoryMap (
785     _In_ ULONG Phase,
786     _In_ PBL_MEMORY_DATA MemoryData
787     )
788 {
789     ULONG ImageSize;
790     PVOID ImageBase;
791     KDESCRIPTOR Gdt, Idt;
792     NTSTATUS Status;
793     PHYSICAL_ADDRESS PhysicalAddress;
794 
795     /* If this is phase 2, map the memory regions */
796     if (Phase != 1)
797     {
798         return Mmx86pMapMemoryRegions(Phase, MemoryData);
799     }
800 
801     /* Get the application image base/size */
802     Status = BlGetApplicationBaseAndSize(&ImageBase, &ImageSize);
803     if (!NT_SUCCESS(Status))
804     {
805         return Status;
806     }
807 
808     /* Map the image back at the same place */
809     PhysicalAddress.QuadPart = (ULONG_PTR)ImageBase;
810     Status = Mmx86MapInitStructure(ImageBase, ImageSize, PhysicalAddress);
811     if (!NT_SUCCESS(Status))
812     {
813         return Status;
814     }
815 
816     /* Map the first 4MB of memory */
817     PhysicalAddress.QuadPart = 0;
818     Status = Mmx86MapInitStructure(NULL, 4 * 1024 * 1024, PhysicalAddress);
819     if (!NT_SUCCESS(Status))
820     {
821         return Status;
822     }
823 
824     /* Map the GDT */
825     _sgdt(&Gdt.Limit);
826     PhysicalAddress.QuadPart = Gdt.Base;
827     Status = Mmx86MapInitStructure((PVOID)Gdt.Base, Gdt.Limit + 1, PhysicalAddress);
828     if (!NT_SUCCESS(Status))
829     {
830         return Status;
831     }
832 
833     /* Map the IDT */
834     __sidt(&Idt.Limit);
835     PhysicalAddress.QuadPart = Idt.Base;
836     Status = Mmx86MapInitStructure((PVOID)Idt.Base, Idt.Limit + 1, PhysicalAddress);
837     if (!NT_SUCCESS(Status))
838     {
839         return Status;
840     }
841 
842     /* Map the reference page */
843     PhysicalAddress.QuadPart = (ULONG_PTR)MmArchReferencePage;
844     Status = Mmx86MapInitStructure(MmArchReferencePage,
845                                    MmArchReferencePageSize,
846                                    PhysicalAddress);
847     if (!NT_SUCCESS(Status))
848     {
849         return Status;
850     }
851 
852     /* More to do */
853     return Mmx86pMapMemoryRegions(Phase, MemoryData);
854 }
855 
856 NTSTATUS
857 MmDefInitializeTranslation (
858     _In_ PBL_MEMORY_DATA MemoryData,
859     _In_ BL_TRANSLATION_TYPE TranslationType
860     )
861 {
862     NTSTATUS Status;
863     PHYSICAL_ADDRESS PhysicalAddress;
864     ULONG PdeIndex;
865 
866     /* Set the global function pointers for memory translation */
867     Mmx86TranslateVirtualAddress = MmDefpTranslateVirtualAddress;
868     Mmx86MapPhysicalAddress = MmDefpMapPhysicalAddress;
869     Mmx86UnmapVirtualAddress = MmDefpUnmapVirtualAddress;
870     Mmx86RemapVirtualAddress = MmDefpRemapVirtualAddress;
871     Mmx86FlushTlb = MmDefpFlushTlb;
872     Mmx86FlushTlbEntry = MmDefpFlushTlbEntry;
873     Mmx86DestroySelfMap = MmDefpDestroySelfMap;
874 
875     /* Check what mode we're currently in */
876     if (TranslationType == BlVirtual)
877     {
878         EfiPrintf(L"Virtual->Virtual not yet supported\r\n");
879         return STATUS_NOT_IMPLEMENTED;
880     }
881     else if (TranslationType != BlNone)
882     {
883         /* Not even Windows supports PAE->Virtual downgrade */
884         return STATUS_NOT_IMPLEMENTED;
885     }
886 
887     /* The None->Virtual case */
888     MmPdpt = NULL;
889     Mmx86SelfMapBase.QuadPart = 0;
890     MmArchReferencePage = NULL;
891 
892     /* Truncate all memory above 4GB so that we don't use it */
893     Status = MmPaTruncateMemory(0x100000);
894     if (!NT_SUCCESS(Status))
895     {
896         goto Failure;
897     }
898 
899     /* Allocate a page directory */
900     Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
901                                                BlLoaderPageDirectory,
902                                                1,
903                                                0,
904                                                0,
905                                                &MmMdlUnmappedAllocated,
906                                                0,
907                                                0);
908     if (!NT_SUCCESS(Status))
909     {
910         goto Failure;
911     }
912 
913     /* Zero out the page directory */
914     MmPdpt = (PVOID)PhysicalAddress.LowPart;
915     RtlZeroMemory(MmPdpt, PAGE_SIZE);
916 
917     /* Set the page size */
918     MmArchReferencePageSize = PAGE_SIZE;
919 
920     /* Allocate the self-map page */
921     Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
922                                                BlLoaderReferencePage,
923                                                1,
924                                                0,
925                                                0,
926                                                &MmMdlUnmappedAllocated,
927                                                0,
928                                                0);
929     if (!NT_SUCCESS(Status))
930     {
931         goto Failure;
932     }
933 
934     /* Set the reference page */
935     MmArchReferencePage = (PVOID)PhysicalAddress.LowPart;
936 
937     /* Zero it out */
938     RtlZeroMemory(MmArchReferencePage, MmArchReferencePageSize);
939 
940     /* Allocate 4MB worth of self-map pages */
941     Status = MmPaReserveSelfMapPages(&Mmx86SelfMapBase,
942                                      (4 * 1024 * 1024) >> PAGE_SHIFT,
943                                      (4 * 1024 * 1024) >> PAGE_SHIFT);
944     if (!NT_SUCCESS(Status))
945     {
946         goto Failure;
947     }
948 
949     /* Zero them out */
950     RtlZeroMemory((PVOID)Mmx86SelfMapBase.LowPart, 4 * 1024 * 1024);
951     EfiPrintf(L"PDPT at 0x%p Reference Page at 0x%p Self-map at 0x%p\r\n",
952               MmPdpt, MmArchReferencePage, Mmx86SelfMapBase.LowPart);
953 
954     /* Align PTE base to 4MB region */
955     MmPteBase = (PVOID)(Mmx86SelfMapBase.LowPart & ~0x3FFFFF);
956 
957     /* The PDE is the PTE of the PTE base */
958     MmPdeBase = MiAddressToPte(MmPteBase);
959     PdeIndex = MiAddressToPdeOffset(MmPdeBase);
960     MmPdpt[PdeIndex].u.Hard.Valid = 1;
961     MmPdpt[PdeIndex].u.Hard.Write = 1;
962     MmPdpt[PdeIndex].u.Hard.PageFrameNumber = (ULONG_PTR)MmPdpt >> PAGE_SHIFT;
963     MmArchReferencePage[PdeIndex]++;
964 
965     /* Remove PTE_BASE from free virtual memory */
966     Status = MmMdRemoveRegionFromMdlEx(&MmMdlFreeVirtual,
967                                        BL_MM_REMOVE_VIRTUAL_REGION_FLAG,
968                                        PTE_BASE >> PAGE_SHIFT,
969                                        (4 * 1024 * 1024) >> PAGE_SHIFT,
970                                        0);
971     if (!NT_SUCCESS(Status))
972     {
973         goto Failure;
974     }
975 
976     /* Remove HAL_HEAP from free virtual memory */
977     Status = MmMdRemoveRegionFromMdlEx(&MmMdlFreeVirtual,
978                                        BL_MM_REMOVE_VIRTUAL_REGION_FLAG,
979                                        MM_HAL_VA_START >> PAGE_SHIFT,
980                                        (4 * 1024 * 1024) >> PAGE_SHIFT,
981                                        0);
982     if (!NT_SUCCESS(Status))
983     {
984         goto Failure;
985     }
986 
987     /* Initialize the virtual->physical memory mappings */
988     Status = Mmx86InitializeMemoryMap(1, MemoryData);
989     if (!NT_SUCCESS(Status))
990     {
991         goto Failure;
992     }
993 
994     /* Turn on paging with the new CR3 */
995     __writecr3((ULONG_PTR)MmPdpt);
996     BlpArchEnableTranslation();
997     EfiPrintf(L"Paging... %d\r\n", BlMmIsTranslationEnabled());
998 
999     /* Return success */
1000     return Status;
1001 
1002 Failure:
1003     /* Free reference page if we allocated it */
1004     if (MmArchReferencePage)
1005     {
1006         PhysicalAddress.QuadPart = (ULONG_PTR)MmArchReferencePage;
1007         BlMmFreePhysicalPages(PhysicalAddress);
1008     }
1009 
1010     /* Free page directory if we allocated it */
1011     if (MmPdpt)
1012     {
1013         PhysicalAddress.QuadPart = (ULONG_PTR)MmPdpt;
1014         BlMmFreePhysicalPages(PhysicalAddress);
1015     }
1016 
1017     /* Free the self map if we allocated it */
1018     if (Mmx86SelfMapBase.QuadPart)
1019     {
1020         MmPaReleaseSelfMapPages(Mmx86SelfMapBase);
1021     }
1022 
1023     /* All done */
1024     return Status;
1025 }
1026 
1027 NTSTATUS
1028 MmArchInitialize (
1029     _In_ ULONG Phase,
1030     _In_ PBL_MEMORY_DATA MemoryData,
1031     _In_ BL_TRANSLATION_TYPE TranslationType,
1032     _In_ BL_TRANSLATION_TYPE RequestedTranslationType
1033     )
1034 {
1035     NTSTATUS Status;
1036     ULONGLONG IncreaseUserVa, PerfCounter, CpuRandom;
1037     CPU_INFO CpuInfo;
1038 
1039     /* For phase 2, just map deferred regions */
1040     if (Phase != 1)
1041     {
1042         return Mmx86pMapMemoryRegions(2, MemoryData);
1043     }
1044 
1045     /* What translation type are we switching to? */
1046     switch (RequestedTranslationType)
1047     {
1048         /* Physical memory */
1049         case BlNone:
1050 
1051             /* Initialize everything to default/null values */
1052             MmArchLargePageSize = 1;
1053             MmArchKsegBase = 0;
1054             MmArchKsegBias = 0;
1055             MmArchKsegAddressRange.Minimum = 0;
1056             MmArchKsegAddressRange.Maximum = (ULONGLONG)~0;
1057             MmArchTopOfApplicationAddressSpace = 0;
1058             Mmx86SelfMapBase.QuadPart = 0;
1059 
1060             /* Set stub functions */
1061             BlMmRelocateSelfMap = MmArchNullFunction;
1062             BlMmFlushTlb = MmArchNullFunction;
1063 
1064             /* Set success */
1065             Status = STATUS_SUCCESS;
1066             break;
1067 
1068         case BlVirtual:
1069 
1070             /* Set the large page size to 1024 pages (4MB) */
1071             MmArchLargePageSize = (4 * 1024 * 1024) / PAGE_SIZE;
1072 
1073             /* Check if /USERVA option was used */
1074             Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
1075                                             BcdOSLoaderInteger_IncreaseUserVa,
1076                                             &IncreaseUserVa);
1077             if (NT_SUCCESS(Status) && (IncreaseUserVa))
1078             {
1079                 /* Yes -- load the kernel at 0xE0000000 instead */
1080                 MmArchKsegBase = 0xE0000000;
1081             }
1082             else
1083             {
1084                 /* Nope, load at the standard 2GB split */
1085                 MmArchKsegBase = 0x80000000;
1086             }
1087 
1088             /* Check if CPUID 01h is supported */
1089             CpuRandom = 0;
1090             if (BlArchIsCpuIdFunctionSupported(1))
1091             {
1092                 /* Call it */
1093                 BlArchCpuId(1, 0, &CpuInfo);
1094 
1095                 /* Check if RDRAND is supported */
1096                 if (CpuInfo.Ecx & 0x40000000)
1097                 {
1098                     EfiPrintf(L"Your CPU can do RDRAND! Good for you!\r\n");
1099                     CpuRandom = 0;
1100                 }
1101             }
1102 
1103             /* Read the TSC */
1104             PerfCounter = BlArchGetPerformanceCounter();
1105             PerfCounter >>= 4;
1106             _rotl16(PerfCounter, 5);
1107 
1108             /* Set the address range */
1109             MmArchKsegAddressRange.Minimum = 0;
1110             MmArchKsegAddressRange.Maximum = (ULONGLONG)~0;
1111 
1112             /* Set the KASLR bias */
1113             MmArchKsegBias = ((PerfCounter ^ CpuRandom) & 0xFFF) << 12;
1114             MmArchKsegBias = 0;
1115             MmArchKsegBase += MmArchKsegBias;
1116 
1117             /* Set the kernel range */
1118             MmArchKsegAddressRange.Minimum = MmArchKsegBase;
1119             MmArchKsegAddressRange.Maximum = (ULONGLONG)~0;
1120 
1121             /* Set the boot application top maximum */
1122             MmArchTopOfApplicationAddressSpace = 0x70000000 - 1; // Windows bug
1123 
1124             /* Initialize virtual address space translation */
1125             Status = MmDefInitializeTranslation(MemoryData, TranslationType);
1126             if (NT_SUCCESS(Status))
1127             {
1128                 /* Set stub functions */
1129                 BlMmRelocateSelfMap = MmDefRelocateSelfMap;
1130                 BlMmFlushTlb = Mmx86FlushTlb;
1131                 BlMmMoveVirtualAddressRange = MmDefMoveVirtualAddressRange;
1132                 BlMmZeroVirtualAddressRange = MmDefZeroVirtualAddressRange;
1133             }
1134             break;
1135 
1136         case BlPae:
1137 
1138             /* We don't support PAE */
1139             Status = STATUS_NOT_SUPPORTED;
1140             break;
1141 
1142         default:
1143 
1144             /* Invalid architecture type*/
1145             Status = STATUS_INVALID_PARAMETER;
1146             break;
1147     }
1148 
1149     /* Back to caller */
1150     return Status;
1151 }
1152