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; } }
STATIC UINT64* GetBlockEntryListFromAddress ( IN UINT64 *RootTable, IN UINT64 RegionStart, OUT UINTN *TableLevel, IN OUT UINT64 *BlockEntrySize, OUT UINT64 **LastBlockEntry ) { UINTN RootTableLevel; UINTN RootTableEntryCount; UINT64 *TranslationTable; UINT64 *BlockEntry; UINT64 *SubTableBlockEntry; UINT64 BlockEntryAddress; UINTN BaseAddressAlignment; UINTN PageLevel; UINTN Index; UINTN IndexLevel; UINTN T0SZ; UINT64 Attributes; UINT64 TableAttributes; // Initialize variable BlockEntry = NULL; // Ensure the parameters are valid if (!(TableLevel && BlockEntrySize && LastBlockEntry)) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // Ensure the Region is aligned on 4KB boundary if ((RegionStart & (SIZE_4KB - 1)) != 0) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // Ensure the required size is aligned on 4KB boundary and not 0 if ((*BlockEntrySize & (SIZE_4KB - 1)) != 0 || *BlockEntrySize == 0) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; // Get the Table info from T0SZ GetRootTranslationTableInfo (T0SZ, &RootTableLevel, &RootTableEntryCount); // If the start address is 0x0 then we use the size of the region to identify the alignment if (RegionStart == 0) { // Identify the highest possible alignment for the Region Size BaseAddressAlignment = LowBitSet64 (*BlockEntrySize); } else { // Identify the highest possible alignment for the Base Address BaseAddressAlignment = LowBitSet64 (RegionStart); } // Identify the Page Level the RegionStart must belong to. Note that PageLevel // should be at least 1 since block translations are not supported at level 0 PageLevel = MAX (3 - ((BaseAddressAlignment - 12) / 9), 1); // If the required size is smaller than the current block size then we need to go to the page below. // The PageLevel was calculated on the Base Address alignment but did not take in account the alignment // of the allocation size while (*BlockEntrySize < TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel)) { // It does not fit so we need to go a page level above PageLevel++; } // // Get the Table Descriptor for the corresponding PageLevel. We need to decompose RegionStart to get appropriate entries // TranslationTable = RootTable; for (IndexLevel = RootTableLevel; IndexLevel <= PageLevel; IndexLevel++) { BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel, RegionStart); if ((IndexLevel != 3) && ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) { // Go to the next table TranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE); // If we are at the last level then update the last level to next level if (IndexLevel == PageLevel) { // Enter the next level PageLevel++; } } else if ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) { // If we are not at the last level then we need to split this BlockEntry if (IndexLevel != PageLevel) { // Retrieve the attributes from the block entry Attributes = *BlockEntry & TT_ATTRIBUTES_MASK; // Convert the block entry attributes into Table descriptor attributes TableAttributes = TT_TABLE_AP_NO_PERMISSION; if (Attributes & TT_PXN_MASK) { TableAttributes = TT_TABLE_PXN; } // XN maps to UXN in the EL1&0 translation regime if (Attributes & TT_XN_MASK) { TableAttributes = TT_TABLE_XN; } if (Attributes & TT_NS) { TableAttributes = TT_TABLE_NS; } // Get the address corresponding at this entry BlockEntryAddress = RegionStart; BlockEntryAddress = BlockEntryAddress >> TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel); // Shift back to right to set zero before the effective address BlockEntryAddress = BlockEntryAddress << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel); // Set the correct entry type for the next page level if ((IndexLevel + 1) == 3) { Attributes |= TT_TYPE_BLOCK_ENTRY_LEVEL3; } else { Attributes |= TT_TYPE_BLOCK_ENTRY; } // Create a new translation table TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE); if (TranslationTable == NULL) { return NULL; } // Populate the newly created lower level table SubTableBlockEntry = TranslationTable; for (Index = 0; Index < TT_ENTRY_COUNT; Index++) { *SubTableBlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1))); SubTableBlockEntry++; } // Fill the BlockEntry with the new TranslationTable *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY; } } else { if (IndexLevel != PageLevel) {
STATIC UINT64* GetBlockEntryListFromAddress ( IN UINT64 *RootTable, IN UINT64 RegionStart, OUT UINTN *TableLevel, IN OUT UINT64 *BlockEntrySize, IN OUT UINT64 **LastBlockEntry ) { UINTN RootTableLevel; UINTN RootTableEntryCount; UINT64 *TranslationTable; UINT64 *BlockEntry; UINT64 BlockEntryAddress; UINTN BaseAddressAlignment; UINTN PageLevel; UINTN Index; UINTN IndexLevel; UINTN T0SZ; UINT64 Attributes; UINT64 TableAttributes; // Initialize variable BlockEntry = NULL; // Ensure the parameters are valid if (!(TableLevel && BlockEntrySize && LastBlockEntry)) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // Ensure the Region is aligned on 4KB boundary if ((RegionStart & (SIZE_4KB - 1)) != 0) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // Ensure the required size is aligned on 4KB boundary if ((*BlockEntrySize & (SIZE_4KB - 1)) != 0) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // // Calculate LastBlockEntry from T0SZ - this is the last block entry of the root Translation table // T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; // Get the Table info from T0SZ GetRootTranslationTableInfo (T0SZ, &RootTableLevel, &RootTableEntryCount); // The last block of the root table depends on the number of entry in this table *LastBlockEntry = TT_LAST_BLOCK_ADDRESS(RootTable, RootTableEntryCount); // If the start address is 0x0 then we use the size of the region to identify the alignment if (RegionStart == 0) { // Identify the highest possible alignment for the Region Size for (BaseAddressAlignment = 0; BaseAddressAlignment < 64; BaseAddressAlignment++) { if ((1 << BaseAddressAlignment) & *BlockEntrySize) { break; } } } else { // Identify the highest possible alignment for the Base Address for (BaseAddressAlignment = 0; BaseAddressAlignment < 64; BaseAddressAlignment++) { if ((1 << BaseAddressAlignment) & RegionStart) { break; } } } // Identify the Page Level the RegionStart must belongs to PageLevel = 3 - ((BaseAddressAlignment - 12) / 9); // If the required size is smaller than the current block size then we need to go to the page below. // The PageLevel was calculated on the Base Address alignment but did not take in account the alignment // of the allocation size if (*BlockEntrySize < TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel)) { // It does not fit so we need to go a page level above PageLevel++; } // Expose the found PageLevel to the caller *TableLevel = PageLevel; // Now, we have the Table Level we can get the Block Size associated to this table *BlockEntrySize = TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel); // // Get the Table Descriptor for the corresponding PageLevel. We need to decompose RegionStart to get appropriate entries // TranslationTable = RootTable; for (IndexLevel = RootTableLevel; IndexLevel <= PageLevel; IndexLevel++) { BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel, RegionStart); if ((IndexLevel != 3) && ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) { // Go to the next table TranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE); // If we are at the last level then update the output if (IndexLevel == PageLevel) { // And get the appropriate BlockEntry at the next level BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel + 1, RegionStart); // Set the last block for this new table *LastBlockEntry = TT_LAST_BLOCK_ADDRESS(TranslationTable, TT_ENTRY_COUNT); } } else if ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) { // If we are not at the last level then we need to split this BlockEntry if (IndexLevel != PageLevel) { // Retrieve the attributes from the block entry Attributes = *BlockEntry & TT_ATTRIBUTES_MASK; // Convert the block entry attributes into Table descriptor attributes TableAttributes = TT_TABLE_AP_NO_PERMISSION; if (Attributes & TT_PXN_MASK) { TableAttributes = TT_TABLE_PXN; } if (Attributes & TT_UXN_MASK) { TableAttributes = TT_TABLE_XN; } if (Attributes & TT_NS) { TableAttributes = TT_TABLE_NS; } // Get the address corresponding at this entry BlockEntryAddress = RegionStart; BlockEntryAddress = BlockEntryAddress >> TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel); // Shift back to right to set zero before the effective address BlockEntryAddress = BlockEntryAddress << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel); // Set the correct entry type for the next page level if ((IndexLevel + 1) == 3) { Attributes |= TT_TYPE_BLOCK_ENTRY_LEVEL3; } else { Attributes |= TT_TYPE_BLOCK_ENTRY; } // Create a new translation table TranslationTable = (UINT64*)AllocatePages (EFI_SIZE_TO_PAGES((TT_ENTRY_COUNT * sizeof(UINT64)) + TT_ALIGNMENT_DESCRIPTION_TABLE)); if (TranslationTable == NULL) { return NULL; } TranslationTable = (UINT64*)((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE); // Fill the BlockEntry with the new TranslationTable *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY; // Update the last block entry with the newly created translation table *LastBlockEntry = TT_LAST_BLOCK_ADDRESS(TranslationTable, TT_ENTRY_COUNT); // Populate the newly created lower level table BlockEntry = TranslationTable; for (Index = 0; Index < TT_ENTRY_COUNT; Index++) { *BlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1))); BlockEntry++; } // Block Entry points at the beginning of the Translation Table BlockEntry = TranslationTable; } } else { if (IndexLevel != PageLevel) {
EFI_STATUS SyncCacheConfig ( IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol ) { EFI_STATUS Status; UINT32 PageAttribute = 0; UINT64 *FirstLevelTableAddress; UINTN TableLevel; UINTN TableCount; UINTN NumberOfDescriptors; EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; UINTN Tcr; UINTN T0SZ; UINT64 BaseAddressGcdRegion; UINT64 EndAddressGcdRegion; // This code assumes MMU is enabled and filed with section translations ASSERT (ArmMmuEnabled ()); // // Get the memory space map from GCD // MemorySpaceMap = NULL; Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); ASSERT_EFI_ERROR (Status); // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were // a client) to update its copy of the attributes. This is bad architecture and should be replaced // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead. // Obtain page table base FirstLevelTableAddress = (UINT64*)(ArmGetTTBR0BaseAddress ()); // Get Translation Control Register value Tcr = ArmGetTCR (); // Get Address Region Size T0SZ = Tcr & TCR_T0SZ_MASK; // Get the level of the first table for the indicated Address Region Size GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount); // First Attribute of the Page Tables PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel); // We scan from the start of the memory map (ie: at the address 0x0) BaseAddressGcdRegion = 0x0; EndAddressGcdRegion = GetNextEntryAttribute (FirstLevelTableAddress, TableCount, TableLevel, BaseAddressGcdRegion, &PageAttribute, &BaseAddressGcdRegion); // 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; }