]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
ArmPkg/CpuDxe: Added support to not set a memory region with the same attribute
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / AArch64 / Mmu.c
index 40a4c1b0aab170769f40daee29397ca4184e7de9..72f9b3cc63f27d5066cdc828a0866a7ae4020387 100644 (file)
@@ -194,3 +194,149 @@ 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
+    ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\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;\r
+    break;\r
+  case EFI_MEMORY_WB:\r
+    ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
+    break;\r
+  default:\r
+    DEBUG ((EFI_D_ERROR, "EfiAttributeToArmAttribute: 0x%lX attributes is not supported.\n", EfiAttributes));\r
+    ASSERT (0);\r
+    ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\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_WP) {\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 (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
+  } 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