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