2 * File managing the MMU for ARMv7 architecture
4 * Copyright (c) 2011-2016, ARM Limited. All rights reserved.
6 * This program and the accompanying materials
7 * are licensed and made available under the terms and conditions of the BSD License
8 * which accompanies this distribution. The full text of the license may be found at
9 * http://opensource.org/licenses/bsd-license.php
11 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Chipset/ArmV7.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/CacheMaintenanceLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/ArmLib.h>
22 #include <Library/BaseLib.h>
23 #include <Library/DebugLib.h>
24 #include <Library/PcdLib.h>
26 #define ID_MMFR0_SHARELVL_SHIFT 12
27 #define ID_MMFR0_SHARELVL_MASK 0xf
28 #define ID_MMFR0_SHARELVL_ONE 0
29 #define ID_MMFR0_SHARELVL_TWO 1
31 #define ID_MMFR0_INNERSHR_SHIFT 28
32 #define ID_MMFR0_INNERSHR_MASK 0xf
33 #define ID_MMFR0_OUTERSHR_SHIFT 8
34 #define ID_MMFR0_OUTERSHR_MASK 0xf
36 #define ID_MMFR0_SHR_IMP_UNCACHED 0
37 #define ID_MMFR0_SHR_IMP_HW_COHERENT 1
38 #define ID_MMFR0_SHR_IGNORED 0xf
40 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
60 ConvertSectionAttributesToPageAttributes (
61 IN UINT32 SectionAttributes
,
62 IN BOOLEAN IsLargePage
65 UINT32 PageAttributes
;
68 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes
, IsLargePage
);
69 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes
);
70 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes
, IsLargePage
);
71 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes
);
72 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes
);
74 return PageAttributes
;
79 PreferNonshareableMemory (
86 if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride
)) {
91 // Check whether the innermost level of shareability (the level we will use
92 // by default to map normal memory) is implemented with hardware coherency
93 // support. Otherwise, revert to mapping as non-shareable.
95 Mmfr
= ArmReadIdMmfr0 ();
96 switch ((Mmfr
>> ID_MMFR0_SHARELVL_SHIFT
) & ID_MMFR0_SHARELVL_MASK
) {
97 case ID_MMFR0_SHARELVL_ONE
:
98 // one level of shareability
99 Val
= (Mmfr
>> ID_MMFR0_OUTERSHR_SHIFT
) & ID_MMFR0_OUTERSHR_MASK
;
101 case ID_MMFR0_SHARELVL_TWO
:
102 // two levels of shareability
103 Val
= (Mmfr
>> ID_MMFR0_INNERSHR_SHIFT
) & ID_MMFR0_INNERSHR_MASK
;
106 // unexpected value -> shareable is the safe option
110 return Val
!= ID_MMFR0_SHR_IMP_HW_COHERENT
;
115 PopulateLevel2PageTable (
116 IN UINT32
*SectionEntry
,
117 IN UINT32 PhysicalBase
,
118 IN UINT32 RemainLength
,
119 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
125 UINT32 PageAttributes
;
126 UINT32 SectionDescriptor
;
127 UINT32 TranslationTable
;
128 UINT32 BaseSectionAddress
;
130 switch (Attributes
) {
131 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
132 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
133 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_BACK
;
135 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
136 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
137 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_THROUGH
;
139 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
140 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
141 PageAttributes
= TT_DESCRIPTOR_PAGE_DEVICE
;
143 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
144 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
145 PageAttributes
= TT_DESCRIPTOR_PAGE_UNCACHED
;
148 PageAttributes
= TT_DESCRIPTOR_PAGE_UNCACHED
;
152 if (PreferNonshareableMemory ()) {
153 PageAttributes
&= ~TT_DESCRIPTOR_PAGE_S_SHARED
;
156 // Check if the Section Entry has already been populated. Otherwise attach a
157 // Level 2 Translation Table to it
158 if (*SectionEntry
!= 0) {
159 // The entry must be a page table. Otherwise it exists an overlapping in the memory map
160 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry
)) {
161 TranslationTable
= *SectionEntry
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
;
162 } else if ((*SectionEntry
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
163 // Case where a virtual memory map descriptor overlapped a section entry
165 // Allocate a Level2 Page Table for this Section
166 TranslationTable
= (UINTN
)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE
+ TRANSLATION_TABLE_PAGE_ALIGNMENT
));
167 TranslationTable
= ((UINTN
)TranslationTable
+ TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
;
169 // Translate the Section Descriptor into Page Descriptor
170 SectionDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (*SectionEntry
, FALSE
);
172 BaseSectionAddress
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry
);
174 // Populate the new Level2 Page Table for the section
175 PageEntry
= (UINT32
*)TranslationTable
;
176 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
177 PageEntry
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress
+ (Index
<< 12)) | SectionDescriptor
;
180 // Overwrite the section entry to point to the new Level2 Translation Table
181 *SectionEntry
= (TranslationTable
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) |
182 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes
) ? (1 << 3) : 0) |
183 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
185 // We do not support the other section type (16MB Section)
190 TranslationTable
= (UINTN
)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE
+ TRANSLATION_TABLE_PAGE_ALIGNMENT
));
191 TranslationTable
= ((UINTN
)TranslationTable
+ TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
;
193 ZeroMem ((VOID
*)TranslationTable
, TRANSLATION_TABLE_PAGE_SIZE
);
195 *SectionEntry
= (TranslationTable
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) |
196 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes
) ? (1 << 3) : 0) |
197 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
200 PageEntry
= ((UINT32
*)(TranslationTable
) + ((PhysicalBase
& TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
201 Pages
= RemainLength
/ TT_DESCRIPTOR_PAGE_SIZE
;
203 for (Index
= 0; Index
< Pages
; Index
++) {
204 *PageEntry
++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase
) | PageAttributes
;
205 PhysicalBase
+= TT_DESCRIPTOR_PAGE_SIZE
;
212 FillTranslationTable (
213 IN UINT32
*TranslationTable
,
214 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryRegion
217 UINT32
*SectionEntry
;
222 ASSERT(MemoryRegion
->Length
> 0);
224 if (MemoryRegion
->PhysicalBase
>= SIZE_4GB
) {
228 PhysicalBase
= MemoryRegion
->PhysicalBase
;
229 RemainLength
= MIN(MemoryRegion
->Length
, SIZE_4GB
- PhysicalBase
);
231 switch (MemoryRegion
->Attributes
) {
232 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
233 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
235 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
236 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
238 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
239 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(0);
241 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
242 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
244 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
245 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
247 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
248 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
250 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
251 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(1);
253 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
254 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(1);
257 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
261 if (PreferNonshareableMemory ()) {
262 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
265 // Get the first section entry for this mapping
266 SectionEntry
= TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable
, MemoryRegion
->VirtualBase
);
268 while (RemainLength
!= 0) {
269 if (PhysicalBase
% TT_DESCRIPTOR_SECTION_SIZE
== 0) {
270 if (RemainLength
>= TT_DESCRIPTOR_SECTION_SIZE
) {
271 // Case: Physical address aligned on the Section Size (1MB) && the length is greater than the Section Size
272 *SectionEntry
++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase
) | Attributes
;
273 PhysicalBase
+= TT_DESCRIPTOR_SECTION_SIZE
;
275 // Case: Physical address aligned on the Section Size (1MB) && the length does not fill a section
276 PopulateLevel2PageTable (SectionEntry
++, PhysicalBase
, RemainLength
, MemoryRegion
->Attributes
);
278 // It must be the last entry
282 // Case: Physical address NOT aligned on the Section Size (1MB)
283 PopulateLevel2PageTable (SectionEntry
++, PhysicalBase
, RemainLength
, MemoryRegion
->Attributes
);
284 // Aligned the address
285 PhysicalBase
= (PhysicalBase
+ TT_DESCRIPTOR_SECTION_SIZE
) & ~(TT_DESCRIPTOR_SECTION_SIZE
-1);
287 // If it is the last entry
288 if (RemainLength
< TT_DESCRIPTOR_SECTION_SIZE
) {
292 RemainLength
-= TT_DESCRIPTOR_SECTION_SIZE
;
299 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryTable
,
300 OUT VOID
**TranslationTableBase OPTIONAL
,
301 OUT UINTN
*TranslationTableSize OPTIONAL
304 VOID
* TranslationTable
;
305 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute
;
306 UINT32 TTBRAttributes
;
308 // Allocate pages for translation table.
309 TranslationTable
= AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE
+ TRANSLATION_TABLE_SECTION_ALIGNMENT
));
310 if (TranslationTable
== NULL
) {
311 return RETURN_OUT_OF_RESOURCES
;
313 TranslationTable
= (VOID
*)(((UINTN
)TranslationTable
+ TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
);
315 if (TranslationTableBase
!= NULL
) {
316 *TranslationTableBase
= TranslationTable
;
319 if (TranslationTableSize
!= NULL
) {
320 *TranslationTableSize
= TRANSLATION_TABLE_SECTION_SIZE
;
323 ZeroMem (TranslationTable
, TRANSLATION_TABLE_SECTION_SIZE
);
325 // By default, mark the translation table as belonging to a uncached region
326 TranslationTableAttribute
= ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
;
327 while (MemoryTable
->Length
!= 0) {
328 // Find the memory attribute for the Translation Table
329 if (((UINTN
)TranslationTable
>= MemoryTable
->PhysicalBase
) && ((UINTN
)TranslationTable
<= MemoryTable
->PhysicalBase
- 1 + MemoryTable
->Length
)) {
330 TranslationTableAttribute
= MemoryTable
->Attributes
;
333 FillTranslationTable (TranslationTable
, MemoryTable
);
337 // Translate the Memory Attributes into Translation Table Register Attributes
338 if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
) ||
339 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
)) {
340 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_NON_CACHEABLE
: TTBR_NON_CACHEABLE
;
341 } else if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
) ||
342 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
)) {
343 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC
: TTBR_WRITE_BACK_ALLOC
;
344 } else if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
) ||
345 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
)) {
346 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_WRITE_THROUGH
: TTBR_WRITE_THROUGH
;
348 ASSERT (0); // No support has been found for the attributes of the memory region that the translation table belongs to.
349 return RETURN_UNSUPPORTED
;
352 if (TTBRAttributes
& TTBR_SHAREABLE
) {
353 if (PreferNonshareableMemory ()) {
354 TTBRAttributes
^= TTBR_SHAREABLE
;
357 // Unlike the S bit in the short descriptors, which implies inner shareable
358 // on an implementation that supports two levels, the meaning of the S bit
359 // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
360 // However, we should only set this bit after we have confirmed that the
361 // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
363 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
364 TTBRAttributes
|= TTBR_NOT_OUTER_SHAREABLE
;
369 ArmCleanInvalidateDataCache ();
370 ArmInvalidateInstructionCache ();
372 ArmDisableDataCache ();
373 ArmDisableInstructionCache();
374 // TLBs are also invalidated when calling ArmDisableMmu()
377 // Make sure nothing sneaked into the cache
378 ArmCleanInvalidateDataCache ();
379 ArmInvalidateInstructionCache ();
381 ArmSetTTBR0 ((VOID
*)(UINTN
)(((UINTN
)TranslationTable
& ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
) | (TTBRAttributes
& 0x7F)));
384 // The TTBCR register value is undefined at reset in the Non-Secure world.
385 // Writing 0 has the effect of:
386 // Clearing EAE: Use short descriptors, as mandated by specification.
387 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
388 // Clearing N: Perform all translation table walks through TTBR0.
389 // (0 is the default reset value in systems not implementing
390 // the Security Extensions.)
394 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
395 DOMAIN_ACCESS_CONTROL_NONE(14) |
396 DOMAIN_ACCESS_CONTROL_NONE(13) |
397 DOMAIN_ACCESS_CONTROL_NONE(12) |
398 DOMAIN_ACCESS_CONTROL_NONE(11) |
399 DOMAIN_ACCESS_CONTROL_NONE(10) |
400 DOMAIN_ACCESS_CONTROL_NONE( 9) |
401 DOMAIN_ACCESS_CONTROL_NONE( 8) |
402 DOMAIN_ACCESS_CONTROL_NONE( 7) |
403 DOMAIN_ACCESS_CONTROL_NONE( 6) |
404 DOMAIN_ACCESS_CONTROL_NONE( 5) |
405 DOMAIN_ACCESS_CONTROL_NONE( 4) |
406 DOMAIN_ACCESS_CONTROL_NONE( 3) |
407 DOMAIN_ACCESS_CONTROL_NONE( 2) |
408 DOMAIN_ACCESS_CONTROL_NONE( 1) |
409 DOMAIN_ACCESS_CONTROL_CLIENT(0));
411 ArmEnableInstructionCache();
412 ArmEnableDataCache();
414 return RETURN_SUCCESS
;
419 ConvertSectionToPages (
420 IN EFI_PHYSICAL_ADDRESS BaseAddress
423 UINT32 FirstLevelIdx
;
424 UINT32 SectionDescriptor
;
425 UINT32 PageTableDescriptor
;
426 UINT32 PageDescriptor
;
429 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
430 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
432 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
434 // Obtain page table base
435 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
437 // Calculate index into first level translation table for start of modification
438 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
439 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
441 // Get section attributes and convert to page attributes
442 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
443 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
445 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
446 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)AllocatePages (1);
447 if (PageTable
== NULL
) {
448 return EFI_OUT_OF_RESOURCES
;
451 // Write the page table entries out
452 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
453 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
456 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
457 WriteBackInvalidateDataCacheRange ((VOID
*)PageTable
, TT_DESCRIPTOR_PAGE_SIZE
);
459 // Formulate page table entry, Domain=0, NS=0
460 PageTableDescriptor
= (((UINTN
)PageTable
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
462 // Write the page table entry out, replacing section entry
463 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
471 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
473 IN UINT64 Attributes
,
474 OUT BOOLEAN
*FlushTlbs OPTIONAL
480 UINT32 FirstLevelIdx
;
482 UINT32 NumPageEntries
;
485 UINT32 PageTableIndex
;
486 UINT32 PageTableEntry
;
487 UINT32 CurrentPageTableEntry
;
490 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
491 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
493 Status
= EFI_SUCCESS
;
495 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
496 // EntryValue: values at bit positions specified by EntryMask
497 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
;
498 if (Attributes
& EFI_MEMORY_XP
) {
499 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN
;
501 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
504 // Although the PI spec is unclear on this, the GCD guarantees that only
505 // one Attribute bit is set at a time, so the order of the conditionals below
506 // is irrelevant. If no memory attribute is specified, we preserve whatever
507 // memory type is set in the page tables, and update the permission attributes
509 if (Attributes
& EFI_MEMORY_UC
) {
510 // modify cacheability attributes
511 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
512 // map to strongly ordered
513 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
514 } else if (Attributes
& EFI_MEMORY_WC
) {
515 // modify cacheability attributes
516 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
517 // map to normal non-cachable
518 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
519 } else if (Attributes
& EFI_MEMORY_WT
) {
520 // modify cacheability attributes
521 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
522 // write through with no-allocate
523 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
524 } else if (Attributes
& EFI_MEMORY_WB
) {
525 // modify cacheability attributes
526 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
527 // write back (with allocate)
528 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
529 } else if (Attributes
& CACHE_ATTRIBUTE_MASK
) {
530 // catch unsupported memory type attributes
532 return EFI_UNSUPPORTED
;
535 if (Attributes
& EFI_MEMORY_RO
) {
536 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RO_RO
;
538 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RW_RW
;
541 // Obtain page table base
542 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
544 // Calculate number of 4KB page table entries to change
545 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
547 // Iterate for the number of 4KB pages to change
549 for(p
= 0; p
< NumPageEntries
; p
++) {
550 // Calculate index into first level translation table for page table value
552 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
553 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
555 // Read the descriptor from the first level page table
556 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
558 // Does this descriptor need to be converted from section entry to 4K pages?
559 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
560 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
561 if (EFI_ERROR(Status
)) {
566 // Re-read descriptor
567 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
568 if (FlushTlbs
!= NULL
) {
573 // Obtain page table base address
574 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
576 // Calculate index into the page table
577 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
578 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
581 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
583 // Mask off appropriate fields
584 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
586 // Mask in new attributes and/or permissions
587 PageTableEntry
|= EntryValue
;
589 if (CurrentPageTableEntry
!= PageTableEntry
) {
590 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
592 // Clean/invalidate the cache for this page, but only
593 // if we are modifying the memory type attributes
594 if (((CurrentPageTableEntry
^ PageTableEntry
) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) != 0) {
595 WriteBackInvalidateDataCacheRange (Mva
, TT_DESCRIPTOR_PAGE_SIZE
);
598 // Only need to update if we are changing the entry
599 PageTable
[PageTableIndex
] = PageTableEntry
;
600 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
603 Status
= EFI_SUCCESS
;
604 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
606 } // End first level translation table loop
613 UpdateSectionEntries (
614 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
619 EFI_STATUS Status
= EFI_SUCCESS
;
622 UINT32 FirstLevelIdx
;
625 UINT32 CurrentDescriptor
;
628 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
630 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
631 // EntryValue: values at bit positions specified by EntryMask
633 // Make sure we handle a section range that is unmapped
634 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
| TT_DESCRIPTOR_SECTION_XN_MASK
|
635 TT_DESCRIPTOR_SECTION_AP_MASK
;
636 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
638 // Although the PI spec is unclear on this, the GCD guarantees that only
639 // one Attribute bit is set at a time, so the order of the conditionals below
640 // is irrelevant. If no memory attribute is specified, we preserve whatever
641 // memory type is set in the page tables, and update the permission attributes
643 if (Attributes
& EFI_MEMORY_UC
) {
644 // modify cacheability attributes
645 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
646 // map to strongly ordered
647 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
648 } else if (Attributes
& EFI_MEMORY_WC
) {
649 // modify cacheability attributes
650 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
651 // map to normal non-cachable
652 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
653 } else if (Attributes
& EFI_MEMORY_WT
) {
654 // modify cacheability attributes
655 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
656 // write through with no-allocate
657 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
658 } else if (Attributes
& EFI_MEMORY_WB
) {
659 // modify cacheability attributes
660 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
661 // write back (with allocate)
662 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
663 } else if (Attributes
& CACHE_ATTRIBUTE_MASK
) {
664 // catch unsupported memory type attributes
666 return EFI_UNSUPPORTED
;
669 if (Attributes
& EFI_MEMORY_RO
) {
670 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
672 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
675 if (Attributes
& EFI_MEMORY_XP
) {
676 EntryValue
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
679 // obtain page table base
680 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
682 // calculate index into first level translation table for start of modification
683 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
684 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
686 // calculate number of 1MB first level entries this applies to
687 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
689 // iterate through each descriptor
690 for(i
=0; i
<NumSections
; i
++) {
691 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
693 // has this descriptor already been coverted to pages?
694 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
695 // forward this 1MB range to page table function instead
696 Status
= UpdatePageEntries (
697 (FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
,
698 TT_DESCRIPTOR_SECTION_SIZE
,
702 // still a section entry
704 // mask off appropriate fields
705 Descriptor
= CurrentDescriptor
& ~EntryMask
;
707 // mask in new attributes and/or permissions
708 Descriptor
|= EntryValue
;
710 if (CurrentDescriptor
!= Descriptor
) {
711 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
713 // Clean/invalidate the cache for this section, but only
714 // if we are modifying the memory type attributes
715 if (((CurrentDescriptor
^ Descriptor
) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) != 0) {
716 WriteBackInvalidateDataCacheRange (Mva
, SIZE_1MB
);
719 // Only need to update if we are changing the descriptor
720 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
721 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
724 Status
= EFI_SUCCESS
;
732 ArmSetMemoryAttributes (
733 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
748 if ((BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
== 0) &&
749 Length
>= TT_DESCRIPTOR_SECTION_SIZE
) {
751 ChunkLength
= Length
- Length
% TT_DESCRIPTOR_SECTION_SIZE
;
754 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
755 BaseAddress
, ChunkLength
, Attributes
));
757 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
);
763 // Process page by page until the next section boundary, but only if
764 // we have more than a section's worth of area to deal with after that.
766 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
767 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
768 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
769 ChunkLength
= Length
;
773 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
774 BaseAddress
, ChunkLength
, Attributes
));
776 Status
= UpdatePageEntries (BaseAddress
, ChunkLength
, Attributes
,
780 if (EFI_ERROR (Status
)) {
784 BaseAddress
+= ChunkLength
;
785 Length
-= ChunkLength
;
795 ArmSetMemoryRegionNoExec (
796 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
800 return EFI_UNSUPPORTED
;
804 ArmClearMemoryRegionNoExec (
805 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
809 return EFI_UNSUPPORTED
;
813 ArmSetMemoryRegionReadOnly (
814 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
818 return EFI_UNSUPPORTED
;
822 ArmClearMemoryRegionReadOnly (
823 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
827 return EFI_UNSUPPORTED
;
832 ArmMmuBaseLibConstructor (
836 return RETURN_SUCCESS
;