]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Library/ArmLib/AArch64/AArch64Mmu.c
ArmPkg: ArmLib: purge incorrect ArmDrainWriteBuffer () alias
[mirror_edk2.git] / ArmPkg / Library / ArmLib / AArch64 / AArch64Mmu.c
index 05b2a197d5af59f247e9f58fad687b3cb698e54b..f967a6478840c99aaa4401b7ec99ac67eb1bb801 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
 *  File managing the MMU for ARMv8 architecture\r
 *\r
-*  Copyright (c) 2011-2013, ARM Limited. All rights reserved.\r
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.\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,24 +34,26 @@ ArmMemoryAttributeToPageAttribute (
 {\r
   switch (Attributes) {\r
   case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:\r
-    return TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
-  case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:\r
-    return TT_ATTR_INDX_MEMORY_WRITE_THROUGH;\r
-  case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:\r
-    return TT_ATTR_INDX_DEVICE_MEMORY;\r
-  case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:\r
-    return TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
   case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:\r
-    return TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
+    return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
+\r
+  case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:\r
   case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:\r
-    return TT_ATTR_INDX_MEMORY_WRITE_THROUGH;\r
-  case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:\r
-    return TT_ATTR_INDX_DEVICE_MEMORY;\r
+    return TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;\r
+\r
+  // Uncached and device mappings are treated as outer shareable by default,\r
+  case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:\r
   case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:\r
     return TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
+\r
   default:\r
     ASSERT(0);\r
-    return TT_ATTR_INDX_DEVICE_MEMORY;\r
+  case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:\r
+  case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:\r
+    if (ArmReadCurrentEL () == AARCH64_EL2)\r
+      return TT_ATTR_INDX_DEVICE_MEMORY | TT_TABLE_XN;\r
+    else\r
+      return TT_ATTR_INDX_DEVICE_MEMORY | TT_TABLE_UXN | TT_TABLE_PXN;\r
   }\r
 }\r
 \r
@@ -97,48 +99,6 @@ PageAttributeToGcdAttribute (
   return GcdAttributes;\r
 }\r
 \r
-UINT64\r
-GcdAttributeToPageAttribute (\r
-  IN UINT64 GcdAttributes\r
-  )\r
-{\r
-  UINT64  PageAttributes;\r
-\r
-  switch (GcdAttributes & 0xFF) {\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;\r
-    break;\r
-  case EFI_MEMORY_WB:\r
-    PageAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
-    break;\r
-  default:\r
-    DEBUG ((EFI_D_ERROR, "GcdAttributeToPageAttribute: 0x%X attributes is not supported.\n", GcdAttributes));\r
-    ASSERT (0);\r
-    // If no match has been found then we mark the memory as device memory.\r
-    // The only side effect of using device memory should be a slow down in the performance.\r
-    PageAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\r
-  }\r
-\r
-  // Determine protection attributes\r
-  if (GcdAttributes & EFI_MEMORY_WP) {\r
-    // Read only cases map to write-protect\r
-    PageAttributes |= TT_AP_RO_RO;\r
-  }\r
-\r
-  // Process eXecute Never attribute\r
-  if (GcdAttributes & EFI_MEMORY_XP) {\r
-    PageAttributes |= (TT_PXN_MASK | TT_UXN_MASK);\r
-  }\r
-\r
-  return PageAttributes;\r
-}\r
-\r
 ARM_MEMORY_REGION_ATTRIBUTES\r
 GcdAttributeToArmAttribute (\r
   IN UINT64 GcdAttributes\r
@@ -244,13 +204,14 @@ GetBlockEntryListFromAddress (
   IN  UINT64        RegionStart,\r
   OUT UINTN        *TableLevel,\r
   IN OUT UINT64    *BlockEntrySize,\r
-  IN OUT UINT64   **LastBlockEntry\r
+  OUT UINT64      **LastBlockEntry\r
   )\r
 {\r
   UINTN   RootTableLevel;\r
   UINTN   RootTableEntryCount;\r
   UINT64 *TranslationTable;\r
   UINT64 *BlockEntry;\r
+  UINT64 *SubTableBlockEntry;\r
   UINT64  BlockEntryAddress;\r
   UINTN   BaseAddressAlignment;\r
   UINTN   PageLevel;\r
@@ -264,55 +225,48 @@ GetBlockEntryListFromAddress (
   BlockEntry = NULL;\r
 \r
   // Ensure the parameters are valid\r
-  ASSERT (TableLevel && BlockEntrySize && LastBlockEntry);\r
+  if (!(TableLevel && BlockEntrySize && LastBlockEntry)) {\r
+    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
+    return NULL;\r
+  }\r
 \r
   // Ensure the Region is aligned on 4KB boundary\r
-  ASSERT ((RegionStart & (SIZE_4KB - 1)) == 0);\r
+  if ((RegionStart & (SIZE_4KB - 1)) != 0) {\r
+    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
+    return NULL;\r
+  }\r
 \r
-  // Ensure the required size is aligned on 4KB boundary\r
-  ASSERT ((*BlockEntrySize & (SIZE_4KB - 1)) == 0);\r
+  // Ensure the required size is aligned on 4KB boundary and not 0\r
+  if ((*BlockEntrySize & (SIZE_4KB - 1)) != 0 || *BlockEntrySize == 0) {\r
+    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
+    return NULL;\r
+  }\r
 \r
-  //\r
-  // Calculate LastBlockEntry from T0SZ - this is the last block entry of the root Translation table\r
-  //\r
   T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
   // Get the Table info from T0SZ\r
   GetRootTranslationTableInfo (T0SZ, &RootTableLevel, &RootTableEntryCount);\r
-  // The last block of the root table depends on the number of entry in this table\r
-  *LastBlockEntry = (UINT64*)((UINTN)RootTable + ((RootTableEntryCount - 1) * sizeof(UINT64)));\r
 \r
   // If the start address is 0x0 then we use the size of the region to identify the alignment\r
   if (RegionStart == 0) {\r
     // Identify the highest possible alignment for the Region Size\r
-    for (BaseAddressAlignment = 0; BaseAddressAlignment < 64; BaseAddressAlignment++) {\r
-      if ((1 << BaseAddressAlignment) & *BlockEntrySize) {\r
-        break;\r
-      }\r
-    }\r
+    BaseAddressAlignment = LowBitSet64 (*BlockEntrySize);\r
   } else {\r
     // Identify the highest possible alignment for the Base Address\r
-    for (BaseAddressAlignment = 0; BaseAddressAlignment < 64; BaseAddressAlignment++) {\r
-      if ((1 << BaseAddressAlignment) & RegionStart) {\r
-        break;\r
-      }\r
-    }\r
+    BaseAddressAlignment = LowBitSet64 (RegionStart);\r
   }\r
 \r
-  // Identify the Page Level the RegionStart must belongs to\r
-  PageLevel = 3 - ((BaseAddressAlignment - 12) / 9);\r
+  // Identify the Page Level the RegionStart must belong to. Note that PageLevel\r
+  // should be at least 1 since block translations are not supported at level 0\r
+  PageLevel = MAX (3 - ((BaseAddressAlignment - 12) / 9), 1);\r
 \r
-  // If the required size is smaller than the current block size then we need to go to the page bellow.\r
-  if (*BlockEntrySize < TT_ADDRESS_AT_LEVEL(PageLevel)) {\r
+  // If the required size is smaller than the current block size then we need to go to the page below.\r
+  // The PageLevel was calculated on the Base Address alignment but did not take in account the alignment\r
+  // of the allocation size\r
+  while (*BlockEntrySize < TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel)) {\r
     // It does not fit so we need to go a page level above\r
     PageLevel++;\r
   }\r
 \r
-  // Expose the found PageLevel to the caller\r
-  *TableLevel = PageLevel;\r
-\r
-  // Now, we have the Table Level we can get the Block Size associated to this table\r
-  *BlockEntrySize = TT_ADDRESS_AT_LEVEL(PageLevel);\r
-\r
   //\r
   // Get the Table Descriptor for the corresponding PageLevel. We need to decompose RegionStart to get appropriate entries\r
   //\r
@@ -325,13 +279,10 @@ GetBlockEntryListFromAddress (
       // Go to the next table\r
       TranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
 \r
-      // If we are at the last level then update the output\r
+      // If we are at the last level then update the last level to next level\r
       if (IndexLevel == PageLevel) {\r
-        // And get the appropriate BlockEntry at the next level\r
-        BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel + 1, RegionStart);\r
-\r
-        // Set the last block for this new table\r
-        *LastBlockEntry = (UINT64*)((UINTN)TranslationTable + ((TT_ENTRY_COUNT - 1) * sizeof(UINT64)));\r
+        // Enter the next level\r
+        PageLevel++;\r
       }\r
     } else if ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) {\r
       // If we are not at the last level then we need to split this BlockEntry\r
@@ -344,7 +295,8 @@ GetBlockEntryListFromAddress (
         if (Attributes & TT_PXN_MASK) {\r
           TableAttributes = TT_TABLE_PXN;\r
         }\r
-        if (Attributes & TT_UXN_MASK) {\r
+        // XN maps to UXN in the EL1&0 translation regime\r
+        if (Attributes & TT_XN_MASK) {\r
           TableAttributes = TT_TABLE_XN;\r
         }\r
         if (Attributes & TT_NS) {\r
@@ -357,43 +309,40 @@ GetBlockEntryListFromAddress (
         // Shift back to right to set zero before the effective address\r
         BlockEntryAddress = BlockEntryAddress << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel);\r
 \r
-        // Set the correct entry type\r
-        if (IndexLevel + 1 == 3) {\r
+        // Set the correct entry type for the next page level\r
+        if ((IndexLevel + 1) == 3) {\r
           Attributes |= TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
         } else {\r
           Attributes |= TT_TYPE_BLOCK_ENTRY;\r
         }\r
 \r
         // Create a new translation table\r
-        TranslationTable = (UINT64*)AllocatePages (EFI_SIZE_TO_PAGES((TT_ENTRY_COUNT * sizeof(UINT64)) + TT_ALIGNMENT_DESCRIPTION_TABLE));\r
+        TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE);\r
         if (TranslationTable == NULL) {\r
           return NULL;\r
         }\r
-        TranslationTable = (UINT64*)((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
-\r
-        // Fill the new BlockEntry with the TranslationTable\r
-        *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY;\r
-        // Update the last block entry with the newly created translation table\r
-        *LastBlockEntry = (UINT64*)((UINTN)TranslationTable + ((TT_ENTRY_COUNT - 1) * sizeof(UINT64)));\r
 \r
         // Populate the newly created lower level table\r
-        BlockEntry = TranslationTable;\r
+        SubTableBlockEntry = TranslationTable;\r
         for (Index = 0; Index < TT_ENTRY_COUNT; Index++) {\r
-          *BlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1)));\r
-          BlockEntry++;\r
+          *SubTableBlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1)));\r
+          SubTableBlockEntry++;\r
         }\r
-        // Block Entry points at the beginning of the Translation Table\r
-        BlockEntry = TranslationTable;\r
+\r
+        // Fill the BlockEntry with the new TranslationTable\r
+        *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY;\r
       }\r
     } else {\r
-      // Case of Invalid Entry and we are at a page level above of the one targetted.\r
       if (IndexLevel != PageLevel) {\r
+        //\r
+        // Case when we have an Invalid Entry and we are at a page level above of the one targetted.\r
+        //\r
+\r
         // Create a new translation table\r
-        TranslationTable = (UINT64*)AllocatePages (EFI_SIZE_TO_PAGES((TT_ENTRY_COUNT * sizeof(UINT64)) + TT_ALIGNMENT_DESCRIPTION_TABLE));\r
+        TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE);\r
         if (TranslationTable == NULL) {\r
           return NULL;\r
         }\r
-        TranslationTable = (UINT64*)((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
 \r
         ZeroMem (TranslationTable, TT_ENTRY_COUNT * sizeof(UINT64));\r
 \r
@@ -403,37 +352,46 @@ GetBlockEntryListFromAddress (
     }\r
   }\r
 \r
+  // Expose the found PageLevel to the caller\r
+  *TableLevel = PageLevel;\r
+\r
+  // Now, we have the Table Level we can get the Block Size associated to this table\r
+  *BlockEntrySize = TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel);\r
+\r
+  // The last block of the root table depends on the number of entry in this table,\r
+  // otherwise it is always the (TT_ENTRY_COUNT - 1)th entry in the table.\r
+  *LastBlockEntry = TT_LAST_BLOCK_ADDRESS(TranslationTable,\r
+      (PageLevel == RootTableLevel) ? RootTableEntryCount : TT_ENTRY_COUNT);\r
+\r
   return BlockEntry;\r
 }\r
 \r
 STATIC\r
 RETURN_STATUS\r
