/** @file Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IntelVTdDmarPei.h" #define ALIGN_VALUE_UP(Value, Alignment) (((Value) + (Alignment) - 1) & (~((Alignment) - 1))) #define ALIGN_VALUE_LOW(Value, Alignment) ((Value) & (~((Alignment) - 1))) /** Allocate zero pages. @param[in] Pages the number of pages. @return the page address. @retval NULL No resource to allocate pages. **/ VOID * EFIAPI AllocateZeroPages ( IN UINTN Pages ) { VOID *Addr; Addr = AllocatePages (Pages); if (Addr == NULL) { return NULL; } ZeroMem (Addr, EFI_PAGES_TO_SIZE (Pages)); return Addr; } /** Set second level paging entry attribute based upon IoMmuAccess. @param[in] PtEntry The paging entry. @param[in] IoMmuAccess The IOMMU access. **/ VOID SetSecondLevelPagingEntryAttribute ( IN VTD_SECOND_LEVEL_PAGING_ENTRY *PtEntry, IN UINT64 IoMmuAccess ) { PtEntry->Bits.Read = ((IoMmuAccess & EDKII_IOMMU_ACCESS_READ) != 0); PtEntry->Bits.Write = ((IoMmuAccess & EDKII_IOMMU_ACCESS_WRITE) != 0); DEBUG ((DEBUG_VERBOSE, "SetSecondLevelPagingEntryAttribute - 0x%x - 0x%x\n", PtEntry, IoMmuAccess)); } /** Create second level paging entry table. @param[in] VTdUnitInfo The VTd engine unit information. @param[in] SecondLevelPagingEntry The second level paging entry. @param[in] MemoryBase The base of the memory. @param[in] MemoryLimit The limit of the memory. @param[in] IoMmuAccess The IOMMU access. @return The second level paging entry. **/ VTD_SECOND_LEVEL_PAGING_ENTRY * CreateSecondLevelPagingEntryTable ( IN VTD_UNIT_INFO *VTdUnitInfo, IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry, IN UINT64 MemoryBase, IN UINT64 MemoryLimit, IN UINT64 IoMmuAccess ) { UINTN Index5; UINTN Index4; UINTN Index3; UINTN Index2; UINTN Lvl5Start; UINTN Lvl5End; UINTN Lvl4PagesStart; UINTN Lvl4PagesEnd; UINTN Lvl4Start; UINTN Lvl4End; UINTN Lvl3Start; UINTN Lvl3End; VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl5PtEntry; VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl4PtEntry; VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl3PtEntry; VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl2PtEntry; UINT64 BaseAddress; UINT64 EndAddress; BOOLEAN Is5LevelPaging; if (MemoryLimit == 0) { return EFI_SUCCESS; } Lvl4PagesStart = 0; Lvl4PagesEnd = 0; Lvl4PtEntry = NULL; Lvl5PtEntry = NULL; BaseAddress = ALIGN_VALUE_LOW (MemoryBase, SIZE_2MB); EndAddress = ALIGN_VALUE_UP (MemoryLimit, SIZE_2MB); DEBUG ((DEBUG_INFO, "CreateSecondLevelPagingEntryTable: BaseAddress - 0x%016lx, EndAddress - 0x%016lx\n", BaseAddress, EndAddress)); if (SecondLevelPagingEntry == NULL) { SecondLevelPagingEntry = AllocateZeroPages (1); if (SecondLevelPagingEntry == NULL) { DEBUG ((DEBUG_ERROR, "Could not Alloc LVL4 or LVL5 PT. \n")); return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) SecondLevelPagingEntry, EFI_PAGES_TO_SIZE (1)); } DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry:0x%016lx\n", SecondLevelPagingEntry)); // // If no access is needed, just create not present entry. // if (IoMmuAccess == 0) { DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry:0x%016lx\n", (UINTN) SecondLevelPagingEntry)); return SecondLevelPagingEntry; } Is5LevelPaging = VTdUnitInfo->Is5LevelPaging; if (Is5LevelPaging) { Lvl5Start = RShiftU64 (BaseAddress, 48) & 0x1FF; Lvl5End = RShiftU64 (EndAddress - 1, 48) & 0x1FF; DEBUG ((DEBUG_INFO, " Lvl5Start - 0x%x, Lvl5End - 0x%x\n", Lvl5Start, Lvl5End)); Lvl4Start = RShiftU64 (BaseAddress, 39) & 0x1FF; Lvl4End = RShiftU64 (EndAddress - 1, 39) & 0x1FF; Lvl4PagesStart = (Lvl5Start<<9) | Lvl4Start; Lvl4PagesEnd = (Lvl5End<<9) | Lvl4End; DEBUG ((DEBUG_INFO, " Lvl4PagesStart - 0x%x, Lvl4PagesEnd - 0x%x\n", Lvl4PagesStart, Lvl4PagesEnd)); Lvl5PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) SecondLevelPagingEntry; } else { Lvl5Start = RShiftU64 (BaseAddress, 48) & 0x1FF; Lvl5End = Lvl5Start; Lvl4Start = RShiftU64 (BaseAddress, 39) & 0x1FF; Lvl4End = RShiftU64 (EndAddress - 1, 39) & 0x1FF; DEBUG ((DEBUG_INFO, " Lvl4Start - 0x%x, Lvl4End - 0x%x\n", Lvl4Start, Lvl4End)); Lvl4PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) SecondLevelPagingEntry; } for (Index5 = Lvl5Start; Index5 <= Lvl5End; Index5++) { if (Is5LevelPaging) { if (Lvl5PtEntry[Index5].Uint64 == 0) { Lvl5PtEntry[Index5].Uint64 = (UINT64) (UINTN) AllocateZeroPages (1); if (Lvl5PtEntry[Index5].Uint64 == 0) { DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index5)); ASSERT (FALSE); return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl5PtEntry[Index5].Uint64, SIZE_4KB); SetSecondLevelPagingEntryAttribute (&Lvl5PtEntry[Index5], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); } Lvl4Start = Lvl4PagesStart & 0x1FF; if (((Index5+1)<<9) > Lvl4PagesEnd) { Lvl4End = SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY) - 1;; Lvl4PagesStart = (Index5+1)<<9; } else { Lvl4End = Lvl4PagesEnd & 0x1FF; } DEBUG ((DEBUG_INFO, " Lvl5(0x%x): Lvl4Start - 0x%x, Lvl4End - 0x%x\n", Index5, Lvl4Start, Lvl4End)); Lvl4PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(Lvl5PtEntry[Index5].Bits.AddressLo, Lvl5PtEntry[Index5].Bits.AddressHi); } for (Index4 = Lvl4Start; Index4 <= Lvl4End; Index4++) { if (Lvl4PtEntry[Index4].Uint64 == 0) { Lvl4PtEntry[Index4].Uint64 = (UINT64) (UINTN) AllocateZeroPages (1); if (Lvl4PtEntry[Index4].Uint64 == 0) { DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index4)); ASSERT(FALSE); return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl4PtEntry[Index4].Uint64, SIZE_4KB); SetSecondLevelPagingEntryAttribute (&Lvl4PtEntry[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); } Lvl3Start = RShiftU64 (BaseAddress, 30) & 0x1FF; if (ALIGN_VALUE_LOW(BaseAddress + SIZE_1GB, SIZE_1GB) <= EndAddress) { Lvl3End = SIZE_4KB / sizeof (VTD_SECOND_LEVEL_PAGING_ENTRY) - 1; } else { Lvl3End = RShiftU64 (EndAddress - 1, 30) & 0x1FF; } DEBUG ((DEBUG_INFO, " Lvl4(0x%x): Lvl3Start - 0x%x, Lvl3End - 0x%x\n", Index4, Lvl3Start, Lvl3End)); Lvl3PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(Lvl4PtEntry[Index4].Bits.AddressLo, Lvl4PtEntry[Index4].Bits.AddressHi); for (Index3 = Lvl3Start; Index3 <= Lvl3End; Index3++) { if (Lvl3PtEntry[Index3].Uint64 == 0) { Lvl3PtEntry[Index3].Uint64 = (UINT64) (UINTN) AllocateZeroPages (1); if (Lvl3PtEntry[Index3].Uint64 == 0) { DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL3 PAGE FAIL (0x%x, 0x%x)!!!!!!\n", Index4, Index3)); ASSERT(FALSE); return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl3PtEntry[Index3].Uint64, SIZE_4KB); SetSecondLevelPagingEntryAttribute (&Lvl3PtEntry[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); } Lvl2PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(Lvl3PtEntry[Index3].Bits.AddressLo, Lvl3PtEntry[Index3].Bits.AddressHi); for (Index2 = 0; Index2 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index2++) { Lvl2PtEntry[Index2].Uint64 = BaseAddress; SetSecondLevelPagingEntryAttribute (&Lvl2PtEntry[Index2], IoMmuAccess); Lvl2PtEntry[Index2].Bits.PageSize = 1; BaseAddress += SIZE_2MB; if (BaseAddress >= MemoryLimit) { break; } } FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl2PtEntry, SIZE_4KB); if (BaseAddress >= MemoryLimit) { break; } } FlushPageTableMemory (VTdUnitInfo, (UINTN) &Lvl3PtEntry[Lvl3Start], (UINTN) &Lvl3PtEntry[Lvl3End + 1] - (UINTN) &Lvl3PtEntry[Lvl3Start]); if (BaseAddress >= MemoryLimit) { break; } } FlushPageTableMemory (VTdUnitInfo, (UINTN) &Lvl4PtEntry[Lvl4Start], (UINTN) &Lvl4PtEntry[Lvl4End + 1] - (UINTN) &Lvl4PtEntry[Lvl4Start]); } FlushPageTableMemory (VTdUnitInfo, (UINTN) &Lvl5PtEntry[Lvl5Start], (UINTN) &Lvl5PtEntry[Lvl5End + 1] - (UINTN) &Lvl5PtEntry[Lvl5Start]); DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry:0x%016lx\n", (UINTN)SecondLevelPagingEntry)); return SecondLevelPagingEntry; } /** Create context entry. @param[in] VTdUnitInfo The VTd engine unit information. @retval EFI_SUCCESS The context entry is created. @retval EFI_OUT_OF_RESOURCE No enough resource to create context entry. **/ EFI_STATUS CreateContextEntry ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINTN RootPages; UINTN ContextPages; UINTN EntryTablePages; VOID *Buffer; UINTN RootIndex; UINTN ContextIndex; VTD_ROOT_ENTRY *RootEntryBase; VTD_ROOT_ENTRY *RootEntry; VTD_CONTEXT_ENTRY *ContextEntryTable; VTD_CONTEXT_ENTRY *ContextEntry; VTD_SOURCE_ID SourceId; VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry; UINT64 Pt; RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER); ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER); EntryTablePages = RootPages + ContextPages * (VTD_ROOT_ENTRY_NUMBER); Buffer = AllocateZeroPages (EntryTablePages); if (Buffer == NULL) { DEBUG ((DEBUG_ERROR, "Could not Alloc Root Entry Table.. \n")); return EFI_OUT_OF_RESOURCES; } DEBUG ((DEBUG_ERROR, "RootEntryTable address - 0x%x\n", Buffer)); VTdUnitInfo->RootEntryTable = (UINT32) (UINTN) Buffer; VTdUnitInfo->RootEntryTablePageSize = (UINT16) EntryTablePages; RootEntryBase = (VTD_ROOT_ENTRY *) Buffer; Buffer = (UINT8 *) Buffer + EFI_PAGES_TO_SIZE (RootPages); if (VTdUnitInfo->FixedSecondLevelPagingEntry == 0) { DEBUG ((DEBUG_ERROR, "FixedSecondLevelPagingEntry is empty\n")); ASSERT(FALSE); } for (RootIndex = 0; RootIndex < VTD_ROOT_ENTRY_NUMBER; RootIndex++) { SourceId.Index.RootIndex = (UINT8) RootIndex; RootEntry = &RootEntryBase[SourceId.Index.RootIndex]; RootEntry->Bits.ContextTablePointerLo = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 12); RootEntry->Bits.ContextTablePointerHi = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 32); RootEntry->Bits.Present = 1; Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (ContextPages); ContextEntryTable = (VTD_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(RootEntry->Bits.ContextTablePointerLo, RootEntry->Bits.ContextTablePointerHi) ; for (ContextIndex = 0; ContextIndex < VTD_CONTEXT_ENTRY_NUMBER; ContextIndex++) { SourceId.Index.ContextIndex = (UINT8) ContextIndex; ContextEntry = &ContextEntryTable[SourceId.Index.ContextIndex]; ContextEntry->Bits.TranslationType = 0; ContextEntry->Bits.FaultProcessingDisable = 0; ContextEntry->Bits.Present = 0; ContextEntry->Bits.AddressWidth = VTdUnitInfo->Is5LevelPaging ? 0x3 : 0x2; if (VTdUnitInfo->FixedSecondLevelPagingEntry != 0) { SecondLevelPagingEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTdUnitInfo->FixedSecondLevelPagingEntry; Pt = (UINT64)RShiftU64 ((UINT64) (UINTN) SecondLevelPagingEntry, 12); ContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt; ContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20); ContextEntry->Bits.DomainIdentifier = ((1 << (UINT8)((UINTN)VTdUnitInfo->CapReg.Bits.ND * 2 + 4)) - 1); ContextEntry->Bits.Present = 1; } } } FlushPageTableMemory (VTdUnitInfo, VTdUnitInfo->RootEntryTable, EFI_PAGES_TO_SIZE(EntryTablePages)); return EFI_SUCCESS; } /** Create extended context entry. @param[in] VTdUnitInfo The VTd engine unit information. @retval EFI_SUCCESS The extended context entry is created. @retval EFI_OUT_OF_RESOURCE No enough resource to create extended context entry. **/ EFI_STATUS CreateExtContextEntry ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINTN RootPages; UINTN ContextPages; UINTN EntryTablePages; VOID *Buffer; UINTN RootIndex; UINTN ContextIndex; VTD_EXT_ROOT_ENTRY *ExtRootEntryBase; VTD_EXT_ROOT_ENTRY *ExtRootEntry; VTD_EXT_CONTEXT_ENTRY *ExtContextEntryTable; VTD_EXT_CONTEXT_ENTRY *ExtContextEntry; VTD_SOURCE_ID SourceId; VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry; UINT64 Pt; RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER); ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER); EntryTablePages = RootPages + ContextPages * (VTD_ROOT_ENTRY_NUMBER); Buffer = AllocateZeroPages (EntryTablePages); if (Buffer == NULL) { DEBUG ((DEBUG_INFO, "Could not Alloc Root Entry Table !\n")); return EFI_OUT_OF_RESOURCES; } DEBUG ((DEBUG_ERROR, "ExtRootEntryTable address - 0x%x\n", Buffer)); VTdUnitInfo->ExtRootEntryTable = (UINT32) (UINTN) Buffer; VTdUnitInfo->ExtRootEntryTablePageSize = (UINT16) EntryTablePages; ExtRootEntryBase = (VTD_EXT_ROOT_ENTRY *) Buffer; Buffer = (UINT8 *) Buffer + EFI_PAGES_TO_SIZE (RootPages); if (VTdUnitInfo->FixedSecondLevelPagingEntry == 0) { DEBUG ((DEBUG_ERROR, "FixedSecondLevelPagingEntry is empty\n")); ASSERT(FALSE); } for (RootIndex = 0; RootIndex < VTD_ROOT_ENTRY_NUMBER; RootIndex++) { SourceId.Index.RootIndex = (UINT8)RootIndex; ExtRootEntry = &ExtRootEntryBase[SourceId.Index.RootIndex]; ExtRootEntry->Bits.LowerContextTablePointerLo = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 12); ExtRootEntry->Bits.LowerContextTablePointerHi = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 32); ExtRootEntry->Bits.LowerPresent = 1; ExtRootEntry->Bits.UpperContextTablePointerLo = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 12) + 1; ExtRootEntry->Bits.UpperContextTablePointerHi = (UINT32) RShiftU64 (RShiftU64 ((UINT64) (UINTN) Buffer, 12) + 1, 20); ExtRootEntry->Bits.UpperPresent = 1; Buffer = (UINT8 *) Buffer + EFI_PAGES_TO_SIZE (ContextPages); ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS (ExtRootEntry->Bits.LowerContextTablePointerLo, ExtRootEntry->Bits.LowerContextTablePointerHi) ; for (ContextIndex = 0; ContextIndex < VTD_CONTEXT_ENTRY_NUMBER; ContextIndex++) { SourceId.Index.ContextIndex = (UINT8) ContextIndex; ExtContextEntry = &ExtContextEntryTable[SourceId.Index.ContextIndex]; ExtContextEntry->Bits.TranslationType = 0; ExtContextEntry->Bits.FaultProcessingDisable = 0; ExtContextEntry->Bits.Present = 0; ExtContextEntry->Bits.AddressWidth = VTdUnitInfo->Is5LevelPaging ? 0x3 : 0x2; if (VTdUnitInfo->FixedSecondLevelPagingEntry != 0) { SecondLevelPagingEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTdUnitInfo->FixedSecondLevelPagingEntry; Pt = (UINT64)RShiftU64 ((UINT64) (UINTN) SecondLevelPagingEntry, 12); ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt; ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20); ExtContextEntry->Bits.DomainIdentifier = ((1 << (UINT8) ((UINTN) VTdUnitInfo->CapReg.Bits.ND * 2 + 4)) - 1); ExtContextEntry->Bits.Present = 1; } } } FlushPageTableMemory (VTdUnitInfo, VTdUnitInfo->ExtRootEntryTable, EFI_PAGES_TO_SIZE(EntryTablePages)); return EFI_SUCCESS; } #define VTD_PG_R BIT0 #define VTD_PG_W BIT1 #define VTD_PG_X BIT2 #define VTD_PG_EMT (BIT3 | BIT4 | BIT5) #define VTD_PG_TM (BIT62) #define VTD_PG_PS BIT7 #define PAGE_PROGATE_BITS (VTD_PG_TM | VTD_PG_EMT | VTD_PG_W | VTD_PG_R) #define PAGING_4K_MASK 0xFFF #define PAGING_2M_MASK 0x1FFFFF #define PAGING_1G_MASK 0x3FFFFFFF #define PAGING_VTD_INDEX_MASK 0x1FF #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull typedef enum { PageNone, Page4K, Page2M, Page1G, } PAGE_ATTRIBUTE; typedef struct { PAGE_ATTRIBUTE Attribute; UINT64 Length; UINT64 AddressMask; } PAGE_ATTRIBUTE_TABLE; PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64}, {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64}, {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, }; /** Return length according to page attributes. @param[in] PageAttributes The page attribute of the page entry. @return The length of page entry. **/ UINTN PageAttributeToLength ( IN PAGE_ATTRIBUTE PageAttribute ) { UINTN Index; for (Index = 0; Index < sizeof (mPageAttributeTable) / sizeof (mPageAttributeTable[0]); Index++) { if (PageAttribute == mPageAttributeTable[Index].Attribute) { return (UINTN) mPageAttributeTable[Index].Length; } } return 0; } /** Return page table entry to match the address. @param[in] VTdUnitInfo The VTd engine unit information. @param[in] SecondLevelPagingEntry The second level paging entry in VTd table for the device. @param[in] Address The address to be checked. @param[out] PageAttributes The page attribute of the page entry. @return The page entry. **/ VOID * GetSecondLevelPageTableEntry ( IN VTD_UNIT_INFO *VTdUnitInfo, IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry, IN PHYSICAL_ADDRESS Address, OUT PAGE_ATTRIBUTE *PageAttribute ) { UINTN Index1; UINTN Index2; UINTN Index3; UINTN Index4; UINTN Index5; UINT64 *L1PageTable; UINT64 *L2PageTable; UINT64 *L3PageTable; UINT64 *L4PageTable; UINT64 *L5PageTable; BOOLEAN Is5LevelPaging; Index5 = ((UINTN) RShiftU64 (Address, 48)) & PAGING_VTD_INDEX_MASK; Index4 = ((UINTN) RShiftU64 (Address, 39)) & PAGING_VTD_INDEX_MASK; Index3 = ((UINTN) Address >> 30) & PAGING_VTD_INDEX_MASK; Index2 = ((UINTN) Address >> 21) & PAGING_VTD_INDEX_MASK; Index1 = ((UINTN) Address >> 12) & PAGING_VTD_INDEX_MASK; Is5LevelPaging = VTdUnitInfo->Is5LevelPaging; if (Is5LevelPaging) { L5PageTable = (UINT64 *) SecondLevelPagingEntry; if (L5PageTable[Index5] == 0) { L5PageTable[Index5] = (UINT64) (UINTN) AllocateZeroPages (1); if (L5PageTable[Index5] == 0) { DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL5 PAGE FAIL (0x%x)!!!!!!\n", Index4)); ASSERT(FALSE); *PageAttribute = PageNone; return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) L5PageTable[Index5], SIZE_4KB); SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L5PageTable[Index5], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); FlushPageTableMemory (VTdUnitInfo, (UINTN) &L5PageTable[Index5], sizeof(L5PageTable[Index5])); } L4PageTable = (UINT64 *) (UINTN) (L5PageTable[Index5] & PAGING_4K_ADDRESS_MASK_64); } else { L4PageTable = (UINT64 *)SecondLevelPagingEntry; } if (L4PageTable[Index4] == 0) { L4PageTable[Index4] = (UINT64) (UINTN) AllocateZeroPages (1); if (L4PageTable[Index4] == 0) { DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index4)); ASSERT(FALSE); *PageAttribute = PageNone; return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) L4PageTable[Index4], SIZE_4KB); SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L4PageTable[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); FlushPageTableMemory (VTdUnitInfo, (UINTN) &L4PageTable[Index4], sizeof(L4PageTable[Index4])); } L3PageTable = (UINT64 *) (UINTN) (L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64); if (L3PageTable[Index3] == 0) { L3PageTable[Index3] = (UINT64) (UINTN) AllocateZeroPages (1); if (L3PageTable[Index3] == 0) { DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL3 PAGE FAIL (0x%x, 0x%x)!!!!!!\n", Index4, Index3)); ASSERT(FALSE); *PageAttribute = PageNone; return NULL; } FlushPageTableMemory (VTdUnitInfo, (UINTN) L3PageTable[Index3], SIZE_4KB); SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L3PageTable[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); FlushPageTableMemory (VTdUnitInfo, (UINTN) &L3PageTable[Index3], sizeof (L3PageTable[Index3])); } if ((L3PageTable[Index3] & VTD_PG_PS) != 0) { // 1G *PageAttribute = Page1G; return &L3PageTable[Index3]; } L2PageTable = (UINT64 *) (UINTN) (L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64); if (L2PageTable[Index2] == 0) { L2PageTable[Index2] = Address & PAGING_2M_ADDRESS_MASK_64; SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L2PageTable[Index2], 0); L2PageTable[Index2] |= VTD_PG_PS; FlushPageTableMemory (VTdUnitInfo, (UINTN) &L2PageTable[Index2], sizeof (L2PageTable[Index2])); } if ((L2PageTable[Index2] & VTD_PG_PS) != 0) { // 2M *PageAttribute = Page2M; return &L2PageTable[Index2]; } // 4k L1PageTable = (UINT64 *) (UINTN) (L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64); if ((L1PageTable[Index1] == 0) && (Address != 0)) { *PageAttribute = PageNone; return NULL; } *PageAttribute = Page4K; return &L1PageTable[Index1]; } /** Modify memory attributes of page entry. @param[in] VTdUnitInfo The VTd engine unit information. @param[in] PageEntry The page entry. @param[in] IoMmuAccess The IOMMU access. @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. **/ VOID ConvertSecondLevelPageEntryAttribute ( IN VTD_UNIT_INFO *VTdUnitInfo, IN VTD_SECOND_LEVEL_PAGING_ENTRY *PageEntry, IN UINT64 IoMmuAccess, OUT BOOLEAN *IsModified ) { UINT64 CurrentPageEntry; UINT64 NewPageEntry; CurrentPageEntry = PageEntry->Uint64; SetSecondLevelPagingEntryAttribute (PageEntry, IoMmuAccess); FlushPageTableMemory (VTdUnitInfo, (UINTN) PageEntry, sizeof(*PageEntry)); NewPageEntry = PageEntry->Uint64; if (CurrentPageEntry != NewPageEntry) { *IsModified = TRUE; DEBUG ((DEBUG_VERBOSE, "ConvertSecondLevelPageEntryAttribute 0x%lx", CurrentPageEntry)); DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry)); } else { *IsModified = FALSE; } } /** This function returns if there is need to split page entry. @param[in] BaseAddress The base address to be checked. @param[in] Length The length to be checked. @param[in] PageAttribute The page attribute of the page entry. @retval SplitAttributes on if there is need to split page entry. **/ PAGE_ATTRIBUTE NeedSplitPage ( IN PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN PAGE_ATTRIBUTE PageAttribute ) { UINT64 PageEntryLength; PageEntryLength = PageAttributeToLength (PageAttribute); if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) { return PageNone; } if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) { return Page4K; } return Page2M; } /** This function splits one page entry to small page entries. @param[in] VTdUnitInfo The VTd engine unit information. @param[in] PageEntry The page entry to be splitted. @param[in] PageAttribute The page attribute of the page entry. @param[in] SplitAttribute How to split the page entry. @retval RETURN_SUCCESS The page entry is splitted. @retval RETURN_UNSUPPORTED The page entry does not support to be splitted. @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. **/ RETURN_STATUS SplitSecondLevelPage ( IN VTD_UNIT_INFO *VTdUnitInfo, IN VTD_SECOND_LEVEL_PAGING_ENTRY *PageEntry, IN PAGE_ATTRIBUTE PageAttribute, IN PAGE_ATTRIBUTE SplitAttribute ) { UINT64 BaseAddress; UINT64 *NewPageEntry; UINTN Index; ASSERT (PageAttribute == Page2M || PageAttribute == Page1G); if (PageAttribute == Page2M) { // // Split 2M to 4K // ASSERT (SplitAttribute == Page4K); if (SplitAttribute == Page4K) { NewPageEntry = AllocateZeroPages (1); DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry)); if (NewPageEntry == NULL) { return RETURN_OUT_OF_RESOURCES; } BaseAddress = PageEntry->Uint64 & PAGING_2M_ADDRESS_MASK_64; for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | (PageEntry->Uint64 & PAGE_PROGATE_BITS); } FlushPageTableMemory (VTdUnitInfo, (UINTN)NewPageEntry, SIZE_4KB); PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry; SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); FlushPageTableMemory (VTdUnitInfo, (UINTN)PageEntry, sizeof(*PageEntry)); return RETURN_SUCCESS; } else { return RETURN_UNSUPPORTED; } } else if (PageAttribute == Page1G) { // // Split 1G to 2M // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table. // ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K); if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) { NewPageEntry = AllocateZeroPages (1); DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry)); if (NewPageEntry == NULL) { return RETURN_OUT_OF_RESOURCES; } BaseAddress = PageEntry->Uint64 & PAGING_1G_ADDRESS_MASK_64; for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | VTD_PG_PS | (PageEntry->Uint64 & PAGE_PROGATE_BITS); } FlushPageTableMemory (VTdUnitInfo, (UINTN)NewPageEntry, SIZE_4KB); PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry; SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE); FlushPageTableMemory (VTdUnitInfo, (UINTN)PageEntry, sizeof(*PageEntry)); return RETURN_SUCCESS; } else { return RETURN_UNSUPPORTED; } } else { return RETURN_UNSUPPORTED; } } /** Set VTd attribute for a system memory on second level page entry @param[in] VTdUnitInfo The VTd engine unit information. @param[in] SecondLevelPagingEntry The second level paging entry in VTd table for the device. @param[in] BaseAddress The base of device memory address to be used as the DMA memory. @param[in] Length The length of device memory address to be used as the DMA memory. @param[in] IoMmuAccess The IOMMU access. @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by BaseAddress and Length. @retval EFI_INVALID_PARAMETER BaseAddress is not IoMmu Page size aligned. @retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned. @retval EFI_INVALID_PARAMETER Length is 0. @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access. @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU. @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by BaseAddress and Length. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access. @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation. **/ EFI_STATUS SetSecondLevelPagingAttribute ( IN VTD_UNIT_INFO *VTdUnitInfo, IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry, IN UINT64 BaseAddress, IN UINT64 Length, IN UINT64 IoMmuAccess ) { VTD_SECOND_LEVEL_PAGING_ENTRY *PageEntry; PAGE_ATTRIBUTE PageAttribute; UINTN PageEntryLength; PAGE_ATTRIBUTE SplitAttribute; EFI_STATUS Status; BOOLEAN IsEntryModified; DEBUG ((DEBUG_INFO, "SetSecondLevelPagingAttribute (0x%016lx - 0x%016lx : %x) \n", BaseAddress, Length, IoMmuAccess)); DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry Base - 0x%x\n", SecondLevelPagingEntry)); if (BaseAddress != ALIGN_VALUE(BaseAddress, SIZE_4KB)) { DEBUG ((DEBUG_ERROR, "SetSecondLevelPagingAttribute - Invalid Alignment\n")); return EFI_UNSUPPORTED; } if (Length != ALIGN_VALUE(Length, SIZE_4KB)) { DEBUG ((DEBUG_ERROR, "SetSecondLevelPagingAttribute - Invalid Alignment\n")); return EFI_UNSUPPORTED; } while (Length != 0) { PageEntry = GetSecondLevelPageTableEntry (VTdUnitInfo, SecondLevelPagingEntry, BaseAddress, &PageAttribute); if (PageEntry == NULL) { DEBUG ((DEBUG_ERROR, "PageEntry - NULL\n")); return RETURN_UNSUPPORTED; } PageEntryLength = PageAttributeToLength (PageAttribute); SplitAttribute = NeedSplitPage (BaseAddress, Length, PageAttribute); if (SplitAttribute == PageNone) { ConvertSecondLevelPageEntryAttribute (VTdUnitInfo, PageEntry, IoMmuAccess, &IsEntryModified); if (IsEntryModified) { //mVtdUnitInformation[VtdIndex].HasDirtyPages = TRUE; } // // Convert success, move to next // BaseAddress += PageEntryLength; Length -= PageEntryLength; } else { Status = SplitSecondLevelPage (VTdUnitInfo, PageEntry, PageAttribute, SplitAttribute); if (RETURN_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "SplitSecondLevelPage - %r\n", Status)); return RETURN_UNSUPPORTED; } //mVtdUnitInformation[VtdIndex].HasDirtyPages = TRUE; // // Just split current page // Convert success in next around // } } return EFI_SUCCESS; } /** Create Fixed Second Level Paging Entry. @param[in] VTdUnitInfo The VTd engine unit information. @retval EFI_SUCCESS Setup translation table successfully. @retval EFI_OUT_OF_RESOURCES Setup translation table fail. **/ EFI_STATUS CreateFixedSecondLevelPagingEntry ( IN VTD_UNIT_INFO *VTdUnitInfo ) { EFI_STATUS Status; UINT64 IoMmuAccess; UINT64 BaseAddress; UINT64 Length; VOID *Hob; DMA_BUFFER_INFO *DmaBufferInfo; VTdUnitInfo->FixedSecondLevelPagingEntry = (UINT32) (UINTN) CreateSecondLevelPagingEntryTable (VTdUnitInfo, NULL, 0, SIZE_4GB, 0); if (VTdUnitInfo->FixedSecondLevelPagingEntry == 0) { DEBUG ((DEBUG_ERROR, "FixedSecondLevelPagingEntry is empty\n")); return EFI_OUT_OF_RESOURCES; } Hob = GetFirstGuidHob (&mDmaBufferInfoGuid); DmaBufferInfo = GET_GUID_HOB_DATA (Hob); BaseAddress = DmaBufferInfo->DmaBufferBase; Length = DmaBufferInfo->DmaBufferLimit - DmaBufferInfo->DmaBufferBase; IoMmuAccess = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE; DEBUG ((DEBUG_INFO, " BaseAddress = 0x%lx\n", BaseAddress)); DEBUG ((DEBUG_INFO, " Length = 0x%lx\n", Length)); DEBUG ((DEBUG_INFO, " IoMmuAccess = 0x%lx\n", IoMmuAccess)); Status = SetSecondLevelPagingAttribute (VTdUnitInfo, (VTD_SECOND_LEVEL_PAGING_ENTRY*) (UINTN) VTdUnitInfo->FixedSecondLevelPagingEntry, BaseAddress, Length, IoMmuAccess); return Status; } /** Setup VTd translation table. @param[in] VTdInfo The VTd engine context information. @retval EFI_SUCCESS Setup translation table successfully. @retval EFI_OUT_OF_RESOURCES Setup translation table fail. **/ EFI_STATUS SetupTranslationTable ( IN VTD_INFO *VTdInfo ) { EFI_STATUS Status; UINTN Index; VTD_UNIT_INFO *VtdUnitInfo; for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) { VtdUnitInfo = &VTdInfo->VtdUnitInfo[Index]; Status = CreateFixedSecondLevelPagingEntry (VtdUnitInfo); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "CreateFixedSecondLevelPagingEntry failed - %r\n", Status)); return Status; } if (VtdUnitInfo->ECapReg.Bits.ECS) { DEBUG ((DEBUG_INFO, "CreateExtContextEntry - %d\n", Index)); Status = CreateExtContextEntry (VtdUnitInfo); } else { DEBUG ((DEBUG_INFO, "CreateContextEntry - %d\n", Index)); Status = CreateContextEntry (VtdUnitInfo); } if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; } /** Find the VTd index by the Segment and SourceId. @param[in] VTdInfo The VTd engine context information. @param[in] Segment The segment of the source. @param[in] SourceId The SourceId of the source. @param[out] ExtContextEntry The ExtContextEntry of the source. @param[out] ContextEntry The ContextEntry of the source. @return The index of the VTd engine. @retval (UINTN)-1 The VTd engine is not found. **/ UINTN FindVtdIndexBySegmentSourceId ( IN VTD_INFO *VTdInfo, IN UINT16 Segment, IN VTD_SOURCE_ID SourceId, OUT VTD_EXT_CONTEXT_ENTRY **ExtContextEntry, OUT VTD_CONTEXT_ENTRY **ContextEntry ) { UINTN VtdIndex; VTD_ROOT_ENTRY *RootEntryBase; VTD_ROOT_ENTRY *RootEntry; VTD_CONTEXT_ENTRY *ContextEntryTable; VTD_CONTEXT_ENTRY *ThisContextEntry; VTD_EXT_ROOT_ENTRY *ExtRootEntryBase; VTD_EXT_ROOT_ENTRY *ExtRootEntry; VTD_EXT_CONTEXT_ENTRY *ExtContextEntryTable; VTD_EXT_CONTEXT_ENTRY *ThisExtContextEntry; for (VtdIndex = 0; VtdIndex < VTdInfo->VTdEngineCount; VtdIndex++) { if (GetPciDataIndex (&VTdInfo->VtdUnitInfo[VtdIndex], Segment, SourceId) != (UINTN)-1) { DEBUG ((DEBUG_INFO, "Find VtdIndex(0x%x) for S%04x B%02x D%02x F%02x\n", VtdIndex, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function)); break; } } if (VtdIndex >= VTdInfo->VTdEngineCount) { for (VtdIndex = 0; VtdIndex < VTdInfo->VTdEngineCount; VtdIndex++) { if (Segment != VTdInfo->VtdUnitInfo[VtdIndex].Segment) { continue; } if (VTdInfo->VtdUnitInfo[VtdIndex].PciDeviceInfo.IncludeAllFlag) { DEBUG ((DEBUG_INFO, "Find IncludeAllFlag VtdIndex(0x%x) for S%04x B%02x D%02x F%02x\n", VtdIndex, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function)); break; } } } if (VtdIndex < VTdInfo->VTdEngineCount) { ExtRootEntryBase = (VTD_EXT_ROOT_ENTRY *) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].ExtRootEntryTable; if (ExtRootEntryBase != 0) { ExtRootEntry = &ExtRootEntryBase[SourceId.Index.RootIndex]; ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(ExtRootEntry->Bits.LowerContextTablePointerLo, ExtRootEntry->Bits.LowerContextTablePointerHi) ; ThisExtContextEntry = &ExtContextEntryTable[SourceId.Index.ContextIndex]; if (ThisExtContextEntry->Bits.AddressWidth == 0) { DEBUG ((DEBUG_INFO, "ExtContextEntry AddressWidth : 0x%x\n", ThisExtContextEntry->Bits.AddressWidth)); return (UINTN)-1; } *ExtContextEntry = ThisExtContextEntry; *ContextEntry = NULL; } else { RootEntryBase = (VTD_ROOT_ENTRY*) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].RootEntryTable; RootEntry = &RootEntryBase[SourceId.Index.RootIndex]; ContextEntryTable = (VTD_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(RootEntry->Bits.ContextTablePointerLo, RootEntry->Bits.ContextTablePointerHi) ; ThisContextEntry = &ContextEntryTable[SourceId.Index.ContextIndex]; if (ThisContextEntry->Bits.AddressWidth == 0) { DEBUG ((DEBUG_INFO, "ContextEntry AddressWidth : 0x%x\n", ThisContextEntry->Bits.AddressWidth)); return (UINTN)-1; } *ExtContextEntry = NULL; *ContextEntry = ThisContextEntry; } return VtdIndex; } return (UINTN)-1; } /** Always enable the VTd page attribute for the device. @param[in] VTdInfo The VTd engine context information. @param[in] Segment The Segment used to identify a VTd engine. @param[in] SourceId The SourceId used to identify a VTd engine and table entry. @param[in] MemoryBase The base of the memory. @param[in] MemoryLimit The limit of the memory. @param[in] IoMmuAccess The IOMMU access. @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device. **/ EFI_STATUS EnableRmrrPageAttribute ( IN VTD_INFO *VTdInfo, IN UINT16 Segment, IN VTD_SOURCE_ID SourceId, IN UINT64 MemoryBase, IN UINT64 MemoryLimit, IN UINT64 IoMmuAccess ) { EFI_STATUS Status; UINTN VtdIndex; VTD_EXT_CONTEXT_ENTRY *ExtContextEntry; VTD_CONTEXT_ENTRY *ContextEntry; VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry; UINT64 Pt; DEBUG ((DEBUG_INFO, "EnableRmrrPageAttribute (S%04x B%02x D%02x F%02x)\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function)); VtdIndex = FindVtdIndexBySegmentSourceId (VTdInfo, Segment, SourceId, &ExtContextEntry, &ContextEntry); if (VtdIndex == (UINTN)-1) { DEBUG ((DEBUG_ERROR, "EnableRmrrPageAttribute - Can not locate Pci device (S%04x B%02x D%02x F%02x) !\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function)); return EFI_DEVICE_ERROR; } if (VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry == 0) { DEBUG ((DEBUG_INFO, "CreateSecondLevelPagingEntry - %d\n", VtdIndex)); VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry = (UINT32)(UINTN)CreateSecondLevelPagingEntryTable (&VTdInfo->VtdUnitInfo[VtdIndex], NULL, 0, SIZE_4GB, 0); if (VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry == 0) { return EFI_OUT_OF_RESOURCES; } Status =SetSecondLevelPagingAttribute (&VTdInfo->VtdUnitInfo[VtdIndex], (VTD_SECOND_LEVEL_PAGING_ENTRY*)(UINTN)VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry, MemoryBase, MemoryLimit + 1 - MemoryBase, IoMmuAccess); if (EFI_ERROR (Status)) { return Status; } } SecondLevelPagingEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry; Pt = (UINT64) RShiftU64 ((UINT64) (UINTN) SecondLevelPagingEntry, 12); if (ExtContextEntry != NULL) { ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt; ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20); ExtContextEntry->Bits.DomainIdentifier = ((1 << (UINT8) ((UINTN) VTdInfo->VtdUnitInfo[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1); ExtContextEntry->Bits.Present = 1; FlushPageTableMemory (&VTdInfo->VtdUnitInfo[VtdIndex], (UINTN) ExtContextEntry, sizeof(*ExtContextEntry)); } else if (ContextEntry != NULL) { ContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt; ContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64 (Pt, 20); ContextEntry->Bits.DomainIdentifier = ((1 << (UINT8) ((UINTN) VTdInfo->VtdUnitInfo[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1); ContextEntry->Bits.Present = 1; FlushPageTableMemory (&VTdInfo->VtdUnitInfo[VtdIndex], (UINTN) ContextEntry, sizeof (*ContextEntry)); } return EFI_SUCCESS; }