]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
ArmPkg/CpuDxe: handle implied attributes in EfiAttributeToArmAttribute
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / AArch64 / Mmu.c
index 40a4c1b0aab170769f40daee29397ca4184e7de9..3e216c7cb235165b3df686aabff58628dd4ab801 100644 (file)
@@ -3,6 +3,7 @@
 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
+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
@@ -32,9 +33,8 @@ GetFirstPageAttribute (
   // 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
@@ -90,7 +90,7 @@ GetNextEntryAttribute (
           SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
               *StartGcdRegion,\r
               (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion,\r
-              PageAttributeToGcdAttribute (EntryAttribute));\r
+              PageAttributeToGcdAttribute (*PrevEntryAttribute));\r
         }\r
 \r
         // Start of the new region\r
@@ -194,3 +194,154 @@ SyncCacheConfig (
 \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
+    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) {\r
+    ArmAttributes |= TT_AP_RO_RO;\r
+  }\r
+\r
+  // Process eXecute Never attribute\r
+  if (EfiAttributes & EFI_MEMORY_XP) {\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, RegionLength, RegionAttributes);\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 'BaseAdress') 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
+    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 (TranslationTable, TableLevel,\r
+      (UINTN*)TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount),\r
+      BaseAddress, RegionLength, RegionAttributes);\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