/** @file\r
* File managing the MMU for ARMv8 architecture\r
*\r
-* Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
+* Copyright (c) 2011-2020, 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
\r
STATIC\r
VOID\r
-ReplaceLiveEntry (\r
+ReplaceTableEntry (\r
IN UINT64 *Entry,\r
IN UINT64 Value,\r
- IN UINT64 RegionStart\r
+ IN UINT64 RegionStart,\r
+ IN BOOLEAN IsLiveBlockMapping\r
)\r
{\r
- if (!ArmMmuEnabled ()) {\r
+ if (!ArmMmuEnabled () || !IsLiveBlockMapping) {\r
*Entry = Value;\r
+ ArmUpdateTranslationTableEntry (Entry, (VOID *)(UINTN)RegionStart);\r
} else {\r
ArmReplaceLiveTranslationEntry (Entry, Value, RegionStart);\r
}\r
\r
STATIC\r
VOID\r
-LookupAddresstoRootTable (\r
- IN UINT64 MaxAddress,\r
- OUT UINTN *T0SZ,\r
- OUT UINTN *TableEntryCount\r
+FreePageTablesRecursive (\r
+ IN UINT64 *TranslationTable\r
)\r
{\r
- UINTN TopBit;\r
-\r
- // Check the parameters are not NULL\r
- ASSERT ((T0SZ != NULL) && (TableEntryCount != NULL));\r
+ UINTN Index;\r
\r
- // Look for the highest bit set in MaxAddress\r
- for (TopBit = 63; TopBit != 0; TopBit--) {\r
- if ((1ULL << TopBit) & MaxAddress) {\r
- // MaxAddress top bit is found\r
- TopBit = TopBit + 1;\r
- break;\r
+ for (Index = 0; Index < TT_ENTRY_COUNT; Index++) {\r
+ if ((TranslationTable[Index] & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
+ FreePageTablesRecursive ((VOID *)(UINTN)(TranslationTable[Index] &\r
+ TT_ADDRESS_MASK_BLOCK_ENTRY));\r
}\r
}\r
- ASSERT (TopBit != 0);\r
-\r
- // Calculate T0SZ from the top bit of the MaxAddress\r
- *T0SZ = 64 - TopBit;\r
-\r
- // Get the Table info from T0SZ\r
- GetRootTranslationTableInfo (*T0SZ, NULL, TableEntryCount);\r
+ FreePages (TranslationTable, 1);\r
}\r
\r
STATIC\r
-UINT64*\r
-GetBlockEntryListFromAddress (\r
- IN UINT64 *RootTable,\r
- IN UINT64 RegionStart,\r
- OUT UINTN *TableLevel,\r
- IN OUT UINT64 *BlockEntrySize,\r
- OUT UINT64 **LastBlockEntry\r
+EFI_STATUS\r
+UpdateRegionMappingRecursive (\r
+ IN UINT64 RegionStart,\r
+ IN UINT64 RegionEnd,\r
+ IN UINT64 AttributeSetMask,\r
+ IN UINT64 AttributeClearMask,\r
+ IN UINT64 *PageTable,\r
+ IN UINTN Level\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
- UINTN Index;\r
- UINTN IndexLevel;\r
- UINTN T0SZ;\r
- UINT64 Attributes;\r
- UINT64 TableAttributes;\r
-\r
- // Initialize variable\r
- BlockEntry = NULL;\r
-\r
- // Ensure the parameters are valid\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
- if ((RegionStart & (SIZE_4KB - 1)) != 0) {\r
- ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
- return NULL;\r
- }\r
+ UINTN BlockShift;\r
+ UINT64 BlockMask;\r
+ UINT64 BlockEnd;\r
+ UINT64 *Entry;\r
+ UINT64 EntryValue;\r
+ VOID *TranslationTable;\r
+ EFI_STATUS Status;\r
\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
- T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
- // Get the Table info from T0SZ\r
- GetRootTranslationTableInfo (T0SZ, &RootTableLevel, &RootTableEntryCount);\r
+ ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);\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
- BaseAddressAlignment = LowBitSet64 (*BlockEntrySize);\r
- } else {\r
- // Identify the highest possible alignment for the Base Address\r
- BaseAddressAlignment = LowBitSet64 (RegionStart);\r
- }\r
+ BlockShift = (Level + 1) * BITS_PER_LEVEL + MIN_T0SZ;\r
+ BlockMask = MAX_UINT64 >> BlockShift;\r
\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 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
+ DEBUG ((DEBUG_VERBOSE, "%a(%d): %llx - %llx set %lx clr %lx\n", __FUNCTION__,\r
+ Level, RegionStart, RegionEnd, AttributeSetMask, AttributeClearMask));\r
\r
- //\r
- // Get the Table Descriptor for the corresponding PageLevel. We need to decompose RegionStart to get appropriate entries\r
- //\r
-\r
- TranslationTable = RootTable;\r
- for (IndexLevel = RootTableLevel; IndexLevel <= PageLevel; IndexLevel++) {\r
- BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel, RegionStart);\r
-\r
- if ((IndexLevel != 3) && ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) {\r
- // 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 last level to next level\r
- if (IndexLevel == PageLevel) {\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
- if (IndexLevel != PageLevel) {\r
- // Retrieve the attributes from the block entry\r
- Attributes = *BlockEntry & TT_ATTRIBUTES_MASK;\r
-\r
- // Convert the block entry attributes into Table descriptor attributes\r
- TableAttributes = TT_TABLE_AP_NO_PERMISSION;\r
- if (Attributes & TT_NS) {\r
- TableAttributes = TT_TABLE_NS;\r
- }\r
+ for (; RegionStart < RegionEnd; RegionStart = BlockEnd) {\r
+ BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);\r
+ Entry = &PageTable[(RegionStart >> (64 - BlockShift)) & (TT_ENTRY_COUNT - 1)];\r
\r
- // Get the address corresponding at this entry\r
- BlockEntryAddress = RegionStart;\r
- BlockEntryAddress = BlockEntryAddress >> TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel);\r
- // 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 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
+ // If RegionStart or BlockEnd is not aligned to the block size at this\r
+ // level, we will have to create a table mapping in order to map less\r
+ // than a block, and recurse to create the block or page entries at\r
+ // the next level. No block mappings are allowed at all at level 0,\r
+ // so in that case, we have to recurse unconditionally.\r
+ //\r
+ if (Level == 0 || ((RegionStart | BlockEnd) & BlockMask) != 0) {\r
+ ASSERT (Level < 3);\r
\r
- // Create a new translation table\r
+ if ((*Entry & TT_TYPE_MASK) != TT_TYPE_TABLE_ENTRY) {\r
+ //\r
+ // No table entry exists yet, so we need to allocate a page table\r
+ // for the next level.\r
+ //\r
TranslationTable = AllocatePages (1);\r
if (TranslationTable == NULL) {\r
- return NULL;\r
+ return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- // Populate the newly created lower level table\r
- SubTableBlockEntry = TranslationTable;\r
- for (Index = 0; Index < TT_ENTRY_COUNT; Index++) {\r
- *SubTableBlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1)));\r
- SubTableBlockEntry++;\r
+ if (!ArmMmuEnabled ()) {\r
+ //\r
+ // Make sure we are not inadvertently hitting in the caches\r
+ // when populating the page tables.\r
+ //\r
+ InvalidateDataCacheRange (TranslationTable, EFI_PAGE_SIZE);\r
}\r
\r
- // Fill the BlockEntry with the new TranslationTable\r
- ReplaceLiveEntry (BlockEntry,\r
- (UINTN)TranslationTable | TableAttributes | TT_TYPE_TABLE_ENTRY,\r
- RegionStart);\r
+ if ((*Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) {\r
+ //\r
+ // We are splitting an existing block entry, so we have to populate\r
+ // the new table with the attributes of the block entry it replaces.\r
+ //\r
+ Status = UpdateRegionMappingRecursive (RegionStart & ~BlockMask,\r
+ (RegionStart | BlockMask) + 1, *Entry & TT_ATTRIBUTES_MASK,\r
+ 0, TranslationTable, Level + 1);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // The range we passed to UpdateRegionMappingRecursive () is block\r
+ // aligned, so it is guaranteed that no further pages were allocated\r
+ // by it, and so we only have to free the page we allocated here.\r
+ //\r
+ FreePages (TranslationTable, 1);\r
+ return Status;\r
+ }\r
+ } else {\r
+ ZeroMem (TranslationTable, EFI_PAGE_SIZE);\r
+ }\r
+ } else {\r
+ TranslationTable = (VOID *)(UINTN)(*Entry & TT_ADDRESS_MASK_BLOCK_ENTRY);\r
}\r
- } else {\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 = AllocatePages (1);\r
- if (TranslationTable == NULL) {\r
- return NULL;\r
+ //\r
+ // Recurse to the next level\r
+ //\r
+ Status = UpdateRegionMappingRecursive (RegionStart, BlockEnd,\r
+ AttributeSetMask, AttributeClearMask, TranslationTable,\r
+ Level + 1);\r
+ if (EFI_ERROR (Status)) {\r
+ if ((*Entry & TT_TYPE_MASK) != TT_TYPE_TABLE_ENTRY) {\r
+ //\r
+ // We are creating a new table entry, so on failure, we can free all\r
+ // allocations we made recursively, given that the whole subhierarchy\r
+ // has not been wired into the live page tables yet. (This is not\r
+ // possible for existing table entries, since we cannot revert the\r
+ // modifications we made to the subhierarchy it represents.)\r
+ //\r
+ FreePageTablesRecursive (TranslationTable);\r
}\r
+ return Status;\r
+ }\r
\r
- ZeroMem (TranslationTable, TT_ENTRY_COUNT * sizeof(UINT64));\r
-\r
- // Fill the new BlockEntry with the TranslationTable\r
- *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TT_TYPE_TABLE_ENTRY;\r
+ if ((*Entry & TT_TYPE_MASK) != TT_TYPE_TABLE_ENTRY) {\r
+ EntryValue = (UINTN)TranslationTable | TT_TYPE_TABLE_ENTRY;\r
+ ReplaceTableEntry (Entry, EntryValue, RegionStart,\r
+ (*Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY);\r
}\r
+ } else {\r
+ EntryValue = (*Entry & AttributeClearMask) | AttributeSetMask;\r
+ EntryValue |= RegionStart;\r
+ EntryValue |= (Level == 3) ? TT_TYPE_BLOCK_ENTRY_LEVEL3\r
+ : TT_TYPE_BLOCK_ENTRY;\r
+\r
+ ReplaceTableEntry (Entry, EntryValue, RegionStart, FALSE);\r
}\r
}\r
+ return EFI_SUCCESS;\r
+}\r
\r
- // Expose the found PageLevel to the caller\r
- *TableLevel = PageLevel;\r
+STATIC\r
+VOID\r
+LookupAddresstoRootTable (\r
+ IN UINT64 MaxAddress,\r
+ OUT UINTN *T0SZ,\r
+ OUT UINTN *TableEntryCount\r
+ )\r
+{\r
+ UINTN TopBit;\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
+ // Check the parameters are not NULL\r
+ ASSERT ((T0SZ != NULL) && (TableEntryCount != NULL));\r
+\r
+ // Look for the highest bit set in MaxAddress\r
+ for (TopBit = 63; TopBit != 0; TopBit--) {\r
+ if ((1ULL << TopBit) & MaxAddress) {\r
+ // MaxAddress top bit is found\r
+ TopBit = TopBit + 1;\r
+ break;\r
+ }\r
+ }\r
+ ASSERT (TopBit != 0);\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
+ // Calculate T0SZ from the top bit of the MaxAddress\r
+ *T0SZ = 64 - TopBit;\r
\r
- return BlockEntry;\r
+ // Get the Table info from T0SZ\r
+ GetRootTranslationTableInfo (*T0SZ, NULL, TableEntryCount);\r
}\r
\r
STATIC\r
EFI_STATUS\r
UpdateRegionMapping (\r
- IN UINT64 *RootTable,\r
IN UINT64 RegionStart,\r
IN UINT64 RegionLength,\r
- IN UINT64 Attributes,\r
- IN UINT64 BlockEntryMask\r
+ IN UINT64 AttributeSetMask,\r
+ IN UINT64 AttributeClearMask\r
)\r
{\r
- UINT32 Type;\r
- UINT64 *BlockEntry;\r
- UINT64 *LastBlockEntry;\r
- UINT64 BlockEntrySize;\r
- UINTN TableLevel;\r
-\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
+ UINTN RootTableLevel;\r
+ UINTN T0SZ;\r
+\r
+ if (((RegionStart | RegionLength) & EFI_PAGE_MASK)) {\r
return EFI_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 size of the Block Entry and the address of the last BlockEntry of the Table Descriptor\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
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- if (TableLevel != 3) {\r
- Type = TT_TYPE_BLOCK_ENTRY;\r
- } else {\r
- Type = TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
- }\r
-\r
- do {\r
- // Fill the Block Entry with attribute and output block address\r
- *BlockEntry &= BlockEntryMask;\r
- *BlockEntry |= (RegionStart & TT_ADDRESS_MASK_BLOCK_ENTRY) | Attributes | Type;\r
-\r
- ArmUpdateTranslationTableEntry (BlockEntry, (VOID *)RegionStart);\r
-\r
- // Go to the next BlockEntry\r
- RegionStart += BlockEntrySize;\r
- RegionLength -= BlockEntrySize;\r
- BlockEntry++;\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 && 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
+ T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
+ GetRootTranslationTableInfo (T0SZ, &RootTableLevel, NULL);\r
\r
- return EFI_SUCCESS;\r
+ return UpdateRegionMappingRecursive (RegionStart, RegionStart + RegionLength,\r
+ AttributeSetMask, AttributeClearMask, ArmGetTTBR0BaseAddress (),\r
+ RootTableLevel);\r
}\r
\r
STATIC\r
)\r
{\r
return UpdateRegionMapping (\r
- RootTable,\r
MemoryRegion->VirtualBase,\r
MemoryRegion->Length,\r
ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | TT_AF,\r
IN UINT64 Attributes\r
)\r
{\r
- EFI_STATUS Status;\r
- UINT64 *TranslationTable;\r
UINT64 PageAttributes;\r
UINT64 PageAttributeMask;\r
\r
TT_PXN_MASK | TT_XN_MASK);\r
}\r
\r
- TranslationTable = ArmGetTTBR0BaseAddress ();\r
-\r
- Status = UpdateRegionMapping (\r
- TranslationTable,\r
- BaseAddress,\r
- Length,\r
- PageAttributes,\r
- PageAttributeMask);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- return EFI_SUCCESS;\r
+ return UpdateRegionMapping (BaseAddress, Length, PageAttributes,\r
+ PageAttributeMask);\r
}\r
\r
STATIC\r
IN UINT64 BlockEntryMask\r
)\r
{\r
- EFI_STATUS Status;\r
- UINT64 *RootTable;\r
-\r
- RootTable = ArmGetTTBR0BaseAddress ();\r
-\r
- Status = UpdateRegionMapping (RootTable, BaseAddress, Length, Attributes, BlockEntryMask);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- return EFI_SUCCESS;\r
+ return UpdateRegionMapping (BaseAddress, Length, Attributes, BlockEntryMask);\r
}\r
\r
EFI_STATUS\r
)\r
{\r
VOID* TranslationTable;\r
- UINT32 TranslationTableAttribute;\r
UINT64 MaxAddress;\r
UINTN T0SZ;\r
UINTN RootTableEntryCount;\r
*TranslationTableSize = RootTableEntryCount * sizeof(UINT64);\r
}\r
\r
+ //\r
+ // Make sure we are not inadvertently hitting in the caches\r
+ // when populating the page tables.\r
+ //\r
+ InvalidateDataCacheRange (TranslationTable,\r
+ RootTableEntryCount * sizeof(UINT64));\r
ZeroMem (TranslationTable, RootTableEntryCount * sizeof(UINT64));\r
\r
- TranslationTableAttribute = TT_ATTR_INDX_INVALID;\r
while (MemoryTable->Length != 0) {\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 (EFI_ERROR (Status)) {\r
goto FREE_TRANSLATION_TABLE;\r
MemoryTable++;\r
}\r
\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_THROUGH, MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH) | // mapped to EFI_MEMORY_WT\r