2 * File managing the MMU for ARMv7 architecture
4 * Copyright (c) 2011-2016, ARM Limited. All rights reserved.
6 * SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Chipset/ArmV7.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/CacheMaintenanceLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/ArmLib.h>
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/PcdLib.h>
20 #define ID_MMFR0_SHARELVL_SHIFT 12
21 #define ID_MMFR0_SHARELVL_MASK 0xf
22 #define ID_MMFR0_SHARELVL_ONE 0
23 #define ID_MMFR0_SHARELVL_TWO 1
25 #define ID_MMFR0_INNERSHR_SHIFT 28
26 #define ID_MMFR0_INNERSHR_MASK 0xf
27 #define ID_MMFR0_OUTERSHR_SHIFT 8
28 #define ID_MMFR0_OUTERSHR_MASK 0xf
30 #define ID_MMFR0_SHR_IMP_UNCACHED 0
31 #define ID_MMFR0_SHR_IMP_HW_COHERENT 1
32 #define ID_MMFR0_SHR_IGNORED 0xf
34 #define __EFI_MEMORY_RWX 0 // no restrictions
36 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
56 ConvertSectionAttributesToPageAttributes (
57 IN UINT32 SectionAttributes
,
58 IN BOOLEAN IsLargePage
61 UINT32 PageAttributes
;
64 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes
, IsLargePage
);
65 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes
);
66 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes
, IsLargePage
);
67 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes
);
68 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes
);
70 return PageAttributes
;
75 PreferNonshareableMemory (
82 if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride
)) {
87 // Check whether the innermost level of shareability (the level we will use
88 // by default to map normal memory) is implemented with hardware coherency
89 // support. Otherwise, revert to mapping as non-shareable.
91 Mmfr
= ArmReadIdMmfr0 ();
92 switch ((Mmfr
>> ID_MMFR0_SHARELVL_SHIFT
) & ID_MMFR0_SHARELVL_MASK
) {
93 case ID_MMFR0_SHARELVL_ONE
:
94 // one level of shareability
95 Val
= (Mmfr
>> ID_MMFR0_OUTERSHR_SHIFT
) & ID_MMFR0_OUTERSHR_MASK
;
97 case ID_MMFR0_SHARELVL_TWO
:
98 // two levels of shareability
99 Val
= (Mmfr
>> ID_MMFR0_INNERSHR_SHIFT
) & ID_MMFR0_INNERSHR_MASK
;
102 // unexpected value -> shareable is the safe option
106 return Val
!= ID_MMFR0_SHR_IMP_HW_COHERENT
;
111 PopulateLevel2PageTable (
112 IN UINT32
*SectionEntry
,
113 IN UINT32 PhysicalBase
,
114 IN UINT32 RemainLength
,
115 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
121 UINT32 PageAttributes
;
122 UINT32 SectionDescriptor
;
123 UINT32 TranslationTable
;
124 UINT32 BaseSectionAddress
;
125 UINT32 FirstPageOffset
;
127 switch (Attributes
) {
128 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
129 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
130 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_BACK
;
132 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE
:
133 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE
:
134 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_BACK
;
135 PageAttributes
&= ~TT_DESCRIPTOR_PAGE_S_SHARED
;
137 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
138 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
139 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_THROUGH
;
141 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
142 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
143 PageAttributes
= TT_DESCRIPTOR_PAGE_DEVICE
;
145 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
146 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
147 PageAttributes
= TT_DESCRIPTOR_PAGE_UNCACHED
;
150 PageAttributes
= TT_DESCRIPTOR_PAGE_UNCACHED
;
154 if (PreferNonshareableMemory ()) {
155 PageAttributes
&= ~TT_DESCRIPTOR_PAGE_S_SHARED
;
158 // Check if the Section Entry has already been populated. Otherwise attach a
159 // Level 2 Translation Table to it
160 if (*SectionEntry
!= 0) {
161 // The entry must be a page table. Otherwise it exists an overlapping in the memory map
162 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry
)) {
163 TranslationTable
= *SectionEntry
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
;
164 } else if ((*SectionEntry
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
165 // Case where a virtual memory map descriptor overlapped a section entry
167 // Allocate a Level2 Page Table for this Section
168 TranslationTable
= (UINTN
)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE
+ TRANSLATION_TABLE_PAGE_ALIGNMENT
));
169 TranslationTable
= ((UINTN
)TranslationTable
+ TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
;
171 // Translate the Section Descriptor into Page Descriptor
172 SectionDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (*SectionEntry
, FALSE
);
174 BaseSectionAddress
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry
);
176 // Populate the new Level2 Page Table for the section
177 PageEntry
= (UINT32
*)TranslationTable
;
178 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
179 PageEntry
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress
+ (Index
<< 12)) | SectionDescriptor
;
182 // Overwrite the section entry to point to the new Level2 Translation Table
183 *SectionEntry
= (TranslationTable
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) |
184 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes
) ? (1 << 3) : 0) |
185 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
187 // We do not support the other section type (16MB Section)
192 TranslationTable
= (UINTN
)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE
+ TRANSLATION_TABLE_PAGE_ALIGNMENT
));
193 TranslationTable
= ((UINTN
)TranslationTable
+ TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK
;
195 ZeroMem ((VOID
*)TranslationTable
, TRANSLATION_TABLE_PAGE_SIZE
);
197 *SectionEntry
= (TranslationTable
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) |
198 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes
) ? (1 << 3) : 0) |
199 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
202 FirstPageOffset
= (PhysicalBase
& TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
203 PageEntry
= (UINT32
*)TranslationTable
+ FirstPageOffset
;
204 Pages
= RemainLength
/ TT_DESCRIPTOR_PAGE_SIZE
;
206 ASSERT (FirstPageOffset
+ Pages
<= TRANSLATION_TABLE_PAGE_COUNT
);
208 for (Index
= 0; Index
< Pages
; Index
++) {
209 *PageEntry
++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase
) | PageAttributes
;
210 PhysicalBase
+= TT_DESCRIPTOR_PAGE_SIZE
;
217 FillTranslationTable (
218 IN UINT32
*TranslationTable
,
219 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryRegion
222 UINT32
*SectionEntry
;
226 UINT32 PageMapLength
;
228 ASSERT(MemoryRegion
->Length
> 0);
230 if (MemoryRegion
->PhysicalBase
>= SIZE_4GB
) {
234 PhysicalBase
= MemoryRegion
->PhysicalBase
;
235 RemainLength
= MIN(MemoryRegion
->Length
, SIZE_4GB
- PhysicalBase
);
237 switch (MemoryRegion
->Attributes
) {
238 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
239 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
241 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE
:
242 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
243 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
245 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
246 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
248 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
249 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(0);
251 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
252 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
254 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
255 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
257 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE
:
258 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
259 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
261 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
262 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
264 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
265 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(1);
267 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
268 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(1);
271 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
275 if (PreferNonshareableMemory ()) {
276 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
279 // Get the first section entry for this mapping
280 SectionEntry
= TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable
, MemoryRegion
->VirtualBase
);
282 while (RemainLength
!= 0) {
283 if (PhysicalBase
% TT_DESCRIPTOR_SECTION_SIZE
== 0 &&
284 RemainLength
>= TT_DESCRIPTOR_SECTION_SIZE
) {
285 // Case: Physical address aligned on the Section Size (1MB) && the length
286 // is greater than the Section Size
287 *SectionEntry
++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase
) | Attributes
;
288 PhysicalBase
+= TT_DESCRIPTOR_SECTION_SIZE
;
289 RemainLength
-= TT_DESCRIPTOR_SECTION_SIZE
;
291 PageMapLength
= MIN (RemainLength
, TT_DESCRIPTOR_SECTION_SIZE
-
292 (PhysicalBase
% TT_DESCRIPTOR_SECTION_SIZE
));
294 // Case: Physical address aligned on the Section Size (1MB) && the length
295 // does not fill a section
296 // Case: Physical address NOT aligned on the Section Size (1MB)
297 PopulateLevel2PageTable (SectionEntry
++, PhysicalBase
, PageMapLength
,
298 MemoryRegion
->Attributes
);
300 // If it is the last entry
301 if (RemainLength
< TT_DESCRIPTOR_SECTION_SIZE
) {
305 PhysicalBase
+= PageMapLength
;
306 RemainLength
-= PageMapLength
;
314 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryTable
,
315 OUT VOID
**TranslationTableBase OPTIONAL
,
316 OUT UINTN
*TranslationTableSize OPTIONAL
319 VOID
* TranslationTable
;
320 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute
;
321 UINT32 TTBRAttributes
;
323 // Allocate pages for translation table.
324 TranslationTable
= AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE
+ TRANSLATION_TABLE_SECTION_ALIGNMENT
));
325 if (TranslationTable
== NULL
) {
326 return RETURN_OUT_OF_RESOURCES
;
328 TranslationTable
= (VOID
*)(((UINTN
)TranslationTable
+ TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
);
330 if (TranslationTableBase
!= NULL
) {
331 *TranslationTableBase
= TranslationTable
;
334 if (TranslationTableSize
!= NULL
) {
335 *TranslationTableSize
= TRANSLATION_TABLE_SECTION_SIZE
;
338 ZeroMem (TranslationTable
, TRANSLATION_TABLE_SECTION_SIZE
);
340 // By default, mark the translation table as belonging to a uncached region
341 TranslationTableAttribute
= ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
;
342 while (MemoryTable
->Length
!= 0) {
343 // Find the memory attribute for the Translation Table
344 if (((UINTN
)TranslationTable
>= MemoryTable
->PhysicalBase
) && ((UINTN
)TranslationTable
<= MemoryTable
->PhysicalBase
- 1 + MemoryTable
->Length
)) {
345 TranslationTableAttribute
= MemoryTable
->Attributes
;
348 FillTranslationTable (TranslationTable
, MemoryTable
);
352 // Translate the Memory Attributes into Translation Table Register Attributes
353 if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
) ||
354 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
)) {
355 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC
: TTBR_WRITE_BACK_ALLOC
;
357 // Page tables must reside in memory mapped as write-back cacheable
359 return RETURN_UNSUPPORTED
;
362 if (TTBRAttributes
& TTBR_SHAREABLE
) {
363 if (PreferNonshareableMemory ()) {
364 TTBRAttributes
^= TTBR_SHAREABLE
;
367 // Unlike the S bit in the short descriptors, which implies inner shareable
368 // on an implementation that supports two levels, the meaning of the S bit
369 // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
370 // However, we should only set this bit after we have confirmed that the
371 // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
373 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
374 TTBRAttributes
|= TTBR_NOT_OUTER_SHAREABLE
;
379 ArmCleanInvalidateDataCache ();
380 ArmInvalidateInstructionCache ();
382 ArmDisableDataCache ();
383 ArmDisableInstructionCache();
384 // TLBs are also invalidated when calling ArmDisableMmu()
387 // Make sure nothing sneaked into the cache
388 ArmCleanInvalidateDataCache ();
389 ArmInvalidateInstructionCache ();
391 ArmSetTTBR0 ((VOID
*)(UINTN
)(((UINTN
)TranslationTable
& ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
) | (TTBRAttributes
& 0x7F)));
394 // The TTBCR register value is undefined at reset in the Non-Secure world.
395 // Writing 0 has the effect of:
396 // Clearing EAE: Use short descriptors, as mandated by specification.
397 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
398 // Clearing N: Perform all translation table walks through TTBR0.
399 // (0 is the default reset value in systems not implementing
400 // the Security Extensions.)
404 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
405 DOMAIN_ACCESS_CONTROL_NONE(14) |
406 DOMAIN_ACCESS_CONTROL_NONE(13) |
407 DOMAIN_ACCESS_CONTROL_NONE(12) |
408 DOMAIN_ACCESS_CONTROL_NONE(11) |
409 DOMAIN_ACCESS_CONTROL_NONE(10) |
410 DOMAIN_ACCESS_CONTROL_NONE( 9) |
411 DOMAIN_ACCESS_CONTROL_NONE( 8) |
412 DOMAIN_ACCESS_CONTROL_NONE( 7) |
413 DOMAIN_ACCESS_CONTROL_NONE( 6) |
414 DOMAIN_ACCESS_CONTROL_NONE( 5) |
415 DOMAIN_ACCESS_CONTROL_NONE( 4) |
416 DOMAIN_ACCESS_CONTROL_NONE( 3) |
417 DOMAIN_ACCESS_CONTROL_NONE( 2) |
418 DOMAIN_ACCESS_CONTROL_NONE( 1) |
419 DOMAIN_ACCESS_CONTROL_CLIENT(0));
421 ArmEnableInstructionCache();
422 ArmEnableDataCache();
424 return RETURN_SUCCESS
;
429 ConvertSectionToPages (
430 IN EFI_PHYSICAL_ADDRESS BaseAddress
433 UINT32 FirstLevelIdx
;
434 UINT32 SectionDescriptor
;
435 UINT32 PageTableDescriptor
;
436 UINT32 PageDescriptor
;
439 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
440 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
442 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
444 // Obtain page table base
445 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
447 // Calculate index into first level translation table for start of modification
448 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
449 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
451 // Get section attributes and convert to page attributes
452 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
453 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
455 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
456 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)AllocatePages (1);
457 if (PageTable
== NULL
) {
458 return EFI_OUT_OF_RESOURCES
;
461 // Write the page table entries out
462 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
463 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
466 // Formulate page table entry, Domain=0, NS=0
467 PageTableDescriptor
= (((UINTN
)PageTable
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
469 // Write the page table entry out, replacing section entry
470 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
478 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
480 IN UINT64 Attributes
,
481 OUT BOOLEAN
*FlushTlbs OPTIONAL
487 UINT32 FirstLevelIdx
;
489 UINT32 NumPageEntries
;
492 UINT32 PageTableIndex
;
493 UINT32 PageTableEntry
;
494 UINT32 CurrentPageTableEntry
;
497 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
498 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
500 Status
= EFI_SUCCESS
;
502 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
503 // EntryValue: values at bit positions specified by EntryMask
504 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
;
505 if (Attributes
& EFI_MEMORY_XP
) {
506 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN
;
508 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
511 // Although the PI spec is unclear on this, the GCD guarantees that only
512 // one Attribute bit is set at a time, so the order of the conditionals below
513 // is irrelevant. If no memory attribute is specified, we preserve whatever
514 // memory type is set in the page tables, and update the permission attributes
516 if (Attributes
& EFI_MEMORY_UC
) {
517 // modify cacheability attributes
518 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
519 // map to strongly ordered
520 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
521 } else if (Attributes
& EFI_MEMORY_WC
) {
522 // modify cacheability attributes
523 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
524 // map to normal non-cachable
525 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
526 } else if (Attributes
& EFI_MEMORY_WT
) {
527 // modify cacheability attributes
528 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
529 // write through with no-allocate
530 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
531 } else if (Attributes
& EFI_MEMORY_WB
) {
532 // modify cacheability attributes
533 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
534 // write back (with allocate)
535 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
536 } else if (Attributes
& CACHE_ATTRIBUTE_MASK
) {
537 // catch unsupported memory type attributes
539 return EFI_UNSUPPORTED
;
542 if (Attributes
& EFI_MEMORY_RO
) {
543 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RO_RO
;
545 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RW_RW
;
548 // Obtain page table base
549 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
551 // Calculate number of 4KB page table entries to change
552 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
554 // Iterate for the number of 4KB pages to change
556 for(p
= 0; p
< NumPageEntries
; p
++) {
557 // Calculate index into first level translation table for page table value
559 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
560 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
562 // Read the descriptor from the first level page table
563 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
565 // Does this descriptor need to be converted from section entry to 4K pages?
566 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
567 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
568 if (EFI_ERROR(Status
)) {
573 // Re-read descriptor
574 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
575 if (FlushTlbs
!= NULL
) {
580 // Obtain page table base address
581 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
583 // Calculate index into the page table
584 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
585 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
588 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
590 // Mask off appropriate fields
591 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
593 // Mask in new attributes and/or permissions
594 PageTableEntry
|= EntryValue
;
596 if (CurrentPageTableEntry
!= PageTableEntry
) {
597 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
599 // Only need to update if we are changing the entry
600 PageTable
[PageTableIndex
] = PageTableEntry
;
601 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
604 Status
= EFI_SUCCESS
;
605 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
607 } // End first level translation table loop
614 UpdateSectionEntries (
615 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
620 EFI_STATUS Status
= EFI_SUCCESS
;
623 UINT32 FirstLevelIdx
;
626 UINT32 CurrentDescriptor
;
629 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
631 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
632 // EntryValue: values at bit positions specified by EntryMask
634 // Make sure we handle a section range that is unmapped
635 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
| TT_DESCRIPTOR_SECTION_XN_MASK
|
636 TT_DESCRIPTOR_SECTION_AP_MASK
;
637 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
639 // Although the PI spec is unclear on this, the GCD guarantees that only
640 // one Attribute bit is set at a time, so the order of the conditionals below
641 // is irrelevant. If no memory attribute is specified, we preserve whatever
642 // memory type is set in the page tables, and update the permission attributes
644 if (Attributes
& EFI_MEMORY_UC
) {
645 // modify cacheability attributes
646 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
647 // map to strongly ordered
648 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
649 } else if (Attributes
& EFI_MEMORY_WC
) {
650 // modify cacheability attributes
651 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
652 // map to normal non-cachable
653 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
654 } else if (Attributes
& EFI_MEMORY_WT
) {
655 // modify cacheability attributes
656 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
657 // write through with no-allocate
658 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
659 } else if (Attributes
& EFI_MEMORY_WB
) {
660 // modify cacheability attributes
661 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
662 // write back (with allocate)
663 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
664 } else if (Attributes
& CACHE_ATTRIBUTE_MASK
) {
665 // catch unsupported memory type attributes
667 return EFI_UNSUPPORTED
;
670 if (Attributes
& EFI_MEMORY_RO
) {
671 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
673 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
676 if (Attributes
& EFI_MEMORY_XP
) {
677 EntryValue
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
680 // obtain page table base
681 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
683 // calculate index into first level translation table for start of modification
684 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
685 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
687 // calculate number of 1MB first level entries this applies to
688 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
690 // iterate through each descriptor
691 for(i
=0; i
<NumSections
; i
++) {
692 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
694 // has this descriptor already been converted to pages?
695 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
696 // forward this 1MB range to page table function instead
697 Status
= UpdatePageEntries (
698 (FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
,
699 TT_DESCRIPTOR_SECTION_SIZE
,
703 // still a section entry
705 if (CurrentDescriptor
!= 0) {
706 // mask off appropriate fields
707 Descriptor
= CurrentDescriptor
& ~EntryMask
;
709 Descriptor
= ((UINTN
)FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
712 // mask in new attributes and/or permissions
713 Descriptor
|= EntryValue
;
715 if (CurrentDescriptor
!= Descriptor
) {
716 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
718 // Only need to update if we are changing the descriptor
719 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
720 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
723 Status
= EFI_SUCCESS
;
731 ArmSetMemoryAttributes (
732 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
741 if (BaseAddress
> (UINT64
)MAX_ADDRESS
) {
742 return EFI_UNSUPPORTED
;
745 Length
= MIN (Length
, (UINT64
)MAX_ADDRESS
- BaseAddress
+ 1);
752 if ((BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
== 0) &&
753 Length
>= TT_DESCRIPTOR_SECTION_SIZE
) {
755 ChunkLength
= Length
- Length
% TT_DESCRIPTOR_SECTION_SIZE
;
758 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
759 BaseAddress
, ChunkLength
, Attributes
));
761 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
);
767 // Process page by page until the next section boundary, but only if
768 // we have more than a section's worth of area to deal with after that.
770 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
771 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
772 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
773 ChunkLength
= Length
;
777 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
778 BaseAddress
, ChunkLength
, Attributes
));
780 Status
= UpdatePageEntries (BaseAddress
, ChunkLength
, Attributes
,
784 if (EFI_ERROR (Status
)) {
788 BaseAddress
+= ChunkLength
;
789 Length
-= ChunkLength
;
799 ArmSetMemoryRegionNoExec (
800 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
804 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_XP
);
808 ArmClearMemoryRegionNoExec (
809 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
813 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);
817 ArmSetMemoryRegionReadOnly (
818 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
822 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_RO
);
826 ArmClearMemoryRegionReadOnly (
827 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
831 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);