]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmPkg/CpuDxe: Added support to not set a memory region with the same attribute
authorOlivier Martin <olivier.martin@arm.com>
Mon, 19 Aug 2013 17:38:39 +0000 (17:38 +0000)
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 19 Aug 2013 17:38:39 +0000 (17:38 +0000)
Changing the attribute implies some cache management (clean & invalidate).
Preventing the cache management should improve the performance.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14568 6f19259b-4bc3-4df7-8a09-765794883524

ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
ArmPkg/Drivers/CpuDxe/ArmV6/Mmu.c
ArmPkg/Drivers/CpuDxe/CpuDxe.h
ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c

index 40a4c1b0aab170769f40daee29397ca4184e7de9..72f9b3cc63f27d5066cdc828a0866a7ae4020387 100644 (file)
@@ -194,3 +194,149 @@ SyncCacheConfig (
 \r
   return EFI_SUCCESS;\r
 }\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
+    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
index 38b709de15deb163a90813d9265e998deaf21969..474f105d229b05315141e7cc9f4527e24f14fd3d 100644 (file)
@@ -696,3 +696,185 @@ SetMemoryAttributes (
 \r
   return Status;\r
 }\r
 \r
   return Status;\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
+      // Map to strongly ordered\r
+      ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
+      break;\r
+\r
+    case EFI_MEMORY_WC:\r
+      // Map to normal non-cachable\r
+      ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
+      break;\r
+\r
+    case EFI_MEMORY_WT:\r
+      // Write through with no-allocate\r
+      ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
+      break;\r
+\r
+    case EFI_MEMORY_WB:\r
+      // Write back (with allocate)\r
+      ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
+      break;\r
+\r
+    case EFI_MEMORY_WP:\r
+    case EFI_MEMORY_XP:\r
+    case EFI_MEMORY_RP:\r
+    case EFI_MEMORY_UCE:\r
+    default:\r
+      // Cannot be implemented UEFI definition unclear for ARM\r
+      // Cause a page fault if these ranges are accessed.\r
+      ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;\r
+      DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes));\r
+      break;\r
+  }\r
+\r
+  // Determine protection attributes\r
+  if (EfiAttributes & EFI_MEMORY_WP) {\r
+    ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;\r
+  } else {\r
+    ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;\r
+  }\r
+\r
+  // Determine eXecute Never attribute\r
+  if (EfiAttributes & EFI_MEMORY_XP) {\r
+    ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;\r
+  }\r
+\r
+  return ArmAttributes;\r
+}\r
+\r
+EFI_STATUS\r
+GetMemoryRegionPage (\r
+  IN     UINT32                  *PageTable,\r
+  IN OUT UINTN                   *BaseAddress,\r
+  OUT    UINTN                   *RegionLength,\r
+  OUT    UINTN                   *RegionAttributes\r
+  )\r
+{\r
+  UINT32      PageAttributes;\r
+  UINT32      TableIndex;\r
+  UINT32      PageDescriptor;\r
+\r
+  // Convert the section attributes into page attributes\r
+  PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);\r
+\r
+  // Calculate index into first level translation table for start of modification\r
+  TableIndex = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(*BaseAddress) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
+  ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
+\r
+  // Go through the page table to find the end of the section\r
+  for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {\r
+    // Get the section at the given index\r
+    PageDescriptor = PageTable[TableIndex];\r
+\r
+    if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {\r
+      // Case: End of the boundary of the region\r
+      return EFI_SUCCESS;\r
+    } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {\r
+      if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {\r
+        *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;\r
+      } else {\r
+        // Case: End of the boundary of the region\r
+        return EFI_SUCCESS;\r
+      }\r
+    } else {\r
+      // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.\r
+      ASSERT(0);\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\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
+  UINT32                      TableIndex;\r
+  UINT32                      PageAttributes;\r
+  UINT32                      PageTableIndex;\r
+  UINT32                      SectionDescriptor;\r
+  ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
+  UINT32                     *PageTable;\r
+\r
+  // Initialize the arguments\r
+  *RegionLength = 0;\r
+\r
+  // Obtain page table base\r
+  FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
+\r
+  // Calculate index into first level translation table for start of modification\r
+  TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
+  ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);\r
+\r
+  // Get the section at the given index\r
+  SectionDescriptor = FirstLevelTable[TableIndex];\r
+\r
+  // If 'BaseAddress' belongs to the section then round it to the section boundary\r
+  if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||\r
+      ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))\r
+  {\r
+    *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;\r
+    *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;\r
+  } else {\r
+    // Otherwise, we round it to the page boundary\r
+    *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;\r
+\r
+    // Get the attribute at the page table level (Level 2)\r
+    PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
+\r
+    // Calculate index into first level translation table for start of modification\r
+    PageTableIndex = TT_DESCRIPTOR_PAGE_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
+    ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
+\r
+    PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;\r
+    *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |\r
+                        TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);\r
+  }\r
+\r
+  for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {\r
+    // Get the section at the given index\r
+    SectionDescriptor = FirstLevelTable[TableIndex];\r
+\r
+    // If the entry is a level-2 page table then we scan it to find the end of the region\r
+    if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE) {\r
+      // Extract the page table location from the descriptor\r
+      PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
+\r
+      // Scan the page table to find the end of the region.\r
+      Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);\r
+\r
+      // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop\r
+      if (Status == EFI_SUCCESS) {\r
+        break;\r
+      }\r
+    } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||\r
+               ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {\r
+      if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {\r
+        // If the attributes of the section differ from the one targeted then we exit the loop\r
+        break;\r
+      } else {\r
+        *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;\r
+      }\r
+    } else {\r
+      // If we are on an invalid section then it means it is the end of our section.\r
+      break;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
index 075f2a18bf11471fc1e4e0dc1c5370d50230e382..d5b50641e360d6017184335b474245d8713ffacd 100644 (file)
@@ -148,6 +148,19 @@ SetMemoryAttributes (
   IN EFI_PHYSICAL_ADDRESS      VirtualMask\r
   );\r
 \r
   IN EFI_PHYSICAL_ADDRESS      VirtualMask\r
   );\r
 \r
