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