]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
ArmPkg/CpuDxe: move PageAttributeToGcdAttribute() out of 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
191fa79b 4* Copyright (c) 2011-2020, ARM Limited. All rights reserved.\r
d7f03464 5* Copyright (c) 2016, Linaro Limited. All rights reserved.\r
b7a09b71 6* Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
d7f03464 7*\r
4059386c 8* SPDX-License-Identifier: BSD-2-Clause-Patent\r
d7f03464
AB
9*\r
10**/\r
11\r
12#include <Uefi.h>\r
13#include <Chipset/AArch64.h>\r
14#include <Library/BaseMemoryLib.h>\r
15#include <Library/CacheMaintenanceLib.h>\r
16#include <Library/MemoryAllocationLib.h>\r
17#include <Library/ArmLib.h>\r
18#include <Library/ArmMmuLib.h>\r
19#include <Library/BaseLib.h>\r
20#include <Library/DebugLib.h>\r
21\r
22// We use this index definition to define an invalid block entry\r
23#define TT_ATTR_INDX_INVALID ((UINT32)~0)\r
24\r
25STATIC\r
26UINT64\r
27ArmMemoryAttributeToPageAttribute (\r
28 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes\r
29 )\r
30{\r
31 switch (Attributes) {\r
829633e3
PL
32 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:\r
33 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:\r
34 return TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
35\r
d7f03464
AB
36 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:\r
37 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:\r
38 return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
39\r
40 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:\r
41 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:\r
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
46 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:\r
47 return TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
48\r
49 default:\r
4249278a 50 ASSERT (0);\r
d7f03464
AB
51 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:\r
52 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:\r
53 if (ArmReadCurrentEL () == AARCH64_EL2)\r
54 return TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;\r
55 else\r
56 return TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK;\r
57 }\r
58}\r
59\r
e93cb72e
AB
60#define MIN_T0SZ 16\r
61#define BITS_PER_LEVEL 9\r
d7f03464
AB
62\r
63VOID\r
64GetRootTranslationTableInfo (\r
65 IN UINTN T0SZ,\r
66 OUT UINTN *TableLevel,\r
67 OUT UINTN *TableEntryCount\r
68 )\r
69{\r
d7f03464
AB
70 // Get the level of the root table\r
71 if (TableLevel) {\r
e93cb72e 72 *TableLevel = (T0SZ - MIN_T0SZ) / BITS_PER_LEVEL;\r
d7f03464
AB
73 }\r
74\r
d7f03464 75 if (TableEntryCount) {\r
e93cb72e 76 *TableEntryCount = 1UL << (BITS_PER_LEVEL - (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL);\r
d7f03464
AB
77 }\r
78}\r
79\r
80STATIC\r
81VOID\r
191fa79b 82ReplaceTableEntry (\r
d7f03464 83 IN UINT64 *Entry,\r
d5788777 84 IN UINT64 Value,\r
191fa79b
AB
85 IN UINT64 RegionStart,\r
86 IN BOOLEAN IsLiveBlockMapping\r
d7f03464
AB
87 )\r
88{\r
191fa79b 89 if (!ArmMmuEnabled () || !IsLiveBlockMapping) {\r
d7f03464 90 *Entry = Value;\r
191fa79b 91 ArmUpdateTranslationTableEntry (Entry, (VOID *)(UINTN)RegionStart);\r
d7f03464 92 } else {\r
d5788777 93 ArmReplaceLiveTranslationEntry (Entry, Value, RegionStart);\r
d7f03464
AB
94 }\r
95}\r
96\r
97STATIC\r
98VOID\r
191fa79b 99FreePageTablesRecursive (\r
d390920e
AB
100 IN UINT64 *TranslationTable,\r
101 IN UINTN Level\r
d7f03464
AB
102 )\r
103{\r
191fa79b 104 UINTN Index;\r
d7f03464 105\r
d390920e
AB
106 ASSERT (Level <= 3);\r
107\r
108 if (Level < 3) {\r
109 for (Index = 0; Index < TT_ENTRY_COUNT; Index++) {\r
110 if ((TranslationTable[Index] & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
111 FreePageTablesRecursive ((VOID *)(UINTN)(TranslationTable[Index] &\r
112 TT_ADDRESS_MASK_BLOCK_ENTRY),\r
113 Level + 1);\r
114 }\r
d7f03464
AB
115 }\r
116 }\r
191fa79b 117 FreePages (TranslationTable, 1);\r
d7f03464
AB
118}\r
119\r
5fc89953
AB
120STATIC\r
121BOOLEAN\r
122IsBlockEntry (\r
123 IN UINT64 Entry,\r
124 IN UINTN Level\r
125 )\r
126{\r
127 if (Level == 3) {\r
128 return (Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
129 }\r
130 return (Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY;\r
131}\r
132\r
133STATIC\r
134BOOLEAN\r
135IsTableEntry (\r
136 IN UINT64 Entry,\r
137 IN UINTN Level\r
138 )\r
139{\r
140 if (Level == 3) {\r
141 //\r
142 // TT_TYPE_TABLE_ENTRY aliases TT_TYPE_BLOCK_ENTRY_LEVEL3\r
143 // so we need to take the level into account as well.\r
144 //\r
145 return FALSE;\r
146 }\r
147 return (Entry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY;\r
148}\r
149\r
d7f03464 150STATIC\r
191fa79b
AB
151EFI_STATUS\r
152UpdateRegionMappingRecursive (\r
153 IN UINT64 RegionStart,\r
154 IN UINT64 RegionEnd,\r
155 IN UINT64 AttributeSetMask,\r
156 IN UINT64 AttributeClearMask,\r
157 IN UINT64 *PageTable,\r
158 IN UINTN Level\r
d7f03464
AB
159 )\r
160{\r
191fa79b
AB
161 UINTN BlockShift;\r
162 UINT64 BlockMask;\r
163 UINT64 BlockEnd;\r
164 UINT64 *Entry;\r
165 UINT64 EntryValue;\r
166 VOID *TranslationTable;\r
167 EFI_STATUS Status;\r
d7f03464 168\r
191fa79b 169 ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);\r
d7f03464 170\r
191fa79b
AB
171 BlockShift = (Level + 1) * BITS_PER_LEVEL + MIN_T0SZ;\r
172 BlockMask = MAX_UINT64 >> BlockShift;\r
d7f03464 173\r
191fa79b
AB
174 DEBUG ((DEBUG_VERBOSE, "%a(%d): %llx - %llx set %lx clr %lx\n", __FUNCTION__,\r
175 Level, RegionStart, RegionEnd, AttributeSetMask, AttributeClearMask));\r
d7f03464 176\r
191fa79b
AB
177 for (; RegionStart < RegionEnd; RegionStart = BlockEnd) {\r
178 BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);\r
179 Entry = &PageTable[(RegionStart >> (64 - BlockShift)) & (TT_ENTRY_COUNT - 1)];\r
d7f03464 180\r
191fa79b
AB
181 //\r
182 // If RegionStart or BlockEnd is not aligned to the block size at this\r
183 // level, we will have to create a table mapping in order to map less\r
184 // than a block, and recurse to create the block or page entries at\r
185 // the next level. No block mappings are allowed at all at level 0,\r
186 // so in that case, we have to recurse unconditionally.\r
f7079d1b
AB
187 // If we are changing a table entry and the AttributeClearMask is non-zero,\r
188 // we cannot replace it with a block entry without potentially losing\r
189 // attribute information, so keep the table entry in that case.\r
191fa79b 190 //\r
f7079d1b
AB
191 if (Level == 0 || ((RegionStart | BlockEnd) & BlockMask) != 0 ||\r
192 (IsTableEntry (*Entry, Level) && AttributeClearMask != 0)) {\r
191fa79b 193 ASSERT (Level < 3);\r
d7f03464 194\r
5fc89953 195 if (!IsTableEntry (*Entry, Level)) {\r
191fa79b
AB
196 //\r
197 // No table entry exists yet, so we need to allocate a page table\r
198 // for the next level.\r
199 //\r
674e127e 200 TranslationTable = AllocatePages (1);\r
d7f03464 201 if (TranslationTable == NULL) {\r
191fa79b 202 return EFI_OUT_OF_RESOURCES;\r
d7f03464
AB
203 }\r
204\r
748fea62
AB
205 if (!ArmMmuEnabled ()) {\r
206 //\r
207 // Make sure we are not inadvertently hitting in the caches\r
208 // when populating the page tables.\r
209 //\r
210 InvalidateDataCacheRange (TranslationTable, EFI_PAGE_SIZE);\r
211 }\r
212\r
f7079d1b
AB
213 ZeroMem (TranslationTable, EFI_PAGE_SIZE);\r
214\r
5fc89953 215 if (IsBlockEntry (*Entry, Level)) {\r
191fa79b
AB
216 //\r
217 // We are splitting an existing block entry, so we have to populate\r
218 // the new table with the attributes of the block entry it replaces.\r
219 //\r
220 Status = UpdateRegionMappingRecursive (RegionStart & ~BlockMask,\r
221 (RegionStart | BlockMask) + 1, *Entry & TT_ATTRIBUTES_MASK,\r
222 0, TranslationTable, Level + 1);\r
223 if (EFI_ERROR (Status)) {\r
224 //\r
225 // The range we passed to UpdateRegionMappingRecursive () is block\r
226 // aligned, so it is guaranteed that no further pages were allocated\r
227 // by it, and so we only have to free the page we allocated here.\r
228 //\r
229 FreePages (TranslationTable, 1);\r
230 return Status;\r
231 }\r
d7f03464 232 }\r
191fa79b
AB
233 } else {\r
234 TranslationTable = (VOID *)(UINTN)(*Entry & TT_ADDRESS_MASK_BLOCK_ENTRY);\r
d7f03464 235 }\r
d7f03464 236\r
191fa79b
AB
237 //\r
238 // Recurse to the next level\r
239 //\r
240 Status = UpdateRegionMappingRecursive (RegionStart, BlockEnd,\r
241 AttributeSetMask, AttributeClearMask, TranslationTable,\r
242 Level + 1);\r
243 if (EFI_ERROR (Status)) {\r
5fc89953 244 if (!IsTableEntry (*Entry, Level)) {\r
191fa79b
AB
245 //\r
246 // We are creating a new table entry, so on failure, we can free all\r
247 // allocations we made recursively, given that the whole subhierarchy\r
248 // has not been wired into the live page tables yet. (This is not\r
249 // possible for existing table entries, since we cannot revert the\r
250 // modifications we made to the subhierarchy it represents.)\r
251 //\r
d390920e 252 FreePageTablesRecursive (TranslationTable, Level + 1);\r
d7f03464 253 }\r
191fa79b
AB
254 return Status;\r
255 }\r
d7f03464 256\r
5fc89953 257 if (!IsTableEntry (*Entry, Level)) {\r
191fa79b
AB
258 EntryValue = (UINTN)TranslationTable | TT_TYPE_TABLE_ENTRY;\r
259 ReplaceTableEntry (Entry, EntryValue, RegionStart,\r
5fc89953 260 IsBlockEntry (*Entry, Level));\r
d7f03464 261 }\r
191fa79b
AB
262 } else {\r
263 EntryValue = (*Entry & AttributeClearMask) | AttributeSetMask;\r
264 EntryValue |= RegionStart;\r
265 EntryValue |= (Level == 3) ? TT_TYPE_BLOCK_ENTRY_LEVEL3\r
266 : TT_TYPE_BLOCK_ENTRY;\r
267\r
f7079d1b
AB
268 if (IsTableEntry (*Entry, Level)) {\r
269 //\r
270 // We are replacing a table entry with a block entry. This is only\r
271 // possible if we are keeping none of the original attributes.\r
272 // We can free the table entry's page table, and all the ones below\r
273 // it, since we are dropping the only possible reference to it.\r
274 //\r
275 ASSERT (AttributeClearMask == 0);\r
276 TranslationTable = (VOID *)(UINTN)(*Entry & TT_ADDRESS_MASK_BLOCK_ENTRY);\r
277 ReplaceTableEntry (Entry, EntryValue, RegionStart, TRUE);\r
278 FreePageTablesRecursive (TranslationTable, Level + 1);\r
279 } else {\r
280 ReplaceTableEntry (Entry, EntryValue, RegionStart, FALSE);\r
281 }\r
d7f03464
AB
282 }\r
283 }\r
191fa79b
AB
284 return EFI_SUCCESS;\r
285}\r
d7f03464 286\r
191fa79b
AB
287STATIC\r
288VOID\r
289LookupAddresstoRootTable (\r
290 IN UINT64 MaxAddress,\r
291 OUT UINTN *T0SZ,\r
292 OUT UINTN *TableEntryCount\r
293 )\r
294{\r
295 UINTN TopBit;\r
d7f03464 296\r
191fa79b
AB
297 // Check the parameters are not NULL\r
298 ASSERT ((T0SZ != NULL) && (TableEntryCount != NULL));\r
d7f03464 299\r
191fa79b
AB
300 // Look for the highest bit set in MaxAddress\r
301 for (TopBit = 63; TopBit != 0; TopBit--) {\r
302 if ((1ULL << TopBit) & MaxAddress) {\r
303 // MaxAddress top bit is found\r
304 TopBit = TopBit + 1;\r
305 break;\r
306 }\r
307 }\r
308 ASSERT (TopBit != 0);\r
d7f03464 309\r
191fa79b
AB
310 // Calculate T0SZ from the top bit of the MaxAddress\r
311 *T0SZ = 64 - TopBit;\r
312\r
313 // Get the Table info from T0SZ\r
314 GetRootTranslationTableInfo (*T0SZ, NULL, TableEntryCount);\r
d7f03464
AB
315}\r
316\r
317STATIC\r
f49ea03d 318EFI_STATUS\r
d7f03464 319UpdateRegionMapping (\r
d7f03464
AB
320 IN UINT64 RegionStart,\r
321 IN UINT64 RegionLength,\r
191fa79b
AB
322 IN UINT64 AttributeSetMask,\r
323 IN UINT64 AttributeClearMask\r
d7f03464
AB
324 )\r
325{\r
191fa79b
AB
326 UINTN RootTableLevel;\r
327 UINTN T0SZ;\r
328\r
329 if (((RegionStart | RegionLength) & EFI_PAGE_MASK)) {\r
f49ea03d 330 return EFI_INVALID_PARAMETER;\r
d7f03464
AB
331 }\r
332\r
191fa79b
AB
333 T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
334 GetRootTranslationTableInfo (T0SZ, &RootTableLevel, NULL);\r
d7f03464 335\r
191fa79b
AB
336 return UpdateRegionMappingRecursive (RegionStart, RegionStart + RegionLength,\r
337 AttributeSetMask, AttributeClearMask, ArmGetTTBR0BaseAddress (),\r
338 RootTableLevel);\r
d7f03464
AB
339}\r
340\r
341STATIC\r
f49ea03d 342EFI_STATUS\r
d7f03464
AB
343FillTranslationTable (\r
344 IN UINT64 *RootTable,\r
345 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion\r
346 )\r
347{\r
348 return UpdateRegionMapping (\r
d7f03464
AB
349 MemoryRegion->VirtualBase,\r
350 MemoryRegion->Length,\r
351 ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | TT_AF,\r
352 0\r
353 );\r
354}\r
355\r
e0307a7d
AB
356STATIC\r
357UINT64\r
358GcdAttributeToPageAttribute (\r
359 IN UINT64 GcdAttributes\r
360 )\r
361{\r
362 UINT64 PageAttributes;\r
363\r
364 switch (GcdAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
365 case EFI_MEMORY_UC:\r
366 PageAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\r
367 break;\r
368 case EFI_MEMORY_WC:\r
369 PageAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
370 break;\r
371 case EFI_MEMORY_WT:\r
372 PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;\r
373 break;\r
374 case EFI_MEMORY_WB:\r
375 PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
376 break;\r
377 default:\r
378 PageAttributes = TT_ATTR_INDX_MASK;\r
379 break;\r
380 }\r
381\r
382 if ((GcdAttributes & EFI_MEMORY_XP) != 0 ||\r
383 (GcdAttributes & EFI_MEMORY_CACHETYPE_MASK) == EFI_MEMORY_UC) {\r
384 if (ArmReadCurrentEL () == AARCH64_EL2) {\r
385 PageAttributes |= TT_XN_MASK;\r
386 } else {\r
387 PageAttributes |= TT_UXN_MASK | TT_PXN_MASK;\r
388 }\r
389 }\r
390\r
391 if ((GcdAttributes & EFI_MEMORY_RO) != 0) {\r
392 PageAttributes |= TT_AP_RO_RO;\r
393 }\r
394\r
395 return PageAttributes | TT_AF;\r
396}\r
397\r
f49ea03d 398EFI_STATUS\r
521f3ced 399ArmSetMemoryAttributes (\r
d7f03464
AB
400 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
401 IN UINT64 Length,\r
d9c0d991 402 IN UINT64 Attributes\r
d7f03464
AB
403 )\r
404{\r
e0307a7d
AB
405 UINT64 PageAttributes;\r
406 UINT64 PageAttributeMask;\r
407\r
408 PageAttributes = GcdAttributeToPageAttribute (Attributes);\r
409 PageAttributeMask = 0;\r
410\r
411 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {\r
412 //\r
413 // No memory type was set in Attributes, so we are going to update the\r
414 // permissions only.\r
415 //\r
416 PageAttributes &= TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK;\r
417 PageAttributeMask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK |\r
418 TT_PXN_MASK | TT_XN_MASK);\r
419 }\r
d7f03464 420\r
191fa79b
AB
421 return UpdateRegionMapping (BaseAddress, Length, PageAttributes,\r
422 PageAttributeMask);\r
d7f03464
AB
423}\r
424\r
425STATIC\r
f49ea03d 426EFI_STATUS\r
d7f03464
AB
427SetMemoryRegionAttribute (\r
428 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
429 IN UINT64 Length,\r
430 IN UINT64 Attributes,\r
431 IN UINT64 BlockEntryMask\r
432 )\r
433{\r
191fa79b 434 return UpdateRegionMapping (BaseAddress, Length, Attributes, BlockEntryMask);\r
d7f03464
AB
435}\r
436\r
f49ea03d 437EFI_STATUS\r
d7f03464
AB
438ArmSetMemoryRegionNoExec (\r
439 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
440 IN UINT64 Length\r
441 )\r
442{\r
443 UINT64 Val;\r
444\r
445 if (ArmReadCurrentEL () == AARCH64_EL1) {\r
446 Val = TT_PXN_MASK | TT_UXN_MASK;\r
447 } else {\r
448 Val = TT_XN_MASK;\r
449 }\r
450\r
451 return SetMemoryRegionAttribute (\r
452 BaseAddress,\r
453 Length,\r
454 Val,\r
455 ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
456}\r
457\r
f49ea03d 458EFI_STATUS\r
d7f03464
AB
459ArmClearMemoryRegionNoExec (\r
460 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
461 IN UINT64 Length\r
462 )\r
463{\r
464 UINT64 Mask;\r
465\r
466 // XN maps to UXN in the EL1&0 translation regime\r
467 Mask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_PXN_MASK | TT_XN_MASK);\r
468\r
469 return SetMemoryRegionAttribute (\r
470 BaseAddress,\r
471 Length,\r
472 0,\r
473 Mask);\r
474}\r
475\r
f49ea03d 476EFI_STATUS\r
d7f03464
AB
477ArmSetMemoryRegionReadOnly (\r
478 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
479 IN UINT64 Length\r
480 )\r
481{\r
482 return SetMemoryRegionAttribute (\r
483 BaseAddress,\r
484 Length,\r
485 TT_AP_RO_RO,\r
486 ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
487}\r
488\r
f49ea03d 489EFI_STATUS\r
d7f03464
AB
490ArmClearMemoryRegionReadOnly (\r
491 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
492 IN UINT64 Length\r
493 )\r
494{\r
495 return SetMemoryRegionAttribute (\r
496 BaseAddress,\r
497 Length,\r
498 TT_AP_RW_RW,\r
499 ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK));\r
500}\r
501\r
f49ea03d 502EFI_STATUS\r
d7f03464
AB
503EFIAPI\r
504ArmConfigureMmu (\r
505 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,\r
506 OUT VOID **TranslationTableBase OPTIONAL,\r
507 OUT UINTN *TranslationTableSize OPTIONAL\r
508 )\r
509{\r
510 VOID* TranslationTable;\r
d7f03464 511 UINT64 MaxAddress;\r
d7f03464
AB
512 UINTN T0SZ;\r
513 UINTN RootTableEntryCount;\r
514 UINT64 TCR;\r
f49ea03d 515 EFI_STATUS Status;\r
d7f03464 516\r
4249278a 517 if (MemoryTable == NULL) {\r
d7f03464 518 ASSERT (MemoryTable != NULL);\r
f49ea03d 519 return EFI_INVALID_PARAMETER;\r
d7f03464
AB
520 }\r
521\r
e36b243c
AB
522 //\r
523 // Limit the virtual address space to what we can actually use: UEFI\r
524 // mandates a 1:1 mapping, so no point in making the virtual address\r
525 // space larger than the physical address space. We also have to take\r
526 // into account the architectural limitations that result from UEFI's\r
527 // use of 4 KB pages.\r
528 //\r
529 MaxAddress = MIN (LShiftU64 (1ULL, ArmGetPhysicalAddressBits ()) - 1,\r
1c36f028 530 MAX_ALLOC_ADDRESS);\r
d7f03464
AB
531\r
532 // Lookup the Table Level to get the information\r
533 LookupAddresstoRootTable (MaxAddress, &T0SZ, &RootTableEntryCount);\r
534\r
535 //\r
536 // Set TCR that allows us to retrieve T0SZ in the subsequent functions\r
537 //\r
538 // Ideally we will be running at EL2, but should support EL1 as well.\r
539 // UEFI should not run at EL3.\r
540 if (ArmReadCurrentEL () == AARCH64_EL2) {\r
541 //Note: Bits 23 and 31 are reserved(RES1) bits in TCR_EL2\r
542 TCR = T0SZ | (1UL << 31) | (1UL << 23) | TCR_TG0_4KB;\r
543\r
544 // Set the Physical Address Size using MaxAddress\r
545 if (MaxAddress < SIZE_4GB) {\r
546 TCR |= TCR_PS_4GB;\r
547 } else if (MaxAddress < SIZE_64GB) {\r
548 TCR |= TCR_PS_64GB;\r
549 } else if (MaxAddress < SIZE_1TB) {\r
550 TCR |= TCR_PS_1TB;\r
551 } else if (MaxAddress < SIZE_4TB) {\r
552 TCR |= TCR_PS_4TB;\r
553 } else if (MaxAddress < SIZE_16TB) {\r
554 TCR |= TCR_PS_16TB;\r
555 } else if (MaxAddress < SIZE_256TB) {\r
556 TCR |= TCR_PS_256TB;\r
557 } else {\r
4249278a
AB
558 DEBUG ((DEBUG_ERROR,\r
559 "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n",\r
560 MaxAddress));\r
d7f03464 561 ASSERT (0); // Bigger than 48-bit memory space are not supported\r
f49ea03d 562 return EFI_UNSUPPORTED;\r
d7f03464
AB
563 }\r
564 } else if (ArmReadCurrentEL () == AARCH64_EL1) {\r
565 // Due to Cortex-A57 erratum #822227 we must set TG1[1] == 1, regardless of EPD1.\r
566 TCR = T0SZ | TCR_TG0_4KB | TCR_TG1_4KB | TCR_EPD1;\r
567\r
568 // Set the Physical Address Size using MaxAddress\r
569 if (MaxAddress < SIZE_4GB) {\r
570 TCR |= TCR_IPS_4GB;\r
571 } else if (MaxAddress < SIZE_64GB) {\r
572 TCR |= TCR_IPS_64GB;\r
573 } else if (MaxAddress < SIZE_1TB) {\r
574 TCR |= TCR_IPS_1TB;\r
575 } else if (MaxAddress < SIZE_4TB) {\r
576 TCR |= TCR_IPS_4TB;\r
577 } else if (MaxAddress < SIZE_16TB) {\r
578 TCR |= TCR_IPS_16TB;\r
579 } else if (MaxAddress < SIZE_256TB) {\r
580 TCR |= TCR_IPS_256TB;\r
581 } else {\r
4249278a
AB
582 DEBUG ((DEBUG_ERROR,\r
583 "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n",\r
584 MaxAddress));\r
d7f03464 585 ASSERT (0); // Bigger than 48-bit memory space are not supported\r
f49ea03d 586 return EFI_UNSUPPORTED;\r
d7f03464
AB
587 }\r
588 } else {\r
589 ASSERT (0); // UEFI is only expected to run at EL2 and EL1, not EL3.\r
f49ea03d 590 return EFI_UNSUPPORTED;\r
d7f03464
AB
591 }\r
592\r
35718840
AB
593 //\r
594 // Translation table walks are always cache coherent on ARMv8-A, so cache\r
595 // maintenance on page tables is never needed. Since there is a risk of\r
596 // loss of coherency when using mismatched attributes, and given that memory\r
597 // is mapped cacheable except for extraordinary cases (such as non-coherent\r
598 // DMA), have the page table walker perform cached accesses as well, and\r
599 // assert below that that matches the attributes we use for CPU accesses to\r
600 // the region.\r
601 //\r
602 TCR |= TCR_SH_INNER_SHAREABLE |\r
603 TCR_RGN_OUTER_WRITE_BACK_ALLOC |\r
604 TCR_RGN_INNER_WRITE_BACK_ALLOC;\r
605\r
d7f03464
AB
606 // Set TCR\r
607 ArmSetTCR (TCR);\r
608\r
aa961dea
AB
609 // Allocate pages for translation table\r
610 TranslationTable = AllocatePages (1);\r
d7f03464 611 if (TranslationTable == NULL) {\r
f49ea03d 612 return EFI_OUT_OF_RESOURCES;\r
d7f03464 613 }\r
4249278a
AB
614 //\r
615 // We set TTBR0 just after allocating the table to retrieve its location from\r
616 // the subsequent functions without needing to pass this value across the\r
617 // functions. The MMU is only enabled after the translation tables are\r
618 // populated.\r
619 //\r
d7f03464
AB
620 ArmSetTTBR0 (TranslationTable);\r
621\r
622 if (TranslationTableBase != NULL) {\r
623 *TranslationTableBase = TranslationTable;\r
624 }\r
625\r
626 if (TranslationTableSize != NULL) {\r
4249278a 627 *TranslationTableSize = RootTableEntryCount * sizeof (UINT64);\r
d7f03464
AB
628 }\r
629\r
748fea62
AB
630 //\r
631 // Make sure we are not inadvertently hitting in the caches\r
632 // when populating the page tables.\r
633 //\r
634 InvalidateDataCacheRange (TranslationTable,\r
4249278a
AB
635 RootTableEntryCount * sizeof (UINT64));\r
636 ZeroMem (TranslationTable, RootTableEntryCount * sizeof (UINT64));\r
d7f03464 637\r
d7f03464 638 while (MemoryTable->Length != 0) {\r
d7f03464 639 Status = FillTranslationTable (TranslationTable, MemoryTable);\r
f49ea03d 640 if (EFI_ERROR (Status)) {\r
4249278a 641 goto FreeTranslationTable;\r
d7f03464
AB
642 }\r
643 MemoryTable++;\r
644 }\r
645\r
4249278a
AB
646 //\r
647 // EFI_MEMORY_UC ==> MAIR_ATTR_DEVICE_MEMORY\r
648 // EFI_MEMORY_WC ==> MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE\r
649 // EFI_MEMORY_WT ==> MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH\r
650 // EFI_MEMORY_WB ==> MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK\r
651 //\r
652 ArmSetMAIR (\r
653 MAIR_ATTR (TT_ATTR_INDX_DEVICE_MEMORY, MAIR_ATTR_DEVICE_MEMORY) |\r
654 MAIR_ATTR (TT_ATTR_INDX_MEMORY_NON_CACHEABLE, MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE) |\r
655 MAIR_ATTR (TT_ATTR_INDX_MEMORY_WRITE_THROUGH, MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH) |\r
656 MAIR_ATTR (TT_ATTR_INDX_MEMORY_WRITE_BACK, MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK)\r
657 );\r
d7f03464
AB
658\r
659 ArmDisableAlignmentCheck ();\r
526f160f 660 ArmEnableStackAlignmentCheck ();\r
d7f03464
AB
661 ArmEnableInstructionCache ();\r
662 ArmEnableDataCache ();\r
663\r
664 ArmEnableMmu ();\r
f49ea03d 665 return EFI_SUCCESS;\r
d7f03464 666\r
4249278a 667FreeTranslationTable:\r
aa961dea 668 FreePages (TranslationTable, 1);\r
d7f03464
AB
669 return Status;\r
670}\r
671\r
672RETURN_STATUS\r
673EFIAPI\r
674ArmMmuBaseLibConstructor (\r
675 VOID\r
676 )\r
677{\r
678 extern UINT32 ArmReplaceLiveTranslationEntrySize;\r
679\r
680 //\r
681 // The ArmReplaceLiveTranslationEntry () helper function may be invoked\r
682 // with the MMU off so we have to ensure that it gets cleaned to the PoC\r
683 //\r
684 WriteBackDataCacheRange (ArmReplaceLiveTranslationEntry,\r
685 ArmReplaceLiveTranslationEntrySize);\r
686\r
687 return RETURN_SUCCESS;\r
688}\r