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
48 PreferNonshareableMemory (
55 if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride
)) {
60 // Check whether the innermost level of shareability (the level we will use
61 // by default to map normal memory) is implemented with hardware coherency
62 // support. Otherwise, revert to mapping as non-shareable.
64 Mmfr
= ArmReadIdMmfr0 ();
65 switch ((Mmfr
>> ID_MMFR0_SHARELVL_SHIFT
) & ID_MMFR0_SHARELVL_MASK
) {
66 case ID_MMFR0_SHARELVL_ONE
:
67 // one level of shareability
68 Val
= (Mmfr
>> ID_MMFR0_OUTERSHR_SHIFT
) & ID_MMFR0_OUTERSHR_MASK
;
70 case ID_MMFR0_SHARELVL_TWO
:
71 // two levels of shareability
72 Val
= (Mmfr
>> ID_MMFR0_INNERSHR_SHIFT
) & ID_MMFR0_INNERSHR_MASK
;
75 // unexpected value -> shareable is the safe option
79 return Val
!= ID_MMFR0_SHR_IMP_HW_COHERENT
;
84 PopulateLevel2PageTable (
85 IN UINT32
*SectionEntry
,
86 IN UINT32 PhysicalBase
,
87 IN UINT32 RemainLength
,
88 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
94 UINT32 PageAttributes
;
95 UINT32 SectionDescriptor
;
96 UINT32 TranslationTable
;
97 UINT32 BaseSectionAddress
;
98 UINT32 FirstPageOffset
;
100 switch (Attributes
) {
101 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
:
102 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
103 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_BACK
;
105 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE
:
106 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE
:
107 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_BACK
;
108 PageAttributes
&= ~TT_DESCRIPTOR_PAGE_S_SHARED
;
110 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
111 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
112 PageAttributes
= TT_DESCRIPTOR_PAGE_WRITE_THROUGH
;
114 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
115 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
116 PageAttributes
= TT_DESCRIPTOR_PAGE_DEVICE
;
118 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
119 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
120 PageAttributes
= TT_DESCRIPTOR_PAGE_UNCACHED
;
123 PageAttributes
= TT_DESCRIPTOR_PAGE_UNCACHED
;
127 if (PreferNonshareableMemory ()) {
128 PageAttributes
&= ~TT_DESCRIPTOR_PAGE_S_SHARED
;
131 // Check if the Section Entry has already been populated. Otherwise attach a
132 // Level 2 Translation Table to it
133 if (*SectionEntry
!= 0) {
134 // The entry must be a page table. Otherwise it exists an overlapping in the memory map
135 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry
)) {
136 TranslationTable
= *SectionEntry
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
;
137 } else if ((*SectionEntry
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
138 // Case where a virtual memory map descriptor overlapped a section entry
140 // Allocate a Level2 Page Table for this Section
141 TranslationTable
= (UINTN
)AllocateAlignedPages (
142 EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_PAGE_SIZE
),
143 TRANSLATION_TABLE_PAGE_ALIGNMENT
);
145 // Translate the Section Descriptor into Page Descriptor
146 SectionDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (*SectionEntry
, FALSE
);
148 BaseSectionAddress
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry
);
151 // Make sure we are not inadvertently hitting in the caches
152 // when populating the page tables
154 InvalidateDataCacheRange ((VOID
*)TranslationTable
,
155 TRANSLATION_TABLE_PAGE_SIZE
);
157 // Populate the new Level2 Page Table for the section
158 PageEntry
= (UINT32
*)TranslationTable
;
159 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
160 PageEntry
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress
+ (Index
<< 12)) | SectionDescriptor
;
163 // Overwrite the section entry to point to the new Level2 Translation Table
164 *SectionEntry
= (TranslationTable
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) |
165 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes
) ? (1 << 3) : 0) |
166 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
168 // We do not support the other section type (16MB Section)
173 TranslationTable
= (UINTN
)AllocateAlignedPages (
174 EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_PAGE_SIZE
),
175 TRANSLATION_TABLE_PAGE_ALIGNMENT
);
177 // Make sure we are not inadvertently hitting in the caches
178 // when populating the page tables
180 InvalidateDataCacheRange ((VOID
*)TranslationTable
,
181 TRANSLATION_TABLE_PAGE_SIZE
);
182 ZeroMem ((VOID
*)TranslationTable
, TRANSLATION_TABLE_PAGE_SIZE
);
184 *SectionEntry
= (TranslationTable
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) |
185 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes
) ? (1 << 3) : 0) |
186 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
189 FirstPageOffset
= (PhysicalBase
& TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
190 PageEntry
= (UINT32
*)TranslationTable
+ FirstPageOffset
;
191 Pages
= RemainLength
/ TT_DESCRIPTOR_PAGE_SIZE
;
193 ASSERT (FirstPageOffset
+ Pages
<= TRANSLATION_TABLE_PAGE_COUNT
);
195 for (Index
= 0; Index
< Pages
; Index
++) {
196 *PageEntry
++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase
) | PageAttributes
;
197 PhysicalBase
+= TT_DESCRIPTOR_PAGE_SIZE
;
201 // Invalidate again to ensure that any line fetches that may have occurred
202 // [speculatively] since the previous invalidate are evicted again.
204 ArmDataMemoryBarrier ();
205 InvalidateDataCacheRange ((UINT32
*)TranslationTable
+ FirstPageOffset
,
206 RemainLength
/ TT_DESCRIPTOR_PAGE_SIZE
* sizeof (*PageEntry
));
211 FillTranslationTable (
212 IN UINT32
*TranslationTable
,
213 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryRegion
216 UINT32
*SectionEntry
;
220 UINT32 PageMapLength
;
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_BACK_NONSHAREABLE
:
236 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
237 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
239 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH
:
240 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
242 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
:
243 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(0);
245 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
:
246 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
248 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
:
249 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
251 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE
:
252 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
253 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
255 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH
:
256 Attributes
= TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
258 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
:
259 Attributes
= TT_DESCRIPTOR_SECTION_DEVICE(1);
261 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED
:
262 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(1);
265 Attributes
= TT_DESCRIPTOR_SECTION_UNCACHED(0);
269 if (PreferNonshareableMemory ()) {
270 Attributes
&= ~TT_DESCRIPTOR_SECTION_S_SHARED
;
273 // Get the first section entry for this mapping
274 SectionEntry
= TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable
, MemoryRegion
->VirtualBase
);
276 while (RemainLength
!= 0) {
277 if (PhysicalBase
% TT_DESCRIPTOR_SECTION_SIZE
== 0 &&
278 RemainLength
>= TT_DESCRIPTOR_SECTION_SIZE
) {
279 // Case: Physical address aligned on the Section Size (1MB) && the length
280 // is greater than the Section Size
281 *SectionEntry
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase
) | Attributes
;
284 // Issue a DMB to ensure that the page table entry update made it to
285 // memory before we issue the invalidate, otherwise, a subsequent
286 // speculative fetch could observe the old value.
288 ArmDataMemoryBarrier ();
289 ArmInvalidateDataCacheEntryByMVA ((UINTN
)SectionEntry
++);
291 PhysicalBase
+= TT_DESCRIPTOR_SECTION_SIZE
;
292 RemainLength
-= TT_DESCRIPTOR_SECTION_SIZE
;
294 PageMapLength
= MIN (RemainLength
, TT_DESCRIPTOR_SECTION_SIZE
-
295 (PhysicalBase
% TT_DESCRIPTOR_SECTION_SIZE
));
297 // Case: Physical address aligned on the Section Size (1MB) && the length
298 // does not fill a section
299 // Case: Physical address NOT aligned on the Section Size (1MB)
300 PopulateLevel2PageTable (SectionEntry
, PhysicalBase
, PageMapLength
,
301 MemoryRegion
->Attributes
);
304 // Issue a DMB to ensure that the page table entry update made it to
305 // memory before we issue the invalidate, otherwise, a subsequent
306 // speculative fetch could observe the old value.
308 ArmDataMemoryBarrier ();
309 ArmInvalidateDataCacheEntryByMVA ((UINTN
)SectionEntry
++);
311 // If it is the last entry
312 if (RemainLength
< TT_DESCRIPTOR_SECTION_SIZE
) {
316 PhysicalBase
+= PageMapLength
;
317 RemainLength
-= PageMapLength
;
325 IN ARM_MEMORY_REGION_DESCRIPTOR
*MemoryTable
,
326 OUT VOID
**TranslationTableBase OPTIONAL
,
327 OUT UINTN
*TranslationTableSize OPTIONAL
330 VOID
*TranslationTable
;
331 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute
;
332 UINT32 TTBRAttributes
;
334 TranslationTable
= AllocateAlignedPages (
335 EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE
),
336 TRANSLATION_TABLE_SECTION_ALIGNMENT
);
337 if (TranslationTable
== NULL
) {
338 return RETURN_OUT_OF_RESOURCES
;
341 if (TranslationTableBase
!= NULL
) {
342 *TranslationTableBase
= TranslationTable
;
345 if (TranslationTableSize
!= NULL
) {
346 *TranslationTableSize
= TRANSLATION_TABLE_SECTION_SIZE
;
350 // Make sure we are not inadvertently hitting in the caches
351 // when populating the page tables
353 InvalidateDataCacheRange (TranslationTable
, TRANSLATION_TABLE_SECTION_SIZE
);
354 ZeroMem (TranslationTable
, TRANSLATION_TABLE_SECTION_SIZE
);
356 // By default, mark the translation table as belonging to a uncached region
357 TranslationTableAttribute
= ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED
;
358 while (MemoryTable
->Length
!= 0) {
359 // Find the memory attribute for the Translation Table
360 if (((UINTN
)TranslationTable
>= MemoryTable
->PhysicalBase
) && ((UINTN
)TranslationTable
<= MemoryTable
->PhysicalBase
- 1 + MemoryTable
->Length
)) {
361 TranslationTableAttribute
= MemoryTable
->Attributes
;
364 FillTranslationTable (TranslationTable
, MemoryTable
);
368 // Translate the Memory Attributes into Translation Table Register Attributes
369 if ((TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
) ||
370 (TranslationTableAttribute
== ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK
)) {
371 TTBRAttributes
= ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC
: TTBR_WRITE_BACK_ALLOC
;
373 // Page tables must reside in memory mapped as write-back cacheable
375 return RETURN_UNSUPPORTED
;
378 if (TTBRAttributes
& TTBR_SHAREABLE
) {
379 if (PreferNonshareableMemory ()) {
380 TTBRAttributes
^= TTBR_SHAREABLE
;
383 // Unlike the S bit in the short descriptors, which implies inner shareable
384 // on an implementation that supports two levels, the meaning of the S bit
385 // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
386 // However, we should only set this bit after we have confirmed that the
387 // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
389 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
390 TTBRAttributes
|= TTBR_NOT_OUTER_SHAREABLE
;
395 ArmSetTTBR0 ((VOID
*)((UINTN
)TranslationTable
| TTBRAttributes
));
398 // The TTBCR register value is undefined at reset in the Non-Secure world.
399 // Writing 0 has the effect of:
400 // Clearing EAE: Use short descriptors, as mandated by specification.
401 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
402 // Clearing N: Perform all translation table walks through TTBR0.
403 // (0 is the default reset value in systems not implementing
404 // the Security Extensions.)
408 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
409 DOMAIN_ACCESS_CONTROL_NONE(14) |
410 DOMAIN_ACCESS_CONTROL_NONE(13) |
411 DOMAIN_ACCESS_CONTROL_NONE(12) |
412 DOMAIN_ACCESS_CONTROL_NONE(11) |
413 DOMAIN_ACCESS_CONTROL_NONE(10) |
414 DOMAIN_ACCESS_CONTROL_NONE( 9) |
415 DOMAIN_ACCESS_CONTROL_NONE( 8) |
416 DOMAIN_ACCESS_CONTROL_NONE( 7) |
417 DOMAIN_ACCESS_CONTROL_NONE( 6) |
418 DOMAIN_ACCESS_CONTROL_NONE( 5) |
419 DOMAIN_ACCESS_CONTROL_NONE( 4) |
420 DOMAIN_ACCESS_CONTROL_NONE( 3) |
421 DOMAIN_ACCESS_CONTROL_NONE( 2) |
422 DOMAIN_ACCESS_CONTROL_NONE( 1) |
423 DOMAIN_ACCESS_CONTROL_CLIENT(0));
425 ArmEnableInstructionCache();
426 ArmEnableDataCache();
428 return RETURN_SUCCESS
;