TianoCore EDK2 master
Loading...
Searching...
No Matches
ArmMmuLibCore.c
Go to the documentation of this file.
1
12#include <Uefi.h>
13#include <Pi/PiMultiPhase.h>
14#include <AArch64/AArch64.h>
18#include <Library/ArmLib.h>
19#include <Library/ArmMmuLib.h>
20#include <Library/BaseLib.h>
21#include <Library/DebugLib.h>
22#include <Library/HobLib.h>
23#include "ArmMmuLibInternal.h"
24
25STATIC ARM_REPLACE_LIVE_TRANSLATION_ENTRY mReplaceLiveEntryFunc = ArmReplaceLiveTranslationEntry;
26
32BOOLEAN
34 VOID
35 )
36{
37 if (ArmReadCurrentEL () == AARCH64_EL2) {
38 return (ArmReadHcr () & ARM_HCR_E2H) != 0;
39 }
40
41 return TRUE;
42}
43
45UINT64
46ArmMemoryAttributeToPageAttribute (
47 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
48 )
49{
50 UINT64 Permissions;
51
52 switch (Attributes) {
53 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO:
54 Permissions = TT_AP_NO_RO;
55 break;
56
57 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP:
58 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
60 Permissions = TT_XN_MASK;
61 } else {
62 Permissions = TT_UXN_MASK | TT_PXN_MASK;
63 }
64
65 break;
66 default:
67 Permissions = 0;
68 break;
69 }
70
71 switch (Attributes) {
72 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
73 return TT_ATTR_INDX_MEMORY_WRITE_BACK | Permissions;
74
75 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
76 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO:
77 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP:
78 return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE | Permissions;
79
80 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
81 return TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;
82
83 // Uncached and device mappings are treated as outer shareable by default,
84 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
85 return TT_ATTR_INDX_MEMORY_NON_CACHEABLE | Permissions;
86
87 default:
88 ASSERT (0);
89 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
90 return TT_ATTR_INDX_DEVICE_MEMORY | Permissions;
91 }
92}
93
94// T0SZ can be below MIN_T0SZ when LPA2 is in use, meaning the page table starts at level -1
95#define MIN_T0SZ 16
96#define BITS_PER_LEVEL 9
97#define MAX_VA_BITS_48 48
98#define MAX_VA_BITS 52
99
100STATIC
101VOID
102SetOutputAddress (
103 IN UINTN *Entry,
104 IN UINTN Address,
105 IN BOOLEAN Lpa2Enabled
106 )
107{
108 if (Lpa2Enabled) {
109 *Entry &= ~(TT_ADDRESS_MASK_BLOCK_ENTRY_LPA2 | TT_UPPER_ADDRESS_MASK);
110 *Entry |= ((UINTN)Address & TT_ADDRESS_MASK_BLOCK_ENTRY_LPA2) | (((UINTN)Address >> 50) << 8);
111 } else {
112 *Entry &= ~TT_ADDRESS_MASK_BLOCK_ENTRY;
113 *Entry |= (Address & TT_ADDRESS_MASK_BLOCK_ENTRY);
114 }
115}
116
117STATIC
118UINT64
119GetOutputAddress (
120 IN UINT64 Entry,
121 IN BOOLEAN Lpa2Enabled
122 )
123{
124 if (Lpa2Enabled) {
125 return (Entry & TT_ADDRESS_MASK_BLOCK_ENTRY_LPA2) | ((Entry & TT_UPPER_ADDRESS_MASK) << (50 - 8));
126 } else {
127 return Entry & TT_ADDRESS_MASK_BLOCK_ENTRY;
128 }
129}
130
131STATIC
132UINTN
133GetRootTableEntryCount (
134 IN UINTN T0SZ
135 )
136{
137 return TT_ENTRY_COUNT >> (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL;
138}
139
140STATIC
141INTN
142GetRootTableLevel (
143 IN UINTN T0SZ
144 )
145{
146 INTN RootTableLevel;
147
148 RootTableLevel = (T0SZ < MIN_T0SZ) ? -1 : (INTN)(T0SZ - MIN_T0SZ) / BITS_PER_LEVEL;
149 ASSERT (RootTableLevel >= 0 || ArmLpa2Enabled ());
150
151 return RootTableLevel;
152}
153
154STATIC
155VOID
156ReplaceTableEntry (
157 IN UINT64 *Entry,
158 IN UINT64 Value,
159 IN UINT64 RegionStart,
160 IN UINT64 BlockMask,
161 IN BOOLEAN IsLiveBlockMapping
162 )
163{
164 BOOLEAN DisableMmu;
165
166 //
167 // Replacing a live block entry with a table entry (or vice versa) requires a
168 // break-before-make sequence as per the architecture. This means the mapping
169 // must be made invalid and cleaned from the TLBs first, and this is a bit of
170 // a hassle if the mapping in question covers the code that is actually doing
171 // the mapping and the unmapping, and so we only bother with this if actually
172 // necessary.
173 //
174
175 if (!IsLiveBlockMapping || !ArmMmuEnabled ()) {
176 // If the mapping is not a live block mapping, or the MMU is not on yet, we
177 // can simply overwrite the entry.
178 *Entry = Value;
179 ArmUpdateTranslationTableEntry (Entry, (VOID *)(UINTN)RegionStart);
180 } else {
181 // If the mapping in question does not cover the code that updates the
182 // entry in memory, or the entry that we are intending to update, we can
183 // use an ordinary break before make. Otherwise, we will need to
184 // temporarily disable the MMU.
185 DisableMmu = FALSE;
186 if ((((RegionStart ^ (UINTN)mReplaceLiveEntryFunc) & ~BlockMask) == 0) ||
187 (((RegionStart ^ (UINTN)Entry) & ~BlockMask) == 0))
188 {
189 DisableMmu = TRUE;
190 DEBUG ((DEBUG_WARN, "%a: splitting block entry with MMU disabled\n", __func__));
191 }
192
193 mReplaceLiveEntryFunc (Entry, Value, RegionStart, DisableMmu);
194 }
195}
196
197STATIC
198VOID
199FreePageTablesRecursive (
200 IN UINT64 *TranslationTable,
201 IN UINTN Level,
202 IN BOOLEAN Lpa2Enabled
203 )
204{
205 UINTN Index;
206
207 ASSERT (Level <= 3);
208
209 if (Level < 3) {
210 for (Index = 0; Index < TT_ENTRY_COUNT; Index++) {
211 if ((TranslationTable[Index] & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {
212 FreePageTablesRecursive (
213 (VOID *)GetOutputAddress (
214 TranslationTable[Index],
215 Lpa2Enabled
216 ),
217 Level + 1,
218 Lpa2Enabled
219 );
220 }
221 }
222 }
223
224 FreePages (TranslationTable, 1);
225}
226
227STATIC
228BOOLEAN
229IsBlockEntry (
230 IN UINT64 Entry,
231 IN UINTN Level
232 )
233{
234 if (Level == 3) {
235 return (Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3;
236 }
237
238 return (Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY;
239}
240
241STATIC
242BOOLEAN
243IsTableEntry (
244 IN UINT64 Entry,
245 IN UINTN Level
246 )
247{
248 if (Level == 3) {
249 //
250 // TT_TYPE_TABLE_ENTRY aliases TT_TYPE_BLOCK_ENTRY_LEVEL3
251 // so we need to take the level into account as well.
252 //
253 return FALSE;
254 }
255
256 return (Entry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY;
257}
258
259STATIC
261UpdateRegionMappingRecursive (
262 IN UINT64 RegionStart,
263 IN UINT64 RegionEnd,
264 IN UINT64 AttributeSetMask,
265 IN UINT64 AttributeClearMask,
266 IN UINT64 *PageTable,
267 IN INTN Level,
268 IN BOOLEAN IsRootTable,
269 IN BOOLEAN TableIsLive,
270 IN BOOLEAN Lpa2Enabled
271 )
272{
273 UINTN BlockShift;
274 UINT64 BlockMask;
275 UINT64 BlockEnd;
276 UINT64 *Entry;
277 UINT64 EntryValue;
278 VOID *TranslationTable;
279 EFI_STATUS Status;
280 BOOLEAN NextTableIsLive;
281
282 ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);
283
284 BlockShift = (Level + 1) * BITS_PER_LEVEL + MIN_T0SZ;
285 BlockMask = MAX_UINT64 >> BlockShift;
286
287 DEBUG ((
288 DEBUG_VERBOSE,
289 "%a(%d): %llx - %llx set %lx clr %lx\n",
290 __func__,
291 Level,
292 RegionStart,
293 RegionEnd,
294 AttributeSetMask,
295 AttributeClearMask
296 ));
297
298 for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
299 BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
300 Entry = &PageTable[(RegionStart >> (64 - BlockShift)) & (TT_ENTRY_COUNT - 1)];
301
302 //
303 // If RegionStart or BlockEnd is not aligned to the block size at this
304 // level, we will have to create a table mapping in order to map less
305 // than a block, and recurse to create the block or page entries at
306 // the next level. No block mappings are allowed at all at level 0,
307 // so in that case, we have to recurse unconditionally.
308 //
309 // One special case to take into account is any region that covers the page
310 // table itself: if we'd cover such a region with block mappings, we are
311 // more likely to end up in the situation later where we need to disable
312 // the MMU in order to update page table entries safely, so prefer page
313 // mappings in that particular case.
314 //
315 if ((Level <= 0) || (((RegionStart | BlockEnd) & BlockMask) != 0) ||
316 ((Level < 3) && (((UINT64)PageTable & ~BlockMask) == RegionStart)) ||
317 IsTableEntry (*Entry, Level))
318 {
319 ASSERT (Level < 3);
320
321 if (!IsTableEntry (*Entry, Level)) {
322 //
323 // If the region we are trying to map is already covered by a block
324 // entry with the right attributes, don't bother splitting it up.
325 //
326 if (IsBlockEntry (*Entry, Level) &&
327 ((*Entry & TT_ATTRIBUTES_MASK & ~AttributeClearMask) == AttributeSetMask))
328 {
329 continue;
330 }
331
332 //
333 // No table entry exists yet, so we need to allocate a page table
334 // for the next level.
335 //
336 TranslationTable = AllocatePages (1);
337 if (TranslationTable == NULL) {
338 return EFI_OUT_OF_RESOURCES;
339 }
340
341 if (!ArmMmuEnabled ()) {
342 //
343 // Make sure we are not inadvertently hitting in the caches
344 // when populating the page tables.
345 //
346 InvalidateDataCacheRange (TranslationTable, EFI_PAGE_SIZE);
347 }
348
349 ZeroMem (TranslationTable, EFI_PAGE_SIZE);
350
351 if (IsBlockEntry (*Entry, Level)) {
352 //
353 // We are splitting an existing block entry, so we have to populate
354 // the new table with the attributes of the block entry it replaces.
355 //
356 Status = UpdateRegionMappingRecursive (
357 RegionStart & ~BlockMask,
358 (RegionStart | BlockMask) + 1,
359 *Entry & TT_ATTRIBUTES_MASK,
360 0,
361 TranslationTable,
362 Level + 1,
363 FALSE,
364 FALSE,
365 Lpa2Enabled
366 );
367 if (EFI_ERROR (Status)) {
368 //
369 // The range we passed to UpdateRegionMappingRecursive () is block
370 // aligned, so it is guaranteed that no further pages were allocated
371 // by it, and so we only have to free the page we allocated here.
372 //
373 FreePages (TranslationTable, 1);
374 return Status;
375 }
376 }
377
378 NextTableIsLive = FALSE;
379 } else {
380 TranslationTable = (VOID *)GetOutputAddress (*Entry, Lpa2Enabled);
381 NextTableIsLive = TableIsLive;
382 }
383
384 //
385 // Recurse to the next level
386 //
387 Status = UpdateRegionMappingRecursive (
388 RegionStart,
389 BlockEnd,
390 AttributeSetMask,
391 AttributeClearMask,
392 TranslationTable,
393 Level + 1,
394 FALSE,
395 NextTableIsLive,
396 Lpa2Enabled
397 );
398 if (EFI_ERROR (Status)) {
399 if (!IsTableEntry (*Entry, Level)) {
400 //
401 // We are creating a new table entry, so on failure, we can free all
402 // allocations we made recursively, given that the whole subhierarchy
403 // has not been wired into the live page tables yet. (This is not
404 // possible for existing table entries, since we cannot revert the
405 // modifications we made to the subhierarchy it represents.)
406 //
407 FreePageTablesRecursive (TranslationTable, Level + 1, Lpa2Enabled);
408 }
409
410 return Status;
411 }
412
413 if (!IsTableEntry (*Entry, Level)) {
414 EntryValue = TT_TYPE_TABLE_ENTRY;
415 SetOutputAddress (&EntryValue, (UINTN)TranslationTable, Lpa2Enabled);
416
417 ReplaceTableEntry (
418 Entry,
419 EntryValue,
420 RegionStart,
421 BlockMask,
422 TableIsLive && IsBlockEntry (*Entry, Level)
423 );
424 }
425 } else {
426 EntryValue = (*Entry & AttributeClearMask) | AttributeSetMask;
427 // Below clears shareability bits when LPA2 is in use
428 SetOutputAddress (&EntryValue, RegionStart, Lpa2Enabled);
429 EntryValue |= (Level == 3) ? TT_TYPE_BLOCK_ENTRY_LEVEL3
430 : TT_TYPE_BLOCK_ENTRY;
431
432 ReplaceTableEntry (Entry, EntryValue, RegionStart, BlockMask, FALSE);
433 }
434 }
435
436 return EFI_SUCCESS;
437}
438
439STATIC
441UpdateRegionMapping (
442 IN UINT64 RegionStart,
443 IN UINT64 RegionLength,
444 IN UINT64 AttributeSetMask,
445 IN UINT64 AttributeClearMask,
446 IN UINT64 *RootTable,
447 IN BOOLEAN TableIsLive,
448 IN BOOLEAN Lpa2Enabled
449 )
450{
451 UINTN T0SZ;
452
453 if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) {
454 DEBUG ((
455 DEBUG_ERROR,
456 "%a RegionStart: 0x%llx or RegionLength: 0x%llx are not page aligned!\n",
457 __func__,
458 RegionStart,
459 RegionLength
460 ));
461 return EFI_INVALID_PARAMETER;
462 }
463
464 T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;
465
466 return UpdateRegionMappingRecursive (
467 RegionStart,
468 RegionStart + RegionLength,
469 AttributeSetMask,
470 AttributeClearMask,
471 RootTable,
472 GetRootTableLevel (T0SZ),
473 TRUE,
474 TableIsLive,
475 Lpa2Enabled
476 );
477}
478
479STATIC
481FillTranslationTable (
482 IN UINT64 *RootTable,
483 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion,
484 IN BOOLEAN Lpa2Enabled
485 )
486{
487 return UpdateRegionMapping (
488 MemoryRegion->VirtualBase,
489 MemoryRegion->Length,
490 ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | TT_AF,
491 0,
492 RootTable,
493 FALSE,
494 Lpa2Enabled
495 );
496}
497
498STATIC
499UINT64
500GcdAttributeToPageAttribute (
501 IN UINT64 GcdAttributes
502 )
503{
504 UINT64 PageAttributes;
505
506 switch (GcdAttributes & EFI_MEMORY_CACHETYPE_MASK) {
507 case EFI_MEMORY_UC:
508 PageAttributes = TT_ATTR_INDX_DEVICE_MEMORY;
509 break;
510 case EFI_MEMORY_WC:
511 PageAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;
512 break;
513 case EFI_MEMORY_WT:
514 PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;
515 break;
516 case EFI_MEMORY_WB:
517 PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;
518 break;
519 default:
520 PageAttributes = TT_ATTR_INDX_MASK;
521 break;
522 }
523
524 if (((GcdAttributes & EFI_MEMORY_XP) != 0) ||
525 ((GcdAttributes & EFI_MEMORY_CACHETYPE_MASK) == EFI_MEMORY_UC))
526 {
527 if (!TranslationRegimeIsDual ()) {
528 PageAttributes |= TT_XN_MASK;
529 } else {
530 PageAttributes |= TT_UXN_MASK | TT_PXN_MASK;
531 }
532 }
533
534 if ((GcdAttributes & EFI_MEMORY_RO) != 0) {
535 PageAttributes |= TT_AP_NO_RO;
536 }
537
538 if ((GcdAttributes & EFI_MEMORY_RP) == 0) {
539 PageAttributes |= TT_AF;
540 }
541
542 return PageAttributes;
543}
544
580 IN EFI_PHYSICAL_ADDRESS BaseAddress,
581 IN UINT64 Length,
582 IN UINT64 Attributes,
583 IN UINT64 AttributeMask
584 )
585{
586 UINT64 PageAttributes;
587 UINT64 PageAttributeMask;
588
589 PageAttributes = GcdAttributeToPageAttribute (Attributes);
590 PageAttributeMask = 0;
591
592 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {
593 //
594 // No memory type was set in Attributes, so we are going to update the
595 // permissions only.
596 //
597 PageAttributes &= TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK | TT_AF;
598 PageAttributeMask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK |
599 TT_PXN_MASK | TT_XN_MASK | TT_AF);
600 if (AttributeMask != 0) {
601 if (((AttributeMask & ~(UINT64)(EFI_MEMORY_RP|EFI_MEMORY_RO|EFI_MEMORY_XP)) != 0) ||
602 ((Attributes & ~AttributeMask) != 0))
603 {
604 return EFI_INVALID_PARAMETER;
605 }
606
607 // Add attributes omitted from AttributeMask to the set of attributes to preserve
608 PageAttributeMask |= GcdAttributeToPageAttribute (~AttributeMask) &
609 (TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK | TT_AF);
610 }
611 } else {
612 ASSERT (AttributeMask == 0);
613 if (AttributeMask != 0) {
614 return EFI_INVALID_PARAMETER;
615 }
616 }
617
618 return UpdateRegionMapping (
619 BaseAddress,
620 Length,
621 PageAttributes,
622 PageAttributeMask,
623 ArmGetTTBR0BaseAddress (),
624 TRUE,
626 );
627}
628
630EFIAPI
631ArmConfigureMmu (
632 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
633 OUT VOID **TranslationTableBase OPTIONAL,
634 OUT UINTN *TranslationTableSize OPTIONAL
635 )
636{
637 VOID *TranslationTable;
638 UINTN MaxAddressBits;
639 UINT64 MaxAddress;
640 UINTN T0SZ;
641 UINTN RootTableEntryCount;
642 UINT64 TCR;
643 EFI_STATUS Status;
644
645 ASSERT (ArmReadCurrentEL () < AARCH64_EL3);
646 if (ArmReadCurrentEL () == AARCH64_EL3) {
647 return EFI_UNSUPPORTED;
648 }
649
650 if (MemoryTable == NULL) {
651 ASSERT (MemoryTable != NULL);
652 return EFI_INVALID_PARAMETER;
653 }
654
655 //
656 // Limit the virtual address space to what we can actually use: UEFI
657 // mandates a 1:1 mapping, so no point in making the virtual address
658 // space larger than the physical address space. We also have to take
659 // into account the architectural limitations that result from UEFI's
660 // use of 4 KB pages.
661 //
662 if (ArmHas52BitTgran4 ()) {
663 MaxAddressBits = MIN (ArmGetPhysicalAddressBits (), MAX_VA_BITS);
664 } else {
665 MaxAddressBits = MIN (ArmGetPhysicalAddressBits (), MAX_VA_BITS_48);
666 }
667
668 MaxAddress = LShiftU64 (1ULL, MaxAddressBits) - 1;
669
670 T0SZ = 64 - MaxAddressBits;
671 RootTableEntryCount = GetRootTableEntryCount (T0SZ);
672
673 //
674 // Set TCR that allows us to retrieve T0SZ in the subsequent functions
675 //
676 if (!TranslationRegimeIsDual ()) {
677 // Note: Bits 23 and 31 are reserved(RES1) bits in TCR_EL2
678 TCR = T0SZ | (1UL << 31) | (1UL << 23) | TCR_TG0_4KB;
679
680 // Set the Physical Address Size using MaxAddress
681 if (MaxAddress < SIZE_4GB) {
682 TCR |= TCR_PS_4GB;
683 } else if (MaxAddress < SIZE_64GB) {
684 TCR |= TCR_PS_64GB;
685 } else if (MaxAddress < SIZE_1TB) {
686 TCR |= TCR_PS_1TB;
687 } else if (MaxAddress < SIZE_4TB) {
688 TCR |= TCR_PS_4TB;
689 } else if (MaxAddress < SIZE_16TB) {
690 TCR |= TCR_PS_16TB;
691 } else if (MaxAddress < SIZE_256TB) {
692 TCR |= TCR_PS_256TB;
693 } else if ((MaxAddress < SIZE_4PB) && ArmHas52BitTgran4 ()) {
694 TCR |= TCR_PS_4PB | TCR_DS_NVHE;
695 } else {
696 DEBUG ((
697 DEBUG_ERROR,
698 "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n",
699 MaxAddress
700 ));
701 ASSERT (0); // Bigger than 48/52-bit memory space are not supported
702 return EFI_UNSUPPORTED;
703 }
704 } else {
705 // Due to Cortex-A57 erratum #822227 we must set TG1[1] == 1, regardless of EPD1.
706 TCR = T0SZ | TCR_TG0_4KB | TCR_TG1_4KB | TCR_EPD1;
707
708 // Set the Physical Address Size using MaxAddress
709 if (MaxAddress < SIZE_4GB) {
710 TCR |= TCR_IPS_4GB;
711 } else if (MaxAddress < SIZE_64GB) {
712 TCR |= TCR_IPS_64GB;
713 } else if (MaxAddress < SIZE_1TB) {
714 TCR |= TCR_IPS_1TB;
715 } else if (MaxAddress < SIZE_4TB) {
716 TCR |= TCR_IPS_4TB;
717 } else if (MaxAddress < SIZE_16TB) {
718 TCR |= TCR_IPS_16TB;
719 } else if (MaxAddress < SIZE_256TB) {
720 TCR |= TCR_IPS_256TB;
721 } else if ((MaxAddress < SIZE_4PB) && ArmHas52BitTgran4 ()) {
722 TCR |= TCR_IPS_4PB | TCR_DS;
723 } else {
724 DEBUG ((
725 DEBUG_ERROR,
726 "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n",
727 MaxAddress
728 ));
729 ASSERT (0); // Bigger than 48/52-bit memory space are not supported
730 return EFI_UNSUPPORTED;
731 }
732 }
733
734 //
735 // Translation table walks are always cache coherent on ARMv8-A, so cache
736 // maintenance on page tables is never needed. Since there is a risk of
737 // loss of coherency when using mismatched attributes, and given that memory
738 // is mapped cacheable except for extraordinary cases (such as non-coherent
739 // DMA), have the page table walker perform cached accesses as well, and
740 // assert below that matches the attributes we use for CPU accesses to
741 // the region.
742 //
743 TCR |= TCR_SH_INNER_SHAREABLE |
744 TCR_RGN_OUTER_WRITE_BACK_ALLOC |
745 TCR_RGN_INNER_WRITE_BACK_ALLOC;
746
747 // Set TCR
748 ArmSetTCR (TCR);
749
750 // Allocate pages for translation table
751 TranslationTable = AllocatePages (1);
752 if (TranslationTable == NULL) {
753 return EFI_OUT_OF_RESOURCES;
754 }
755
756 if (TranslationTableBase != NULL) {
757 *TranslationTableBase = TranslationTable;
758 }
759
760 if (TranslationTableSize != NULL) {
761 *TranslationTableSize = RootTableEntryCount * sizeof (UINT64);
762 }
763
764 if (!ArmMmuEnabled ()) {
765 //
766 // Make sure we are not inadvertently hitting in the caches
767 // when populating the page tables.
768 //
770 TranslationTable,
771 RootTableEntryCount * sizeof (UINT64)
772 );
773 }
774
775 ZeroMem (TranslationTable, RootTableEntryCount * sizeof (UINT64));
776
777 while (MemoryTable->Length != 0) {
778 Status = FillTranslationTable (TranslationTable, MemoryTable, ArmLpa2Enabled ());
779 if (EFI_ERROR (Status)) {
780 goto FreeTranslationTable;
781 }
782
783 MemoryTable++;
784 }
785
786 //
787 // EFI_MEMORY_UC ==> MAIR_ATTR_DEVICE_MEMORY
788 // EFI_MEMORY_WC ==> MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE
789 // EFI_MEMORY_WT ==> MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH
790 // EFI_MEMORY_WB ==> MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK
791 //
792 ArmSetMAIR (
793 MAIR_ATTR (TT_ATTR_INDX_DEVICE_MEMORY, MAIR_ATTR_DEVICE_MEMORY) |
794 MAIR_ATTR (TT_ATTR_INDX_MEMORY_NON_CACHEABLE, MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE) |
795 MAIR_ATTR (TT_ATTR_INDX_MEMORY_WRITE_THROUGH, MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH) |
796 MAIR_ATTR (TT_ATTR_INDX_MEMORY_WRITE_BACK, MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK)
797 );
798
799 if ((TCR & TCR_IPS_MASK) == TCR_IPS_4PB) {
800 ArmSetTTBR0 (
801 (VOID *)
802 (((UINTN)TranslationTable & 0xffffffffffc0) |
803 (((UINTN)TranslationTable >> 48) << 2))
804 );
805 } else {
806 ArmSetTTBR0 (TranslationTable);
807 }
808
809 if (!ArmMmuEnabled ()) {
810 ArmDisableAlignmentCheck ();
811 ArmEnableStackAlignmentCheck ();
812 ArmEnableInstructionCache ();
813 ArmEnableDataCache ();
814
815 ArmEnableMmu ();
816 }
817
818 return EFI_SUCCESS;
819
820FreeTranslationTable:
821 FreePages (TranslationTable, 1);
822 return Status;
823}
824
832BOOLEAN
834 VOID
835 )
836{
837 UINT64 TCR;
838
839 TCR = ArmGetTCR ();
840
841 return !TranslationRegimeIsDual () ?
842 ((TCR & TCR_DS_NVHE) != 0) :
843 ((TCR & TCR_DS) != 0);
844}
845
846RETURN_STATUS
847EFIAPI
848ArmMmuBaseLibConstructor (
849 VOID
850 )
851{
852 extern UINT32 ArmReplaceLiveTranslationEntrySize;
853 VOID *Hob;
854
855 Hob = GetFirstGuidHob (&gArmMmuReplaceLiveTranslationEntryFuncGuid);
856 if (Hob != NULL) {
857 mReplaceLiveEntryFunc = *(ARM_REPLACE_LIVE_TRANSLATION_ENTRY *)GET_GUID_HOB_DATA (Hob);
858 } else {
859 //
860 // The ArmReplaceLiveTranslationEntry () helper function may be invoked
861 // with the MMU off so we have to ensure that it gets cleaned to the PoC
862 //
864 (VOID *)(UINTN)ArmReplaceLiveTranslationEntry,
865 ArmReplaceLiveTranslationEntrySize
866 );
867 }
868
869 return RETURN_SUCCESS;
870}
UINT64 UINTN
INT64 INTN
BOOLEAN EFIAPI ArmHas52BitTgran4(VOID)
Definition: AArch64Lib.c:122
VOID *EFIAPI WriteBackDataCacheRange(IN VOID *Address, IN UINTN Length)
VOID *EFIAPI InvalidateDataCacheRange(IN VOID *Address, IN UINTN Length)
BOOLEAN ArmLpa2Enabled(VOID)
EFI_STATUS ArmSetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes, IN UINT64 AttributeMask)
STATIC BOOLEAN TranslationRegimeIsDual(VOID)
Definition: ArmMmuLibCore.c:33
VOID *EFIAPI GetFirstGuidHob(IN CONST EFI_GUID *Guid)
Definition: HobLib.c:215
UINT64 EFIAPI LShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: LShiftU64.c:28
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID EFIAPI FreePages(IN VOID *Buffer, IN UINTN Pages)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define VOID
Definition: Base.h:269
#define RETURN_SUCCESS
Definition: Base.h:1066
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define DEBUG(Expression)
Definition: DebugLib.h:435
VOID *EFIAPI AllocatePages(IN UINTN Pages)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112