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 __EFI_MEMORY_RWX 0 // no restrictions
42 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
62 ConvertSectionAttributesToPageAttributes (
63 IN UINT32 SectionAttributes
,
64 IN BOOLEAN IsLargePage
67 UINT32 PageAttributes
;
70 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes
, IsLargePage
);
71 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes
);
72 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes
, IsLargePage
);
73 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes
);
74 PageAttributes
|= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes
);
76 return PageAttributes
;
81 PreferNonshareableMemory (
88 if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride
)) {
93 // Check whether the innermost level of shareability (the level we will use
94 // by default to map normal memory) is implemented with hardware coherency
95 // support. Otherwise, revert to mapping as non-shareable.
97 Mmfr
= ArmReadIdMmfr0 ();
98 switch ((Mmfr
>> ID_MMFR0_SHARELVL_SHIFT
) & ID_MMFR0_SHARELVL_MASK
) {
99 case ID_MMFR0_SHARELVL_ONE
:
100 // one level of shareability
101 Val
= (Mmfr
>> ID_MMFR0_OUTERSHR_SHIFT
) & ID_MMFR0_OUTERSHR_MASK
;
103 case ID_MMFR0_SHARELVL_TWO
:
104 // two levels of shareability
105 Val
= (Mmfr
>> ID_MMFR0_INNERSHR_SHIFT
) & ID_MMFR0_INNERSHR_MASK
;
108 // unexpected value -> shareable is the safe option
112 return Val
!= ID_MMFR0_SHR_IMP_HW_COHERENT
;
117 PopulateLevel2PageTable (
118 IN UINT32
*SectionEntry
,
119 IN UINT32 PhysicalBase
,
120 IN UINT32 RemainLength
,
121 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
127 UINT32 PageAttributes
;
128 UINT32 SectionDescriptor
;
129 UINT32 TranslationTable
;
130 UINT32 BaseSectionAddress
;
132 switch (Attributes
) {
133 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
134 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
135 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_BACK
;
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 PageEntry
= ((UINT32
*)(TranslationTable
) + ((PhysicalBase
& TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
203 Pages
= RemainLength
/ TT_DESCRIPTOR_PAGE_SIZE
;
205 for (Index
= 0; Index
< Pages
; Index
++) {
206 *PageEntry
++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase
) | PageAttributes
;
207 PhysicalBase
+= TT_DESCRIPTOR_PAGE_SIZE
;
214 FillTranslationTable (
215 IN UINT32
*TranslationTable
,
216 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryRegion
219 UINT32
*SectionEntry
;
224 ASSERT(MemoryRegion
->Length
> 0);
226 if (MemoryRegion
->PhysicalBase
>= SIZE_4GB
) {
230 PhysicalBase
= MemoryRegion
->PhysicalBase
;
231 RemainLength
= MIN(MemoryRegion
->Length
, SIZE_4GB
- PhysicalBase
);
233 switch (MemoryRegion
->Attributes
) {
234 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
235 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
237 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
238 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
240 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
241 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(0);
243 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
244 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
246 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
247 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
249 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
250 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
252 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
253 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(1);
255 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
256 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(1);
259 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
263 if (PreferNonshareableMemory ()) {
264 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
267 // Get the first section entry for this mapping
268 SectionEntry
= TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable
, MemoryRegion
->VirtualBase
);
270 while (RemainLength
!= 0) {
271 if (PhysicalBase
% TT_DESCRIPTOR_SECTION_SIZE
== 0) {
272 if (RemainLength
>= TT_DESCRIPTOR_SECTION_SIZE
) {
273 // Case: Physical address aligned on the Section Size (1MB) && the length is greater than the Section Size
274 *SectionEntry
++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase
) | Attributes
;
275 PhysicalBase
+= TT_DESCRIPTOR_SECTION_SIZE
;
277 // Case: Physical address aligned on the Section Size (1MB) && the length does not fill a section
278 PopulateLevel2PageTable (SectionEntry
++, PhysicalBase
, RemainLength
, MemoryRegion
->Attributes
);
280 // It must be the last entry
284 // Case: Physical address NOT aligned on the Section Size (1MB)
285 PopulateLevel2PageTable (SectionEntry
++, PhysicalBase
, RemainLength
, MemoryRegion
->Attributes
);
286 // Aligned the address
287 PhysicalBase
= (PhysicalBase
+ TT_DESCRIPTOR_SECTION_SIZE
) & ~(TT_DESCRIPTOR_SECTION_SIZE
-1);
289 // If it is the last entry
290 if (RemainLength
< TT_DESCRIPTOR_SECTION_SIZE
) {
294 RemainLength
-= TT_DESCRIPTOR_SECTION_SIZE
;
301 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryTable
,
302 OUT VOID
**TranslationTableBase OPTIONAL
,
303 OUT UINTN
*TranslationTableSize OPTIONAL
306 VOID
* TranslationTable
;
307 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute
;
308 UINT32 TTBRAttributes
;
310 // Allocate pages for translation table.
311 TranslationTable
= AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE
+ TRANSLATION_TABLE_SECTION_ALIGNMENT
));
312 if (TranslationTable
== NULL
) {
313 return RETURN_OUT_OF_RESOURCES
;
315 TranslationTable
= (VOID
*)(((UINTN
)TranslationTable
+ TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
);
317 if (TranslationTableBase
!= NULL
) {
318 *TranslationTableBase
= TranslationTable
;
321 if (TranslationTableSize
!= NULL
) {
322 *TranslationTableSize
= TRANSLATION_TABLE_SECTION_SIZE
;
325 ZeroMem (TranslationTable
, TRANSLATION_TABLE_SECTION_SIZE
);
327 // By default, mark the translation table as belonging to a uncached region
328 TranslationTableAttribute
= ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
;
329 while (MemoryTable
->Length
!= 0) {
330 // Find the memory attribute for the Translation Table
331 if (((UINTN
)TranslationTable
>= MemoryTable
->PhysicalBase
) && ((UINTN
)TranslationTable
<= MemoryTable
->PhysicalBase
- 1 + MemoryTable
->Length
)) {
332 TranslationTableAttribute
= MemoryTable
->Attributes
;
335 FillTranslationTable (TranslationTable
, MemoryTable
);
339 // Translate the Memory Attributes into Translation Table Register Attributes
340 if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
) ||
341 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
)) {
342 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_NON_CACHEABLE
: TTBR_NON_CACHEABLE
;
343 } else if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
) ||
344 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
)) {
345 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC
: TTBR_WRITE_BACK_ALLOC
;
346 } else if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
) ||
347 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
)) {
348 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_WRITE_THROUGH
: TTBR_WRITE_THROUGH
;
350 ASSERT (0); // No support has been found for the attributes of the memory region that the translation table belongs to.
351 return RETURN_UNSUPPORTED
;
354 if (TTBRAttributes
& TTBR_SHAREABLE
) {
355 if (PreferNonshareableMemory ()) {
356 TTBRAttributes
^= TTBR_SHAREABLE
;
359 // Unlike the S bit in the short descriptors, which implies inner shareable
360 // on an implementation that supports two levels, the meaning of the S bit
361 // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
362 // However, we should only set this bit after we have confirmed that the
363 // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
365 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
366 TTBRAttributes
|= TTBR_NOT_OUTER_SHAREABLE
;
371 ArmCleanInvalidateDataCache ();
372 ArmInvalidateInstructionCache ();
374 ArmDisableDataCache ();
375 ArmDisableInstructionCache();
376 // TLBs are also invalidated when calling ArmDisableMmu()
379 // Make sure nothing sneaked into the cache
380 ArmCleanInvalidateDataCache ();
381 ArmInvalidateInstructionCache ();
383 ArmSetTTBR0 ((VOID
*)(UINTN
)(((UINTN
)TranslationTable
& ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK
) | (TTBRAttributes
& 0x7F)));
386 // The TTBCR register value is undefined at reset in the Non-Secure world.
387 // Writing 0 has the effect of:
388 // Clearing EAE: Use short descriptors, as mandated by specification.
389 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
390 // Clearing N: Perform all translation table walks through TTBR0.
391 // (0 is the default reset value in systems not implementing
392 // the Security Extensions.)
396 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
397 DOMAIN_ACCESS_CONTROL_NONE(14) |
398 DOMAIN_ACCESS_CONTROL_NONE(13) |
399 DOMAIN_ACCESS_CONTROL_NONE(12) |
400 DOMAIN_ACCESS_CONTROL_NONE(11) |
401 DOMAIN_ACCESS_CONTROL_NONE(10) |
402 DOMAIN_ACCESS_CONTROL_NONE( 9) |
403 DOMAIN_ACCESS_CONTROL_NONE( 8) |
404 DOMAIN_ACCESS_CONTROL_NONE( 7) |
405 DOMAIN_ACCESS_CONTROL_NONE( 6) |
406 DOMAIN_ACCESS_CONTROL_NONE( 5) |
407 DOMAIN_ACCESS_CONTROL_NONE( 4) |
408 DOMAIN_ACCESS_CONTROL_NONE( 3) |
409 DOMAIN_ACCESS_CONTROL_NONE( 2) |
410 DOMAIN_ACCESS_CONTROL_NONE( 1) |
411 DOMAIN_ACCESS_CONTROL_CLIENT(0));
413 ArmEnableInstructionCache();
414 ArmEnableDataCache();
416 return RETURN_SUCCESS
;
421 ConvertSectionToPages (
422 IN EFI_PHYSICAL_ADDRESS BaseAddress
425 UINT32 FirstLevelIdx
;
426 UINT32 SectionDescriptor
;
427 UINT32 PageTableDescriptor
;
428 UINT32 PageDescriptor
;
431 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
432 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
434 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
436 // Obtain page table base
437 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
439 // Calculate index into first level translation table for start of modification
440 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
441 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
443 // Get section attributes and convert to page attributes
444 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
445 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
447 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
448 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)AllocatePages (1);
449 if (PageTable
== NULL
) {
450 return EFI_OUT_OF_RESOURCES
;
453 // Write the page table entries out
454 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
455 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
458 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
459 WriteBackInvalidateDataCacheRange ((VOID
*)PageTable
, TT_DESCRIPTOR_PAGE_SIZE
);
461 // Formulate page table entry, Domain=0, NS=0
462 PageTableDescriptor
= (((UINTN
)PageTable
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
464 // Write the page table entry out, replacing section entry
465 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
473 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
475 IN UINT64 Attributes
,
476 OUT BOOLEAN
*FlushTlbs OPTIONAL
482 UINT32 FirstLevelIdx
;
484 UINT32 NumPageEntries
;
487 UINT32 PageTableIndex
;
488 UINT32 PageTableEntry
;
489 UINT32 CurrentPageTableEntry
;
492 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
493 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
495 Status
= EFI_SUCCESS
;
497 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
498 // EntryValue: values at bit positions specified by EntryMask
499 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
;
500 if (Attributes
& EFI_MEMORY_XP
) {
501 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN
;
503 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
506 // Although the PI spec is unclear on this, the GCD guarantees that only
507 // one Attribute bit is set at a time, so the order of the conditionals below
508 // is irrelevant. If no memory attribute is specified, we preserve whatever
509 // memory type is set in the page tables, and update the permission attributes
511 if (Attributes
& EFI_MEMORY_UC
) {
512 // modify cacheability attributes
513 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
514 // map to strongly ordered
515 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
516 } else if (Attributes
& EFI_MEMORY_WC
) {
517 // modify cacheability attributes
518 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
519 // map to normal non-cachable
520 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
521 } else if (Attributes
& EFI_MEMORY_WT
) {
522 // modify cacheability attributes
523 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
524 // write through with no-allocate
525 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
526 } else if (Attributes
& EFI_MEMORY_WB
) {
527 // modify cacheability attributes
528 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
529 // write back (with allocate)
530 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
531 } else if (Attributes
& CACHE_ATTRIBUTE_MASK
) {
532 // catch unsupported memory type attributes
534 return EFI_UNSUPPORTED
;
537 if (Attributes
& EFI_MEMORY_RO
) {
538 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RO_RO
;
540 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RW_RW
;
543 // Obtain page table base
544 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
546 // Calculate number of 4KB page table entries to change
547 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
549 // Iterate for the number of 4KB pages to change
551 for(p
= 0; p
< NumPageEntries
; p
++) {
552 // Calculate index into first level translation table for page table value
554 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
555 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
557 // Read the descriptor from the first level page table
558 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
560 // Does this descriptor need to be converted from section entry to 4K pages?
561 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
562 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
563 if (EFI_ERROR(Status
)) {
568 // Re-read descriptor
569 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
570 if (FlushTlbs
!= NULL
) {
575 // Obtain page table base address
576 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
578 // Calculate index into the page table
579 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
580 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
583 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
585 // Mask off appropriate fields
586 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
588 // Mask in new attributes and/or permissions
589 PageTableEntry
|= EntryValue
;
591 if (CurrentPageTableEntry
!= PageTableEntry
) {
592 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
594 // Clean/invalidate the cache for this page, but only
595 // if we are modifying the memory type attributes
596 if (((CurrentPageTableEntry
^ PageTableEntry
) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) != 0) {
597 WriteBackInvalidateDataCacheRange (Mva
, TT_DESCRIPTOR_PAGE_SIZE
);
600 // Only need to update if we are changing the entry
601 PageTable
[PageTableIndex
] = PageTableEntry
;
602 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
605 Status
= EFI_SUCCESS
;
606 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
608 } // End first level translation table loop
615 UpdateSectionEntries (
616 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
621 EFI_STATUS Status
= EFI_SUCCESS
;
624 UINT32 FirstLevelIdx
;
627 UINT32 CurrentDescriptor
;
630 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
632 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
633 // EntryValue: values at bit positions specified by EntryMask
635 // Make sure we handle a section range that is unmapped
636 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
| TT_DESCRIPTOR_SECTION_XN_MASK
|
637 TT_DESCRIPTOR_SECTION_AP_MASK
;
638 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
640 // Although the PI spec is unclear on this, the GCD guarantees that only
641 // one Attribute bit is set at a time, so the order of the conditionals below
642 // is irrelevant. If no memory attribute is specified, we preserve whatever
643 // memory type is set in the page tables, and update the permission attributes
645 if (Attributes
& EFI_MEMORY_UC
) {
646 // modify cacheability attributes
647 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
648 // map to strongly ordered
649 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
650 } else if (Attributes
& EFI_MEMORY_WC
) {
651 // modify cacheability attributes
652 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
653 // map to normal non-cachable
654 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
655 } else if (Attributes
& EFI_MEMORY_WT
) {
656 // modify cacheability attributes
657 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
658 // write through with no-allocate
659 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
660 } else if (Attributes
& EFI_MEMORY_WB
) {
661 // modify cacheability attributes
662 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
663 // write back (with allocate)
664 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
665 } else if (Attributes
& CACHE_ATTRIBUTE_MASK
) {
666 // catch unsupported memory type attributes
668 return EFI_UNSUPPORTED
;
671 if (Attributes
& EFI_MEMORY_RO
) {
672 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
674 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
677 if (Attributes
& EFI_MEMORY_XP
) {
678 EntryValue
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
681 // obtain page table base
682 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
684 // calculate index into first level translation table for start of modification
685 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
686 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
688 // calculate number of 1MB first level entries this applies to
689 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
691 // iterate through each descriptor
692 for(i
=0; i
<NumSections
; i
++) {
693 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
695 // has this descriptor already been coverted to pages?
696 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
697 // forward this 1MB range to page table function instead
698 Status
= UpdatePageEntries (
699 (FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
,
700 TT_DESCRIPTOR_SECTION_SIZE
,
704 // still a section entry
706 // mask off appropriate fields
707 Descriptor
= CurrentDescriptor
& ~EntryMask
;
709 // mask in new attributes and/or permissions
710 Descriptor
|= EntryValue
;
712 if (CurrentDescriptor
!= Descriptor
) {
713 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
715 // Clean/invalidate the cache for this section, but only
716 // if we are modifying the memory type attributes
717 if (((CurrentDescriptor
^ Descriptor
) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) != 0) {
718 WriteBackInvalidateDataCacheRange (Mva
, SIZE_1MB
);
721 // Only need to update if we are changing the descriptor
722 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
723 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
726 Status
= EFI_SUCCESS
;
734 ArmSetMemoryAttributes (
735 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
750 if ((BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
== 0) &&
751 Length
>= TT_DESCRIPTOR_SECTION_SIZE
) {
753 ChunkLength
= Length
- Length
% TT_DESCRIPTOR_SECTION_SIZE
;
756 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
757 BaseAddress
, ChunkLength
, Attributes
));
759 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
);
765 // Process page by page until the next section boundary, but only if
766 // we have more than a section's worth of area to deal with after that.
768 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
769 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
770 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
771 ChunkLength
= Length
;
775 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
776 BaseAddress
, ChunkLength
, Attributes
));
778 Status
= UpdatePageEntries (BaseAddress
, ChunkLength
, Attributes
,
782 if (EFI_ERROR (Status
)) {
786 BaseAddress
+= ChunkLength
;
787 Length
-= ChunkLength
;
797 ArmSetMemoryRegionNoExec (
798 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
802 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_XP
);
806 ArmClearMemoryRegionNoExec (
807 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
811 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);
815 ArmSetMemoryRegionReadOnly (
816 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
820 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_RO
);
824 ArmClearMemoryRegionReadOnly (
825 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
829 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);
834 ArmMmuBaseLibConstructor (
838 return RETURN_SUCCESS
;