-FillTranslationTable (\r
-  IN  UINT64                        *RootTable,\r
-  IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryRegion\r
+UpdateRegionMapping (\r
+  IN  UINT64  *RootTable,\r
+  IN  UINT64  RegionStart,\r
+  IN  UINT64  RegionLength,\r
+  IN  UINT64  Attributes,\r
+  IN  UINT64  BlockEntryMask\r
   )\r
 {\r
-  UINT64  Attributes;\r
   UINT32  Type;\r
-  UINT64  RegionStart;\r
-  UINT64  RemainingRegionLength;\r
-  UINT64 *BlockEntry;\r
-  UINT64 *LastBlockEntry;\r
+  UINT64  *BlockEntry;\r
+  UINT64  *LastBlockEntry;\r
   UINT64  BlockEntrySize;\r
   UINTN   TableLevel;\r
 \r
   // Ensure the Length is aligned on 4KB boundary\r
-  ASSERT ((MemoryRegion->Length > 0) && ((MemoryRegion->Length & (SIZE_4KB - 1)) == 0));\r
-\r
-  // Variable initialization\r
-  Attributes = ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | TT_AF;\r
-  RemainingRegionLength = MemoryRegion->Length;\r
-  RegionStart = MemoryRegion->VirtualBase;\r
+  if ((RegionLength == 0) || ((RegionLength & (SIZE_4KB - 1)) != 0)) {\r
+    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
+    return RETURN_INVALID_PARAMETER;\r
+  }\r
 \r
   do {\r
     // Get the first Block Entry that matches the Virtual Address and also the information on the Table Descriptor\r
     // such as the the size of the Block Entry and the address of the last BlockEntry of the Table Descriptor\r
-    BlockEntrySize = RemainingRegionLength;\r
+    BlockEntrySize = RegionLength;\r
     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
@@ -448,18 +406,42 @@ FillTranslationTable (
 \r
     do {\r
       // Fill the Block Entry with attribute and output block address\r
-      *BlockEntry = (RegionStart & TT_ADDRESS_MASK_BLOCK_ENTRY) | Attributes | Type;\r
+      *BlockEntry &= BlockEntryMask;\r
+      *BlockEntry |= (RegionStart & TT_ADDRESS_MASK_BLOCK_ENTRY) | Attributes | Type;\r
 \r
       // Go to the next BlockEntry\r
       RegionStart += BlockEntrySize;\r
-      RemainingRegionLength -= BlockEntrySize;\r
+      RegionLength -= BlockEntrySize;\r
       BlockEntry++;\r
-    } while ((RemainingRegionLength >= BlockEntrySize) && (BlockEntry <= LastBlockEntry));\r
-  } while (RemainingRegionLength != 0);\r
+\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
+          (*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
 }\r
 \r
+STATIC\r
+RETURN_STATUS\r
+FillTranslationTable (\r
+  IN  UINT64                        *RootTable,\r
+  IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryRegion\r
+  )\r
+{\r
+  return UpdateRegionMapping (\r
+           RootTable,\r
+           MemoryRegion->VirtualBase,\r
+           MemoryRegion->Length,\r
+           ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | TT_AF,\r
+           0\r
+           );\r
+}\r
+\r
 RETURN_STATUS\r
 SetMemoryAttributes (\r
   IN EFI_PHYSICAL_ADDRESS      BaseAddress,\r
@@ -467,7 +449,7 @@ SetMemoryAttributes (
   IN UINT64                    Attributes,\r
   IN EFI_PHYSICAL_ADDRESS      VirtualMask\r
   )\r
-{
+{\r
   RETURN_STATUS                Status;\r
   ARM_MEMORY_REGION_DESCRIPTOR MemoryRegion;\r
   UINT64                      *TranslationTable;\r
@@ -479,16 +461,35 @@ SetMemoryAttributes (
 \r
   TranslationTable = ArmGetTTBR0BaseAddress ();\r
 \r
-  Status = FillTranslationTable (TranslationTable, &MemoryRegion);
-  if (RETURN_ERROR (Status)) {
-    return Status;
+  Status = FillTranslationTable (TranslationTable, &MemoryRegion);\r
+  if (RETURN_ERROR (Status)) {\r
+    return Status;\r
   }\r
 \r
-  // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks\r
-  // flush and invalidate pages\r
-  ArmCleanInvalidateDataCache ();\r
+  // Invalidate all TLB entries so changes are synced\r
+  ArmInvalidateTlb ();\r
 \r
-  ArmInvalidateInstructionCache ();\r
+  return RETURN_SUCCESS;\r
+}\r
+\r
+STATIC\r
+RETURN_STATUS\r
+SetMemoryRegionAttribute (\r
+  IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
+  IN  UINT64                    Length,\r
+  IN  UINT64                    Attributes,\r
+  IN  UINT64                    BlockEntryMask\r
+  )\r
+{\r
+  RETURN_STATUS                Status;\r
+  UINT64                       *RootTable;\r
+\r
+  RootTable = ArmGetTTBR0BaseAddress ();\r
+\r
+  Status = UpdateRegionMapping (RootTable, BaseAddress, Length, Attributes, BlockEntryMask);\r
+  if (RETURN_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
   // Invalidate all TLB entries so changes are synced\r
   ArmInvalidateTlb ();\r
@@ -496,6 +497,71 @@ SetMemoryAttributes (
   return RETURN_SUCCESS;\r
 }\r
 \r
+RETURN_STATUS\r
+ArmSetMemoryRegionNoExec (\r
+  IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
+  IN  UINT64                    Length\r
+  )\r
+{\r
+  UINT64    Val;\r
+\r
+  if (ArmReadCurrentEL () == AARCH64_EL1) {\r
+    Val = TT_PXN_MASK | TT_UXN_MASK;\r
+  } else {\r
+    Val = TT_XN_MASK;\r
+  }\r
+\r
+  return SetMemoryRegionAttribute (\r
+           BaseAddress,\r
+           Length,\r
+           Val,\r
+           ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
+}\r
+\r
+RETURN_STATUS\r
+ArmClearMemoryRegionNoExec (\r
+  IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
+  IN  UINT64                    Length\r
+  )\r
+{\r
+  UINT64 Mask;\r
+\r
+  // XN maps to UXN in the EL1&0 translation regime\r
+  Mask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_PXN_MASK | TT_XN_MASK);\r
+\r
+  return SetMemoryRegionAttribute (\r
+           BaseAddress,\r
+           Length,\r
+           0,\r
+           Mask);\r
+}\r
+\r
+RETURN_STATUS\r
+ArmSetMemoryRegionReadOnly (\r
+  IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
+  IN  UINT64                    Length\r
+  )\r
+{\r
+  return SetMemoryRegionAttribute (\r
+           BaseAddress,\r
+           Length,\r
+           TT_AP_RO_RO,\r
+           ~TT_ADDRESS_MASK_BLOCK_ENTRY);\r
+}\r
+\r
+RETURN_STATUS\r
+ArmClearMemoryRegionReadOnly (\r
+  IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
+  IN  UINT64                    Length\r
+  )\r
+{\r
+  return SetMemoryRegionAttribute (\r
+           BaseAddress,\r
+           Length,\r
+           TT_AP_NO_RO,\r
+           ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK));\r
+}\r
+\r
 RETURN_STATUS\r
 EFIAPI\r
 ArmConfigureMmu (\r
@@ -515,7 +581,10 @@ ArmConfigureMmu (
   UINT64                        TCR;\r
   RETURN_STATUS                 Status;\r
 \r
-  ASSERT (MemoryTable != NULL);\r
+  if(MemoryTable == NULL) {\r
+    ASSERT (MemoryTable != NULL);\r
+    return RETURN_INVALID_PARAMETER;\r
+  }\r
 \r
   // Identify the highest address of the memory table\r
   MaxAddress = MemoryTable->PhysicalBase + MemoryTable->Length - 1;\r
@@ -534,8 +603,10 @@ ArmConfigureMmu (
   //\r
   // Set TCR that allows us to retrieve T0SZ in the subsequent functions\r
   //\r
-  if ((ArmReadCurrentEL () == AARCH64_EL2) || (ArmReadCurrentEL () == AARCH64_EL3)) {\r
-    //Note: Bits 23 and 31 are reserved bits in TCR_EL2 and TCR_EL3\r
+  // Ideally we will be running at EL2, but should support EL1 as well.\r
+  // UEFI should not run at EL3.\r
+  if (ArmReadCurrentEL () == AARCH64_EL2) {\r
+    //Note: Bits 23 and 31 are reserved(RES1) bits in TCR_EL2\r
     TCR = T0SZ | (1UL << 31) | (1UL << 23) | TCR_TG0_4KB;\r
 \r
     // Set the Physical Address Size using MaxAddress\r
@@ -552,12 +623,34 @@ ArmConfigureMmu (
     } else if (MaxAddress < SIZE_256TB) {\r
       TCR |= TCR_PS_256TB;\r
     } else {\r
-      DEBUG ((EFI_D_ERROR, "ArmConfigureMmu: The MaxAddress 0x%lX is not supported by this MMU support.\n", MaxAddress));\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
+    }\r
+  } else if (ArmReadCurrentEL () == AARCH64_EL1) {\r
+    // Due to Cortex-A57 erratum #822227 we must set TG1[1] == 1, regardless of EPD1.\r
+    TCR = T0SZ | TCR_TG0_4KB | TCR_TG1_4KB | TCR_EPD1;\r
+\r
+    // Set the Physical Address Size using MaxAddress\r
+    if (MaxAddress < SIZE_4GB) {\r
+      TCR |= TCR_IPS_4GB;\r
+    } else if (MaxAddress < SIZE_64GB) {\r
+      TCR |= TCR_IPS_64GB;\r
+    } else if (MaxAddress < SIZE_1TB) {\r
+      TCR |= TCR_IPS_1TB;\r
+    } else if (MaxAddress < SIZE_4TB) {\r
+      TCR |= TCR_IPS_4TB;\r
+    } else if (MaxAddress < SIZE_16TB) {\r
+      TCR |= TCR_IPS_16TB;\r
+    } else if (MaxAddress < SIZE_256TB) {\r
+      TCR |= TCR_IPS_256TB;\r
+    } 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
     }\r
   } else {\r
-    ASSERT (0); // Bigger than 48-bit memory space are not supported\r
+    ASSERT (0); // UEFI is only expected to run at EL2 and EL1, not EL3.\r
     return RETURN_UNSUPPORTED;\r
   }\r
 \r
@@ -565,12 +658,11 @@ ArmConfigureMmu (
   ArmSetTCR (TCR);\r
 \r
   // Allocate pages for translation table\r
-  TranslationTablePageCount = EFI_SIZE_TO_PAGES((RootTableEntryCount * sizeof(UINT64)) + TT_ALIGNMENT_DESCRIPTION_TABLE);\r
-  TranslationTable = AllocatePages (TranslationTablePageCount);\r
+  TranslationTablePageCount = EFI_SIZE_TO_PAGES(RootTableEntryCount * sizeof(UINT64));\r
+  TranslationTable = (UINT64*)AllocateAlignedPages (TranslationTablePageCount, TT_ALIGNMENT_DESCRIPTION_TABLE);\r
   if (TranslationTable == NULL) {\r
     return RETURN_OUT_OF_RESOURCES;\r
   }\r
-  TranslationTable = (VOID*)((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\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
   // after the translation tables are populated.\r
@@ -628,6 +720,9 @@ ArmConfigureMmu (
     goto FREE_TRANSLATION_TABLE;\r
   }\r
 \r
+  // Set again TCR after getting the Translation Table attributes\r
+  ArmSetTCR (TCR);\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
               MAIR_ATTR(TT_ATTR_INDX_MEMORY_WRITE_THROUGH, MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH) | // mapped to EFI_MEMORY_WT\r