\r
return EFI_SUCCESS;\r
}\r
+\r
+UINT64\r
+EfiAttributeToArmAttribute (\r
+ IN UINT64 EfiAttributes\r
+ )\r
+{\r
+ UINT64 ArmAttributes;\r
+\r
+ switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
+ case EFI_MEMORY_UC:\r
+ ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\r
+ break;\r
+ case EFI_MEMORY_WC:\r
+ ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
+ break;\r
+ case EFI_MEMORY_WT:\r
+ ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH;\r
+ break;\r
+ case EFI_MEMORY_WB:\r
+ ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK;\r
+ break;\r
+ default:\r
+ DEBUG ((EFI_D_ERROR, "EfiAttributeToArmAttribute: 0x%lX attributes is not supported.\n", EfiAttributes));\r
+ ASSERT (0);\r
+ ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;\r
+ }\r
+\r
+ // Set the access flag to match the block attributes\r
+ ArmAttributes |= TT_AF;\r
+\r
+ // Determine protection attributes\r
+ if (EfiAttributes & EFI_MEMORY_WP) {\r
+ ArmAttributes |= TT_AP_RO_RO;\r
+ }\r
+\r
+ // Process eXecute Never attribute\r
+ if (EfiAttributes & EFI_MEMORY_XP) {\r
+ ArmAttributes |= TT_PXN_MASK;\r
+ }\r
+\r
+ return ArmAttributes;\r
+}\r
+\r
+// This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.\r
+// And then the function will identify the size of the region that has the same page table attribute.\r
+EFI_STATUS\r
+GetMemoryRegionRec (\r
+ IN UINT64 *TranslationTable,\r
+ IN UINTN TableLevel,\r
+ IN UINT64 *LastBlockEntry,\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 *NextTranslationTable;\r
+ UINT64 *BlockEntry;\r
+ UINT64 BlockEntryType;\r
+ UINT64 EntryType;\r
+\r
+ if (TableLevel != 3) {\r
+ BlockEntryType = TT_TYPE_BLOCK_ENTRY;\r
+ } else {\r
+ BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
+ }\r
+\r
+ // Find the block entry linked to the Base Address\r
+ BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress);\r
+ EntryType = *BlockEntry & TT_TYPE_MASK;\r
+\r
+ if (EntryType == TT_TYPE_TABLE_ENTRY) {\r
+ NextTranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
+\r
+ // The entry is a page table, so we go to the next level\r
+ Status = GetMemoryRegionRec (\r
+ NextTranslationTable, // Address of the next level page table\r
+ TableLevel + 1, // Next Page Table level\r
+ (UINTN*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable, TT_ENTRY_COUNT),\r
+ BaseAddress, RegionLength, RegionAttributes);\r
+\r
+ // In case of 'Success', it means the end of the block region has been found into the upper\r
+ // level translation table\r
+ if (!EFI_ERROR(Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ } else if (EntryType == BlockEntryType) {\r
+ // We have found the BlockEntry attached to the address. We save its start address (the start\r
+ // address might be before the 'BaseAdress') and attributes\r
+ *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL(TableLevel) - 1);\r
+ *RegionLength = 0;\r
+ *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK;\r
+ } else {\r
+ // We have an 'Invalid' entry\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ while (BlockEntry <= LastBlockEntry) {\r
+ if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) {\r
+ *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel);\r
+ } else {\r
+ // In case we have found the end of the region we return success\r
+ return EFI_SUCCESS;\r
+ }\r
+ BlockEntry++;\r
+ }\r
+\r
+ // If we have reached the end of the TranslationTable and we have not found the end of the region then\r
+ // we return EFI_NOT_FOUND.\r
+ // The caller will continue to look for the memory region at its level\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+EFI_STATUS\r
+GetMemoryRegion (\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 *TranslationTable;\r
+ UINTN TableLevel;\r
+ UINTN EntryCount;\r
+ UINTN T0SZ;\r
+\r
+ ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL));\r
+\r
+ TranslationTable = ArmGetTTBR0BaseAddress ();\r
+\r
+ T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
+ // Get the Table info from T0SZ\r
+ GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount);\r
+\r
+ Status = GetMemoryRegionRec (TranslationTable, TableLevel,\r
+ (UINTN*)TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount),\r
+ BaseAddress, RegionLength, RegionAttributes);\r
+\r
+ // If the region continues up to the end of the root table then GetMemoryRegionRec()\r
+ // will return EFI_NOT_FOUND\r
+ if (Status == EFI_NOT_FOUND) {\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return Status;\r
+ }\r
+}\r
\r
return Status;\r
}\r
+\r
+UINT64\r
+EfiAttributeToArmAttribute (\r
+ IN UINT64 EfiAttributes\r
+ )\r
+{\r
+ UINT64 ArmAttributes;\r
+\r
+ switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
+ case EFI_MEMORY_UC:\r
+ // Map to strongly ordered\r
+ ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
+ break;\r
+\r
+ case EFI_MEMORY_WC:\r
+ // Map to normal non-cachable\r
+ ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
+ break;\r
+\r
+ case EFI_MEMORY_WT:\r
+ // Write through with no-allocate\r
+ ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
+ break;\r
+\r
+ case EFI_MEMORY_WB:\r
+ // Write back (with allocate)\r
+ ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
+ break;\r
+\r
+ case EFI_MEMORY_WP:\r
+ case EFI_MEMORY_XP:\r
+ case EFI_MEMORY_RP:\r
+ case EFI_MEMORY_UCE:\r
+ default:\r
+ // Cannot be implemented UEFI definition unclear for ARM\r
+ // Cause a page fault if these ranges are accessed.\r
+ ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;\r
+ DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes));\r
+ break;\r
+ }\r
+\r
+ // Determine protection attributes\r
+ if (EfiAttributes & EFI_MEMORY_WP) {\r
+ ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;\r
+ } else {\r
+ ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;\r
+ }\r
+\r
+ // Determine eXecute Never attribute\r
+ if (EfiAttributes & EFI_MEMORY_XP) {\r
+ ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;\r
+ }\r
+\r
+ return ArmAttributes;\r
+}\r
+\r
+EFI_STATUS\r
+GetMemoryRegionPage (\r
+ IN UINT32 *PageTable,\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ )\r
+{\r
+ UINT32 PageAttributes;\r
+ UINT32 TableIndex;\r
+ UINT32 PageDescriptor;\r
+\r
+ // Convert the section attributes into page attributes\r
+ PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);\r
+\r
+ // Calculate index into first level translation table for start of modification\r
+ TableIndex = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(*BaseAddress) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
+ ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
+\r
+ // Go through the page table to find the end of the section\r
+ for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {\r
+ // Get the section at the given index\r
+ PageDescriptor = PageTable[TableIndex];\r
+\r
+ if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {\r
+ // Case: End of the boundary of the region\r
+ return EFI_SUCCESS;\r
+ } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {\r
+ if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {\r
+ *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;\r
+ } else {\r
+ // Case: End of the boundary of the region\r
+ return EFI_SUCCESS;\r
+ }\r
+ } else {\r
+ // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.\r
+ ASSERT(0);\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+EFI_STATUS\r
+GetMemoryRegion (\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 TableIndex;\r
+ UINT32 PageAttributes;\r
+ UINT32 PageTableIndex;\r
+ UINT32 SectionDescriptor;\r
+ ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
+ UINT32 *PageTable;\r
+\r
+ // Initialize the arguments\r
+ *RegionLength = 0;\r
+\r
+ // Obtain page table base\r
+ FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
+\r
+ // Calculate index into first level translation table for start of modification\r
+ TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
+ ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);\r
+\r
+ // Get the section at the given index\r
+ SectionDescriptor = FirstLevelTable[TableIndex];\r
+\r
+ // If 'BaseAddress' belongs to the section then round it to the section boundary\r
+ if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||\r
+ ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))\r
+ {\r
+ *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;\r
+ *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;\r
+ } else {\r
+ // Otherwise, we round it to the page boundary\r
+ *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;\r
+\r
+ // Get the attribute at the page table level (Level 2)\r
+ PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
+\r
+ // Calculate index into first level translation table for start of modification\r
+ PageTableIndex = TT_DESCRIPTOR_PAGE_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
+ ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
+\r
+ PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;\r
+ *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |\r
+ TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);\r
+ }\r
+\r
+ for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {\r
+ // Get the section at the given index\r
+ SectionDescriptor = FirstLevelTable[TableIndex];\r
+\r
+ // If the entry is a level-2 page table then we scan it to find the end of the region\r
+ if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE) {\r
+ // Extract the page table location from the descriptor\r
+ PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
+\r
+ // Scan the page table to find the end of the region.\r
+ Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);\r
+\r
+ // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop\r
+ if (Status == EFI_SUCCESS) {\r
+ break;\r
+ }\r
+ } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||\r
+ ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {\r
+ if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {\r
+ // If the attributes of the section differ from the one targeted then we exit the loop\r
+ break;\r
+ } else {\r
+ *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;\r
+ }\r
+ } else {\r
+ // If we are on an invalid section then it means it is the end of our section.\r
+ break;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
IN EFI_PHYSICAL_ADDRESS VirtualMask\r
);\r
\r
+// The ARM Attributes might be defined on 64-bit (case of the long format description table)\r
+UINT64\r
+EfiAttributeToArmAttribute (\r
+ IN UINT64 EfiAttributes\r
+ );\r
+\r
+EFI_STATUS\r
+GetMemoryRegion (\r
+ IN OUT UINTN *BaseAddress,\r
+ OUT UINTN *RegionLength,\r
+ OUT UINTN *RegionAttributes\r
+ );\r
+\r
VOID\r
GetRootTranslationTableInfo (\r
IN UINTN T0SZ,\r
IN EFI_CPU_ARCH_PROTOCOL *This,\r
IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
IN UINT64 Length,\r
- IN UINT64 Attributes\r
+ IN UINT64 EfiAttributes\r
)\r
{\r
- DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx)\n", BaseAddress, Length, Attributes));\r
+ EFI_STATUS Status;\r
+ UINTN ArmAttributes;\r
+ UINTN RegionBaseAddress;\r
+ UINTN RegionLength;\r
+ UINTN RegionArmAttributes;\r
\r
if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
// Minimum granularity is SIZE_4KB (4KB on ARM)\r
- DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, Attributes));\r
+ DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));\r
return EFI_UNSUPPORTED;\r
}\r
\r
- return SetMemoryAttributes (BaseAddress, Length, Attributes, 0);\r
+ // Convert the 'Attribute' into ARM Attribute\r
+ ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);\r
+\r
+ // Get the region starting from 'BaseAddress' and its 'Attribute'\r
+ RegionBaseAddress = BaseAddress;\r
+ Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);\r
+\r
+ // Data & Instruction Caches are flushed when we set new memory attributes.\r
+ // So, we only set the attributes if the new region is different.\r
+ if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||\r
+ ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))\r
+ {\r
+ return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
}\r
\r
EFI_STATUS\r