]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
ArmPkg/ArmMmuLib ARM: fix page size granularity in initial MMU setting
[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 * 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
10 *
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.
13 *
14 **/
15
16 #include <Uefi.h>
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>
25
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
30
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
35
36 #define ID_MMFR0_SHR_IMP_UNCACHED 0
37 #define ID_MMFR0_SHR_IMP_HW_COHERENT 1
38 #define ID_MMFR0_SHR_IGNORED 0xf
39
40 #define __EFI_MEMORY_RWX 0 // no restrictions
41
42 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
43 EFI_MEMORY_WC | \
44 EFI_MEMORY_WT | \
45 EFI_MEMORY_WB | \
46 EFI_MEMORY_UCE | \
47 EFI_MEMORY_WP)
48
49 UINTN
50 EFIAPI
51 ArmReadIdMmfr0 (
52 VOID
53 );
54
55 BOOLEAN
56 EFIAPI
57 ArmHasMpExtensions (
58 VOID
59 );
60
61 UINT32
62 ConvertSectionAttributesToPageAttributes (
63 IN UINT32 SectionAttributes,
64 IN BOOLEAN IsLargePage
65 )
66 {
67 UINT32 PageAttributes;
68
69 PageAttributes = 0;
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);
75
76 return PageAttributes;
77 }
78
79 STATIC
80 BOOLEAN
81 PreferNonshareableMemory (
82 VOID
83 )
84 {
85 UINTN Mmfr;
86 UINTN Val;
87
88 if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride)) {
89 return TRUE;
90 }
91
92 //
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.
96 //
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;
102 break;
103 case ID_MMFR0_SHARELVL_TWO:
104 // two levels of shareability
105 Val = (Mmfr >> ID_MMFR0_INNERSHR_SHIFT) & ID_MMFR0_INNERSHR_MASK;
106 break;
107 default:
108 // unexpected value -> shareable is the safe option
109 ASSERT (FALSE);
110 return FALSE;
111 }
112 return Val != ID_MMFR0_SHR_IMP_HW_COHERENT;
113 }
114
115 STATIC
116 VOID
117 PopulateLevel2PageTable (
118 IN UINT32 *SectionEntry,
119 IN UINT32 PhysicalBase,
120 IN UINT32 RemainLength,
121 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
122 )
123 {
124 UINT32* PageEntry;
125 UINT32 Pages;
126 UINT32 Index;
127 UINT32 PageAttributes;
128 UINT32 SectionDescriptor;
129 UINT32 TranslationTable;
130 UINT32 BaseSectionAddress;
131 UINT32 FirstPageOffset;
132
133 switch (Attributes) {
134 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
135 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
136 PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
137 break;
138 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
139 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
140 PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
141 break;
142 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
143 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
144 PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
145 break;
146 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
147 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
148 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
149 break;
150 default:
151 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
152 break;
153 }
154
155 if (PreferNonshareableMemory ()) {
156 PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
157 }
158
159 // Check if the Section Entry has already been populated. Otherwise attach a
160 // Level 2 Translation Table to it
161 if (*SectionEntry != 0) {
162 // The entry must be a page table. Otherwise it exists an overlapping in the memory map
163 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry)) {
164 TranslationTable = *SectionEntry & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK;
165 } else if ((*SectionEntry & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
166 // Case where a virtual memory map descriptor overlapped a section entry
167
168 // Allocate a Level2 Page Table for this Section
169 TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));
170 TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;
171
172 // Translate the Section Descriptor into Page Descriptor
173 SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
174
175 BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);
176
177 // Populate the new Level2 Page Table for the section
178 PageEntry = (UINT32*)TranslationTable;
179 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
180 PageEntry[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress + (Index << 12)) | SectionDescriptor;
181 }
182
183 // Overwrite the section entry to point to the new Level2 Translation Table
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 } else {
188 // We do not support the other section type (16MB Section)
189 ASSERT(0);
190 return;
191 }
192 } else {
193 TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));
194 TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;
195
196 ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);
197
198 *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
199 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
200 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
201 }
202
203 FirstPageOffset = (PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
204 PageEntry = (UINT32 *)TranslationTable + FirstPageOffset;
205 Pages = RemainLength / TT_DESCRIPTOR_PAGE_SIZE;
206
207 ASSERT (FirstPageOffset + Pages <= TRANSLATION_TABLE_PAGE_COUNT);
208
209 for (Index = 0; Index < Pages; Index++) {
210 *PageEntry++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;
211 PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;
212 }
213
214 }
215
216 STATIC
217 VOID
218 FillTranslationTable (
219 IN UINT32 *TranslationTable,
220 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion
221 )
222 {
223 UINT32 *SectionEntry;
224 UINT32 Attributes;
225 UINT32 PhysicalBase;
226 UINT64 RemainLength;
227 UINT32 PageMapLength;
228
229 ASSERT(MemoryRegion->Length > 0);
230
231 if (MemoryRegion->PhysicalBase >= SIZE_4GB) {
232 return;
233 }
234
235 PhysicalBase = MemoryRegion->PhysicalBase;
236 RemainLength = MIN(MemoryRegion->Length, SIZE_4GB - PhysicalBase);
237
238 switch (MemoryRegion->Attributes) {
239 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
240 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
241 break;
242 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
243 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
244 break;
245 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
246 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);
247 break;
248 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
249 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
250 break;
251 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
252 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
253 break;
254 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
255 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
256 break;
257 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
258 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
259 break;
260 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
261 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);
262 break;
263 default:
264 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
265 break;
266 }
267
268 if (PreferNonshareableMemory ()) {
269 Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
270 }
271
272 // Get the first section entry for this mapping
273 SectionEntry = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);
274
275 while (RemainLength != 0) {
276 if (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE == 0 &&
277 RemainLength >= TT_DESCRIPTOR_SECTION_SIZE) {
278 // Case: Physical address aligned on the Section Size (1MB) && the length
279 // is greater than the Section Size
280 *SectionEntry++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase) | Attributes;
281 PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE;
282 RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;
283 } else {
284 PageMapLength = MIN (RemainLength, TT_DESCRIPTOR_SECTION_SIZE) -
285 (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE);
286
287 // Case: Physical address aligned on the Section Size (1MB) && the length
288 // does not fill a section
289 // Case: Physical address NOT aligned on the Section Size (1MB)
290 PopulateLevel2PageTable (SectionEntry++, PhysicalBase, PageMapLength,
291 MemoryRegion->Attributes);
292
293 // If it is the last entry
294 if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {
295 break;
296 }
297
298 PhysicalBase += PageMapLength;
299 RemainLength -= PageMapLength;
300 }
301 }
302 }
303
304 RETURN_STATUS
305 EFIAPI
306 ArmConfigureMmu (
307 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
308 OUT VOID **TranslationTableBase OPTIONAL,
309 OUT UINTN *TranslationTableSize OPTIONAL
310 )
311 {
312 VOID* TranslationTable;
313 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute;
314 UINT32 TTBRAttributes;
315
316 // Allocate pages for translation table.
317 TranslationTable = AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE + TRANSLATION_TABLE_SECTION_ALIGNMENT));
318 if (TranslationTable == NULL) {
319 return RETURN_OUT_OF_RESOURCES;
320 }
321 TranslationTable = (VOID*)(((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK);
322
323 if (TranslationTableBase != NULL) {
324 *TranslationTableBase = TranslationTable;
325 }
326
327 if (TranslationTableSize != NULL) {
328 *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
329 }
330
331 ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
332
333 // By default, mark the translation table as belonging to a uncached region
334 TranslationTableAttribute = ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;
335 while (MemoryTable->Length != 0) {
336 // Find the memory attribute for the Translation Table
337 if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) && ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {
338 TranslationTableAttribute = MemoryTable->Attributes;
339 }
340
341 FillTranslationTable (TranslationTable, MemoryTable);
342 MemoryTable++;
343 }
344
345 // Translate the Memory Attributes into Translation Table Register Attributes
346 if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED) ||
347 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED)) {
348 TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_NON_CACHEABLE : TTBR_NON_CACHEABLE;
349 } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||
350 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {
351 TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC : TTBR_WRITE_BACK_ALLOC;
352 } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) ||
353 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH)) {
354 TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_THROUGH : TTBR_WRITE_THROUGH;
355 } else {
356 ASSERT (0); // No support has been found for the attributes of the memory region that the translation table belongs to.
357 return RETURN_UNSUPPORTED;
358 }
359
360 if (TTBRAttributes & TTBR_SHAREABLE) {
361 if (PreferNonshareableMemory ()) {
362 TTBRAttributes ^= TTBR_SHAREABLE;
363 } else {
364 //
365 // Unlike the S bit in the short descriptors, which implies inner shareable
366 // on an implementation that supports two levels, the meaning of the S bit
367 // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
368 // However, we should only set this bit after we have confirmed that the
369 // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
370 //
371 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
372 TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;
373 }
374 }
375 }
376
377 ArmCleanInvalidateDataCache ();
378 ArmInvalidateInstructionCache ();
379
380 ArmDisableDataCache ();
381 ArmDisableInstructionCache();
382 // TLBs are also invalidated when calling ArmDisableMmu()
383 ArmDisableMmu ();
384
385 // Make sure nothing sneaked into the cache
386 ArmCleanInvalidateDataCache ();
387 ArmInvalidateInstructionCache ();
388
389 ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (TTBRAttributes & 0x7F)));
390
391 //
392 // The TTBCR register value is undefined at reset in the Non-Secure world.
393 // Writing 0 has the effect of:
394 // Clearing EAE: Use short descriptors, as mandated by specification.
395 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
396 // Clearing N: Perform all translation table walks through TTBR0.
397 // (0 is the default reset value in systems not implementing
398 // the Security Extensions.)
399 //
400 ArmSetTTBCR (0);
401
402 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
403 DOMAIN_ACCESS_CONTROL_NONE(14) |
404 DOMAIN_ACCESS_CONTROL_NONE(13) |
405 DOMAIN_ACCESS_CONTROL_NONE(12) |
406 DOMAIN_ACCESS_CONTROL_NONE(11) |
407 DOMAIN_ACCESS_CONTROL_NONE(10) |
408 DOMAIN_ACCESS_CONTROL_NONE( 9) |
409 DOMAIN_ACCESS_CONTROL_NONE( 8) |
410 DOMAIN_ACCESS_CONTROL_NONE( 7) |
411 DOMAIN_ACCESS_CONTROL_NONE( 6) |
412 DOMAIN_ACCESS_CONTROL_NONE( 5) |
413 DOMAIN_ACCESS_CONTROL_NONE( 4) |
414 DOMAIN_ACCESS_CONTROL_NONE( 3) |
415 DOMAIN_ACCESS_CONTROL_NONE( 2) |
416 DOMAIN_ACCESS_CONTROL_NONE( 1) |
417 DOMAIN_ACCESS_CONTROL_CLIENT(0));
418
419 ArmEnableInstructionCache();
420 ArmEnableDataCache();
421 ArmEnableMmu();
422 return RETURN_SUCCESS;
423 }
424
425 STATIC
426 EFI_STATUS
427 ConvertSectionToPages (
428 IN EFI_PHYSICAL_ADDRESS BaseAddress
429 )
430 {
431 UINT32 FirstLevelIdx;
432 UINT32 SectionDescriptor;
433 UINT32 PageTableDescriptor;
434 UINT32 PageDescriptor;
435 UINT32 Index;
436
437 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
438 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
439
440 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
441
442 // Obtain page table base
443 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
444
445 // Calculate index into first level translation table for start of modification
446 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
447 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
448
449 // Get section attributes and convert to page attributes
450 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
451 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
452
453 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
454 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)AllocatePages (1);
455 if (PageTable == NULL) {
456 return EFI_OUT_OF_RESOURCES;
457 }
458
459 // Write the page table entries out
460 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
461 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
462 }
463
464 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
465 WriteBackInvalidateDataCacheRange ((VOID *)PageTable, TT_DESCRIPTOR_PAGE_SIZE);
466
467 // Formulate page table entry, Domain=0, NS=0
468 PageTableDescriptor = (((UINTN)PageTable) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
469
470 // Write the page table entry out, replacing section entry
471 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
472
473 return EFI_SUCCESS;
474 }
475
476 STATIC
477 EFI_STATUS
478 UpdatePageEntries (
479 IN EFI_PHYSICAL_ADDRESS BaseAddress,
480 IN UINT64 Length,
481 IN UINT64 Attributes,
482 OUT BOOLEAN *FlushTlbs OPTIONAL
483 )
484 {
485 EFI_STATUS Status;
486 UINT32 EntryValue;
487 UINT32 EntryMask;
488 UINT32 FirstLevelIdx;
489 UINT32 Offset;
490 UINT32 NumPageEntries;
491 UINT32 Descriptor;
492 UINT32 p;
493 UINT32 PageTableIndex;
494 UINT32 PageTableEntry;
495 UINT32 CurrentPageTableEntry;
496 VOID *Mva;
497
498 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
499 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
500
501 Status = EFI_SUCCESS;
502
503 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
504 // EntryValue: values at bit positions specified by EntryMask
505 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK;
506 if (Attributes & EFI_MEMORY_XP) {
507 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN;
508 } else {
509 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
510 }
511
512 // Although the PI spec is unclear on this, the GCD guarantees that only
513 // one Attribute bit is set at a time, so the order of the conditionals below
514 // is irrelevant. If no memory attribute is specified, we preserve whatever
515 // memory type is set in the page tables, and update the permission attributes
516 // only.
517 if (Attributes & EFI_MEMORY_UC) {
518 // modify cacheability attributes
519 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
520 // map to strongly ordered
521 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
522 } else if (Attributes & EFI_MEMORY_WC) {
523 // modify cacheability attributes
524 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
525 // map to normal non-cachable
526 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
527 } else if (Attributes & EFI_MEMORY_WT) {
528 // modify cacheability attributes
529 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
530 // write through with no-allocate
531 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
532 } else if (Attributes & EFI_MEMORY_WB) {
533 // modify cacheability attributes
534 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
535 // write back (with allocate)
536 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
537 } else if (Attributes & CACHE_ATTRIBUTE_MASK) {
538 // catch unsupported memory type attributes
539 ASSERT (FALSE);
540 return EFI_UNSUPPORTED;
541 }
542
543 if (Attributes & EFI_MEMORY_RO) {
544 EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
545 } else {
546 EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
547 }
548
549 // Obtain page table base
550 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
551
552 // Calculate number of 4KB page table entries to change
553 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
554
555 // Iterate for the number of 4KB pages to change
556 Offset = 0;
557 for(p = 0; p < NumPageEntries; p++) {
558 // Calculate index into first level translation table for page table value
559
560 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
561 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
562
563 // Read the descriptor from the first level page table
564 Descriptor = FirstLevelTable[FirstLevelIdx];
565
566 // Does this descriptor need to be converted from section entry to 4K pages?
567 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
568 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
569 if (EFI_ERROR(Status)) {
570 // Exit for loop
571 break;
572 }
573
574 // Re-read descriptor
575 Descriptor = FirstLevelTable[FirstLevelIdx];
576 if (FlushTlbs != NULL) {
577 *FlushTlbs = TRUE;
578 }
579 }
580
581 // Obtain page table base address
582 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
583
584 // Calculate index into the page table
585 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
586 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
587
588 // Get the entry
589 CurrentPageTableEntry = PageTable[PageTableIndex];
590
591 // Mask off appropriate fields
592 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
593
594 // Mask in new attributes and/or permissions
595 PageTableEntry |= EntryValue;
596
597 if (CurrentPageTableEntry != PageTableEntry) {
598 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
599
600 // Clean/invalidate the cache for this page, but only
601 // if we are modifying the memory type attributes
602 if (((CurrentPageTableEntry ^ PageTableEntry) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) != 0) {
603 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
604 }
605
606 // Only need to update if we are changing the entry
607 PageTable[PageTableIndex] = PageTableEntry;
608 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
609 }
610
611 Status = EFI_SUCCESS;
612 Offset += TT_DESCRIPTOR_PAGE_SIZE;
613
614 } // End first level translation table loop
615
616 return Status;
617 }
618
619 STATIC
620 EFI_STATUS
621 UpdateSectionEntries (
622 IN EFI_PHYSICAL_ADDRESS BaseAddress,
623 IN UINT64 Length,
624 IN UINT64 Attributes
625 )
626 {
627 EFI_STATUS Status = EFI_SUCCESS;
628 UINT32 EntryMask;
629 UINT32 EntryValue;
630 UINT32 FirstLevelIdx;
631 UINT32 NumSections;
632 UINT32 i;
633 UINT32 CurrentDescriptor;
634 UINT32 Descriptor;
635 VOID *Mva;
636 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
637
638 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
639 // EntryValue: values at bit positions specified by EntryMask
640
641 // Make sure we handle a section range that is unmapped
642 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK | TT_DESCRIPTOR_SECTION_XN_MASK |
643 TT_DESCRIPTOR_SECTION_AP_MASK;
644 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
645
646 // Although the PI spec is unclear on this, the GCD guarantees that only
647 // one Attribute bit is set at a time, so the order of the conditionals below
648 // is irrelevant. If no memory attribute is specified, we preserve whatever
649 // memory type is set in the page tables, and update the permission attributes
650 // only.
651 if (Attributes & EFI_MEMORY_UC) {
652 // modify cacheability attributes
653 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
654 // map to strongly ordered
655 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
656 } else if (Attributes & EFI_MEMORY_WC) {
657 // modify cacheability attributes
658 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
659 // map to normal non-cachable
660 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
661 } else if (Attributes & EFI_MEMORY_WT) {
662 // modify cacheability attributes
663 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
664 // write through with no-allocate
665 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
666 } else if (Attributes & EFI_MEMORY_WB) {
667 // modify cacheability attributes
668 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
669 // write back (with allocate)
670 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
671 } else if (Attributes & CACHE_ATTRIBUTE_MASK) {
672 // catch unsupported memory type attributes
673 ASSERT (FALSE);
674 return EFI_UNSUPPORTED;
675 }
676
677 if (Attributes & EFI_MEMORY_RO) {
678 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
679 } else {
680 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
681 }
682
683 if (Attributes & EFI_MEMORY_XP) {
684 EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
685 }
686
687 // obtain page table base
688 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
689
690 // calculate index into first level translation table for start of modification
691 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
692 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
693
694 // calculate number of 1MB first level entries this applies to
695 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
696
697 // iterate through each descriptor
698 for(i=0; i<NumSections; i++) {
699 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
700
701 // has this descriptor already been coverted to pages?
702 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
703 // forward this 1MB range to page table function instead
704 Status = UpdatePageEntries (
705 (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,
706 TT_DESCRIPTOR_SECTION_SIZE,
707 Attributes,
708 NULL);
709 } else {
710 // still a section entry
711
712 // mask off appropriate fields
713 Descriptor = CurrentDescriptor & ~EntryMask;
714
715 // mask in new attributes and/or permissions
716 Descriptor |= EntryValue;
717
718 if (CurrentDescriptor != Descriptor) {
719 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
720
721 // Clean/invalidate the cache for this section, but only
722 // if we are modifying the memory type attributes
723 if (((CurrentDescriptor ^ Descriptor) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) != 0) {
724 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
725 }
726
727 // Only need to update if we are changing the descriptor
728 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
729 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
730 }
731
732 Status = EFI_SUCCESS;
733 }
734 }
735
736 return Status;
737 }
738
739 EFI_STATUS
740 ArmSetMemoryAttributes (
741 IN EFI_PHYSICAL_ADDRESS BaseAddress,
742 IN UINT64 Length,
743 IN UINT64 Attributes
744 )
745 {
746 EFI_STATUS Status;
747 UINT64 ChunkLength;
748 BOOLEAN FlushTlbs;
749
750 if (Length == 0) {
751 return EFI_SUCCESS;
752 }
753
754 FlushTlbs = FALSE;
755 while (Length > 0) {
756 if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
757 Length >= TT_DESCRIPTOR_SECTION_SIZE) {
758
759 ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
760
761 DEBUG ((DEBUG_PAGE,
762 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
763 BaseAddress, ChunkLength, Attributes));
764
765 Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes);
766
767 FlushTlbs = TRUE;
768 } else {
769
770 //
771 // Process page by page until the next section boundary, but only if
772 // we have more than a section's worth of area to deal with after that.
773 //
774 ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
775 (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
776 if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
777 ChunkLength = Length;
778 }
779
780 DEBUG ((DEBUG_PAGE,
781 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
782 BaseAddress, ChunkLength, Attributes));
783
784 Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,
785 &FlushTlbs);
786 }
787
788 if (EFI_ERROR (Status)) {
789 break;
790 }
791
792 BaseAddress += ChunkLength;
793 Length -= ChunkLength;
794 }
795
796 if (FlushTlbs) {
797 ArmInvalidateTlb ();
798 }
799 return Status;
800 }
801
802 EFI_STATUS
803 ArmSetMemoryRegionNoExec (
804 IN EFI_PHYSICAL_ADDRESS BaseAddress,
805 IN UINT64 Length
806 )
807 {
808 return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);
809 }
810
811 EFI_STATUS
812 ArmClearMemoryRegionNoExec (
813 IN EFI_PHYSICAL_ADDRESS BaseAddress,
814 IN UINT64 Length
815 )
816 {
817 return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
818 }
819
820 EFI_STATUS
821 ArmSetMemoryRegionReadOnly (
822 IN EFI_PHYSICAL_ADDRESS BaseAddress,
823 IN UINT64 Length
824 )
825 {
826 return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);
827 }
828
829 EFI_STATUS
830 ArmClearMemoryRegionReadOnly (
831 IN EFI_PHYSICAL_ADDRESS BaseAddress,
832 IN UINT64 Length
833 )
834 {
835 return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
836 }
837
838 RETURN_STATUS
839 EFIAPI
840 ArmMmuBaseLibConstructor (
841 VOID
842 )
843 {
844 return RETURN_SUCCESS;
845 }