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