/** Allocates an aligned buffer for SCSI device. This function allocates an aligned buffer for the SCSI device to perform SCSI pass through operations. The alignment requirement is from SCSI pass through interface. @param ScsiIoDevice The SCSI child device involved for the operation. @param BufferSize The request buffer size. @return A pointer to the aligned buffer or NULL if the allocation fails. **/ VOID * AllocateAlignedBuffer ( IN SCSI_IO_DEV *ScsiIoDevice, IN UINTN BufferSize ) { return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiIoDevice->ScsiIo.IoAlign); }
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) { // // Case when we have an Invalid Entry and we are at a page level above of the one targetted. // // 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; } 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; } } } // Expose the found PageLevel to the caller *TableLevel = PageLevel;
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) {
/** Initialize DMA protection. @param VTdInfo The VTd engine context information. @retval EFI_SUCCESS the DMA protection is initialized. @retval EFI_OUT_OF_RESOURCES no enough resource to initialize DMA protection. **/ EFI_STATUS InitDmaProtection ( IN VTD_INFO *VTdInfo ) { EFI_STATUS Status; UINT32 LowMemoryAlignment; UINT64 HighMemoryAlignment; UINTN MemoryAlignment; UINTN LowBottom; UINTN LowTop; UINTN HighBottom; UINT64 HighTop; DMA_BUFFER_INFO *DmaBufferInfo; VOID *Hob; EFI_PEI_PPI_DESCRIPTOR *OldDescriptor; EDKII_IOMMU_PPI *OldIoMmuPpi; Hob = GetFirstGuidHob (&mDmaBufferInfoGuid); DmaBufferInfo = GET_GUID_HOB_DATA(Hob); DEBUG ((DEBUG_INFO, " DmaBufferSize : 0x%x\n", DmaBufferInfo->DmaBufferSize)); LowMemoryAlignment = GetLowMemoryAlignment (VTdInfo, VTdInfo->EngineMask); HighMemoryAlignment = GetHighMemoryAlignment (VTdInfo, VTdInfo->EngineMask); if (LowMemoryAlignment < HighMemoryAlignment) { MemoryAlignment = (UINTN)HighMemoryAlignment; } else { MemoryAlignment = LowMemoryAlignment; } ASSERT (DmaBufferInfo->DmaBufferSize == ALIGN_VALUE(DmaBufferInfo->DmaBufferSize, MemoryAlignment)); DmaBufferInfo->DmaBufferBase = (UINTN)AllocateAlignedPages (EFI_SIZE_TO_PAGES(DmaBufferInfo->DmaBufferSize), MemoryAlignment); ASSERT (DmaBufferInfo->DmaBufferBase != 0); if (DmaBufferInfo->DmaBufferBase == 0) { DEBUG ((DEBUG_INFO, " InitDmaProtection : OutOfResource\n")); return EFI_OUT_OF_RESOURCES; } DEBUG ((DEBUG_INFO, " DmaBufferBase : 0x%x\n", DmaBufferInfo->DmaBufferBase)); DmaBufferInfo->DmaBufferCurrentTop = DmaBufferInfo->DmaBufferBase + DmaBufferInfo->DmaBufferSize; DmaBufferInfo->DmaBufferCurrentBottom = DmaBufferInfo->DmaBufferBase; // // (Re)Install PPI. // Status = PeiServicesLocatePpi ( &gEdkiiIoMmuPpiGuid, 0, &OldDescriptor, (VOID **) &OldIoMmuPpi ); if (!EFI_ERROR (Status)) { Status = PeiServicesReInstallPpi (OldDescriptor, &mIoMmuPpiList); } else { Status = PeiServicesInstallPpi (&mIoMmuPpiList); } ASSERT_EFI_ERROR (Status); LowBottom = 0; LowTop = DmaBufferInfo->DmaBufferBase; HighBottom = DmaBufferInfo->DmaBufferBase + DmaBufferInfo->DmaBufferSize; HighTop = LShiftU64 (1, VTdInfo->HostAddressWidth + 1); Status = SetDmaProtectedRange ( VTdInfo, VTdInfo->EngineMask, (UINT32)LowBottom, (UINT32)(LowTop - LowBottom), HighBottom, HighTop - HighBottom ); if (EFI_ERROR(Status)) { FreePages ((VOID *)DmaBufferInfo->DmaBufferBase, EFI_SIZE_TO_PAGES(DmaBufferInfo->DmaBufferSize)); } return Status; }