]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
ArmPkg/ArmMmuLib ARM: implement memory permission control routines
[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
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;
136 break;
137 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
138 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
139 PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
140 break;
141 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
142 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
143 PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
144 break;
145 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
146 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
147 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
148 break;
149 default:
150 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
151 break;
152 }
153
154 if (PreferNonshareableMemory ()) {
155 PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
156 }
157
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
166
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;
170
171 // Translate the Section Descriptor into Page Descriptor
172 SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
173
174 BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);
175
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;
180 }
181
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;
186 } else {
187 // We do not support the other section type (16MB Section)
188 ASSERT(0);
189 return;
190 }
191 } else {
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;
194
195 ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);
196
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;
200 }
201
202 PageEntry = ((UINT32 *)(TranslationTable) + ((PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT));
203 Pages = RemainLength / TT_DESCRIPTOR_PAGE_SIZE;
204
205 for (Index = 0; Index < Pages; Index++) {
206 *PageEntry++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;
207 PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;
208 }
209
210 }
211
212 STATIC
213 VOID
214 FillTranslationTable (
215 IN UINT32 *TranslationTable,
216 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion
217 )
218 {
219 UINT32 *SectionEntry;
220 UINT32 Attributes;
221 UINT32 PhysicalBase;
222 UINT64 RemainLength;
223
224 ASSERT(MemoryRegion->Length > 0);
225
226 if (MemoryRegion->PhysicalBase >= SIZE_4GB) {
227 return;
228 }
229
230 PhysicalBase = MemoryRegion->PhysicalBase;
231 RemainLength = MIN(MemoryRegion->Length, SIZE_4GB - PhysicalBase);
232
233 switch (MemoryRegion->Attributes) {
234 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
235 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
236 break;
237 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
238 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
239 break;
240 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
241 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);
242 break;
243 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
244 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
245 break;
246 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
247 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
248 break;
249 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
250 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
251 break;
252 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
253 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
254 break;
255 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
256 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);
257 break;
258 default:
259 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
260 break;
261 }
262
263 if (PreferNonshareableMemory ()) {
264 Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
265 }
266
267 // Get the first section entry for this mapping
268 SectionEntry = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);
269
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;
276 } else {
277 // Case: Physical address aligned on the Section Size (1MB) && the length does not fill a section
278 PopulateLevel2PageTable (SectionEntry++, PhysicalBase, RemainLength, MemoryRegion->Attributes);
279
280 // It must be the last entry
281 break;
282 }
283 } else {
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);
288
289 // If it is the last entry
290 if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {
291 break;
292 }
293 }
294 RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;
295 }
296 }
297
298 RETURN_STATUS
299 EFIAPI
300 ArmConfigureMmu (
301 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
302 OUT VOID **TranslationTableBase OPTIONAL,
303 OUT UINTN *TranslationTableSize OPTIONAL
304 )
305 {
306 VOID* TranslationTable;
307 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute;
308 UINT32 TTBRAttributes;
309
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;
314 }
315 TranslationTable = (VOID*)(((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK);
316
317 if (TranslationTableBase != NULL) {
318 *TranslationTableBase = TranslationTable;
319 }
320
321 if (TranslationTableSize != NULL) {
322 *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
323 }
324
325 ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
326
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;
333 }
334
335 FillTranslationTable (TranslationTable, MemoryTable);
336 MemoryTable++;
337 }
338
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;
349 } else {
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;
352 }
353
354 if (TTBRAttributes & TTBR_SHAREABLE) {
355 if (PreferNonshareableMemory ()) {
356 TTBRAttributes ^= TTBR_SHAREABLE;
357 } else {
358 //
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
364 //
365 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
366 TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;
367 }
368 }
369 }
370
371 ArmCleanInvalidateDataCache ();
372 ArmInvalidateInstructionCache ();
373
374 ArmDisableDataCache ();
375 ArmDisableInstructionCache();
376 // TLBs are also invalidated when calling ArmDisableMmu()
377 ArmDisableMmu ();
378
379 // Make sure nothing sneaked into the cache
380 ArmCleanInvalidateDataCache ();
381 ArmInvalidateInstructionCache ();
382
383 ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (TTBRAttributes & 0x7F)));
384
385 //
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.)
393 //
394 ArmSetTTBCR (0);
395
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));
412
413 ArmEnableInstructionCache();
414 ArmEnableDataCache();
415 ArmEnableMmu();
416 return RETURN_SUCCESS;
417 }
418
419 STATIC
420 EFI_STATUS
421 ConvertSectionToPages (
422 IN EFI_PHYSICAL_ADDRESS BaseAddress
423 )
424 {
425 UINT32 FirstLevelIdx;
426 UINT32 SectionDescriptor;
427 UINT32 PageTableDescriptor;
428 UINT32 PageDescriptor;
429 UINT32 Index;
430
431 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
432 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
433
434 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
435
436 // Obtain page table base
437 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
438
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);
442
443 // Get section attributes and convert to page attributes
444 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
445 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
446
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;
451 }
452
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;
456 }
457
458 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
459 WriteBackInvalidateDataCacheRange ((VOID *)PageTable, TT_DESCRIPTOR_PAGE_SIZE);
460
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;
463
464 // Write the page table entry out, replacing section entry
465 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
466
467 return EFI_SUCCESS;
468 }
469
470 STATIC
471 EFI_STATUS
472 UpdatePageEntries (
473 IN EFI_PHYSICAL_ADDRESS BaseAddress,
474 IN UINT64 Length,
475 IN UINT64 Attributes,
476 OUT BOOLEAN *FlushTlbs OPTIONAL
477 )
478 {
479 EFI_STATUS Status;
480 UINT32 EntryValue;
481 UINT32 EntryMask;
482 UINT32 FirstLevelIdx;
483 UINT32 Offset;
484 UINT32 NumPageEntries;
485 UINT32 Descriptor;
486 UINT32 p;
487 UINT32 PageTableIndex;
488 UINT32 PageTableEntry;
489 UINT32 CurrentPageTableEntry;
490 VOID *Mva;
491
492 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
493 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
494
495 Status = EFI_SUCCESS;
496
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;
502 } else {
503 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
504 }
505
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
510 // only.
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
533 ASSERT (FALSE);
534 return EFI_UNSUPPORTED;
535 }
536
537 if (Attributes & EFI_MEMORY_RO) {
538 EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
539 } else {
540 EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
541 }
542
543 // Obtain page table base
544 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
545
546 // Calculate number of 4KB page table entries to change
547 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
548
549 // Iterate for the number of 4KB pages to change
550 Offset = 0;
551 for(p = 0; p < NumPageEntries; p++) {
552 // Calculate index into first level translation table for page table value
553
554 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
555 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
556
557 // Read the descriptor from the first level page table
558 Descriptor = FirstLevelTable[FirstLevelIdx];
559
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)) {
564 // Exit for loop
565 break;
566 }
567
568 // Re-read descriptor
569 Descriptor = FirstLevelTable[FirstLevelIdx];
570 if (FlushTlbs != NULL) {
571 *FlushTlbs = TRUE;
572 }
573 }
574
575 // Obtain page table base address
576 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
577
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);
581
582 // Get the entry
583 CurrentPageTableEntry = PageTable[PageTableIndex];
584
585 // Mask off appropriate fields
586 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
587
588 // Mask in new attributes and/or permissions
589 PageTableEntry |= EntryValue;
590
591 if (CurrentPageTableEntry != PageTableEntry) {
592 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
593
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);
598 }
599
600 // Only need to update if we are changing the entry
601 PageTable[PageTableIndex] = PageTableEntry;
602 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
603 }
604
605 Status = EFI_SUCCESS;
606 Offset += TT_DESCRIPTOR_PAGE_SIZE;
607
608 } // End first level translation table loop
609
610 return Status;
611 }
612
613 STATIC
614 EFI_STATUS
615 UpdateSectionEntries (
616 IN EFI_PHYSICAL_ADDRESS BaseAddress,
617 IN UINT64 Length,
618 IN UINT64 Attributes
619 )
620 {
621 EFI_STATUS Status = EFI_SUCCESS;
622 UINT32 EntryMask;
623 UINT32 EntryValue;
624 UINT32 FirstLevelIdx;
625 UINT32 NumSections;
626 UINT32 i;
627 UINT32 CurrentDescriptor;
628 UINT32 Descriptor;
629 VOID *Mva;
630 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
631
632 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
633 // EntryValue: values at bit positions specified by EntryMask
634
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;
639
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
644 // only.
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
667 ASSERT (FALSE);
668 return EFI_UNSUPPORTED;
669 }
670
671 if (Attributes & EFI_MEMORY_RO) {
672 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
673 } else {
674 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
675 }
676
677 if (Attributes & EFI_MEMORY_XP) {
678 EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
679 }
680
681 // obtain page table base
682 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
683
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);
687
688 // calculate number of 1MB first level entries this applies to
689 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
690
691 // iterate through each descriptor
692 for(i=0; i<NumSections; i++) {
693 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
694
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,
701 Attributes,
702 NULL);
703 } else {
704 // still a section entry
705
706 // mask off appropriate fields
707 Descriptor = CurrentDescriptor & ~EntryMask;
708
709 // mask in new attributes and/or permissions
710 Descriptor |= EntryValue;
711
712 if (CurrentDescriptor != Descriptor) {
713 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
714
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);
719 }
720
721 // Only need to update if we are changing the descriptor
722 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
723 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
724 }
725
726 Status = EFI_SUCCESS;
727 }
728 }
729
730 return Status;
731 }
732
733 EFI_STATUS
734 ArmSetMemoryAttributes (
735 IN EFI_PHYSICAL_ADDRESS BaseAddress,
736 IN UINT64 Length,
737 IN UINT64 Attributes
738 )
739 {
740 EFI_STATUS Status;
741 UINT64 ChunkLength;
742 BOOLEAN FlushTlbs;
743
744 if (Length == 0) {
745 return EFI_SUCCESS;
746 }
747
748 FlushTlbs = FALSE;
749 while (Length > 0) {
750 if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
751 Length >= TT_DESCRIPTOR_SECTION_SIZE) {
752
753 ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
754
755 DEBUG ((DEBUG_PAGE,
756 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
757 BaseAddress, ChunkLength, Attributes));
758
759 Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes);
760
761 FlushTlbs = TRUE;
762 } else {
763
764 //
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.
767 //
768 ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
769 (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
770 if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
771 ChunkLength = Length;
772 }
773
774 DEBUG ((DEBUG_PAGE,
775 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
776 BaseAddress, ChunkLength, Attributes));
777
778 Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,
779 &FlushTlbs);
780 }
781
782 if (EFI_ERROR (Status)) {
783 break;
784 }
785
786 BaseAddress += ChunkLength;
787 Length -= ChunkLength;
788 }
789
790 if (FlushTlbs) {
791 ArmInvalidateTlb ();
792 }
793 return Status;
794 }
795
796 EFI_STATUS
797 ArmSetMemoryRegionNoExec (
798 IN EFI_PHYSICAL_ADDRESS BaseAddress,
799 IN UINT64 Length
800 )
801 {
802 return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);
803 }
804
805 EFI_STATUS
806 ArmClearMemoryRegionNoExec (
807 IN EFI_PHYSICAL_ADDRESS BaseAddress,
808 IN UINT64 Length
809 )
810 {
811 return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
812 }
813
814 EFI_STATUS
815 ArmSetMemoryRegionReadOnly (
816 IN EFI_PHYSICAL_ADDRESS BaseAddress,
817 IN UINT64 Length
818 )
819 {
820 return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);
821 }
822
823 EFI_STATUS
824 ArmClearMemoryRegionReadOnly (
825 IN EFI_PHYSICAL_ADDRESS BaseAddress,
826 IN UINT64 Length
827 )
828 {
829 return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
830 }
831
832 RETURN_STATUS
833 EFIAPI
834 ArmMmuBaseLibConstructor (
835 VOID
836 )
837 {
838 return RETURN_SUCCESS;
839 }