]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
ArmPkg/ArmMmuLib: Explicitly cast UINT32 data conversions
[mirror_edk2.git] / ArmPkg / Library / ArmMmuLib / Arm / ArmMmuLibCore.c
1 /** @file
2 * File managing the MMU for ARMv7 architecture
3 *
4 * Copyright (c) 2011-2016, ARM Limited. All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-2-Clause-Patent
7 *
8 **/
9
10 #include <Uefi.h>
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>
19
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
24
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
29
30 #define ID_MMFR0_SHR_IMP_UNCACHED 0
31 #define ID_MMFR0_SHR_IMP_HW_COHERENT 1
32 #define ID_MMFR0_SHR_IGNORED 0xf
33
34 UINTN
35 EFIAPI
36 ArmReadIdMmfr0 (
37 VOID
38 );
39
40 BOOLEAN
41 EFIAPI
42 ArmHasMpExtensions (
43 VOID
44 );
45
46 STATIC
47 BOOLEAN
48 PreferNonshareableMemory (
49 VOID
50 )
51 {
52 UINTN Mmfr;
53 UINTN Val;
54
55 if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride)) {
56 return TRUE;
57 }
58
59 //
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.
63 //
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;
69 break;
70 case ID_MMFR0_SHARELVL_TWO:
71 // two levels of shareability
72 Val = (Mmfr >> ID_MMFR0_INNERSHR_SHIFT) & ID_MMFR0_INNERSHR_MASK;
73 break;
74 default:
75 // unexpected value -> shareable is the safe option
76 ASSERT (FALSE);
77 return FALSE;
78 }
79 return Val != ID_MMFR0_SHR_IMP_HW_COHERENT;
80 }
81
82 STATIC
83 VOID
84 PopulateLevel2PageTable (
85 IN UINT32 *SectionEntry,
86 IN UINT32 PhysicalBase,
87 IN UINT32 RemainLength,
88 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
89 )
90 {
91 UINT32* PageEntry;
92 UINT32 Pages;
93 UINT32 Index;
94 UINT32 PageAttributes;
95 UINT32 SectionDescriptor;
96 UINT32 TranslationTable;
97 UINT32 BaseSectionAddress;
98 UINT32 FirstPageOffset;
99
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;
104 break;
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;
109 break;
110 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
111 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
112 PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
113 break;
114 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
115 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
116 PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
117 break;
118 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
119 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
120 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
121 break;
122 default:
123 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
124 break;
125 }
126
127 if (PreferNonshareableMemory ()) {
128 PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
129 }
130
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
139
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);
144
145 // Translate the Section Descriptor into Page Descriptor
146 SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
147
148 BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);
149
150 //
151 // Make sure we are not inadvertently hitting in the caches
152 // when populating the page tables
153 //
154 InvalidateDataCacheRange ((VOID *)TranslationTable,
155 TRANSLATION_TABLE_PAGE_SIZE);
156
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;
161 }
162
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;
167 } else {
168 // We do not support the other section type (16MB Section)
169 ASSERT(0);
170 return;
171 }
172 } else {
173 TranslationTable = (UINTN)AllocateAlignedPages (
174 EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_PAGE_SIZE),
175 TRANSLATION_TABLE_PAGE_ALIGNMENT);
176 //
177 // Make sure we are not inadvertently hitting in the caches
178 // when populating the page tables
179 //
180 InvalidateDataCacheRange ((VOID *)TranslationTable,
181 TRANSLATION_TABLE_PAGE_SIZE);
182 ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);
183
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;
187 }
188
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;
192
193 ASSERT (FirstPageOffset + Pages <= TRANSLATION_TABLE_PAGE_COUNT);
194
195 for (Index = 0; Index < Pages; Index++) {
196 *PageEntry++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;
197 PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;
198 }
199
200 //
201 // Invalidate again to ensure that any line fetches that may have occurred
202 // [speculatively] since the previous invalidate are evicted again.
203 //
204 ArmDataMemoryBarrier ();
205 InvalidateDataCacheRange ((UINT32 *)TranslationTable + FirstPageOffset,
206 RemainLength / TT_DESCRIPTOR_PAGE_SIZE * sizeof (*PageEntry));
207 }
208
209 STATIC
210 VOID
211 FillTranslationTable (
212 IN UINT32 *TranslationTable,
213 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion
214 )
215 {
216 UINT32 *SectionEntry;
217 UINT32 Attributes;
218 UINT32 PhysicalBase;
219 UINT64 RemainLength;
220 UINT32 PageMapLength;
221
222 ASSERT(MemoryRegion->Length > 0);
223
224 if (MemoryRegion->PhysicalBase >= SIZE_4GB) {
225 return;
226 }
227
228 PhysicalBase = (UINT32)MemoryRegion->PhysicalBase;
229 RemainLength = MIN(MemoryRegion->Length, SIZE_4GB - PhysicalBase);
230
231 switch (MemoryRegion->Attributes) {
232 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
233 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
234 break;
235 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
236 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
237 Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
238 break;
239 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
240 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
241 break;
242 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
243 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);
244 break;
245 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
246 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
247 break;
248 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
249 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
250 break;
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;
254 break;
255 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
256 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
257 break;
258 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
259 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
260 break;
261 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
262 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);
263 break;
264 default:
265 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
266 break;
267 }
268
269 if (PreferNonshareableMemory ()) {
270 Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
271 }
272
273 // Get the first section entry for this mapping
274 SectionEntry = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);
275
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;
282
283 //
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.
287 //
288 ArmDataMemoryBarrier ();
289 ArmInvalidateDataCacheEntryByMVA ((UINTN)SectionEntry++);
290
291 PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE;
292 RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;
293 } else {
294 PageMapLength = MIN ((UINT32)RemainLength, TT_DESCRIPTOR_SECTION_SIZE -
295 (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE));
296
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);
302
303 //
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.
307 //
308 ArmDataMemoryBarrier ();
309 ArmInvalidateDataCacheEntryByMVA ((UINTN)SectionEntry++);
310
311 // If it is the last entry
312 if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {
313 break;
314 }
315
316 PhysicalBase += PageMapLength;
317 RemainLength -= PageMapLength;
318 }
319 }
320 }
321
322 RETURN_STATUS
323 EFIAPI
324 ArmConfigureMmu (
325 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
326 OUT VOID **TranslationTableBase OPTIONAL,
327 OUT UINTN *TranslationTableSize OPTIONAL
328 )
329 {
330 VOID *TranslationTable;
331 UINT32 TTBRAttributes;
332
333 TranslationTable = AllocateAlignedPages (
334 EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE),
335 TRANSLATION_TABLE_SECTION_ALIGNMENT);
336 if (TranslationTable == NULL) {
337 return RETURN_OUT_OF_RESOURCES;
338 }
339
340 if (TranslationTableBase != NULL) {
341 *TranslationTableBase = TranslationTable;
342 }
343
344 if (TranslationTableSize != NULL) {
345 *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
346 }
347
348 //
349 // Make sure we are not inadvertently hitting in the caches
350 // when populating the page tables
351 //
352 InvalidateDataCacheRange (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
353 ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
354
355 while (MemoryTable->Length != 0) {
356 FillTranslationTable (TranslationTable, MemoryTable);
357 MemoryTable++;
358 }
359
360 TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC
361 : TTBR_WRITE_BACK_ALLOC;
362 if (TTBRAttributes & TTBR_SHAREABLE) {
363 if (PreferNonshareableMemory ()) {
364 TTBRAttributes ^= TTBR_SHAREABLE;
365 } else {
366 //
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
372 //
373 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
374 TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;
375 }
376 }
377 }
378
379 ArmSetTTBR0 ((VOID *)((UINTN)TranslationTable | TTBRAttributes));
380
381 //
382 // The TTBCR register value is undefined at reset in the Non-Secure world.
383 // Writing 0 has the effect of:
384 // Clearing EAE: Use short descriptors, as mandated by specification.
385 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
386 // Clearing N: Perform all translation table walks through TTBR0.
387 // (0 is the default reset value in systems not implementing
388 // the Security Extensions.)
389 //
390 ArmSetTTBCR (0);
391
392 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
393 DOMAIN_ACCESS_CONTROL_NONE(14) |
394 DOMAIN_ACCESS_CONTROL_NONE(13) |
395 DOMAIN_ACCESS_CONTROL_NONE(12) |
396 DOMAIN_ACCESS_CONTROL_NONE(11) |
397 DOMAIN_ACCESS_CONTROL_NONE(10) |
398 DOMAIN_ACCESS_CONTROL_NONE( 9) |
399 DOMAIN_ACCESS_CONTROL_NONE( 8) |
400 DOMAIN_ACCESS_CONTROL_NONE( 7) |
401 DOMAIN_ACCESS_CONTROL_NONE( 6) |
402 DOMAIN_ACCESS_CONTROL_NONE( 5) |
403 DOMAIN_ACCESS_CONTROL_NONE( 4) |
404 DOMAIN_ACCESS_CONTROL_NONE( 3) |
405 DOMAIN_ACCESS_CONTROL_NONE( 2) |
406 DOMAIN_ACCESS_CONTROL_NONE( 1) |
407 DOMAIN_ACCESS_CONTROL_CLIENT(0));
408
409 ArmEnableInstructionCache();
410 ArmEnableDataCache();
411 ArmEnableMmu();
412 return RETURN_SUCCESS;
413 }