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