]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
ArmPkg/ArmMmuLib ARM: handle unmapped sections when updating permissions
[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_WRITE_BACK) ||
347 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {
348 TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC : TTBR_WRITE_BACK_ALLOC;
349 } else {
350 // Page tables must reside in memory mapped as write-back cacheable
351 ASSERT (0);
352 return RETURN_UNSUPPORTED;
353 }
354
355 if (TTBRAttributes & TTBR_SHAREABLE) {
356 if (PreferNonshareableMemory ()) {
357 TTBRAttributes ^= TTBR_SHAREABLE;
358 } else {
359 //
360 // Unlike the S bit in the short descriptors, which implies inner shareable
361 // on an implementation that supports two levels, the meaning of the S bit
362 // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
363 // However, we should only set this bit after we have confirmed that the
364 // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
365 //
366 if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
367 TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;
368 }
369 }
370 }
371
372 ArmCleanInvalidateDataCache ();
373 ArmInvalidateInstructionCache ();
374
375 ArmDisableDataCache ();
376 ArmDisableInstructionCache();
377 // TLBs are also invalidated when calling ArmDisableMmu()
378 ArmDisableMmu ();
379
380 // Make sure nothing sneaked into the cache
381 ArmCleanInvalidateDataCache ();
382 ArmInvalidateInstructionCache ();
383
384 ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (TTBRAttributes & 0x7F)));
385
386 //
387 // The TTBCR register value is undefined at reset in the Non-Secure world.
388 // Writing 0 has the effect of:
389 // Clearing EAE: Use short descriptors, as mandated by specification.
390 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
391 // Clearing N: Perform all translation table walks through TTBR0.
392 // (0 is the default reset value in systems not implementing
393 // the Security Extensions.)
394 //
395 ArmSetTTBCR (0);
396
397 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
398 DOMAIN_ACCESS_CONTROL_NONE(14) |
399 DOMAIN_ACCESS_CONTROL_NONE(13) |
400 DOMAIN_ACCESS_CONTROL_NONE(12) |
401 DOMAIN_ACCESS_CONTROL_NONE(11) |
402 DOMAIN_ACCESS_CONTROL_NONE(10) |
403 DOMAIN_ACCESS_CONTROL_NONE( 9) |
404 DOMAIN_ACCESS_CONTROL_NONE( 8) |
405 DOMAIN_ACCESS_CONTROL_NONE( 7) |
406 DOMAIN_ACCESS_CONTROL_NONE( 6) |
407 DOMAIN_ACCESS_CONTROL_NONE( 5) |
408 DOMAIN_ACCESS_CONTROL_NONE( 4) |
409 DOMAIN_ACCESS_CONTROL_NONE( 3) |
410 DOMAIN_ACCESS_CONTROL_NONE( 2) |
411 DOMAIN_ACCESS_CONTROL_NONE( 1) |
412 DOMAIN_ACCESS_CONTROL_CLIENT(0));
413
414 ArmEnableInstructionCache();
415 ArmEnableDataCache();
416 ArmEnableMmu();
417 return RETURN_SUCCESS;
418 }
419
420 STATIC
421 EFI_STATUS
422 ConvertSectionToPages (
423 IN EFI_PHYSICAL_ADDRESS BaseAddress
424 )
425 {
426 UINT32 FirstLevelIdx;
427 UINT32 SectionDescriptor;
428 UINT32 PageTableDescriptor;
429 UINT32 PageDescriptor;
430 UINT32 Index;
431
432 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
433 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
434
435 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
436
437 // Obtain page table base
438 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
439
440 // Calculate index into first level translation table for start of modification
441 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
442 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
443
444 // Get section attributes and convert to page attributes
445 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
446 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
447
448 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
449 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)AllocatePages (1);
450 if (PageTable == NULL) {
451 return EFI_OUT_OF_RESOURCES;
452 }
453
454 // Write the page table entries out
455 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
456 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
457 }
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 // Only need to update if we are changing the entry
593 PageTable[PageTableIndex] = PageTableEntry;
594 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
595 }
596
597 Status = EFI_SUCCESS;
598 Offset += TT_DESCRIPTOR_PAGE_SIZE;
599
600 } // End first level translation table loop
601
602 return Status;
603 }
604
605 STATIC
606 EFI_STATUS
607 UpdateSectionEntries (
608 IN EFI_PHYSICAL_ADDRESS BaseAddress,
609 IN UINT64 Length,
610 IN UINT64 Attributes
611 )
612 {
613 EFI_STATUS Status = EFI_SUCCESS;
614 UINT32 EntryMask;
615 UINT32 EntryValue;
616 UINT32 FirstLevelIdx;
617 UINT32 NumSections;
618 UINT32 i;
619 UINT32 CurrentDescriptor;
620 UINT32 Descriptor;
621 VOID *Mva;
622 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
623
624 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
625 // EntryValue: values at bit positions specified by EntryMask
626
627 // Make sure we handle a section range that is unmapped
628 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK | TT_DESCRIPTOR_SECTION_XN_MASK |
629 TT_DESCRIPTOR_SECTION_AP_MASK;
630 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
631
632 // Although the PI spec is unclear on this, the GCD guarantees that only
633 // one Attribute bit is set at a time, so the order of the conditionals below
634 // is irrelevant. If no memory attribute is specified, we preserve whatever
635 // memory type is set in the page tables, and update the permission attributes
636 // only.
637 if (Attributes & EFI_MEMORY_UC) {
638 // modify cacheability attributes
639 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
640 // map to strongly ordered
641 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
642 } else if (Attributes & EFI_MEMORY_WC) {
643 // modify cacheability attributes
644 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
645 // map to normal non-cachable
646 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
647 } else if (Attributes & EFI_MEMORY_WT) {
648 // modify cacheability attributes
649 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
650 // write through with no-allocate
651 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
652 } else if (Attributes & EFI_MEMORY_WB) {
653 // modify cacheability attributes
654 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
655 // write back (with allocate)
656 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
657 } else if (Attributes & CACHE_ATTRIBUTE_MASK) {
658 // catch unsupported memory type attributes
659 ASSERT (FALSE);
660 return EFI_UNSUPPORTED;
661 }
662
663 if (Attributes & EFI_MEMORY_RO) {
664 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
665 } else {
666 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
667 }
668
669 if (Attributes & EFI_MEMORY_XP) {
670 EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
671 }
672
673 // obtain page table base
674 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
675
676 // calculate index into first level translation table for start of modification
677 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
678 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
679
680 // calculate number of 1MB first level entries this applies to
681 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
682
683 // iterate through each descriptor
684 for(i=0; i<NumSections; i++) {
685 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
686
687 // has this descriptor already been coverted to pages?
688 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
689 // forward this 1MB range to page table function instead
690 Status = UpdatePageEntries (
691 (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,
692 TT_DESCRIPTOR_SECTION_SIZE,
693 Attributes,
694 NULL);
695 } else {
696 // still a section entry
697
698 if (CurrentDescriptor != 0) {
699 // mask off appropriate fields
700 Descriptor = CurrentDescriptor & ~EntryMask;
701 } else {
702 Descriptor = ((UINTN)FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT;
703 }
704
705 // mask in new attributes and/or permissions
706 Descriptor |= EntryValue;
707
708 if (CurrentDescriptor != Descriptor) {
709 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
710
711 // Only need to update if we are changing the descriptor
712 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
713 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
714 }
715
716 Status = EFI_SUCCESS;
717 }
718 }
719
720 return Status;
721 }
722
723 EFI_STATUS
724 ArmSetMemoryAttributes (
725 IN EFI_PHYSICAL_ADDRESS BaseAddress,
726 IN UINT64 Length,
727 IN UINT64 Attributes
728 )
729 {
730 EFI_STATUS Status;
731 UINT64 ChunkLength;
732 BOOLEAN FlushTlbs;
733
734 if (Length == 0) {
735 return EFI_SUCCESS;
736 }
737
738 FlushTlbs = FALSE;
739 while (Length > 0) {
740 if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
741 Length >= TT_DESCRIPTOR_SECTION_SIZE) {
742
743 ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
744
745 DEBUG ((DEBUG_PAGE,
746 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
747 BaseAddress, ChunkLength, Attributes));
748
749 Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes);
750
751 FlushTlbs = TRUE;
752 } else {
753
754 //
755 // Process page by page until the next section boundary, but only if
756 // we have more than a section's worth of area to deal with after that.
757 //
758 ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
759 (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
760 if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
761 ChunkLength = Length;
762 }
763
764 DEBUG ((DEBUG_PAGE,
765 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
766 BaseAddress, ChunkLength, Attributes));
767
768 Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,
769 &FlushTlbs);
770 }
771
772 if (EFI_ERROR (Status)) {
773 break;
774 }
775
776 BaseAddress += ChunkLength;
777 Length -= ChunkLength;
778 }
779
780 if (FlushTlbs) {
781 ArmInvalidateTlb ();
782 }
783 return Status;
784 }
785
786 EFI_STATUS
787 ArmSetMemoryRegionNoExec (
788 IN EFI_PHYSICAL_ADDRESS BaseAddress,
789 IN UINT64 Length
790 )
791 {
792 return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);
793 }
794
795 EFI_STATUS
796 ArmClearMemoryRegionNoExec (
797 IN EFI_PHYSICAL_ADDRESS BaseAddress,
798 IN UINT64 Length
799 )
800 {
801 return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
802 }
803
804 EFI_STATUS
805 ArmSetMemoryRegionReadOnly (
806 IN EFI_PHYSICAL_ADDRESS BaseAddress,
807 IN UINT64 Length
808 )
809 {
810 return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);
811 }
812
813 EFI_STATUS
814 ArmClearMemoryRegionReadOnly (
815 IN EFI_PHYSICAL_ADDRESS BaseAddress,
816 IN UINT64 Length
817 )
818 {
819 return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
820 }
821
822 RETURN_STATUS
823 EFIAPI
824 ArmMmuBaseLibConstructor (
825 VOID
826 )
827 {
828 return RETURN_SUCCESS;
829 }