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