]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
ArmPkg/ArmMmuLib AARCH64: fix out of bounds access
[mirror_edk2.git] / ArmPkg / Library / ArmMmuLib / AArch64 / ArmMmuLibCore.c
index 84a689af7c8aef4f969d1a27c0084f6dfc5e9631..d66df3e17a02a17c71b4ff947963de3f584320a6 100644 (file)
@@ -3,6 +3,7 @@
 *\r
 *  Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
 *  Copyright (c) 2016, Linaro Limited. All rights reserved.\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
@@ -34,6 +35,10 @@ ArmMemoryAttributeToPageAttribute (
   )\r
 {\r
   switch (Attributes) {\r
+  case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:\r
+  case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:\r
+    return TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
+\r
   case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:\r
   case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:\r
     return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
@@ -89,7 +94,7 @@ PageAttributeToGcdAttribute (
   // Determine protection attributes\r
   if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) || ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO)) {\r
     // Read only cases map to write-protect\r
-    GcdAttributes |= EFI_MEMORY_WP;\r
+    GcdAttributes |= EFI_MEMORY_RO;\r
   }\r
 \r
   // Process eXecute Never attribute\r
@@ -100,27 +105,6 @@ PageAttributeToGcdAttribute (
   return GcdAttributes;\r
 }\r
 \r
-ARM_MEMORY_REGION_ATTRIBUTES\r
-GcdAttributeToArmAttribute (\r
-  IN UINT64 GcdAttributes\r
-  )\r
-{\r
-  switch (GcdAttributes & 0xFF) {\r
-  case EFI_MEMORY_UC:\r
-    return ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;\r
-  case EFI_MEMORY_WC:\r
-    return ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;\r
-  case EFI_MEMORY_WT:\r
-    return ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH;\r
-  case EFI_MEMORY_WB:\r
-    return ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK;\r
-  default:\r
-    DEBUG ((EFI_D_ERROR, "GcdAttributeToArmAttribute: 0x%lX attributes is not supported.\n", GcdAttributes));\r
-    ASSERT (0);\r
-    return ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;\r
-  }\r
-}\r
-\r
 #define MIN_T0SZ        16\r
 #define BITS_PER_LEVEL  9\r
 \r
@@ -298,7 +282,7 @@ GetBlockEntryListFromAddress (
         }\r
 \r
         // Create a new translation table\r
-        TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE);\r
+        TranslationTable = AllocatePages (1);\r
         if (TranslationTable == NULL) {\r
           return NULL;\r
         }\r
@@ -321,7 +305,7 @@ GetBlockEntryListFromAddress (
         //\r
 \r
         // Create a new translation table\r
-        TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE);\r
+        TranslationTable = AllocatePages (1);\r
         if (TranslationTable == NULL) {\r
           return NULL;\r
         }\r
@@ -349,7 +333,7 @@ GetBlockEntryListFromAddress (
 }\r
 \r
 STATIC\r
