/** @file
DXE Library for VTD ACPI table initialization.
Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
///
/// Global Variables
///
GLOBAL_REMOVE_IF_UNREFERENCED VTD_DATA_HOB *mVtdDataHob;
BOOLEAN mInterruptRemappingSupport;
/**
Get the corresponding device Enable/Disable bit according DevNum and FunNum
@param[in] DevNum - Device Number
@param[in] FunNum - Function Number
@retval If the device is found, return disable/Enable bit in FD/Deven reigster
@retval If not found return 0xFF
**/
UINT16
GetFunDisableBit (
UINT8 DevNum,
UINT8 FunNum
)
{
UINTN Index;
for (Index = 0; Index < mDevEnMapSize; Index++) {
if (mDevEnMap[Index][0] == ((DevNum << 0x08) | FunNum)) {
return mDevEnMap[Index][1];
}
}
return 0xFF;
}
/**
Update the DRHD structure
@param[in, out] DrhdEnginePtr - A pointer to DRHD structure
**/
VOID
UpdateDrhd (
IN OUT VOID *DrhdEnginePtr
)
{
UINT16 Length;
UINT16 DisableBit;
BOOLEAN NeedRemove;
EFI_ACPI_DRHD_ENGINE1_STRUCT *DrhdEngine;
//
// Convert DrhdEnginePtr to EFI_ACPI_DRHD_ENGINE1_STRUCT Pointer
//
DrhdEngine = (EFI_ACPI_DRHD_ENGINE1_STRUCT *) DrhdEnginePtr;
Length = DrhdEngine->DrhdHeader.Header.Length;
DisableBit = GetFunDisableBit (
DrhdEngine->DeviceScope[0].PciPath.Device,
DrhdEngine->DeviceScope[0].PciPath.Function
);
NeedRemove = FALSE;
if ((DisableBit == 0xFF) ||
(DrhdEngine->DrhdHeader.RegisterBaseAddress == 0) ||
((DisableBit == 0x80) &&
(PciSegmentRead32 (PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, 0, DrhdEngine->DeviceScope[0].PciPath.Device, DrhdEngine->DeviceScope[0].PciPath.Function, 0x00)) == 0xFFFFFFFF))
) {
NeedRemove = TRUE;
}
if ((DrhdEngine->DeviceScope[0].PciPath.Device == IGD_DEV_NUM) && (DrhdEngine->DeviceScope[0].PciPath.Function == IGD_FUN_NUM) &&
(PciSegmentRead32 (PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, 0, DrhdEngine->DeviceScope[0].PciPath.Device, DrhdEngine->DeviceScope[0].PciPath.Function, 0x00)) != 0xFFFFFFFF)) {
NeedRemove = FALSE;
}
if (NeedRemove) {
Length -= sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
}
///
/// If no devicescope is left, we set the structure length as 0x00
///
if ((Length > EFI_ACPI_DRHD_ENGINE_HEADER_LENGTH) || (DrhdEngine->DrhdHeader.Flags == 0x01)) {
DrhdEngine->DrhdHeader.Header.Length = Length;
} else {
DrhdEngine->DrhdHeader.Header.Length = 0;
}
}
/**
Get IOAPIC ID from LPC
@retval APIC ID
**/
UINT8
GetIoApicId (
VOID
)
{
UINT32 IoApicAddress;
UINT32 IoApicId;
IoApicAddress = PcdGet32 (PcdSiIoApicBaseAddress);
///
/// Get current IO APIC ID
///
MmioWrite8 ((UINTN) (IoApicAddress + R_IO_APIC_MEM_INDEX_OFFSET), 0);
IoApicId = MmioRead32 ((UINTN) (IoApicAddress + R_IO_APIC_MEM_DATA_OFFSET)) >> 24;
return (UINT8) IoApicId;
}
/**
Update the second DRHD structure
@param[in, out] DrhdEnginePtr - A pointer to DRHD structure
**/
VOID
UpdateDrhd2 (
IN OUT VOID *DrhdEnginePtr
)
{
UINT16 Length;
UINTN DeviceScopeNum;
UINTN ValidDeviceScopeNum;
UINT16 Index;
UINT8 Bus;
UINT8 Path[2];
BOOLEAN NeedRemove;
EFI_ACPI_DRHD_ENGINE3_STRUCT *DrhdEngine;
VOID *HobPtr;
PCH_INFO_HOB *PchInfoHob;
///
/// Convert DrhdEnginePtr to EFI_ACPI_DRHD_ENGINE3_STRUCT Pointer
///
DrhdEngine = (EFI_ACPI_DRHD_ENGINE3_STRUCT *) DrhdEnginePtr;
Length = DrhdEngine->DrhdHeader.Header.Length;
DeviceScopeNum = (DrhdEngine->DrhdHeader.Header.Length - EFI_ACPI_DRHD_ENGINE_HEADER_LENGTH) / sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
Bus = 0;
ValidDeviceScopeNum = 0;
Path[0] = 0;
Path[1] = 0;
HobPtr = GetFirstGuidHob (&gPchInfoHobGuid);
ASSERT (HobPtr != NULL);
if (HobPtr == NULL) {
return;
}
PchInfoHob = (PCH_INFO_HOB *) GET_GUID_HOB_DATA (HobPtr);
ASSERT (PchInfoHob != NULL);
if (PchInfoHob == NULL) {
return;
}
for (Index = 0; Index < DeviceScopeNum; Index++) {
NeedRemove = FALSE;
/**
For HPET and APIC, update device scope if Interrupt remapping is supported. remove device scope
if interrupt remapping is not supported.
- Index = 0 - IOAPIC
- Index = 1 - HPET
**/
if (mInterruptRemappingSupport) {
if (Index == 0) {
///
/// Update source id for IoApic's device scope entry
///
Bus = (UINT8) PchInfoHob->IoApicBusNum;
Path[0] = (UINT8) PchInfoHob->IoApicDevNum;
Path[1] = (UINT8) PchInfoHob->IoApicFuncNum;
DrhdEngine->DeviceScope[Index].DeviceScopeStructureHeader.StartBusNumber = Bus;
DrhdEngine->DeviceScope[Index].PciPath.Device = Path[0];
DrhdEngine->DeviceScope[Index].PciPath.Function = Path[1];
//
// Update APIC ID
//
DrhdEngine->DeviceScope[Index].DeviceScopeStructureHeader.EnumerationId = GetIoApicId ();
}
if (Index == 1) {
///
/// Update source id for HPET's device scope entry
///
Bus = (UINT8) PchInfoHob->HpetBusNum;
Path[0] = (UINT8) PchInfoHob->HpetDevNum;
Path[1] = (UINT8) PchInfoHob->HpetFuncNum;
DrhdEngine->DeviceScope[Index].DeviceScopeStructureHeader.StartBusNumber = Bus;
DrhdEngine->DeviceScope[Index].PciPath.Device = Path[0];
DrhdEngine->DeviceScope[Index].PciPath.Function = Path[1];
}
} else {
if ((Index == 0) || (Index == 1)) {
NeedRemove = TRUE;
}
}
CopyMem (
&DrhdEngine->DeviceScope[ValidDeviceScopeNum],
&DrhdEngine->DeviceScope[Index],
sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE)
);
if (NeedRemove) {
Length -= sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
} else {
ValidDeviceScopeNum++;
}
}
///
/// If no devicescope is left, we set the structure length as 0x00
///
if ((Length > EFI_ACPI_DRHD_ENGINE_HEADER_LENGTH) || (DrhdEngine->DrhdHeader.Flags == 0x01)) {
DrhdEngine->DrhdHeader.Header.Length = Length;
} else {
DrhdEngine->DrhdHeader.Header.Length = 0;
}
}
/**
Update the RMRR structure
@param[in, out] RmrrPtr - A pointer to RMRR structure
**/
VOID
UpdateRmrr (
IN OUT VOID *RmrrPtr
)
{
UINT16 Length;
UINT16 DisableBit;
UINTN DeviceScopeNum;
UINTN ValidDeviceScopeNum;
UINTN Index;
BOOLEAN NeedRemove;
EFI_ACPI_RMRR_USB_STRUC *Rmrr;
///
/// To make sure all devicescope can be checked,
/// we convert the RmrrPtr to EFI_ACPI_RMRR_USB_STRUC pointer
///
Rmrr = (EFI_ACPI_RMRR_USB_STRUC *) RmrrPtr;
Length = Rmrr->RmrrHeader.Header.Length;
ValidDeviceScopeNum = 0;
DeviceScopeNum = (Rmrr->RmrrHeader.Header.Length - EFI_ACPI_RMRR_HEADER_LENGTH) / sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
for (Index = 0; Index < DeviceScopeNum; Index++) {
DisableBit = GetFunDisableBit (
Rmrr->DeviceScope[Index].PciPath.Device,
Rmrr->DeviceScope[Index].PciPath.Function
);
NeedRemove = FALSE;
if ((DisableBit == 0xFF) ||
((DisableBit == 0x80) &&
(PciSegmentRead32 (PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, 0, Rmrr->DeviceScope[Index].PciPath.Device, Rmrr->DeviceScope[Index].PciPath.Function, 0x00)) == 0xFFFFFFFF))
) {
NeedRemove = TRUE;
} else if (DisableBit == 0x8F) {
DEBUG ((DEBUG_ERROR, "Rmrr->RmrrHeader.ReservedMemoryRegionBaseAddress %x\n", Rmrr->RmrrHeader.ReservedMemoryRegionBaseAddress));
if (Rmrr->RmrrHeader.ReservedMemoryRegionBaseAddress != 0) {
DEBUG ((DEBUG_ERROR, "NeedRemove = FALSE\n"));
NeedRemove = FALSE;
} else {
DEBUG ((DEBUG_ERROR, "NeedRemove = TRUE\n"));
NeedRemove = TRUE;
}
}
CopyMem (
&Rmrr->DeviceScope[ValidDeviceScopeNum],
&Rmrr->DeviceScope[Index],
sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE)
);
if (Rmrr->RmrrHeader.ReservedMemoryRegionLimitAddress == 0x0) {
NeedRemove = TRUE;
}
if (NeedRemove) {
Length -= sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
} else {
ValidDeviceScopeNum++;
}
}
///
/// If No deviceScope is left, set length as 0x00
///
if (Length > EFI_ACPI_RMRR_HEADER_LENGTH) {
Rmrr->RmrrHeader.Header.Length = Length;
} else {
Rmrr->RmrrHeader.Header.Length = 0;
}
}
/**
Update the DMAR table
@param[in, out] TableHeader - The table to be set
@param[in, out] Version - Version to publish
**/
VOID
DmarTableUpdate (
IN OUT EFI_ACPI_DESCRIPTION_HEADER *TableHeader,
IN OUT EFI_ACPI_TABLE_VERSION *Version
)
{
EFI_ACPI_DMAR_TABLE *DmarTable;
EFI_ACPI_DMAR_TABLE TempDmarTable;
UINTN Offset;
UINTN StructureLen;
UINT64 McD0BaseAddress;
UINT16 IgdMode;
UINT16 GttMode;
UINT32 IgdMemSize;
UINT32 GttMemSize;
EFI_STATUS Status;
VTD_DXE_CONFIG *VtdDxeConfig;
SA_POLICY_PROTOCOL *SaPolicy;
IgdMemSize = 0;
GttMemSize = 0;
DmarTable = (EFI_ACPI_DMAR_TABLE *) TableHeader;
Status = gBS->LocateProtocol (&gSaPolicyProtocolGuid, NULL, (VOID **) &SaPolicy);
ASSERT_EFI_ERROR (Status);
Status = GetConfigBlock ((VOID *) SaPolicy, &gVtdDxeConfigGuid, (VOID *)&VtdDxeConfig);
ASSERT_EFI_ERROR (Status);
///
/// Set INTR_REMAP bit (BIT 0) if interrupt remapping is supported
///
if (mInterruptRemappingSupport) {
DmarTable->DmarHeader.Flags |= BIT0;
}
if (mVtdDataHob->X2ApicOptOut == 1) {
DmarTable->DmarHeader.Flags |= BIT1;
} else {
DmarTable->DmarHeader.Flags &= 0xFD;
}
///
/// Set DMA_CONTROL_GUARANTEE bit (BIT 2) if Dma Control Guarantee is supported
///
if (mVtdDataHob->DmaControlGuarantee == 1) {
DmarTable->DmarHeader.Flags |= BIT2;
}
///
/// Get OemId
///
CopyMem (DmarTable->DmarHeader.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (DmarTable->DmarHeader.Header.OemId));
DmarTable->DmarHeader.Header.OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);
DmarTable->DmarHeader.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);
DmarTable->DmarHeader.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);
DmarTable->DmarHeader.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);
///
/// Calculate IGD memsize
///
McD0BaseAddress = PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SA_MC_BUS, 0, 0, 0);
IgdMode = ((PciSegmentRead16 (McD0BaseAddress + R_SA_GGC) & B_SA_GGC_GMS_MASK) >> N_SA_GGC_GMS_OFFSET) & 0xFF;
if (IgdMode < 0xF0) {
IgdMemSize = IgdMode * 32 * (1024) * (1024);
} else {
IgdMemSize = 4 * (IgdMode - 0xF0 + 1) * (1024) * (1024);
}
///
/// Calculate GTT mem size
///
GttMemSize = 0;
GttMode = (PciSegmentRead16 (McD0BaseAddress + R_SA_GGC) & B_SA_GGC_GGMS_MASK) >> N_SA_GGC_GGMS_OFFSET;
if (GttMode <= V_SA_GGC_GGMS_8MB) {
GttMemSize = (1 << GttMode) * (1024) * (1024);
}
DmarTable->RmrrIgd.RmrrHeader.ReservedMemoryRegionBaseAddress = (PciSegmentRead32 (McD0BaseAddress + R_SA_BGSM) & ~(0x01));
DmarTable->RmrrIgd.RmrrHeader.ReservedMemoryRegionLimitAddress = DmarTable->RmrrIgd.RmrrHeader.ReservedMemoryRegionBaseAddress + IgdMemSize + GttMemSize - 1;
DEBUG ((DEBUG_INFO, "RMRR Base address IGD %016lX\n", DmarTable->RmrrIgd.RmrrHeader.ReservedMemoryRegionBaseAddress));
DEBUG ((DEBUG_INFO, "RMRR Limit address IGD %016lX\n", DmarTable->RmrrIgd.RmrrHeader.ReservedMemoryRegionLimitAddress));
///
/// Update DRHD structures of DmarTable
///
DmarTable->DrhdEngine1.DrhdHeader.RegisterBaseAddress = ReadVtdBaseAddress(0);
DmarTable->DrhdEngine3.DrhdHeader.RegisterBaseAddress = ReadVtdBaseAddress (2);
DEBUG ((DEBUG_INFO, "VTD base address 1 = %x\n", DmarTable->DrhdEngine1.DrhdHeader.RegisterBaseAddress));
DEBUG ((DEBUG_INFO, "VTD base address 3 = %x\n", DmarTable->DrhdEngine3.DrhdHeader.RegisterBaseAddress));
///
/// copy DmarTable to TempDmarTable to be processed
///
CopyMem (&TempDmarTable, DmarTable, sizeof (EFI_ACPI_DMAR_TABLE));
///
/// Update DRHD structures of temp DMAR table
///
UpdateDrhd (&TempDmarTable.DrhdEngine1);
UpdateDrhd2 (&TempDmarTable.DrhdEngine3);
///
/// Remove unused device scope or entire DRHD structures
///
Offset = (UINTN) (&TempDmarTable.DrhdEngine1);
if (TempDmarTable.DrhdEngine1.DrhdHeader.Header.Length != 0) {
Offset += TempDmarTable.DrhdEngine1.DrhdHeader.Header.Length;
}
if (TempDmarTable.DrhdEngine3.DrhdHeader.Header.Length != 0) {
StructureLen = TempDmarTable.DrhdEngine3.DrhdHeader.Header.Length;
CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.DrhdEngine3, TempDmarTable.DrhdEngine3.DrhdHeader.Header.Length);
Offset += StructureLen;
}
///
/// Remove unused device scope or entire RMRR structures
///
if (TempDmarTable.RmrrIgd.RmrrHeader.Header.Length != 0) {
StructureLen = TempDmarTable.RmrrIgd.RmrrHeader.Header.Length;
CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.RmrrIgd, TempDmarTable.RmrrIgd.RmrrHeader.Header.Length);
Offset += StructureLen;
}
Offset = Offset - (UINTN) &TempDmarTable;
///
/// Re-calculate DMAR table check sum
///
TempDmarTable.DmarHeader.Header.Checksum = (UINT8) (TempDmarTable.DmarHeader.Header.Checksum + TempDmarTable.DmarHeader.Header.Length - Offset);
///
/// Set DMAR table length
///
TempDmarTable.DmarHeader.Header.Length = (UINT32) Offset;
///
/// Replace DMAR table with rebuilt table TempDmarTable
///
CopyMem ((VOID *) DmarTable, (VOID *) &TempDmarTable, TempDmarTable.DmarHeader.Header.Length);
}
/**
EndOfPcieEnumration routine for update DMAR
**/
VOID
UpdateDmarEndOfPcieEnum (
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN NumberOfHandles;
EFI_FV_FILETYPE FileType;
UINT32 FvStatus;
EFI_FV_FILE_ATTRIBUTES Attributes;
UINTN Size;
UINTN i;
INTN Instance;
EFI_ACPI_TABLE_VERSION Version;
EFI_ACPI_COMMON_HEADER *CurrentTable;
UINTN AcpiTableHandle;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
EFI_ACPI_TABLE_PROTOCOL *AcpiTable;
EFI_ACPI_DESCRIPTION_HEADER *VtdAcpiTable;
STATIC BOOLEAN Triggered = FALSE;
if (Triggered) {
return;
}
Triggered = TRUE;
FwVol = NULL;
AcpiTable = NULL;
VtdAcpiTable = NULL;
DEBUG ((DEBUG_INFO, "UpdateDmarEndOfPcieEnum \n"));
///
/// Fix DMAR Table always created, skip install when disabled
///
if ((mVtdDataHob->VtdDisable == TRUE) || (PciSegmentRead32 (PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SA_MC_BUS, 0, 0, R_SA_MC_CAPID0_A_OFFSET)) & BIT23)) {
DEBUG ((DEBUG_INFO, "Vtd Disabled, skip DMAR Table install\n"));
return;
}
///
/// Locate ACPI support protocol
///
Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable);
///
/// Locate protocol.
/// There is little chance we can't find an FV protocol
///
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&NumberOfHandles,
&HandleBuffer
);
ASSERT_EFI_ERROR (Status);
///
/// Looking for FV with ACPI storage file
///
for (i = 0; i < NumberOfHandles; i++) {
///
/// Get the protocol on this handle
/// This should not fail because of LocateHandleBuffer
///
Status = gBS->HandleProtocol (
HandleBuffer[i],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **) &FwVol
);
ASSERT_EFI_ERROR (Status);
///
/// See if it has the ACPI storage file
///
Size = 0;
FvStatus = 0;
Status = FwVol->ReadFile (
FwVol,
&gSaAcpiTableStorageGuid,
NULL,
&Size,
&FileType,
&Attributes,
&FvStatus
);
///
/// If we found it, then we are done
///
if (Status == EFI_SUCCESS) {
break;
}
}
///
/// Our exit status is determined by the success of the previous operations
/// If the protocol was found, Instance already points to it.
///
///
/// Free any allocated buffers
///
FreePool (HandleBuffer);
///
/// Sanity check that we found our data file
///
ASSERT (FwVol);
if (FwVol == NULL) {
return;
}
///
/// By default, a table belongs in all ACPI table versions published.
///
Version = EFI_ACPI_TABLE_VERSION_1_0B | EFI_ACPI_TABLE_VERSION_2_0 | EFI_ACPI_TABLE_VERSION_3_0;
///
/// Read tables from the storage file.
///
Instance = 0;
CurrentTable = NULL;
while (Status == EFI_SUCCESS) {
Status = FwVol->ReadSection (
FwVol,
&gSaAcpiTableStorageGuid,
EFI_SECTION_RAW,
Instance,
(VOID **) &CurrentTable,
&Size,
&FvStatus
);
if (!EFI_ERROR (Status)) {
///
/// Check the Signature ID to modify the table
///
if ((CurrentTable != NULL) && ((EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable)->Signature == EFI_ACPI_VTD_DMAR_TABLE_SIGNATURE) {
VtdAcpiTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
DmarTableUpdate (VtdAcpiTable, &Version);
break;
}
///
/// Increment the instance
///
Instance++;
if (CurrentTable != NULL) {
gBS->FreePool (CurrentTable);
CurrentTable = NULL;
}
}
}
///
/// Update the VTD table in the ACPI tables.
///
AcpiTableHandle = 0;
if (VtdAcpiTable != NULL) {
Status = AcpiTable->InstallAcpiTable (
AcpiTable,
VtdAcpiTable,
VtdAcpiTable->Length,
&AcpiTableHandle
);
FreePool (VtdAcpiTable);
}
}
/**
Locate the VT-d ACPI tables data file and read ACPI SSDT tables.
Publish the appropriate SSDT based on current configuration and capabilities.
@param[in] SaPolicy - SA DXE Policy protocol
@retval EFI_SUCCESS - Vtd initialization complete
@exception EFI_UNSUPPORTED - Vtd is not enabled by policy
**/
EFI_STATUS
VtdInit (
IN SA_POLICY_PROTOCOL *SaPolicy
)
{
EFI_STATUS Status;
UINT64 McD0BaseAddress;
UINT64 McD2BaseAddress;
SYSTEM_AGENT_NVS_AREA_PROTOCOL *SaNvsAreaProtocol;
UINT8 Index;
mInterruptRemappingSupport = FALSE;
mVtdDataHob = NULL;
mVtdDataHob = GetFirstGuidHob(&gVtdDataHobGuid);
if (mVtdDataHob != NULL) {
mInterruptRemappingSupport = mVtdDataHob->InterruptRemappingSupport;
}
///
/// Locate the SA Global NVS Protocol.
///
Status = gBS->LocateProtocol (
&gSaNvsAreaProtocolGuid,
NULL,
(VOID **) &SaNvsAreaProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
McD0BaseAddress = PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SA_MC_BUS, 0, 0, 0);
McD2BaseAddress = PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, IGD_BUS_NUM, IGD_DEV_NUM, IGD_FUN_NUM, 0);
if (mVtdDataHob != NULL) {
SaNvsAreaProtocol->Area->VtdDisable = mVtdDataHob->VtdDisable;
}
for (Index = 0; Index < VTD_ENGINE_NUMBER; Index++) {
SaNvsAreaProtocol->Area->VtdBaseAddress[Index] = ReadVtdBaseAddress (Index);
}
SaNvsAreaProtocol->Area->VtdEngine1Vid = PciSegmentRead16(McD2BaseAddress + PCI_VENDOR_ID_OFFSET);
if (mVtdDataHob != NULL) {
if ((mVtdDataHob->VtdDisable) || (PciSegmentRead32 (McD0BaseAddress + R_SA_MC_CAPID0_A_OFFSET) & BIT23)) {
DEBUG ((DEBUG_WARN, "VTd disabled or no capability!\n"));
return EFI_UNSUPPORTED;
}
}
///
/// Check SA supports VTD and VTD is enabled in setup menu
///
DEBUG ((DEBUG_INFO, "VTd enabled\n"));
return EFI_SUCCESS;
}