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