On Fri, 6 Mar 2020 at 19:50, Leif Lindholm <l...@nuviainc.com> wrote: > > On Fri, Mar 06, 2020 at 17:12:45 +0100, Ard Biesheuvel wrote: > > Replace the slightly overcomplicated page table management code with > > a simplified, recursive implementation that should be far easier to > > reason about. > > > > Signed-off-by: Ard Biesheuvel <ard.biesheu...@linaro.org> > > --- > > ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c | 332 +++++++------------- > > 1 file changed, 108 insertions(+), 224 deletions(-) > > > > diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c > > b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c > > index 204e33c75f95..e36594fea3ad 100644 > > --- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c > > +++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c > > @@ -121,14 +121,16 @@ GetRootTranslationTableInfo ( > > > > STATIC > > VOID > > -ReplaceLiveEntry ( > > +ReplaceTableEntry ( > > IN UINT64 *Entry, > > IN UINT64 Value, > > - IN UINT64 RegionStart > > + IN UINT64 RegionStart, > > + IN BOOLEAN IsLiveBlockMapping > > ) > > { > > - if (!ArmMmuEnabled ()) { > > + if (!ArmMmuEnabled () || !IsLiveBlockMapping) { > > *Entry = Value; > > + ArmUpdateTranslationTableEntry (Entry, (VOID *)(UINTN)RegionStart); > > } else { > > ArmReplaceLiveTranslationEntry (Entry, Value, RegionStart); > > } > > @@ -165,229 +167,132 @@ LookupAddresstoRootTable ( > > } > > > > STATIC > > -UINT64* > > -GetBlockEntryListFromAddress ( > > - IN UINT64 *RootTable, > > - IN UINT64 RegionStart, > > - OUT UINTN *TableLevel, > > - IN OUT UINT64 *BlockEntrySize, > > - OUT UINT64 **LastBlockEntry > > +EFI_STATUS > > +UpdateRegionMappingRec ( > > What does Rec stand for? Record? Recursively? > Could it be written out? > > > + IN UINT64 RegionStart, > > + IN UINT64 RegionEnd, > > + IN UINT64 AttributeSetMask, > > + IN UINT64 AttributeClearMask, > > + IN UINT64 *PageTable, > > + IN UINTN Level > > This sets the scene for a spectacularly unhelpful diff. > Would you consider adding UpdateRegionMappingRec() *before* > LookupAddresstoRootTable() for the only purpose of working against the > shortcomings of the line diff algorithm? That is what I ended up doing > locally for the review. >
Yeah works for me > > ) > > { > > - 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_NS) { > > - TableAttributes = TT_TABLE_NS; > > + UINTN BlockShift; > > + UINT64 BlockMask; > > + UINT64 BlockEnd; > > + UINT64 *Entry; > > + UINT64 EntryValue; > > + VOID *TranslationTable; > > + EFI_STATUS Status; > > + > > + ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0); > > + > > + BlockShift = (Level + 1) * BITS_PER_LEVEL + MIN_T0SZ; > > + BlockMask = MAX_UINT64 >> BlockShift; > > + > > + DEBUG ((DEBUG_VERBOSE, "%a(%d): %llx - %llx set %lx clr %lx\n", > > __FUNCTION__, > > + Level, RegionStart, RegionEnd, AttributeSetMask, AttributeClearMask)); > > + > > + for (; RegionStart < RegionEnd; RegionStart = BlockEnd) { > > + BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1); > > + Entry = &PageTable[(RegionStart >> (64 - BlockShift)) & > > (TT_ENTRY_COUNT - 1)]; > > + > > + // > > + // If RegionStart or BlockEnd is not aligned to the block size at this > > + // level, we will have to create a table mapping in order to map less > > + // than a block, and recurse to create the block or page entries at > > + // the next level. No block mappings are allowed at all at level 0, > > + // so in that case, we have to recurse unconditionally. > > + // > > + if (Level == 0 || ((RegionStart | BlockEnd) & BlockMask) != 0) { > > + ASSERT (Level < 3); > > + > > + if ((*Entry & TT_TYPE_MASK) != TT_TYPE_TABLE_ENTRY) { > > + // > > + // No table entry exists yet, so we need to allocate a page table > > + // for the next level. > > + // > > + TranslationTable = AllocatePages (1); > > + if (TranslationTable == NULL) { > > + return EFI_OUT_OF_RESOURCES; > > } > > > > - // 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; > > + if ((*Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) { > > + // > > + // We are splitting an existing block entry, so we have to > > populate > > + // the new table with the attributes of the block entry it > > replaces. > > + // > > + Status = UpdateRegionMappingRec ( > > + RegionStart & ~BlockMask, > > + (RegionStart | BlockMask) + 1, > > + *Entry & TT_ATTRIBUTES_MASK, > > + 0, > > + TranslationTable, > > + Level + 1); > > + if (EFI_ERROR (Status)) { > > I realise a EFI_OUT_OF_RESOURCES return value here means we would be > in dire straits already, but it would be nice if we could free the > local TranslationTable instead of leaking all allocations through the > tree. > > If not worth the effort (or impossible condition), would still be > worth an explicit comment here. > In PEI, it wouldn't help since FreePages() is a NOP there. But this library is also used by CpuDxe, so it does make sense. And the nice thing about recursion is that it is much simpler to reason about: we only need to free the page if it was allocated in the context of the same call, and everything else comes out in the wash. > > + return Status; > > + } > > } else { > > - Attributes |= TT_TYPE_BLOCK_ENTRY; > > + ZeroMem (TranslationTable, EFI_PAGE_SIZE); > > } > > + } else { > > + TranslationTable = (VOID *)(UINTN)(*Entry & > > TT_ADDRESS_MASK_BLOCK_ENTRY); > > + } > > > > - // Create a new translation table > > - TranslationTable = AllocatePages (1); > > - 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++; > > - } > > + // > > + // Recurse to the next level > > + // > > + Status = UpdateRegionMappingRec ( > > + RegionStart, > > + BlockEnd, > > + AttributeSetMask, > > + AttributeClearMask, > > + TranslationTable, > > + Level + 1); > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > > > - // Fill the BlockEntry with the new TranslationTable > > - ReplaceLiveEntry (BlockEntry, > > - (UINTN)TranslationTable | TableAttributes | TT_TYPE_TABLE_ENTRY, > > - RegionStart); > > + if ((*Entry & TT_TYPE_MASK) != TT_TYPE_TABLE_ENTRY) { > > + ReplaceTableEntry (Entry, > > + (UINT64)TranslationTable | TT_TYPE_TABLE_ENTRY, > > + RegionStart, > > + (*Entry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY); > > } > > } else { > > - if (IndexLevel != PageLevel) { > > - // > > - // Case when we have an Invalid Entry and we are at a page level > > above of the one targetted. > > - // > > + EntryValue = (*Entry & AttributeClearMask) | AttributeSetMask; > > + EntryValue |= RegionStart; > > + EntryValue |= (Level == 3) ? TT_TYPE_BLOCK_ENTRY_LEVEL3 > > + : TT_TYPE_BLOCK_ENTRY; > > > > - // Create a new translation table > > - TranslationTable = AllocatePages (1); > > - if (TranslationTable == NULL) { > > - return NULL; > > - } > > - > > - ZeroMem (TranslationTable, TT_ENTRY_COUNT * sizeof(UINT64)); > > - > > - // Fill the new BlockEntry with the TranslationTable > > - *BlockEntry = ((UINTN)TranslationTable & > > TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TT_TYPE_TABLE_ENTRY; > > - } > > + ReplaceTableEntry (Entry, EntryValue, RegionStart, FALSE); > > } > > } > > - > > - // 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); > > - > > - // The last block of the root table depends on the number of entry in > > this table, > > - // otherwise it is always the (TT_ENTRY_COUNT - 1)th entry in the table. > > - *LastBlockEntry = TT_LAST_BLOCK_ADDRESS(TranslationTable, > > - (PageLevel == RootTableLevel) ? RootTableEntryCount : > > TT_ENTRY_COUNT); > > - > > - return BlockEntry; > > + return EFI_SUCCESS; > > } > > > > STATIC > > EFI_STATUS > > UpdateRegionMapping ( > > - IN UINT64 *RootTable, > > - IN UINT64 RegionStart, > > - IN UINT64 RegionLength, > > - IN UINT64 Attributes, > > - IN UINT64 BlockEntryMask > > + IN UINT64 RegionStart, > > + IN UINT64 RegionSize, > > + IN UINT64 AttributeSetMask, > > + IN UINT64 AttributeClearMask > > The new indentation here (which arguably is an improvement) sort of > masks the unchanged input RegionStart and the rename of > RegionLength->RegionSize. Since it doesn't help anything in this > series, could it be deferred (or broken out)? > I'll revert this change for now (include the name change since it is pointless) > > ) > > { > > - UINT32 Type; > > - UINT64 *BlockEntry; > > - UINT64 *LastBlockEntry; > > - UINT64 BlockEntrySize; > > - UINTN TableLevel; > > + UINTN RootTableLevel; > > + UINTN T0SZ; > > > > - // Ensure the Length is aligned on 4KB boundary > > - if ((RegionLength == 0) || ((RegionLength & (SIZE_4KB - 1)) != 0)) { > > - ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); > > + if ((RegionStart & EFI_PAGE_MASK) != 0 || (RegionSize & EFI_PAGE_MASK) > > != 0) { > > return EFI_INVALID_PARAMETER; > > } > > > > - do { > > - // Get the first Block Entry that matches the Virtual Address and also > > the information on the Table Descriptor > > - // such as the size of the Block Entry and the address of the last > > BlockEntry of the Table Descriptor > > - BlockEntrySize = RegionLength; > > - BlockEntry = GetBlockEntryListFromAddress (RootTable, RegionStart, > > &TableLevel, &BlockEntrySize, &LastBlockEntry); > > - if (BlockEntry == NULL) { > > - // GetBlockEntryListFromAddress() return NULL when it fails to > > allocate new pages from the Translation Tables > > - return EFI_OUT_OF_RESOURCES; > > - } > > + T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; > > + GetRootTranslationTableInfo (T0SZ, &RootTableLevel, NULL); > > > > - if (TableLevel != 3) { > > - Type = TT_TYPE_BLOCK_ENTRY; > > - } else { > > - Type = TT_TYPE_BLOCK_ENTRY_LEVEL3; > > - } > > - > > - do { > > - // Fill the Block Entry with attribute and output block address > > - *BlockEntry &= BlockEntryMask; > > - *BlockEntry |= (RegionStart & TT_ADDRESS_MASK_BLOCK_ENTRY) | > > Attributes | Type; > > - > > - ArmUpdateTranslationTableEntry (BlockEntry, (VOID *)RegionStart); > > - > > - // Go to the next BlockEntry > > - RegionStart += BlockEntrySize; > > - RegionLength -= BlockEntrySize; > > - BlockEntry++; > > - > > - // Break the inner loop when next block is a table > > - // Rerun GetBlockEntryListFromAddress to avoid page table memory leak > > - if (TableLevel != 3 && BlockEntry <= LastBlockEntry && > > - (*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) { > > - break; > > - } > > - } while ((RegionLength >= BlockEntrySize) && (BlockEntry <= > > LastBlockEntry)); > > - } while (RegionLength != 0); > > - > > - return EFI_SUCCESS; > > + return UpdateRegionMappingRec (RegionStart, RegionStart + RegionSize, > > + AttributeSetMask, AttributeClearMask, ArmGetTTBR0BaseAddress (), > > + RootTableLevel); > > } > > > > STATIC > > @@ -398,7 +303,6 @@ FillTranslationTable ( > > ) > > { > > return UpdateRegionMapping ( > > - RootTable, > > MemoryRegion->VirtualBase, > > MemoryRegion->Length, > > ArmMemoryAttributeToPageAttribute (MemoryRegion->Attributes) | > > TT_AF, > > @@ -455,8 +359,6 @@ ArmSetMemoryAttributes ( > > IN UINT64 Attributes > > ) > > { > > - EFI_STATUS Status; > > - UINT64 *TranslationTable; > > UINT64 PageAttributes; > > UINT64 PageAttributeMask; > > > > @@ -473,19 +375,11 @@ ArmSetMemoryAttributes ( > > TT_PXN_MASK | TT_XN_MASK); > > } > > > > - TranslationTable = ArmGetTTBR0BaseAddress (); > > - > > - Status = UpdateRegionMapping ( > > - TranslationTable, > > + return UpdateRegionMapping ( > > BaseAddress, > > Length, > > PageAttributes, > > PageAttributeMask); > > While making a neat and clear diff, this messes up coding style. > Yup. Will fix. > / > Leif > > > - if (EFI_ERROR (Status)) { > > - return Status; > > - } > > - > > - return EFI_SUCCESS; > > } > > > > STATIC > > @@ -497,17 +391,7 @@ SetMemoryRegionAttribute ( > > IN UINT64 BlockEntryMask > > ) > > { > > - EFI_STATUS Status; > > - UINT64 *RootTable; > > - > > - RootTable = ArmGetTTBR0BaseAddress (); > > - > > - Status = UpdateRegionMapping (RootTable, BaseAddress, Length, > > Attributes, BlockEntryMask); > > - if (EFI_ERROR (Status)) { > > - return Status; > > - } > > - > > - return EFI_SUCCESS; > > + return UpdateRegionMapping (BaseAddress, Length, Attributes, > > BlockEntryMask); > > } > > > > EFI_STATUS > > -- > > 2.17.1 > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#55629): https://edk2.groups.io/g/devel/message/55629 Mute This Topic: https://groups.io/mt/71776543/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-