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