X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ArmPkg%2FDrivers%2FCpuDxe%2FAArch64%2FMmu.c;h=29fa08f9e07c0eb2844531015eb3a839e7f84379;hb=49188b2aa481e76190883e73147af72128c878db;hp=da6e6225bed28751dc1c043261912232e422df4f;hpb=5b53eaffe830678cbe651338632b9eee6a5a61c8;p=mirror_edk2.git diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c index da6e6225be..29fa08f9e0 100644 --- a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c +++ b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c @@ -3,14 +3,9 @@ Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
Portions copyright (c) 2010, Apple Inc. All rights reserved.
Portions copyright (c) 2011-2013, ARM Ltd. All rights reserved.
+Copyright (c) 2017, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +SPDX-License-Identifier: BSD-2-Clause-Patent --*/ @@ -20,6 +15,67 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define TT_ATTR_INDX_INVALID ((UINT32)~0) +#define MIN_T0SZ 16 +#define BITS_PER_LEVEL 9 + +STATIC +VOID +GetRootTranslationTableInfo ( + IN UINTN T0SZ, + OUT UINTN *RootTableLevel, + OUT UINTN *RootTableEntryCount + ) +{ + *RootTableLevel = (T0SZ - MIN_T0SZ) / BITS_PER_LEVEL; + *RootTableEntryCount = TT_ENTRY_COUNT >> (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL; +} + +STATIC +UINT64 +PageAttributeToGcdAttribute ( + IN UINT64 PageAttributes + ) +{ + UINT64 GcdAttributes; + + switch (PageAttributes & TT_ATTR_INDX_MASK) { + case TT_ATTR_INDX_DEVICE_MEMORY: + GcdAttributes = EFI_MEMORY_UC; + break; + case TT_ATTR_INDX_MEMORY_NON_CACHEABLE: + GcdAttributes = EFI_MEMORY_WC; + break; + case TT_ATTR_INDX_MEMORY_WRITE_THROUGH: + GcdAttributes = EFI_MEMORY_WT; + break; + case TT_ATTR_INDX_MEMORY_WRITE_BACK: + GcdAttributes = EFI_MEMORY_WB; + break; + default: + DEBUG ((DEBUG_ERROR, + "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n", + PageAttributes)); + ASSERT (0); + // The Global Coherency Domain (GCD) value is defined as a bit set. + // Returning 0 means no attribute has been set. + GcdAttributes = 0; + } + + // Determine protection attributes + if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) || + ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO)) { + // Read only cases map to write-protect + GcdAttributes |= EFI_MEMORY_RO; + } + + // Process eXecute Never attribute + if ((PageAttributes & (TT_PXN_MASK | TT_UXN_MASK)) != 0) { + GcdAttributes |= EFI_MEMORY_XP; + } + + return GcdAttributes; +} + STATIC UINT64 GetFirstPageAttribute ( @@ -32,9 +88,8 @@ GetFirstPageAttribute ( // Get the first entry of the table FirstEntry = *FirstLevelTableAddress; - if ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) { + if ((TableLevel != 3) && (FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) { // Only valid for Levels 0, 1 and 2 - ASSERT (TableLevel < 3); // Get the attribute of the subsequent table return GetFirstPageAttribute ((UINT64*)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1); @@ -89,8 +144,8 @@ GetNextEntryAttribute ( // Update GCD with the last region SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *StartGcdRegion, - (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)) - 1) - *StartGcdRegion, - PageAttributeToGcdAttribute (EntryAttribute)); + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion, + PageAttributeToGcdAttribute (*PrevEntryAttribute)); } // Start of the new region @@ -113,8 +168,8 @@ GetNextEntryAttribute ( // Update GCD with the last region SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *StartGcdRegion, - (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)) - 1) - *StartGcdRegion, - PageAttributeToGcdAttribute (EntryAttribute)); + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion, + PageAttributeToGcdAttribute (*PrevEntryAttribute)); // Start of the new region *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)); @@ -182,13 +237,166 @@ SyncCacheConfig ( BaseAddressGcdRegion, &PageAttribute, &BaseAddressGcdRegion); - // Update GCD with the last region - SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, - BaseAddressGcdRegion, - EndAddressGcdRegion - BaseAddressGcdRegion, - PageAttributeToGcdAttribute (PageAttribute)); + // Update GCD with the last region if valid + if (PageAttribute != TT_ATTR_INDX_INVALID) { + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, + BaseAddressGcdRegion, + EndAddressGcdRegion - BaseAddressGcdRegion, + PageAttributeToGcdAttribute (PageAttribute)); + } FreePool (MemorySpaceMap); return EFI_SUCCESS; } + +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ) +{ + UINT64 ArmAttributes; + + switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) { + case EFI_MEMORY_UC: + if (ArmReadCurrentEL () == AARCH64_EL2) { + ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK; + } else { + ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK; + } + break; + case EFI_MEMORY_WC: + ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE; + break; + case EFI_MEMORY_WT: + ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE; + break; + case EFI_MEMORY_WB: + ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE; + break; + default: + ArmAttributes = TT_ATTR_INDX_MASK; + } + + // Set the access flag to match the block attributes + ArmAttributes |= TT_AF; + + // Determine protection attributes + if (EfiAttributes & EFI_MEMORY_RO) { + ArmAttributes |= TT_AP_RO_RO; + } + + // Process eXecute Never attribute + if (EfiAttributes & EFI_MEMORY_XP) { + ArmAttributes |= TT_PXN_MASK; + } + + return ArmAttributes; +} + +// This function will recursively go down the page table to find the first block address linked to 'BaseAddress'. +// And then the function will identify the size of the region that has the same page table attribute. +EFI_STATUS +GetMemoryRegionRec ( + IN UINT64 *TranslationTable, + IN UINTN TableLevel, + IN UINT64 *LastBlockEntry, + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT64 *NextTranslationTable; + UINT64 *BlockEntry; + UINT64 BlockEntryType; + UINT64 EntryType; + + if (TableLevel != 3) { + BlockEntryType = TT_TYPE_BLOCK_ENTRY; + } else { + BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3; + } + + // Find the block entry linked to the Base Address + BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress); + EntryType = *BlockEntry & TT_TYPE_MASK; + + if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) { + NextTranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE); + + // The entry is a page table, so we go to the next level + Status = GetMemoryRegionRec ( + NextTranslationTable, // Address of the next level page table + TableLevel + 1, // Next Page Table level + (UINTN*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable, TT_ENTRY_COUNT), + BaseAddress, RegionLength, RegionAttributes); + + // In case of 'Success', it means the end of the block region has been found into the upper + // level translation table + if (!EFI_ERROR(Status)) { + return EFI_SUCCESS; + } + + // Now we processed the table move to the next entry + BlockEntry++; + } else if (EntryType == BlockEntryType) { + // We have found the BlockEntry attached to the address. We save its start address (the start + // address might be before the 'BaseAddress') and attributes + *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL(TableLevel) - 1); + *RegionLength = 0; + *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK; + } else { + // We have an 'Invalid' entry + return EFI_UNSUPPORTED; + } + + while (BlockEntry <= LastBlockEntry) { + if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) { + *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel); + } else { + // In case we have found the end of the region we return success + return EFI_SUCCESS; + } + BlockEntry++; + } + + // If we have reached the end of the TranslationTable and we have not found the end of the region then + // we return EFI_NOT_FOUND. + // The caller will continue to look for the memory region at its level + return EFI_NOT_FOUND; +} + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT64 *TranslationTable; + UINTN TableLevel; + UINTN EntryCount; + UINTN T0SZ; + + ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL)); + + TranslationTable = ArmGetTTBR0BaseAddress (); + + T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; + // Get the Table info from T0SZ + GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount); + + Status = GetMemoryRegionRec (TranslationTable, TableLevel, + (UINTN*)TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount), + BaseAddress, RegionLength, RegionAttributes); + + // If the region continues up to the end of the root table then GetMemoryRegionRec() + // will return EFI_NOT_FOUND + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else { + return Status; + } +}