+// The ARM Attributes might be defined on 64-bit (case of the long format description table)\r
+UINT64\r
+EfiAttributeToArmAttribute (\r
+  IN UINT64                    EfiAttributes\r
+  );\r
+\r
+EFI_STATUS\r
+GetMemoryRegion (\r
+  IN OUT UINTN                   *BaseAddress,\r
+  OUT    UINTN                   *RegionLength,\r
+  OUT    UINTN                   *RegionAttributes\r
+  );\r
+\r
 VOID\r
 GetRootTranslationTableInfo (\r
   IN  UINTN    T0SZ,\r
 VOID\r
 GetRootTranslationTableInfo (\r
   IN  UINTN    T0SZ,\r
index e0ee9fc28718b2ddf52b6b65686c87731f9e4fec..723604d1df966ecb0db083e4c4f749443e10de60 100644 (file)
@@ -178,18 +178,37 @@ CpuSetMemoryAttributes (
   IN EFI_CPU_ARCH_PROTOCOL    *This,\r
   IN EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN UINT64                    Length,\r
   IN EFI_CPU_ARCH_PROTOCOL    *This,\r
   IN EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN UINT64                    Length,\r
-  IN UINT64                    Attributes\r
+  IN UINT64                    EfiAttributes\r
   )\r
 {\r
   )\r
 {\r
-  DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx)\n", BaseAddress, Length, Attributes));\r
+  EFI_STATUS  Status;\r
+  UINTN       ArmAttributes;\r
+  UINTN       RegionBaseAddress;\r
+  UINTN       RegionLength;\r
+  UINTN       RegionArmAttributes;\r
 \r
   if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
     // Minimum granularity is SIZE_4KB (4KB on ARM)\r
 \r
   if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
     // Minimum granularity is SIZE_4KB (4KB on ARM)\r
-    DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, Attributes));\r
+    DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));\r
     return EFI_UNSUPPORTED;\r
   }\r
 \r
     return EFI_UNSUPPORTED;\r
   }\r
 \r
-  return SetMemoryAttributes (BaseAddress, Length, Attributes, 0);\r
+  // Convert the 'Attribute' into ARM Attribute\r
+  ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);\r
+\r
+  // Get the region starting from 'BaseAddress' and its 'Attribute'\r
+  RegionBaseAddress = BaseAddress;\r
+  Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);\r
+\r
+  // Data & Instruction Caches are flushed when we set new memory attributes.\r
+  // So, we only set the attributes if the new region is different.\r
+  if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||\r
+      ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))\r
+  {\r
+    return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);\r
+  } else {\r
+    return EFI_SUCCESS;\r
+  }\r
 }\r
 \r
 EFI_STATUS\r
 }\r
 \r
 EFI_STATUS\r