]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
ArmPkg: move ARM version of SetMemoryAttributes to ArmMmuLib
[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
453 IN UINT64 Attributes,\r
454 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
455 )\r
456{\r
f49ea03d 457 EFI_STATUS Status;\r
d7f03464 458 UINT64 *TranslationTable;\r
e0307a7d
AB
459 UINT64 PageAttributes;\r
460 UINT64 PageAttributeMask;\r
461\r
462 PageAttributes = GcdAttributeToPageAttribute (Attributes);\r
463 PageAttributeMask = 0;\r
464\r
465 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {\r
466 //\r
467 // No memory type was set in Attributes, so we are going to update the\r
468 // permissions only.\r
469 //\r
470 PageAttributes &= TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK;\r
471 PageAttributeMask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK |\r
472 TT_PXN_MASK | TT_XN_MASK);\r
473 }\r
d7f03464
AB
474\r
475 TranslationTable = ArmGetTTBR0BaseAddress ();\r
476\r
e0307a7d
AB
477 Status = UpdateRegionMapping (\r
478 TranslationTable,\r
479 BaseAddress,\r
480 Length,\r
481 PageAttributes,\r
482 PageAttributeMask);\r
f49ea03d 483 if (EFI_ERROR (Status)) {\r
d7f03464
AB
484 return Status;\r
485 }\r
486\r
487 // Invalidate all TLB entries so changes are synced\r
488 ArmInvalidateTlb ();\r
489\r
f49ea03d 490 return EFI_SUCCESS;\r
d7f03464
AB
491}\r
492\r
493STATIC\r
f49ea03d 494EFI_STATUS\r
d7f03464
AB
495SetMemoryRegionAttribute (\r
496 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
497 IN UINT64 Length,\r
498 IN UINT64 Attributes,\r
499 IN UINT64 BlockEntryMask\r
500 )\r
501{\r
f49ea03d 502 EFI_STATUS Status;\r
d7f03464
AB
503 UINT64 *RootTable;\r
504\r
505 RootTable = ArmGetTTBR0BaseAddress ();\r
506\r
507 Status = UpdateRegionMapping (RootTable, BaseAddress, Length, Attributes, BlockEntryMask);\r
f49ea03d 508 if (EFI_ERROR (Status)) {\r
d7f03464
AB
509 return Status;\r
510 }\r
511\r
512 // Invalidate all TLB entries so changes are synced\r
513 ArmInvalidateTlb ();\r
514\r
f49ea03d 515 return EFI_SUCCESS;\r
d7f03464
AB
516}\r
517\r
f49ea03d 518EFI_STATUS\r
d7f03464
AB
519ArmSetMemoryRegionNoExec (\r
520 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
521 IN UINT64 Length\r
522 )\r
523{\r
524 UINT64 Val;\r
525\r
526 if (ArmReadCurrentEL () == AARCH64_EL1) {\r
527 Val = TT_PXN_MASK | TT_UXN_MASK;\r
528 } else {\r
529 Val = TT_XN_MASK;\r
530 }\r
531\r
532 return SetMemoryRegionAttribute (\r
533 BaseAddress,\r
534 Length,\r
535 Val,\r
536 ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
537}\r
538\r
f49ea03d 539EFI_STATUS\r
d7f03464
AB
540ArmClearMemoryRegionNoExec (\r
541 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
542 IN UINT64 Length\r
543 )\r
544{\r
545 UINT64 Mask;\r
546\r
547 // XN maps to UXN in the EL1&0 translation regime\r
548 Mask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_PXN_MASK | TT_XN_MASK);\r
549\r
550 return SetMemoryRegionAttribute (\r
551 BaseAddress,\r
552 Length,\r
553 0,\r
554 Mask);\r
555}\r
556\r
f49ea03d 557EFI_STATUS\r
d7f03464
AB
558ArmSetMemoryRegionReadOnly (\r
559 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
560 IN UINT64 Length\r
561 )\r
562{\r
563 return SetMemoryRegionAttribute (\r
564 BaseAddress,\r
565 Length,\r
566 TT_AP_RO_RO,\r
567 ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
568}\r
569\r
f49ea03d 570EFI_STATUS\r
d7f03464
AB
571ArmClearMemoryRegionReadOnly (\r
572 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
573 IN UINT64 Length\r
574 )\r
575{\r
576 return SetMemoryRegionAttribute (\r
577 BaseAddress,\r
578 Length,\r
579 TT_AP_RW_RW,\r
580 ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK));\r
581}\r
582\r
f49ea03d 583EFI_STATUS\r
d7f03464
AB
584EFIAPI\r
585ArmConfigureMmu (\r
586 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,\r
587 OUT VOID **TranslationTableBase OPTIONAL,\r
588 OUT UINTN *TranslationTableSize OPTIONAL\r
589 )\r
590{\r
591 VOID* TranslationTable;\r
d7f03464 592 UINT32 TranslationTableAttribute;\r
d7f03464 593 UINT64 MaxAddress;\r
d7f03464
AB
594 UINTN T0SZ;\r
595 UINTN RootTableEntryCount;\r
596 UINT64 TCR;\r
f49ea03d 597 EFI_STATUS Status;\r
d7f03464
AB
598\r
599 if(MemoryTable == NULL) {\r
600 ASSERT (MemoryTable != NULL);\r
f49ea03d 601 return EFI_INVALID_PARAMETER;\r
d7f03464
AB
602 }\r
603\r
dd82465a
AB
604 // Cover the entire GCD memory space\r
605 MaxAddress = (1UL << PcdGet8 (PcdPrePiCpuMemorySize)) - 1;\r
d7f03464
AB
606\r
607 // Lookup the Table Level to get the information\r
608 LookupAddresstoRootTable (MaxAddress, &T0SZ, &RootTableEntryCount);\r
609\r
610 //\r
611 // Set TCR that allows us to retrieve T0SZ in the subsequent functions\r
612 //\r
613 // Ideally we will be running at EL2, but should support EL1 as well.\r
614 // UEFI should not run at EL3.\r
615 if (ArmReadCurrentEL () == AARCH64_EL2) {\r
616 //Note: Bits 23 and 31 are reserved(RES1) bits in TCR_EL2\r
617 TCR = T0SZ | (1UL << 31) | (1UL << 23) | TCR_TG0_4KB;\r
618\r
619 // Set the Physical Address Size using MaxAddress\r
620 if (MaxAddress < SIZE_4GB) {\r
621 TCR |= TCR_PS_4GB;\r
622 } else if (MaxAddress < SIZE_64GB) {\r
623 TCR |= TCR_PS_64GB;\r
624 } else if (MaxAddress < SIZE_1TB) {\r
625 TCR |= TCR_PS_1TB;\r
626 } else if (MaxAddress < SIZE_4TB) {\r
627 TCR |= TCR_PS_4TB;\r
628 } else if (MaxAddress < SIZE_16TB) {\r
629 TCR |= TCR_PS_16TB;\r
630 } else if (MaxAddress < SIZE_256TB) {\r
631 TCR |= TCR_PS_256TB;\r
632 } else {\r
633 DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n", MaxAddress));\r
634 ASSERT (0); // Bigger than 48-bit memory space are not supported\r
f49ea03d 635 return EFI_UNSUPPORTED;\r
d7f03464
AB
636 }\r
637 } else if (ArmReadCurrentEL () == AARCH64_EL1) {\r
638 // Due to Cortex-A57 erratum #822227 we must set TG1[1] == 1, regardless of EPD1.\r
639 TCR = T0SZ | TCR_TG0_4KB | TCR_TG1_4KB | TCR_EPD1;\r
640\r
641 // Set the Physical Address Size using MaxAddress\r
642 if (MaxAddress < SIZE_4GB) {\r
643 TCR |= TCR_IPS_4GB;\r
644 } else if (MaxAddress < SIZE_64GB) {\r
645 TCR |= TCR_IPS_64GB;\r
646 } else if (MaxAddress < SIZE_1TB) {\r
647 TCR |= TCR_IPS_1TB;\r
648 } else if (MaxAddress < SIZE_4TB) {\r
649 TCR |= TCR_IPS_4TB;\r
650 } else if (MaxAddress < SIZE_16TB) {\r
651 TCR |= TCR_IPS_16TB;\r
652 } else if (MaxAddress < SIZE_256TB) {\r
653 TCR |= TCR_IPS_256TB;\r
654 } else {\r
655 DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n", MaxAddress));\r
656 ASSERT (0); // Bigger than 48-bit memory space are not supported\r
f49ea03d 657 return EFI_UNSUPPORTED;\r
d7f03464
AB
658 }\r
659 } else {\r
660 ASSERT (0); // UEFI is only expected to run at EL2 and EL1, not EL3.\r
f49ea03d 661 return EFI_UNSUPPORTED;\r
d7f03464
AB
662 }\r
663\r
35718840
AB
664 //\r
665 // Translation table walks are always cache coherent on ARMv8-A, so cache\r
666 // maintenance on page tables is never needed. Since there is a risk of\r
667 // loss of coherency when using mismatched attributes, and given that memory\r
668 // is mapped cacheable except for extraordinary cases (such as non-coherent\r
669 // DMA), have the page table walker perform cached accesses as well, and\r
670 // assert below that that matches the attributes we use for CPU accesses to\r
671 // the region.\r
672 //\r
673 TCR |= TCR_SH_INNER_SHAREABLE |\r
674 TCR_RGN_OUTER_WRITE_BACK_ALLOC |\r
675 TCR_RGN_INNER_WRITE_BACK_ALLOC;\r
676\r
d7f03464
AB
677 // Set TCR\r
678 ArmSetTCR (TCR);\r
679\r
aa961dea
AB
680 // Allocate pages for translation table\r
681 TranslationTable = AllocatePages (1);\r
d7f03464 682 if (TranslationTable == NULL) {\r
f49ea03d 683 return EFI_OUT_OF_RESOURCES;\r
d7f03464
AB
684 }\r
685 // We set TTBR0 just after allocating the table to retrieve its location from the subsequent\r
686 // functions without needing to pass this value across the functions. The MMU is only enabled\r
687 // after the translation tables are populated.\r
688 ArmSetTTBR0 (TranslationTable);\r
689\r
690 if (TranslationTableBase != NULL) {\r
691 *TranslationTableBase = TranslationTable;\r
692 }\r
693\r
694 if (TranslationTableSize != NULL) {\r
aa961dea 695 *TranslationTableSize = RootTableEntryCount * sizeof(UINT64);\r
d7f03464
AB
696 }\r
697\r
aa961dea 698 ZeroMem (TranslationTable, RootTableEntryCount * sizeof(UINT64));\r
d7f03464
AB
699\r
700 // Disable MMU and caches. ArmDisableMmu() also invalidates the TLBs\r
701 ArmDisableMmu ();\r
702 ArmDisableDataCache ();\r
703 ArmDisableInstructionCache ();\r
704\r
705 // Make sure nothing sneaked into the cache\r
706 ArmCleanInvalidateDataCache ();\r
707 ArmInvalidateInstructionCache ();\r
708\r
709 TranslationTableAttribute = TT_ATTR_INDX_INVALID;\r
710 while (MemoryTable->Length != 0) {\r
35718840
AB
711\r
712 DEBUG_CODE_BEGIN ();\r
713 // Find the memory attribute for the Translation Table\r
714 if ((UINTN)TranslationTable >= MemoryTable->PhysicalBase &&\r
aa961dea 715 (UINTN)TranslationTable + EFI_PAGE_SIZE <= MemoryTable->PhysicalBase +\r
35718840
AB
716 MemoryTable->Length) {\r
717 TranslationTableAttribute = MemoryTable->Attributes;\r
718 }\r
719 DEBUG_CODE_END ();\r
d7f03464
AB
720\r
721 Status = FillTranslationTable (TranslationTable, MemoryTable);\r
f49ea03d 722 if (EFI_ERROR (Status)) {\r
d7f03464
AB
723 goto FREE_TRANSLATION_TABLE;\r
724 }\r
725 MemoryTable++;\r
726 }\r
727\r
35718840
AB
728 ASSERT (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK ||\r
729 TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK);\r
d7f03464
AB
730\r
731 ArmSetMAIR (MAIR_ATTR(TT_ATTR_INDX_DEVICE_MEMORY, MAIR_ATTR_DEVICE_MEMORY) | // mapped to EFI_MEMORY_UC\r
732 MAIR_ATTR(TT_ATTR_INDX_MEMORY_NON_CACHEABLE, MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE) | // mapped to EFI_MEMORY_WC\r
733 MAIR_ATTR(TT_ATTR_INDX_MEMORY_WRITE_THROUGH, MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH) | // mapped to EFI_MEMORY_WT\r
734 MAIR_ATTR(TT_ATTR_INDX_MEMORY_WRITE_BACK, MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK)); // mapped to EFI_MEMORY_WB\r
735\r
736 ArmDisableAlignmentCheck ();\r
526f160f 737 ArmEnableStackAlignmentCheck ();\r
d7f03464
AB
738 ArmEnableInstructionCache ();\r
739 ArmEnableDataCache ();\r
740\r
741 ArmEnableMmu ();\r
f49ea03d 742 return EFI_SUCCESS;\r
d7f03464
AB
743\r
744FREE_TRANSLATION_TABLE:\r
aa961dea 745 FreePages (TranslationTable, 1);\r
d7f03464
AB
746 return Status;\r
747}\r
748\r
749RETURN_STATUS\r
750EFIAPI\r
751ArmMmuBaseLibConstructor (\r
752 VOID\r
753 )\r
754{\r
755 extern UINT32 ArmReplaceLiveTranslationEntrySize;\r
756\r
757 //\r
758 // The ArmReplaceLiveTranslationEntry () helper function may be invoked\r
759 // with the MMU off so we have to ensure that it gets cleaned to the PoC\r
760 //\r
761 WriteBackDataCacheRange (ArmReplaceLiveTranslationEntry,\r
762 ArmReplaceLiveTranslationEntrySize);\r
763\r
764 return RETURN_SUCCESS;\r
765}\r