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, 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) {
// 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 '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; }