*\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
)\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
// 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
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
}\r
\r
STATIC\r
-RETURN_STATUS\r
+EFI_STATUS\r
UpdateRegionMapping (\r
IN UINT64 *RootTable,\r
IN UINT64 RegionStart,\r
// 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
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
\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
);\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
+ UINT64 PageAttributes;\r
+ UINT64 PageAttributeMask;\r
\r
- MemoryRegion.PhysicalBase = BaseAddress;\r
- MemoryRegion.VirtualBase = BaseAddress;\r
- MemoryRegion.Length = Length;\r
- MemoryRegion.Attributes = GcdAttributeToArmAttribute (Attributes);\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
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
~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
Mask);\r
}\r
\r
-RETURN_STATUS\r
+EFI_STATUS\r
ArmSetMemoryRegionReadOnly (\r
IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
IN UINT64 Length\r
~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
~(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
)\r
{\r
VOID* TranslationTable;\r
- VOID* TranslationTableBuffer;\r
UINT32 TranslationTableAttribute;\r
UINT64 MaxAddress;\r
UINTN T0SZ;\r
UINTN RootTableEntryCount;\r
- UINTN RootTableEntrySize;\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
- // Cover the entire GCD memory space\r
- MaxAddress = (1UL << PcdGet8 (PcdPrePiCpuMemorySize)) - 1;\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
} 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
} 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. Pool allocations are 8 byte aligned,\r
- // but we may require a higher alignment based on the size of the root table.\r
- RootTableEntrySize = RootTableEntryCount * sizeof(UINT64);\r
- if (RootTableEntrySize < EFI_PAGE_SIZE / 2) {\r
- TranslationTableBuffer = AllocatePool (2 * RootTableEntrySize - 8);\r
- //\r
- // Naturally align the root table. Preserves possible NULL value\r
- //\r
- TranslationTable = (VOID *)((UINTN)(TranslationTableBuffer - 1) | (RootTableEntrySize - 1)) + 1;\r
- } else {\r
- TranslationTable = AllocatePages (1);\r
- TranslationTableBuffer = NULL;\r
- }\r
+ // Allocate pages for translation 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
}\r
\r
if (TranslationTableSize != NULL) {\r
- *TranslationTableSize = RootTableEntrySize;\r
+ *TranslationTableSize = RootTableEntryCount * sizeof(UINT64);\r
}\r
\r
- ZeroMem (TranslationTable, RootTableEntrySize);\r
+ ZeroMem (TranslationTable, RootTableEntryCount * sizeof(UINT64));\r
\r
// Disable MMU and caches. ArmDisableMmu() also invalidates the TLBs\r
ArmDisableMmu ();\r
\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
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
- if (TranslationTableBuffer != NULL) {\r
- FreePool (TranslationTableBuffer);\r
- } else {\r
- FreePages (TranslationTable, 1);\r
- }\r
+ FreePages (TranslationTable, 1);\r
return Status;\r
}\r
\r