mirror of https://github.com/acidanthera/audk.git
ArmPkg/CpuDxe: Added support to not set a memory region with the same attribute
Changing the attribute implies some cache management (clean & invalidate). Preventing the cache management should improve the performance. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14568 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
parent
6adbd5b4d2
commit
2e969d2e9e
|
@ -194,3 +194,149 @@ SyncCacheConfig (
|
|||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
UINT64
|
||||
EfiAttributeToArmAttribute (
|
||||
IN UINT64 EfiAttributes
|
||||
)
|
||||
{
|
||||
UINT64 ArmAttributes;
|
||||
|
||||
switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
|
||||
case EFI_MEMORY_UC:
|
||||
ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;
|
||||
break;
|
||||
case EFI_MEMORY_WC:
|
||||
ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;
|
||||
break;
|
||||
case EFI_MEMORY_WT:
|
||||
ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH;
|
||||
break;
|
||||
case EFI_MEMORY_WB:
|
||||
ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK;
|
||||
break;
|
||||
default:
|
||||
DEBUG ((EFI_D_ERROR, "EfiAttributeToArmAttribute: 0x%lX attributes is not supported.\n", EfiAttributes));
|
||||
ASSERT (0);
|
||||
ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;
|
||||
}
|
||||
|
||||
// Set the access flag to match the block attributes
|
||||
ArmAttributes |= TT_AF;
|
||||
|
||||
// Determine protection attributes
|
||||
if (EfiAttributes & EFI_MEMORY_WP) {
|
||||
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 (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;
|
||||
}
|
||||
} 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 'BaseAdress') 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -696,3 +696,185 @@ SetMemoryAttributes (
|
|||
|
||||
return Status;
|
||||
}
|
||||
|
||||
UINT64
|
||||
EfiAttributeToArmAttribute (
|
||||
IN UINT64 EfiAttributes
|
||||
)
|
||||
{
|
||||
UINT64 ArmAttributes;
|
||||
|
||||
switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
|
||||
case EFI_MEMORY_UC:
|
||||
// Map to strongly ordered
|
||||
ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
|
||||
break;
|
||||
|
||||
case EFI_MEMORY_WC:
|
||||
// Map to normal non-cachable
|
||||
ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
|
||||
break;
|
||||
|
||||
case EFI_MEMORY_WT:
|
||||
// Write through with no-allocate
|
||||
ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
|
||||
break;
|
||||
|
||||
case EFI_MEMORY_WB:
|
||||
// Write back (with allocate)
|
||||
ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
|
||||
break;
|
||||
|
||||
case EFI_MEMORY_WP:
|
||||
case EFI_MEMORY_XP:
|
||||
case EFI_MEMORY_RP:
|
||||
case EFI_MEMORY_UCE:
|
||||
default:
|
||||
// Cannot be implemented UEFI definition unclear for ARM
|
||||
// Cause a page fault if these ranges are accessed.
|
||||
ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
|
||||
DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes));
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine protection attributes
|
||||
if (EfiAttributes & EFI_MEMORY_WP) {
|
||||
ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
|
||||
} else {
|
||||
ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
|
||||
}
|
||||
|
||||
// Determine eXecute Never attribute
|
||||
if (EfiAttributes & EFI_MEMORY_XP) {
|
||||
ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
|
||||
}
|
||||
|
||||
return ArmAttributes;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
GetMemoryRegionPage (
|
||||
IN UINT32 *PageTable,
|
||||
IN OUT UINTN *BaseAddress,
|
||||
OUT UINTN *RegionLength,
|
||||
OUT UINTN *RegionAttributes
|
||||
)
|
||||
{
|
||||
UINT32 PageAttributes;
|
||||
UINT32 TableIndex;
|
||||
UINT32 PageDescriptor;
|
||||
|
||||
// Convert the section attributes into page attributes
|
||||
PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);
|
||||
|
||||
// Calculate index into first level translation table for start of modification
|
||||
TableIndex = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(*BaseAddress) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
|
||||
ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);
|
||||
|
||||
// Go through the page table to find the end of the section
|
||||
for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {
|
||||
// Get the section at the given index
|
||||
PageDescriptor = PageTable[TableIndex];
|
||||
|
||||
if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {
|
||||
// Case: End of the boundary of the region
|
||||
return EFI_SUCCESS;
|
||||
} else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
|
||||
if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {
|
||||
*RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;
|
||||
} else {
|
||||
// Case: End of the boundary of the region
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
// We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
|
||||
ASSERT(0);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
GetMemoryRegion (
|
||||
IN OUT UINTN *BaseAddress,
|
||||
OUT UINTN *RegionLength,
|
||||
OUT UINTN *RegionAttributes
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT32 TableIndex;
|
||||
UINT32 PageAttributes;
|
||||
UINT32 PageTableIndex;
|
||||
UINT32 SectionDescriptor;
|
||||
ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
|
||||
UINT32 *PageTable;
|
||||
|
||||
// Initialize the arguments
|
||||
*RegionLength = 0;
|
||||
|
||||
// Obtain page table base
|
||||
FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
|
||||
|
||||
// Calculate index into first level translation table for start of modification
|
||||
TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
|
||||
ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);
|
||||
|
||||
// Get the section at the given index
|
||||
SectionDescriptor = FirstLevelTable[TableIndex];
|
||||
|
||||
// If 'BaseAddress' belongs to the section then round it to the section boundary
|
||||
if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
|
||||
((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))
|
||||
{
|
||||
*BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;
|
||||
*RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;
|
||||
} else {
|
||||
// Otherwise, we round it to the page boundary
|
||||
*BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;
|
||||
|
||||
// Get the attribute at the page table level (Level 2)
|
||||
PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
|
||||
|
||||
// Calculate index into first level translation table for start of modification
|
||||
PageTableIndex = TT_DESCRIPTOR_PAGE_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
|
||||
ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
|
||||
|
||||
PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;
|
||||
*RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |
|
||||
TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
|
||||
}
|
||||
|
||||
for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {
|
||||
// Get the section at the given index
|
||||
SectionDescriptor = FirstLevelTable[TableIndex];
|
||||
|
||||
// If the entry is a level-2 page table then we scan it to find the end of the region
|
||||
if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE) {
|
||||
// Extract the page table location from the descriptor
|
||||
PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
|
||||
|
||||
// Scan the page table to find the end of the region.
|
||||
Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);
|
||||
|
||||
// If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
|
||||
if (Status == EFI_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
} else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
|
||||
((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {
|
||||
if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {
|
||||
// If the attributes of the section differ from the one targeted then we exit the loop
|
||||
break;
|
||||
} else {
|
||||
*RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;
|
||||
}
|
||||
} else {
|
||||
// If we are on an invalid section then it means it is the end of our section.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -148,6 +148,19 @@ SetMemoryAttributes (
|
|||
IN EFI_PHYSICAL_ADDRESS VirtualMask
|
||||
);
|
||||
|
||||
// The ARM Attributes might be defined on 64-bit (case of the long format description table)
|
||||
UINT64
|
||||
EfiAttributeToArmAttribute (
|
||||
IN UINT64 EfiAttributes
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
GetMemoryRegion (
|
||||
IN OUT UINTN *BaseAddress,
|
||||
OUT UINTN *RegionLength,
|
||||
OUT UINTN *RegionAttributes
|
||||
);
|
||||
|
||||
VOID
|
||||
GetRootTranslationTableInfo (
|
||||
IN UINTN T0SZ,
|
||||
|
|
|
@ -178,18 +178,37 @@ CpuSetMemoryAttributes (
|
|||
IN EFI_CPU_ARCH_PROTOCOL *This,
|
||||
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
||||
IN UINT64 Length,
|
||||
IN UINT64 Attributes
|
||||
IN UINT64 EfiAttributes
|
||||
)
|
||||
{
|
||||
DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx)\n", BaseAddress, Length, Attributes));
|
||||
EFI_STATUS Status;
|
||||
UINTN ArmAttributes;
|
||||
UINTN RegionBaseAddress;
|
||||
UINTN RegionLength;
|
||||
UINTN RegionArmAttributes;
|
||||
|
||||
if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
|
||||
// Minimum granularity is SIZE_4KB (4KB on ARM)
|
||||
DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, Attributes));
|
||||
DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return SetMemoryAttributes (BaseAddress, Length, Attributes, 0);
|
||||
// Convert the 'Attribute' into ARM Attribute
|
||||
ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);
|
||||
|
||||
// Get the region starting from 'BaseAddress' and its 'Attribute'
|
||||
RegionBaseAddress = BaseAddress;
|
||||
Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);
|
||||
|
||||
// Data & Instruction Caches are flushed when we set new memory attributes.
|
||||
// So, we only set the attributes if the new region is different.
|
||||
if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||
|
||||
((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
|
||||
{
|
||||
return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);
|
||||
} else {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
|
|
Loading…
Reference in New Issue