\r
Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>\r
Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>\r
-Portions copyright (c) 2011-2013, ARM Ltd. All rights reserved.<BR>\r
+Portions copyright (c) 2011-2021, Arm Limited. All rights reserved.<BR>\r
+Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
\r
--*/\r
\r
+#include <Library/MemoryAllocationLib.h>\r
#include "CpuDxe.h"\r
\r
-#define TT_ATTR_INDX_INVALID ((UINT32)~0)\r
+#define INVALID_ENTRY ((UINT32)~0)\r
+\r
+#define MIN_T0SZ 16\r
+#define BITS_PER_LEVEL 9\r
+\r
+STATIC\r
+VOID\r
+GetRootTranslationTableInfo (\r
+ IN UINTN T0SZ,\r
+ OUT UINTN *RootTableLevel,\r
+ OUT UINTN *RootTableEntryCount\r
+ )\r
+{\r
+ *RootTableLevel = (T0SZ - MIN_T0SZ) / BITS_PER_LEVEL;\r
+ *RootTableEntryCount = TT_ENTRY_COUNT >> (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL;\r
+}\r
+\r
+STATIC\r
+UINT64\r
+PageAttributeToGcdAttribute (\r
+ IN UINT64 PageAttributes\r
+ )\r
+{\r
+ UINT64 GcdAttributes;\r
+\r
+ switch (PageAttributes & TT_ATTR_INDX_MASK) {\r
+ case TT_ATTR_INDX_DEVICE_MEMORY:\r
+ GcdAttributes = EFI_MEMORY_UC;\r
+ break;\r
+ case TT_ATTR_INDX_MEMORY_NON_CACHEABLE:\r
+ GcdAttributes = EFI_MEMORY_WC;\r
+ break;\r
+ case TT_ATTR_INDX_MEMORY_WRITE_THROUGH:\r
+ GcdAttributes = EFI_MEMORY_WT;\r
+ break;\r
+ case TT_ATTR_INDX_MEMORY_WRITE_BACK:\r
+ GcdAttributes = EFI_MEMORY_WB;\r
+ break;\r
+ default:\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n",\r
+ PageAttributes\r
+ ));\r
+ ASSERT (0);\r
+ // The Global Coherency Domain (GCD) value is defined as a bit set.\r
+ // Returning 0 means no attribute has been set.\r
+ GcdAttributes = 0;\r
+ }\r
+\r
+ // Determine protection attributes\r
+ if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) ||\r
+ ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO))\r
+ {\r
+ // Read only cases map to write-protect\r
+ GcdAttributes |= EFI_MEMORY_RO;\r
+ }\r
+\r
+ // Process eXecute Never attribute\r
+ if ((PageAttributes & (TT_PXN_MASK | TT_UXN_MASK)) != 0) {\r
+ GcdAttributes |= EFI_MEMORY_XP;\r
+ }\r
+\r
+ return GcdAttributes;\r
+}\r
\r
STATIC\r
UINT64\r
GetFirstPageAttribute (\r
IN UINT64 *FirstLevelTableAddress,\r
- IN UINTN TableLevel\r
+ IN UINTN TableLevel\r
)\r
{\r
- UINT64 FirstEntry;\r
+ UINT64 FirstEntry;\r
\r
// Get the first entry of the table\r
FirstEntry = *FirstLevelTableAddress;\r
\r
- if ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
+ if ((TableLevel != 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) {\r
// Only valid for Levels 0, 1 and 2\r
- ASSERT (TableLevel < 3);\r
\r
// Get the attribute of the subsequent table\r
- return GetFirstPageAttribute ((UINT64*)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1);\r
+ return GetFirstPageAttribute ((UINT64 *)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1);\r
} else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) ||\r
((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3)))\r
{\r
return FirstEntry & TT_ATTR_INDX_MASK;\r
} else {\r
- return TT_ATTR_INDX_INVALID;\r
+ return INVALID_ENTRY;\r
}\r
}\r
\r
STATIC\r
UINT64\r
GetNextEntryAttribute (\r
- IN UINT64 *TableAddress,\r
+ IN UINT64 *TableAddress,\r
IN UINTN EntryCount,\r
IN UINTN TableLevel,\r
IN UINT64 BaseAddress,\r
- IN OUT UINT32 *PrevEntryAttribute,\r
- IN OUT UINT64 *StartGcdRegion\r
+ IN OUT UINT32 *PrevEntryAttribute,\r
+ IN OUT UINT64 *StartGcdRegion\r
)\r
{\r
- UINTN Index;\r
- UINT64 Entry;\r
- UINT32 EntryAttribute;\r
- UINT32 EntryType;\r
- EFI_STATUS Status;\r
- UINTN NumberOfDescriptors;\r
+ UINTN Index;\r
+ UINT64 Entry;\r
+ UINT32 EntryAttribute;\r
+ UINT32 EntryType;\r
+ EFI_STATUS Status;\r
+ UINTN NumberOfDescriptors;\r
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
\r
// Get the memory space map from GCD\r
MemorySpaceMap = NULL;\r
- Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
+ Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
ASSERT_EFI_ERROR (Status);\r
\r
// We cannot get more than 3-level page table\r
// While the top level table might not contain TT_ENTRY_COUNT entries;\r
// the subsequent ones should be filled up\r
for (Index = 0; Index < EntryCount; Index++) {\r
- Entry = TableAddress[Index];\r
- EntryType = Entry & TT_TYPE_MASK;\r
+ Entry = TableAddress[Index];\r
+ EntryType = Entry & TT_TYPE_MASK;\r
EntryAttribute = Entry & TT_ATTR_INDX_MASK;\r
\r
// If Entry is a Table Descriptor type entry then go through the sub-level table\r
if ((EntryType == TT_TYPE_BLOCK_ENTRY) ||\r
- ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3))) {\r
- if ((*PrevEntryAttribute == TT_ATTR_INDX_INVALID) || (EntryAttribute != *PrevEntryAttribute)) {\r
- if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {\r
+ ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3)))\r
+ {\r
+ if ((*PrevEntryAttribute == INVALID_ENTRY) || (EntryAttribute != *PrevEntryAttribute)) {\r
+ if (*PrevEntryAttribute != INVALID_ENTRY) {\r
// Update GCD with the last region\r
- SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
- *StartGcdRegion,\r
- (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)) - 1) - *StartGcdRegion,\r
- PageAttributeToGcdAttribute (EntryAttribute));\r
+ SetGcdMemorySpaceAttributes (\r
+ MemorySpaceMap,\r
+ NumberOfDescriptors,\r
+ *StartGcdRegion,\r
+ (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))) - *StartGcdRegion,\r
+ PageAttributeToGcdAttribute (*PrevEntryAttribute)\r
+ );\r
}\r
\r
// Start of the new region\r
- *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));\r
+ *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel));\r
*PrevEntryAttribute = EntryAttribute;\r
} else {\r
continue;\r
ASSERT (TableLevel < 3);\r
\r
// Increase the level number and scan the sub-level table\r
- GetNextEntryAttribute ((UINT64*)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE),\r
- TT_ENTRY_COUNT, TableLevel + 1,\r
- (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))),\r
- PrevEntryAttribute, StartGcdRegion);\r
+ GetNextEntryAttribute (\r
+ (UINT64 *)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE),\r
+ TT_ENTRY_COUNT,\r
+ TableLevel + 1,\r
+ (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))),\r
+ PrevEntryAttribute,\r
+ StartGcdRegion\r
+ );\r
} else {\r
- if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {\r
+ if (*PrevEntryAttribute != INVALID_ENTRY) {\r
// Update GCD with the last region\r
- SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
- *StartGcdRegion,\r
- (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)) - 1) - *StartGcdRegion,\r
- PageAttributeToGcdAttribute (EntryAttribute));\r
+ SetGcdMemorySpaceAttributes (\r
+ MemorySpaceMap,\r
+ NumberOfDescriptors,\r
+ *StartGcdRegion,\r
+ (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))) - *StartGcdRegion,\r
+ PageAttributeToGcdAttribute (*PrevEntryAttribute)\r
+ );\r
\r
// Start of the new region\r
- *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));\r
- *PrevEntryAttribute = TT_ATTR_INDX_INVALID;\r
+ *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel));\r
+ *PrevEntryAttribute = INVALID_ENTRY;\r
}\r
}\r
}\r
\r
- return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL(TableLevel));\r
+ FreePool (MemorySpaceMap);\r
+\r
+ return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL (TableLevel));\r
}\r
\r
EFI_STATUS\r
SyncCacheConfig (\r
- IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
+ IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
)\r
{\r
- EFI_STATUS Status;\r
- UINT32 PageAttribute = 0;\r
- UINT64 *FirstLevelTableAddress;\r
- UINTN TableLevel;\r
- UINTN TableCount;\r
- UINTN NumberOfDescriptors;\r
- EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
- UINTN Tcr;\r
- UINTN T0SZ;\r
- UINT64 BaseAddressGcdRegion;\r
- UINT64 EndAddressGcdRegion;\r
+ EFI_STATUS Status;\r
+ UINT32 PageAttribute;\r
+ UINT64 *FirstLevelTableAddress;\r
+ UINTN TableLevel;\r
+ UINTN TableCount;\r
+ UINTN NumberOfDescriptors;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
+ UINTN Tcr;\r
+ UINTN T0SZ;\r
+ UINT64 BaseAddressGcdRegion;\r
+ UINT64 EndAddressGcdRegion;\r
\r
// This code assumes MMU is enabled and filed with section translations\r
ASSERT (ArmMmuEnabled ());\r
// Get the memory space map from GCD\r
//\r
MemorySpaceMap = NULL;\r
- Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
+ Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
ASSERT_EFI_ERROR (Status);\r
\r
// The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs\r
// with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.\r
\r
// Obtain page table base\r
- FirstLevelTableAddress = (UINT64*)(ArmGetTTBR0BaseAddress ());\r
+ FirstLevelTableAddress = (UINT64 *)(ArmGetTTBR0BaseAddress ());\r
\r
// Get Translation Control Register value\r
Tcr = ArmGetTCR ();\r
\r
// We scan from the start of the memory map (ie: at the address 0x0)\r
BaseAddressGcdRegion = 0x0;\r
- EndAddressGcdRegion = GetNextEntryAttribute (FirstLevelTableAddress,\r
- TableCount, TableLevel,\r
- BaseAddressGcdRegion,\r
- &PageAttribute, &BaseAddressGcdRegion);\r
+ EndAddressGcdRegion = GetNextEntryAttribute (\r
+ FirstLevelTableAddress,\r
+ TableCount,\r
+ TableLevel,\r
+ BaseAddressGcdRegion,\r
+ &PageAttribute,\r
+ &BaseAddressGcdRegion\r
+ );\r
\r
- // Update GCD with the last region\r
- SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
+ // Update GCD with the last region if valid\r
+ if (PageAttribute != INVALID_ENTRY) {\r
+ SetGcdMemorySpaceAttributes (\r
+ MemorySpaceMap,\r
+ NumberOfDescriptors,\r
BaseAddressGcdRegion,\r
EndAddressGcdRegion - BaseAddressGcdRegion,\r
- PageAttributeToGcdAttribute (PageAttribute));\r
+ PageAttributeToGcdAttribute (PageAttribute)\r
+ );\r
+ }\r
+\r
+ FreePool (MemorySpaceMap);\r
\r
return EFI_SUCCESS;\r
}\r
+\r
+UINT64\r
+EfiAttributeToArmAttribute (\r
+ IN UINT64 EfiAttributes\r
+ )\r
+{\r
+ UINT64 ArmAttributes;\r
+\r
+ switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
+ case EFI_MEMORY_UC:\r
+ if (ArmReadCurrentEL () == AARCH64_EL2) {\r
+ ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;\r
+ } else {\r
+ ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK;\r
+ }\r
+\r
+ break;\r
+ case EFI_MEMORY_WC:\r
+ ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
+ break;\r
+ case EFI_MEMORY_WT:\r
+ ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;\r
+ break;\r
+ case EFI_MEMORY_WB:\r
+ ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
+ break;\r
+ default:\r
+ ArmAttributes = TT_ATTR_INDX_MASK;\r
+ }\r
+\r
+ // Set the access flag to match the block attributes\r
+ ArmAttributes |= TT_AF;\r
+\r
+ // Determine protection attributes\r
+ if ((EfiAttributes & EFI_MEMORY_RO) != 0) {\r
+ ArmAttributes |= TT_AP_NO_RO;\r
+ }\r
+\r
+ // Process eXecute Never attribute\r
+ if ((EfiAttributes & EFI_MEMORY_XP) != 0) {\r
+ ArmAttributes |= TT_PXN_MASK;\r
+ }\r
+\r
+ return ArmAttributes;\r
+}\r
+\r
+// This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.\r
+// And then the function will identify the size of the region that has the same page table attribute.\r
+EFI_STATUS\r
+GetMemoryRegionRec (\r
+ IN UINT64 *TranslationTable,\r
+ IN UINTN TableLevel,\r
+ IN UINT64 *LastBlockEntry,\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 *NextTranslationTable;\r
+ UINT64 *BlockEntry;\r
+ UINT64 BlockEntryType;\r
+ UINT64 EntryType;\r
+\r
+ if (TableLevel != 3) {\r
+ BlockEntryType = TT_TYPE_BLOCK_ENTRY;\r
+ } else {\r
+ BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
+ }\r
+\r
+ // Find the block entry linked to the Base Address\r
+ BlockEntry = (UINT64 *)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress);\r
+ EntryType = *BlockEntry & TT_TYPE_MASK;\r
+\r
+ if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) {\r
+ NextTranslationTable = (UINT64 *)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
+\r
+ // The entry is a page table, so we go to the next level\r
+ Status = GetMemoryRegionRec (\r
+ NextTranslationTable, // Address of the next level page table\r
+ TableLevel + 1, // Next Page Table level\r
+ (UINTN *)TT_LAST_BLOCK_ADDRESS (NextTranslationTable, TT_ENTRY_COUNT),\r
+ BaseAddress,\r
+ RegionLength,\r
+ RegionAttributes\r
+ );\r
+\r
+ // In case of 'Success', it means the end of the block region has been found into the upper\r
+ // level translation table\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Now we processed the table move to the next entry\r
+ BlockEntry++;\r
+ } else if (EntryType == BlockEntryType) {\r
+ // We have found the BlockEntry attached to the address. We save its start address (the start\r
+ // address might be before the 'BaseAddress') and attributes\r
+ *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL (TableLevel) - 1);\r
+ *RegionLength = 0;\r
+ *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK;\r
+ } else {\r
+ // We have an 'Invalid' entry\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ while (BlockEntry <= LastBlockEntry) {\r
+ if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) {\r
+ *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL (TableLevel);\r
+ } else {\r
+ // In case we have found the end of the region we return success\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ BlockEntry++;\r
+ }\r
+\r
+ // If we have reached the end of the TranslationTable and we have not found the end of the region then\r
+ // we return EFI_NOT_FOUND.\r
+ // The caller will continue to look for the memory region at its level\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+EFI_STATUS\r
+GetMemoryRegion (\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 *TranslationTable;\r
+ UINTN TableLevel;\r
+ UINTN EntryCount;\r
+ UINTN T0SZ;\r
+\r
+ ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL));\r
+\r
+ TranslationTable = ArmGetTTBR0BaseAddress ();\r
+\r
+ T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
+ // Get the Table info from T0SZ\r
+ GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount);\r
+\r
+ Status = GetMemoryRegionRec (\r
+ TranslationTable,\r
+ TableLevel,\r
+ (UINTN *)TT_LAST_BLOCK_ADDRESS (TranslationTable, EntryCount),\r
+ BaseAddress,\r
+ RegionLength,\r
+ RegionAttributes\r
+ );\r
+\r
+ // If the region continues up to the end of the root table then GetMemoryRegionRec()\r
+ // will return EFI_NOT_FOUND\r
+ if (Status == EFI_NOT_FOUND) {\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return Status;\r
+ }\r
+}\r