-RETURN_STATUS\r
+EFI_STATUS\r
 UpdateRegionMapping (\r
   IN  UINT64  *RootTable,\r
   IN  UINT64  RegionStart,\r
@@ -367,7 +351,7 @@ UpdateRegionMapping (
   // Ensure the Length is aligned on 4KB boundary\r
   if ((RegionLength == 0) || ((RegionLength & (SIZE_4KB - 1)) != 0)) {\r
     ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
-    return RETURN_INVALID_PARAMETER;\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   do {\r
@@ -377,7 +361,7 @@ UpdateRegionMapping (
     BlockEntry = GetBlockEntryListFromAddress (RootTable, RegionStart, &TableLevel, &BlockEntrySize, &LastBlockEntry);\r
     if (BlockEntry == NULL) {\r
       // GetBlockEntryListFromAddress() return NULL when it fails to allocate new pages from the Translation Tables\r
-      return RETURN_OUT_OF_RESOURCES;\r
+      return EFI_OUT_OF_RESOURCES;\r
     }\r
 \r
     if (TableLevel != 3) {\r
@@ -398,18 +382,18 @@ UpdateRegionMapping (
 \r
       // Break the inner loop when next block is a table\r
       // Rerun GetBlockEntryListFromAddress to avoid page table memory leak\r
-      if (TableLevel != 3 &&\r
+      if (TableLevel != 3 && BlockEntry <= LastBlockEntry &&\r
           (*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
             break;\r
       }\r
     } while ((RegionLength >= BlockEntrySize) && (BlockEntry <= LastBlockEntry));\r
   } while (RegionLength != 0);\r
 \r
-  return RETURN_SUCCESS;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 STATIC\r
-RETURN_STATUS\r
+EFI_STATUS\r
 FillTranslationTable (\r
   IN  UINT64                        *RootTable,\r
   IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryRegion\r
@@ -424,38 +408,93 @@ FillTranslationTable (
            );\r
 }\r
 \r
-RETURN_STATUS\r
-SetMemoryAttributes (\r
+STATIC\r
+UINT64\r
+GcdAttributeToPageAttribute (\r
+  IN UINT64 GcdAttributes\r
+  )\r
+{\r
+  UINT64 PageAttributes;\r
+\r
+  switch (GcdAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
+  case EFI_MEMORY_UC:\r
+    PageAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\r
+    break;\r
+  case EFI_MEMORY_WC:\r
+    PageAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
+    break;\r
+  case EFI_MEMORY_WT:\r
+    PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;\r
+    break;\r
+  case EFI_MEMORY_WB:\r
+    PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
+    break;\r
+  default:\r
+    PageAttributes = TT_ATTR_INDX_MASK;\r
+    break;\r
+  }\r
+\r
+  if ((GcdAttributes & EFI_MEMORY_XP) != 0 ||\r
+      (GcdAttributes & EFI_MEMORY_CACHETYPE_MASK) == EFI_MEMORY_UC) {\r
+    if (ArmReadCurrentEL () == AARCH64_EL2) {\r
+      PageAttributes |= TT_XN_MASK;\r
+    } else {\r
+      PageAttributes |= TT_UXN_MASK | TT_PXN_MASK;\r
+    }\r
+  }\r
+\r
+  if ((GcdAttributes & EFI_MEMORY_RO) != 0) {\r
+    PageAttributes |= TT_AP_RO_RO;\r
+  }\r
+\r
+  return PageAttributes | TT_AF;\r
+}\r
+\r
+EFI_STATUS\r
+ArmSetMemoryAttributes (\r
   IN EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN UINT64                    Length,\r
-  IN UINT64                    Attributes,\r
-  IN EFI_PHYSICAL_ADDRESS      VirtualMask\r
+  IN UINT64                    Attributes\r
   )\r
 {\r
-  RETURN_STATUS                Status;\r
-  ARM_MEMORY_REGION_DESCRIPTOR MemoryRegion;\r
+  EFI_STATUS                   Status;\r
   UINT64                      *TranslationTable;\r
-\r
-  MemoryRegion.PhysicalBase = BaseAddress;\r
-  MemoryRegion.VirtualBase = BaseAddress;\r
-  MemoryRegion.Length = Length;\r
-  MemoryRegion.Attributes = GcdAttributeToArmAttribute (Attributes);\r
+  UINT64                       PageAttributes;\r
+  UINT64                       PageAttributeMask;\r
+\r
+  PageAttributes = GcdAttributeToPageAttribute (Attributes);\r
+  PageAttributeMask = 0;\r
+\r
+  if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {\r
+    //\r
+    // No memory type was set in Attributes, so we are going to update the\r
+    // permissions only.\r
+    //\r
+    PageAttributes &= TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK;\r
+    PageAttributeMask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK |\r
+                          TT_PXN_MASK | TT_XN_MASK);\r
+  }\r
 \r
   TranslationTable = ArmGetTTBR0BaseAddress ();\r
 \r
-  Status = FillTranslationTable (TranslationTable, &MemoryRegion);\r
-  if (RETURN_ERROR (Status)) {\r
+  Status = UpdateRegionMapping (\r
+             TranslationTable,\r
+             BaseAddress,\r
+             Length,\r
+             PageAttributes,\r
+             PageAttributeMask);\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   // Invalidate all TLB entries so changes are synced\r
   ArmInvalidateTlb ();\r
 \r
-  return RETURN_SUCCESS;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 STATIC\r
-RETURN_STATUS\r
+EFI_STATUS\r
 SetMemoryRegionAttribute (\r
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN  UINT64                    Length,\r
@@ -463,23 +502,23 @@ SetMemoryRegionAttribute (
   IN  UINT64                    BlockEntryMask\r
   )\r
 {\r
-  RETURN_STATUS                Status;\r
+  EFI_STATUS                   Status;\r
   UINT64                       *RootTable;\r
 \r
   RootTable = ArmGetTTBR0BaseAddress ();\r
 \r
   Status = UpdateRegionMapping (RootTable, BaseAddress, Length, Attributes, BlockEntryMask);\r
-  if (RETURN_ERROR (Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   // Invalidate all TLB entries so changes are synced\r
   ArmInvalidateTlb ();\r
 \r
-  return RETURN_SUCCESS;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
-RETURN_STATUS\r
+EFI_STATUS\r
 ArmSetMemoryRegionNoExec (\r
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN  UINT64                    Length\r
@@ -500,7 +539,7 @@ ArmSetMemoryRegionNoExec (
            ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
 }\r
 \r
-RETURN_STATUS\r
+EFI_STATUS\r
 ArmClearMemoryRegionNoExec (\r
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN  UINT64                    Length\r
@@ -518,7 +557,7 @@ ArmClearMemoryRegionNoExec (
            Mask);\r
 }\r
 \r
-RETURN_STATUS\r
+EFI_STATUS\r
 ArmSetMemoryRegionReadOnly (\r
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN  UINT64                    Length\r
@@ -531,7 +570,7 @@ ArmSetMemoryRegionReadOnly (
            ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
 }\r
 \r
-RETURN_STATUS\r
+EFI_STATUS\r
 ArmClearMemoryRegionReadOnly (\r
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
   IN  UINT64                    Length\r
@@ -544,7 +583,7 @@ ArmClearMemoryRegionReadOnly (
            ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK));\r
 }\r
 \r
-RETURN_STATUS\r
+EFI_STATUS\r
 EFIAPI\r
 ArmConfigureMmu (\r
   IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryTable,\r
@@ -553,31 +592,27 @@ ArmConfigureMmu (
   )\r
 {\r
   VOID*                         TranslationTable;\r
-  UINTN                         TranslationTablePageCount;\r
   UINT32                        TranslationTableAttribute;\r
-  ARM_MEMORY_REGION_DESCRIPTOR *MemoryTableEntry;\r
   UINT64                        MaxAddress;\r
-  UINT64                        TopAddress;\r
   UINTN                         T0SZ;\r
   UINTN                         RootTableEntryCount;\r
   UINT64                        TCR;\r
-  RETURN_STATUS                 Status;\r
+  EFI_STATUS                    Status;\r
 \r
   if(MemoryTable == NULL) {\r
     ASSERT (MemoryTable != NULL);\r
-    return RETURN_INVALID_PARAMETER;\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  // Identify the highest address of the memory table\r
-  MaxAddress = MemoryTable->PhysicalBase + MemoryTable->Length - 1;\r
-  MemoryTableEntry = MemoryTable;\r
-  while (MemoryTableEntry->Length != 0) {\r
-    TopAddress = MemoryTableEntry->PhysicalBase + MemoryTableEntry->Length - 1;\r
-    if (TopAddress > MaxAddress) {\r
-      MaxAddress = TopAddress;\r
-    }\r
-    MemoryTableEntry++;\r
-  }\r
+  //\r
+  // Limit the virtual address space to what we can actually use: UEFI\r
+  // mandates a 1:1 mapping, so no point in making the virtual address\r
+  // space larger than the physical address space. We also have to take\r
+  // into account the architectural limitations that result from UEFI's\r
+  // use of 4 KB pages.\r
+  //\r
+  MaxAddress = MIN (LShiftU64 (1ULL, ArmGetPhysicalAddressBits ()) - 1,\r
+                    MAX_ALLOC_ADDRESS);\r
 \r
   // Lookup the Table Level to get the information\r
   LookupAddresstoRootTable (MaxAddress, &T0SZ, &RootTableEntryCount);\r
@@ -607,7 +642,7 @@ ArmConfigureMmu (
     } else {\r
       DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n", MaxAddress));\r
       ASSERT (0); // Bigger than 48-bit memory space are not supported\r
-      return RETURN_UNSUPPORTED;\r
+      return EFI_UNSUPPORTED;\r
     }\r
   } else if (ArmReadCurrentEL () == AARCH64_EL1) {\r
     // Due to Cortex-A57 erratum #822227 we must set TG1[1] == 1, regardless of EPD1.\r
@@ -629,21 +664,33 @@ ArmConfigureMmu (
     } else {\r
       DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU configuration.\n", MaxAddress));\r
       ASSERT (0); // Bigger than 48-bit memory space are not supported\r
-      return RETURN_UNSUPPORTED;\r
+      return EFI_UNSUPPORTED;\r
     }\r
   } else {\r
     ASSERT (0); // UEFI is only expected to run at EL2 and EL1, not EL3.\r
-    return RETURN_UNSUPPORTED;\r
+    return EFI_UNSUPPORTED;\r
   }\r
 \r
+  //\r
+  // Translation table walks are always cache coherent on ARMv8-A, so cache\r
+  // maintenance on page tables is never needed. Since there is a risk of\r
+  // loss of coherency when using mismatched attributes, and given that memory\r
+  // is mapped cacheable except for extraordinary cases (such as non-coherent\r
+  // DMA), have the page table walker perform cached accesses as well, and\r
+  // assert below that that matches the attributes we use for CPU accesses to\r
+  // the region.\r
+  //\r
+  TCR |= TCR_SH_INNER_SHAREABLE |\r
+         TCR_RGN_OUTER_WRITE_BACK_ALLOC |\r
+         TCR_RGN_INNER_WRITE_BACK_ALLOC;\r
+\r
   // Set TCR\r
   ArmSetTCR (TCR);\r
 \r
   // Allocate pages for translation table\r
-  TranslationTablePageCount = EFI_SIZE_TO_PAGES(RootTableEntryCount * sizeof(UINT64));\r
-  TranslationTable = (UINT64*)AllocateAlignedPages (TranslationTablePageCount, TT_ALIGNMENT_DESCRIPTION_TABLE);\r
+  TranslationTable = AllocatePages (1);\r
   if (TranslationTable == NULL) {\r
-    return RETURN_OUT_OF_RESOURCES;\r
+    return EFI_OUT_OF_RESOURCES;\r
   }\r
   // We set TTBR0 just after allocating the table to retrieve its location from the subsequent\r
   // functions without needing to pass this value across the functions. The MMU is only enabled\r
@@ -671,39 +718,25 @@ ArmConfigureMmu (
 \r
   TranslationTableAttribute = TT_ATTR_INDX_INVALID;\r
   while (MemoryTable->Length != 0) {\r
-    // Find the memory attribute for the Translation Table\r
-    if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) &&\r
-        ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {\r
-      TranslationTableAttribute = MemoryTable->Attributes;\r
-    }\r
+\r
+    DEBUG_CODE_BEGIN ();\r
+      // Find the memory attribute for the Translation Table\r
+      if ((UINTN)TranslationTable >= MemoryTable->PhysicalBase &&\r
+          (UINTN)TranslationTable + EFI_PAGE_SIZE <= MemoryTable->PhysicalBase +\r
+                                                          MemoryTable->Length) {\r
+        TranslationTableAttribute = MemoryTable->Attributes;\r
+      }\r
+    DEBUG_CODE_END ();\r
 \r
     Status = FillTranslationTable (TranslationTable, MemoryTable);\r
-    if (RETURN_ERROR (Status)) {\r
+    if (EFI_ERROR (Status)) {\r
       goto FREE_TRANSLATION_TABLE;\r
     }\r
     MemoryTable++;\r
   }\r
 \r
-  // Translate the Memory Attributes into Translation Table Register Attributes\r
-  if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED) ||\r
-      (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED)) {\r
-    TCR |= TCR_SH_NON_SHAREABLE | TCR_RGN_OUTER_NON_CACHEABLE | TCR_RGN_INNER_NON_CACHEABLE;\r
-  } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||\r
-      (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {\r
-    TCR |= TCR_SH_INNER_SHAREABLE | TCR_RGN_OUTER_WRITE_BACK_ALLOC | TCR_RGN_INNER_WRITE_BACK_ALLOC;\r
-  } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) ||\r
-      (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH)) {\r
-    TCR |= TCR_SH_NON_SHAREABLE | TCR_RGN_OUTER_WRITE_THROUGH | TCR_RGN_INNER_WRITE_THROUGH;\r
-  } else {\r
-    // If we failed to find a mapping that contains the root translation table then it probably means the translation table\r
-    // is not mapped in the given memory map.\r
-    ASSERT (0);\r
-    Status = RETURN_UNSUPPORTED;\r
-    goto FREE_TRANSLATION_TABLE;\r
-  }\r
-\r
-  // Set again TCR after getting the Translation Table attributes\r
-  ArmSetTCR (TCR);\r
+  ASSERT (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK ||\r
+          TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK);\r
 \r
   ArmSetMAIR (MAIR_ATTR(TT_ATTR_INDX_DEVICE_MEMORY, MAIR_ATTR_DEVICE_MEMORY) |                      // mapped to EFI_MEMORY_UC\r
               MAIR_ATTR(TT_ATTR_INDX_MEMORY_NON_CACHEABLE, MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE) | // mapped to EFI_MEMORY_WC\r
@@ -711,14 +744,15 @@ ArmConfigureMmu (
               MAIR_ATTR(TT_ATTR_INDX_MEMORY_WRITE_BACK, MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK));       // mapped to EFI_MEMORY_WB\r
 \r
   ArmDisableAlignmentCheck ();\r
+  ArmEnableStackAlignmentCheck ();\r
   ArmEnableInstructionCache ();\r
   ArmEnableDataCache ();\r
 \r
   ArmEnableMmu ();\r
-  return RETURN_SUCCESS;\r
+  return EFI_SUCCESS;\r
 \r
 FREE_TRANSLATION_TABLE:\r
-  FreePages (TranslationTable, TranslationTablePageCount);\r
+  FreePages (TranslationTable, 1);\r
   return Status;\r
 }\r
 \r