[ROSLOAD]: Continue work on loader
[reactos.git] / boot / environ / lib / mm / i386 / mmx86.c
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 }