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