]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/ArmLib/AArch64/AArch64Mmu.c
ArmPkg/AArch64Mmu: Fix XN attribute for device memory
[mirror_edk2.git] / ArmPkg / Library / ArmLib / AArch64 / AArch64Mmu.c
CommitLineData
25402f5d
HL
1/** @file\r
2* File managing the MMU for ARMv8 architecture\r
3*\r
19dc108b 4* Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
25402f5d
HL
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/AArch64.h>\r
18#include <Library/BaseMemoryLib.h>\r
19#include <Library/MemoryAllocationLib.h>\r
20#include <Library/ArmLib.h>\r
21#include <Library/BaseLib.h>\r
22#include <Library/DebugLib.h>\r
23#include "AArch64Lib.h"\r
24#include "ArmLibPrivate.h"\r
25\r
26// We use this index definition to define an invalid block entry\r
27#define TT_ATTR_INDX_INVALID ((UINT32)~0)\r
28\r
29STATIC\r
30UINT64\r
31ArmMemoryAttributeToPageAttribute (\r
32 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes\r
33 )\r
34{\r
35 switch (Attributes) {\r
36 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:\r
25402f5d 37 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:\r
0c9a522f
AB
38 return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
39\r
40 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:\r
25402f5d 41 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:\r
0c9a522f
AB
42 return TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;\r
43\r
44 // Uncached and device mappings are treated as outer shareable by default,\r
45 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:\r
25402f5d
HL
46 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:\r
47 return TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
0c9a522f 48\r
25402f5d
HL
49 default:\r
50 ASSERT(0);\r
0c9a522f
AB
51 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:\r
52 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:\r
6bc35cba 53 if (ArmReadCurrentEL () == AARCH64_EL2)\r
40e06cad 54 return TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;\r
6bc35cba 55 else\r
40e06cad 56 return TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK;\r
25402f5d
HL
57 }\r
58}\r
59\r
60UINT64\r
61PageAttributeToGcdAttribute (\r
62 IN UINT64 PageAttributes\r
63 )\r
64{\r
65 UINT64 GcdAttributes;\r
66\r
67 switch (PageAttributes & TT_ATTR_INDX_MASK) {\r
68 case TT_ATTR_INDX_DEVICE_MEMORY:\r
69 GcdAttributes = EFI_MEMORY_UC;\r
70 break;\r
71 case TT_ATTR_INDX_MEMORY_NON_CACHEABLE:\r
72 GcdAttributes = EFI_MEMORY_WC;\r
73 break;\r
74 case TT_ATTR_INDX_MEMORY_WRITE_THROUGH:\r
75 GcdAttributes = EFI_MEMORY_WT;\r
76 break;\r
77 case TT_ATTR_INDX_MEMORY_WRITE_BACK:\r
78 GcdAttributes = EFI_MEMORY_WB;\r
79 break;\r
80 default:\r
81 DEBUG ((EFI_D_ERROR, "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n", PageAttributes));\r
82 ASSERT (0);\r
83 // The Global Coherency Domain (GCD) value is defined as a bit set.\r
84 // Returning 0 means no attribute has been set.\r
85 GcdAttributes = 0;\r
86 }\r
87\r
88 // Determine protection attributes\r
89 if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) || ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO)) {\r
90 // Read only cases map to write-protect\r
91 GcdAttributes |= EFI_MEMORY_WP;\r
92 }\r
93\r
94 // Process eXecute Never attribute\r
95 if ((PageAttributes & (TT_PXN_MASK | TT_UXN_MASK)) != 0 ) {\r
96 GcdAttributes |= EFI_MEMORY_XP;\r
97 }\r
98\r
99 return GcdAttributes;\r
100}\r
101\r
25402f5d
HL
102ARM_MEMORY_REGION_ATTRIBUTES\r
103GcdAttributeToArmAttribute (\r
104 IN UINT64 GcdAttributes\r
105 )\r
106{\r
107 switch (GcdAttributes & 0xFF) {\r
108 case EFI_MEMORY_UC:\r
109 return ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;\r
110 case EFI_MEMORY_WC:\r
111 return ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;\r
112 case EFI_MEMORY_WT:\r
113 return ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH;\r
114 case EFI_MEMORY_WB:\r
115 return ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK;\r
116 default:\r
117 DEBUG ((EFI_D_ERROR, "GcdAttributeToArmAttribute: 0x%lX attributes is not supported.\n", GcdAttributes));\r
118 ASSERT (0);\r
119 return ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;\r
120 }\r
121}\r
122\r
123// Describe the T0SZ values for each translation table level\r
124typedef struct {\r
125 UINTN MinT0SZ;\r
126 UINTN MaxT0SZ;\r
127 UINTN LargestT0SZ; // Generally (MaxT0SZ == LargestT0SZ) but at the Level3 Table\r
128 // the MaxT0SZ is not at the boundary of the table\r
129} T0SZ_DESCRIPTION_PER_LEVEL;\r
130\r
131// Map table for the corresponding Level of Table\r
132STATIC CONST T0SZ_DESCRIPTION_PER_LEVEL T0SZPerTableLevel[] = {\r
133 { 16, 24, 24 }, // Table Level 0\r
134 { 25, 33, 33 }, // Table Level 1\r
135 { 34, 39, 42 } // Table Level 2\r
136};\r
137\r
138VOID\r
139GetRootTranslationTableInfo (\r
140 IN UINTN T0SZ,\r
141 OUT UINTN *TableLevel,\r
142 OUT UINTN *TableEntryCount\r
143 )\r
144{\r
145 UINTN Index;\r
146\r
147 // Identify the level of the root table from the given T0SZ\r
148 for (Index = 0; Index < sizeof (T0SZPerTableLevel) / sizeof (T0SZ_DESCRIPTION_PER_LEVEL); Index++) {\r
149 if (T0SZ <= T0SZPerTableLevel[Index].MaxT0SZ) {\r
150 break;\r
151 }\r
152 }\r
153\r
154 // If we have not found the corresponding maximum T0SZ then we use the last one\r
155 if (Index == sizeof (T0SZPerTableLevel) / sizeof (T0SZ_DESCRIPTION_PER_LEVEL)) {\r
156 Index--;\r
157 }\r
158\r
159 // Get the level of the root table\r
160 if (TableLevel) {\r
161 *TableLevel = Index;\r
162 }\r
163\r
164 // The Size of the Table is 2^(T0SZ-LargestT0SZ)\r
165 if (TableEntryCount) {\r
166 *TableEntryCount = 1 << (T0SZPerTableLevel[Index].LargestT0SZ - T0SZ + 1);\r
167 }\r
168}\r
169\r
170STATIC\r
171VOID\r
172LookupAddresstoRootTable (\r
173 IN UINT64 MaxAddress,\r
174 OUT UINTN *T0SZ,\r
175 OUT UINTN *TableEntryCount\r
176 )\r
177{\r
178 UINTN TopBit;\r
179\r
180 // Check the parameters are not NULL\r
181 ASSERT ((T0SZ != NULL) && (TableEntryCount != NULL));\r
182\r
183 // Look for the highest bit set in MaxAddress\r
184 for (TopBit = 63; TopBit != 0; TopBit--) {\r
185 if ((1ULL << TopBit) & MaxAddress) {\r
186 // MaxAddress top bit is found\r
187 TopBit = TopBit + 1;\r
188 break;\r
189 }\r
190 }\r
191 ASSERT (TopBit != 0);\r
192\r
193 // Calculate T0SZ from the top bit of the MaxAddress\r
194 *T0SZ = 64 - TopBit;\r
195\r
196 // Get the Table info from T0SZ\r
197 GetRootTranslationTableInfo (*T0SZ, NULL, TableEntryCount);\r
198}\r
199\r
200STATIC\r
201UINT64*\r
202GetBlockEntryListFromAddress (\r
203 IN UINT64 *RootTable,\r
204 IN UINT64 RegionStart,\r
205 OUT UINTN *TableLevel,\r
206 IN OUT UINT64 *BlockEntrySize,\r
edff645f 207 OUT UINT64 **LastBlockEntry\r
25402f5d
HL
208 )\r
209{\r
210 UINTN RootTableLevel;\r
211 UINTN RootTableEntryCount;\r
212 UINT64 *TranslationTable;\r
213 UINT64 *BlockEntry;\r
ebb92353 214 UINT64 *SubTableBlockEntry;\r
25402f5d
HL
215 UINT64 BlockEntryAddress;\r
216 UINTN BaseAddressAlignment;\r
217 UINTN PageLevel;\r
218 UINTN Index;\r
219 UINTN IndexLevel;\r
220 UINTN T0SZ;\r
221 UINT64 Attributes;\r
222 UINT64 TableAttributes;\r
223\r
224 // Initialize variable\r
225 BlockEntry = NULL;\r
226\r
227 // Ensure the parameters are valid\r
19dc108b
OM
228 if (!(TableLevel && BlockEntrySize && LastBlockEntry)) {\r
229 ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
230 return NULL;\r
231 }\r
25402f5d
HL
232\r
233 // Ensure the Region is aligned on 4KB boundary\r
19dc108b
OM
234 if ((RegionStart & (SIZE_4KB - 1)) != 0) {\r
235 ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
236 return NULL;\r
237 }\r
25402f5d 238\r
41f89016
HG
239 // Ensure the required size is aligned on 4KB boundary and not 0\r
240 if ((*BlockEntrySize & (SIZE_4KB - 1)) != 0 || *BlockEntrySize == 0) {\r
19dc108b
OM
241 ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
242 return NULL;\r
243 }\r
25402f5d 244\r
25402f5d
HL
245 T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
246 // Get the Table info from T0SZ\r
247 GetRootTranslationTableInfo (T0SZ, &RootTableLevel, &RootTableEntryCount);\r
25402f5d
HL
248\r
249 // If the start address is 0x0 then we use the size of the region to identify the alignment\r
250 if (RegionStart == 0) {\r
251 // Identify the highest possible alignment for the Region Size\r
41f89016 252 BaseAddressAlignment = LowBitSet64 (*BlockEntrySize);\r
25402f5d
HL
253 } else {\r
254 // Identify the highest possible alignment for the Base Address\r
41f89016 255 BaseAddressAlignment = LowBitSet64 (RegionStart);\r
25402f5d
HL
256 }\r
257\r
54d8d4dc
AB
258 // Identify the Page Level the RegionStart must belong to. Note that PageLevel\r
259 // should be at least 1 since block translations are not supported at level 0\r
260 PageLevel = MAX (3 - ((BaseAddressAlignment - 12) / 9), 1);\r
25402f5d 261\r
6ea162c2
OM
262 // If the required size is smaller than the current block size then we need to go to the page below.\r
263 // The PageLevel was calculated on the Base Address alignment but did not take in account the alignment\r
264 // of the allocation size\r
946067bf 265 while (*BlockEntrySize < TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel)) {\r
25402f5d
HL
266 // It does not fit so we need to go a page level above\r
267 PageLevel++;\r
268 }\r
269\r
25402f5d
HL
270 //\r
271 // Get the Table Descriptor for the corresponding PageLevel. We need to decompose RegionStart to get appropriate entries\r
272 //\r
273\r
274 TranslationTable = RootTable;\r
275 for (IndexLevel = RootTableLevel; IndexLevel <= PageLevel; IndexLevel++) {\r
276 BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel, RegionStart);\r
277\r
278 if ((IndexLevel != 3) && ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) {\r
279 // Go to the next table\r
280 TranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
281\r
edff645f 282 // If we are at the last level then update the last level to next level\r
25402f5d 283 if (IndexLevel == PageLevel) {\r
edff645f
HG
284 // Enter the next level\r
285 PageLevel++;\r
25402f5d
HL
286 }\r
287 } else if ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) {\r
288 // If we are not at the last level then we need to split this BlockEntry\r
289 if (IndexLevel != PageLevel) {\r
290 // Retrieve the attributes from the block entry\r
291 Attributes = *BlockEntry & TT_ATTRIBUTES_MASK;\r
292\r
293 // Convert the block entry attributes into Table descriptor attributes\r
294 TableAttributes = TT_TABLE_AP_NO_PERMISSION;\r
295 if (Attributes & TT_PXN_MASK) {\r
296 TableAttributes = TT_TABLE_PXN;\r
297 }\r
2afeabd1
AB
298 // XN maps to UXN in the EL1&0 translation regime\r
299 if (Attributes & TT_XN_MASK) {\r
25402f5d
HL
300 TableAttributes = TT_TABLE_XN;\r
301 }\r
302 if (Attributes & TT_NS) {\r
303 TableAttributes = TT_TABLE_NS;\r
304 }\r
305\r
306 // Get the address corresponding at this entry\r
307 BlockEntryAddress = RegionStart;\r
308 BlockEntryAddress = BlockEntryAddress >> TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel);\r
309 // Shift back to right to set zero before the effective address\r
310 BlockEntryAddress = BlockEntryAddress << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel);\r
311\r
6ea162c2
OM
312 // Set the correct entry type for the next page level\r
313 if ((IndexLevel + 1) == 3) {\r
25402f5d
HL
314 Attributes |= TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
315 } else {\r
316 Attributes |= TT_TYPE_BLOCK_ENTRY;\r
317 }\r
318\r
319 // Create a new translation table\r
7d189f99 320 TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE);\r
25402f5d
HL
321 if (TranslationTable == NULL) {\r
322 return NULL;\r
323 }\r
25402f5d 324\r
ebb92353
OM
325 // Populate the newly created lower level table\r
326 SubTableBlockEntry = TranslationTable;\r
327 for (Index = 0; Index < TT_ENTRY_COUNT; Index++) {\r
328 *SubTableBlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1)));\r
329 SubTableBlockEntry++;\r
330 }\r
331\r
6ea162c2 332 // Fill the BlockEntry with the new TranslationTable\r
25402f5d 333 *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY;\r
25402f5d
HL
334 }\r
335 } else {\r
25402f5d 336 if (IndexLevel != PageLevel) {\r
8bb7f03a
OM
337 //\r
338 // Case when we have an Invalid Entry and we are at a page level above of the one targetted.\r
339 //\r
340\r
25402f5d 341 // Create a new translation table\r
7d189f99 342 TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE);\r
25402f5d
HL
343 if (TranslationTable == NULL) {\r
344 return NULL;\r
345 }\r
25402f5d
HL
346\r
347 ZeroMem (TranslationTable, TT_ENTRY_COUNT * sizeof(UINT64));\r
348\r
349 // Fill the new BlockEntry with the TranslationTable\r
350 *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TT_TYPE_TABLE_ENTRY;\r
351 }\r
352 }\r
353 }\r
354\r
edff645f
HG
355 // Expose the found PageLevel to the caller\r
356 *TableLevel = PageLevel;\r
357\r
358 // Now, we have the Table Level we can get the Block Size associated to this table\r
359 *BlockEntrySize = TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel);\r
360\r
361 // The last block of the root table depends on the number of entry in this table,\r
362 // otherwise it is always the (TT_ENTRY_COUNT - 1)th entry in the table.\r
363 *LastBlockEntry = TT_LAST_BLOCK_ADDRESS(TranslationTable,\r
364 (PageLevel == RootTableLevel) ? RootTableEntryCount : TT_ENTRY_COUNT);\r
365\r
25402f5d
HL
366 return BlockEntry;\r
367}\r
368\r
369STATIC\r
370RETURN_STATUS\r
5ab77c66
AB
371UpdateRegionMapping (\r
372 IN UINT64 *RootTable,\r
373 IN UINT64 RegionStart,\r
374 IN UINT64 RegionLength,\r
375 IN UINT64 Attributes,\r
376 IN UINT64 BlockEntryMask\r
25402f5d
HL
377 )\r
378{\r
25402f5d 379 UINT32 Type;\r
5ab77c66
AB
380 UINT64 *BlockEntry;\r
381 UINT64 *LastBlockEntry;\r
25402f5d
HL
382 UINT64 BlockEntrySize;\r
383 UINTN TableLevel;\r
384\r
385 // Ensure the Length is aligned on 4KB boundary\r
5ab77c66 386 if ((RegionLength == 0) || ((RegionLength & (SIZE_4KB - 1)) != 0)) {\r
19dc108b
OM
387 ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
388 return RETURN_INVALID_PARAMETER;\r
389 }\r
25402f5d 390\r
25402f5d
HL
391 do {\r
392 // Get the first Block Entry that matches the Virtual Address and also the information on the Table Descriptor\r
393 // such as the the size of the Block Entry and the address of the last BlockEntry of the Table Descriptor\r
5ab77c66 394 BlockEntrySize = RegionLength;\r
25402f5d
HL
395 BlockEntry = GetBlockEntryListFromAddress (RootTable, RegionStart, &TableLevel, &BlockEntrySize, &LastBlockEntry);\r
396 if (BlockEntry == NULL) {\r
397 // GetBlockEntryListFromAddress() return NULL when it fails to allocate new pages from the Translation Tables\r
398 return RETURN_OUT_OF_RESOURCES;\r
399 }\r
400\r
401 if (TableLevel != 3) {\r
402 Type = TT_TYPE_BLOCK_ENTRY;\r
403 } else {\r
404 Type = TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
405 }\r
406\r
407 do {\r
408 // Fill the Block Entry with attribute and output block address\r
5ab77c66
AB
409 *BlockEntry &= BlockEntryMask;\r
410 *BlockEntry |= (RegionStart & TT_ADDRESS_MASK_BLOCK_ENTRY) | Attributes | Type;\r
25402f5d
HL
411\r
412 // Go to the next BlockEntry\r
413 RegionStart += BlockEntrySize;\r
5ab77c66 414 RegionLength -= BlockEntrySize;\r
25402f5d 415 BlockEntry++;\r
84836814
HG
416\r
417 // Break the inner loop when next block is a table\r
418 // Rerun GetBlockEntryListFromAddress to avoid page table memory leak\r
419 if (TableLevel != 3 &&\r
420 (*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
421 break;\r
422 }\r
5ab77c66
AB
423 } while ((RegionLength >= BlockEntrySize) && (BlockEntry <= LastBlockEntry));\r
424 } while (RegionLength != 0);\r
25402f5d
HL
425\r
426 return RETURN_SUCCESS;\r
427}\r
428\r
5ab77c66
AB
429STATIC\r
430RETURN_STATUS\r
431FillTranslationTable (\r
432 IN UINT64 *RootTable,\r
433 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion\r
434 )\r
435{\r
436 return UpdateRegionMapping (\r
437 RootTable,\r
438 MemoryRegion->VirtualBase,\r
439 MemoryRegion->Length,\r
440 ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | TT_AF,\r
441 0\r
442 );\r
443}\r
444\r
25402f5d
HL
445RETURN_STATUS\r
446SetMemoryAttributes (\r
447 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
448 IN UINT64 Length,\r
449 IN UINT64 Attributes,\r
450 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
451 )\r
e6f3ed43 452{\r
25402f5d
HL
453 RETURN_STATUS Status;\r
454 ARM_MEMORY_REGION_DESCRIPTOR MemoryRegion;\r
455 UINT64 *TranslationTable;\r
456\r
457 MemoryRegion.PhysicalBase = BaseAddress;\r
458 MemoryRegion.VirtualBase = BaseAddress;\r
459 MemoryRegion.Length = Length;\r
460 MemoryRegion.Attributes = GcdAttributeToArmAttribute (Attributes);\r
461\r
462 TranslationTable = ArmGetTTBR0BaseAddress ();\r
463\r
e6f3ed43
LL
464 Status = FillTranslationTable (TranslationTable, &MemoryRegion);\r
465 if (RETURN_ERROR (Status)) {\r
466 return Status;\r
25402f5d
HL
467 }\r
468\r
25402f5d
HL
469 // Invalidate all TLB entries so changes are synced\r
470 ArmInvalidateTlb ();\r
471\r
472 return RETURN_SUCCESS;\r
473}\r
474\r
4d9a4f62
AB
475STATIC\r
476RETURN_STATUS\r
477SetMemoryRegionAttribute (\r
478 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
479 IN UINT64 Length,\r
480 IN UINT64 Attributes,\r
481 IN UINT64 BlockEntryMask\r
482 )\r
483{\r
484 RETURN_STATUS Status;\r
485 UINT64 *RootTable;\r
486\r
487 RootTable = ArmGetTTBR0BaseAddress ();\r
488\r
489 Status = UpdateRegionMapping (RootTable, BaseAddress, Length, Attributes, BlockEntryMask);\r
490 if (RETURN_ERROR (Status)) {\r
491 return Status;\r
492 }\r
493\r
494 // Invalidate all TLB entries so changes are synced\r
495 ArmInvalidateTlb ();\r
496\r
497 return RETURN_SUCCESS;\r
498}\r
499\r
500RETURN_STATUS\r
501ArmSetMemoryRegionNoExec (\r
502 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
503 IN UINT64 Length\r
504 )\r
505{\r
506 UINT64 Val;\r
507\r
508 if (ArmReadCurrentEL () == AARCH64_EL1) {\r
509 Val = TT_PXN_MASK | TT_UXN_MASK;\r
510 } else {\r
511 Val = TT_XN_MASK;\r
512 }\r
513\r
514 return SetMemoryRegionAttribute (\r
515 BaseAddress,\r
516 Length,\r
517 Val,\r
518 ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
519}\r
520\r
521RETURN_STATUS\r
522ArmClearMemoryRegionNoExec (\r
523 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
524 IN UINT64 Length\r
525 )\r
526{\r
527 UINT64 Mask;\r
528\r
529 // XN maps to UXN in the EL1&0 translation regime\r
530 Mask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_PXN_MASK | TT_XN_MASK);\r
531\r
532 return SetMemoryRegionAttribute (\r
533 BaseAddress,\r
534 Length,\r
535 0,\r
536 Mask);\r
537}\r
538\r
539RETURN_STATUS\r
540ArmSetMemoryRegionReadOnly (\r
541 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
542 IN UINT64 Length\r
543 )\r
544{\r
545 return SetMemoryRegionAttribute (\r
546 BaseAddress,\r
547 Length,\r
548 TT_AP_RO_RO,\r
549 ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
550}\r
551\r
552RETURN_STATUS\r
553ArmClearMemoryRegionReadOnly (\r
554 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
555 IN UINT64 Length\r
556 )\r
557{\r
558 return SetMemoryRegionAttribute (\r
559 BaseAddress,\r
560 Length,\r
b5d89de1 561 TT_AP_RW_RW,\r
4d9a4f62
AB
562 ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK));\r
563}\r
564\r
25402f5d
HL
565RETURN_STATUS\r
566EFIAPI\r
567ArmConfigureMmu (\r
568 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,\r
569 OUT VOID **TranslationTableBase OPTIONAL,\r
570 OUT UINTN *TranslationTableSize OPTIONAL\r
571 )\r
572{\r
573 VOID* TranslationTable;\r
574 UINTN TranslationTablePageCount;\r
575 UINT32 TranslationTableAttribute;\r
576 ARM_MEMORY_REGION_DESCRIPTOR *MemoryTableEntry;\r
577 UINT64 MaxAddress;\r
578 UINT64 TopAddress;\r
579 UINTN T0SZ;\r
580 UINTN RootTableEntryCount;\r
581 UINT64 TCR;\r
582 RETURN_STATUS Status;\r
583\r
8bb7f03a 584 if(MemoryTable == NULL) {\r
19dc108b
OM
585 ASSERT (MemoryTable != NULL);\r
586 return RETURN_INVALID_PARAMETER;\r
587 }\r
25402f5d
HL
588\r
589 // Identify the highest address of the memory table\r
590 MaxAddress = MemoryTable->PhysicalBase + MemoryTable->Length - 1;\r
591 MemoryTableEntry = MemoryTable;\r
592 while (MemoryTableEntry->Length != 0) {\r
593 TopAddress = MemoryTableEntry->PhysicalBase + MemoryTableEntry->Length - 1;\r
594 if (TopAddress > MaxAddress) {\r
595 MaxAddress = TopAddress;\r
596 }\r
597 MemoryTableEntry++;\r
598 }\r
599\r
600 // Lookup the Table Level to get the information\r
601 LookupAddresstoRootTable (MaxAddress, &T0SZ, &RootTableEntryCount);\r
602\r
603 //\r
604 // Set TCR that allows us to retrieve T0SZ in the subsequent functions\r
605 //\r
e21227c6
OM
606 // Ideally we will be running at EL2, but should support EL1 as well.\r
607 // UEFI should not run at EL3.\r
608 if (ArmReadCurrentEL () == AARCH64_EL2) {\r
609 //Note: Bits 23 and 31 are reserved(RES1) bits in TCR_EL2\r
25402f5d
HL
610 TCR = T0SZ | (1UL << 31) | (1UL << 23) | TCR_TG0_4KB;\r
611\r
612 // Set the Physical Address Size using MaxAddress\r
613 if (MaxAddress < SIZE_4GB) {\r
614 TCR |= TCR_PS_4GB;\r
615 } else if (MaxAddress < SIZE_64GB) {\r
616 TCR |= TCR_PS_64GB;\r
617 } else if (MaxAddress < SIZE_1TB) {\r
618 TCR |= TCR_PS_1TB;\r
619 } else if (MaxAddress < SIZE_4TB) {\r
620 TCR |= TCR_PS_4TB;\r
621 } else if (MaxAddress < SIZE_16TB) {\r
622 TCR |= TCR_PS_16TB;\r
623 } else if (MaxAddress < SIZE_256TB) {\r
624 TCR |= TCR_PS_256TB;\r
625 } else {\r
e21227c6
OM
626 DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n", MaxAddress));\r
627 ASSERT (0); // Bigger than 48-bit memory space are not supported\r
628 return RETURN_UNSUPPORTED;\r
629 }\r
630 } else if (ArmReadCurrentEL () == AARCH64_EL1) {\r
fafb7e9c
MR
631 // Due to Cortex-A57 erratum #822227 we must set TG1[1] == 1, regardless of EPD1.\r
632 TCR = T0SZ | TCR_TG0_4KB | TCR_TG1_4KB | TCR_EPD1;\r
e21227c6
OM
633\r
634 // Set the Physical Address Size using MaxAddress\r
635 if (MaxAddress < SIZE_4GB) {\r
636 TCR |= TCR_IPS_4GB;\r
637 } else if (MaxAddress < SIZE_64GB) {\r
638 TCR |= TCR_IPS_64GB;\r
639 } else if (MaxAddress < SIZE_1TB) {\r
640 TCR |= TCR_IPS_1TB;\r
641 } else if (MaxAddress < SIZE_4TB) {\r
642 TCR |= TCR_IPS_4TB;\r
643 } else if (MaxAddress < SIZE_16TB) {\r
644 TCR |= TCR_IPS_16TB;\r
645 } else if (MaxAddress < SIZE_256TB) {\r
646 TCR |= TCR_IPS_256TB;\r
647 } else {\r
648 DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n", MaxAddress));\r
25402f5d
HL
649 ASSERT (0); // Bigger than 48-bit memory space are not supported\r
650 return RETURN_UNSUPPORTED;\r
651 }\r
652 } else {\r
e21227c6 653 ASSERT (0); // UEFI is only expected to run at EL2 and EL1, not EL3.\r
25402f5d
HL
654 return RETURN_UNSUPPORTED;\r
655 }\r
656\r
657 // Set TCR\r
658 ArmSetTCR (TCR);\r
659\r
660 // Allocate pages for translation table\r
7d189f99
HG
661 TranslationTablePageCount = EFI_SIZE_TO_PAGES(RootTableEntryCount * sizeof(UINT64));\r
662 TranslationTable = (UINT64*)AllocateAlignedPages (TranslationTablePageCount, TT_ALIGNMENT_DESCRIPTION_TABLE);\r
25402f5d
HL
663 if (TranslationTable == NULL) {\r
664 return RETURN_OUT_OF_RESOURCES;\r
665 }\r
25402f5d
HL
666 // We set TTBR0 just after allocating the table to retrieve its location from the subsequent\r
667 // functions without needing to pass this value across the functions. The MMU is only enabled\r
668 // after the translation tables are populated.\r
669 ArmSetTTBR0 (TranslationTable);\r
670\r
671 if (TranslationTableBase != NULL) {\r
672 *TranslationTableBase = TranslationTable;\r
673 }\r
674\r
675 if (TranslationTableSize != NULL) {\r
676 *TranslationTableSize = RootTableEntryCount * sizeof(UINT64);\r
677 }\r
678\r
679 ZeroMem (TranslationTable, RootTableEntryCount * sizeof(UINT64));\r
680\r
681 // Disable MMU and caches. ArmDisableMmu() also invalidates the TLBs\r
682 ArmDisableMmu ();\r
683 ArmDisableDataCache ();\r
684 ArmDisableInstructionCache ();\r
685\r
686 // Make sure nothing sneaked into the cache\r
687 ArmCleanInvalidateDataCache ();\r
688 ArmInvalidateInstructionCache ();\r
689\r
690 TranslationTableAttribute = TT_ATTR_INDX_INVALID;\r
691 while (MemoryTable->Length != 0) {\r
692 // Find the memory attribute for the Translation Table\r
693 if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) &&\r
694 ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {\r
695 TranslationTableAttribute = MemoryTable->Attributes;\r
696 }\r
697\r
698 Status = FillTranslationTable (TranslationTable, MemoryTable);\r
699 if (RETURN_ERROR (Status)) {\r
700 goto FREE_TRANSLATION_TABLE;\r
701 }\r
702 MemoryTable++;\r
703 }\r
704\r
705 // Translate the Memory Attributes into Translation Table Register Attributes\r
706 if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED) ||\r
707 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED)) {\r
708 TCR |= TCR_SH_NON_SHAREABLE | TCR_RGN_OUTER_NON_CACHEABLE | TCR_RGN_INNER_NON_CACHEABLE;\r
709 } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||\r
710 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {\r
711 TCR |= TCR_SH_INNER_SHAREABLE | TCR_RGN_OUTER_WRITE_BACK_ALLOC | TCR_RGN_INNER_WRITE_BACK_ALLOC;\r
712 } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) ||\r
713 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH)) {\r
714 TCR |= TCR_SH_NON_SHAREABLE | TCR_RGN_OUTER_WRITE_THROUGH | TCR_RGN_INNER_WRITE_THROUGH;\r
715 } else {\r
716 // If we failed to find a mapping that contains the root translation table then it probably means the translation table\r
717 // is not mapped in the given memory map.\r
718 ASSERT (0);\r
719 Status = RETURN_UNSUPPORTED;\r
720 goto FREE_TRANSLATION_TABLE;\r
721 }\r
722\r
1eb5b4f2
OM
723 // Set again TCR after getting the Translation Table attributes\r
724 ArmSetTCR (TCR);\r
725\r
25402f5d
HL
726 ArmSetMAIR (MAIR_ATTR(TT_ATTR_INDX_DEVICE_MEMORY, MAIR_ATTR_DEVICE_MEMORY) | // mapped to EFI_MEMORY_UC\r
727 MAIR_ATTR(TT_ATTR_INDX_MEMORY_NON_CACHEABLE, MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE) | // mapped to EFI_MEMORY_WC\r
728 MAIR_ATTR(TT_ATTR_INDX_MEMORY_WRITE_THROUGH, MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH) | // mapped to EFI_MEMORY_WT\r
729 MAIR_ATTR(TT_ATTR_INDX_MEMORY_WRITE_BACK, MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK)); // mapped to EFI_MEMORY_WB\r
730\r
731 ArmDisableAlignmentCheck ();\r
732 ArmEnableInstructionCache ();\r
733 ArmEnableDataCache ();\r
734\r
735 ArmEnableMmu ();\r
736 return RETURN_SUCCESS;\r
737\r
738FREE_TRANSLATION_TABLE:\r
739 FreePages (TranslationTable, TranslationTablePageCount);\r
740 return Status;\r
741}\r