summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c934
1 files changed, 934 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c b/ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c
new file mode 100644
index 0000000..1dea621
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c
@@ -0,0 +1,934 @@
+/** @file
+ This code provides a initialization of intel VT-d (Virtualization Technology for Directed I/O).
+
+@copyright
+ Copyright (c) 1999 - 2013 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+@todo assumption is ANDD table will use device path to be loaded dynamically, need to use pchplatformpolicy if assumption is incorrect.
+**/
+#include "SaInit.h"
+#include "VTd.h"
+
+UINT32 mPchRootComplexBar;
+DXE_PLATFORM_SA_POLICY_PROTOCOL *mDxePlatformSaPolicy;
+DXE_PCH_PLATFORM_POLICY_PROTOCOL *mDxePlatformPchPolicy;
+PCH_SERIES mPchSeries;
+
+/**
+For device that specified by Device Num and Function Num,
+mDevEnMap is used to check device presence.
+ 0x80 means use Device ID to detemine presence
+
+ The structure is used to check if device scope is valid when update DMAR table
+**/
+UINT16 mDevEnMap[][2] = {
+ {
+ 0x0200,
+ 0x80
+ }, ///< D2F0
+ {
+ 0x1D00,
+ 0x80
+ }, ///< D29F0
+ {
+ 0x1A00,
+ 0x80
+ }, ///< D26F0
+ {
+ 0x1400,
+ 0x80
+ } ///< D20F0
+};
+
+BOOLEAN mInterruptRemappingSupport;
+
+UINT16
+GetFunDisableBit (
+ UINT8 DevNum,
+ UINT8 FunNum
+ )
+/**
+ 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
+**/
+{
+ UINTN Index;
+
+ for (Index = 0; Index < sizeof (mDevEnMap) / 4; Index++) {
+ if (mDevEnMap[Index][0] == ((DevNum << 0x08) | FunNum)) {
+ return mDevEnMap[Index][1];
+ }
+ }
+
+ return 0xFF;
+}
+
+VOID
+UpdateDRHD (
+ IN OUT VOID *DrhdEnginePtr
+ )
+/**
+ Update the DRHD structure
+
+ @param[in, out] DrhdEnginePtr - A pointer to DRHD structure
+**/
+{
+ UINT16 Length;
+ UINT16 DisableBit;
+ UINTN DeviceScopeNum;
+ 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->Length;
+ DeviceScopeNum = (DrhdEngine->Length - EFI_ACPI_DRHD_ENGINE_HEADER_LENGTH) / sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
+ DisableBit = GetFunDisableBit (
+ DrhdEngine->DeviceScope[0].PciPath[0],
+ DrhdEngine->DeviceScope[0].PciPath[1]
+ );
+ NeedRemove = FALSE;
+ if ((DisableBit == 0xFF) ||
+ (DrhdEngine->RegisterBaseAddress == 0) ||
+ ((DisableBit == 0x80) &&
+ (MmPci32 (0, 0, DrhdEngine->DeviceScope[0].PciPath[0], DrhdEngine->DeviceScope[0].PciPath[1], 0x00) == 0xFFFFFFFF))
+ ){
+ NeedRemove = TRUE;
+ }
+ 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->Flags == 0x01)) {
+ DrhdEngine->Length = Length;
+ } else {
+ DrhdEngine->Length = 0;
+ }
+}
+
+UINT8
+GetIoApicID (
+ VOID
+ )
+/**
+ Get IOAPIC ID from LPC
+
+ @retval APIC ID
+**/
+{
+ UINT8 VOLATILE *IoapicIndex;
+ UINT32 VOLATILE *IoapicData;
+ UINT32 Data32;
+
+ ///
+ /// Get IOAPIC base
+ ///
+ IoapicIndex = (UINT8 *) (UINTN) (R_PCH_IO_APIC_INDEX + ((PchMmRcrb16 (R_PCH_RCRB_OIC) & 0x0ff) << 12));
+ IoapicData = (UINT32 *) (UINTN) (R_PCH_IO_APIC_DATA + ((PchMmRcrb16 (R_PCH_RCRB_OIC) & 0x0ff) << 12));
+
+ ///
+ /// Get APIC ID from Identification Register (Index 0)
+ ///
+ *IoapicIndex = 0;
+ Data32 = (*IoapicData & 0x0F000000) >> 24;
+
+ return (UINT8) Data32;
+}
+
+VOID
+UpdateDRHD2 (
+ IN OUT VOID *DrhdEnginePtr
+ )
+/**
+ Update the second DRHD structure
+
+ @param[in, out] DrhdEnginePtr - A pointer to DRHD structure
+**/
+{
+ UINT16 Length;
+ UINTN DeviceScopeNum;
+ UINTN ValidDeviceScopeNum;
+ UINT16 Data16;
+ UINT16 HpetReg;
+ UINT16 Index;
+ UINT8 Bus;
+ UINT8 Path[2] = { 0, 0 };
+ BOOLEAN NeedRemove;
+ EFI_ACPI_DRHD_ENGINE2_STRUCT *DrhdEngine;
+
+ ///
+ /// Convert DrhdEnginePtr to EFI_ACPI_DRHD_ENGINE2_STRUCT Pointer
+ ///
+ DrhdEngine = (EFI_ACPI_DRHD_ENGINE2_STRUCT *) DrhdEnginePtr;
+
+ Length = DrhdEngine->Length;
+ DeviceScopeNum = (DrhdEngine->Length - EFI_ACPI_DRHD_ENGINE_HEADER_LENGTH) / sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
+ Data16 = 0;
+ Bus = 0;
+ HpetReg = R_PCH_LPC_HPET0;
+ ValidDeviceScopeNum = 0;
+
+ 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
+ For Serial IO devices, they do not appear in PCI space, use platform policy to determine existence, also remove if PCH not LP
+ - Index = 2 - I2C0
+ - Index = 3 - I2C1
+ - Index = 4 - SPI0
+ - Index = 5 - SPI1
+ - Index = 6 - UART0
+ - Index = 7 - UART1
+ - Index = 8 - SDIO
+ **/
+ if (mInterruptRemappingSupport) {
+ if (Index == 0) {
+ ///
+ /// Update source id for IoApic's device scope entry
+ ///
+ Data16 = PchLpcPciCfg16 (R_PCH_LPC_IOXAPIC);
+ Bus = (UINT8) (Data16 >> 8);
+ if (Bus != 0x00) {
+ Path[0] = (UINT8) ((Data16 & 0xff) >> 3);
+ Path[1] = (UINT8) (Data16 & 0x7);
+ } else {
+ //
+ // BUGBUG: Here we just hardcode, because in this version, R_PCH_LPC_IOXAPIC is initialized AFTER Vtd run. We can NOT get proper setting from PCH
+ // We can NOT get proper setting from PCH
+ /// @todo check if code still needed
+ //
+ DEBUG ((EFI_D_WARN, "BUGBUG: UpdateApicHpet use hardcode value - To be fixed!\n"));
+ Bus = 0xF0;
+ Path[0] = 0x1F;
+ Path[1] = 0x0;
+ }
+ DrhdEngine->DeviceScope[Index].StartBusNumber = Bus;
+ //
+ // Update APIC ID
+ //
+ DrhdEngine->DeviceScope[Index].EnumId = GetIoApicID ();
+ }
+ if (Index == 1) {
+ ///
+ /// Update source id for HPET's device scope entry
+ ///
+ Data16 = PchLpcPciCfg16 (HpetReg);
+ Bus = (UINT8) (Data16 >> 8);
+ Path[0] = (UINT8) ((Data16 & 0xFF) >> 3);
+ Path[1] = (UINT8) (Data16 & 0x7);
+ DrhdEngine->DeviceScope[Index].StartBusNumber = Bus;
+ }
+ } else {
+ if ((Index == 0) || (Index == 1)) {
+ NeedRemove = TRUE;
+ }
+ }
+ /*
+ Pch removed device from PCI space and it is visible by ACPI only, we use platform policy to check
+ if device is present. If Pch is 2 chip, remove all serialio devices.
+ */
+#ifdef SERIAL_IO_FLAG
+ if (mPchSeries == PchLp){
+ if (Index == 2){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoI2c0 == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ if (Index == 3){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoI2c1 == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ if (Index == 4){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoSpi0 == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ if (Index == 5){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoSpi1 == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ if (Index == 6){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoUart0 == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ if (Index == 7){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoUart1 == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ if (Index == 8){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoSdio == 0){
+ NeedRemove = TRUE;
+ }
+ }
+ } else {
+#endif
+ if ((Index >= 2) && (Index <= 8)){
+ NeedRemove = TRUE;
+ }
+#ifdef SERIAL_IO_FLAG
+ }
+#endif
+ 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->Flags == 0x01)) {
+ DrhdEngine->Length = Length;
+ } else {
+ DrhdEngine->Length = 0;
+ }
+}
+
+VOID
+UpdateRMRR (
+ IN OUT VOID *RmrrPtr
+ )
+/**
+ Update the RMRR structure
+
+ @param[in, out] RmrrPtr - A pointer to RMRR structure
+**/
+{
+ 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->Length;
+ ValidDeviceScopeNum = 0;
+ DeviceScopeNum = (Rmrr->Length - EFI_ACPI_RMRR_HEADER_LENGTH) / sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE);
+ for (Index = 0; Index < DeviceScopeNum; Index++) {
+ ///
+ /// here we assume Device will exist on PCH if Device Number is greater than 0x0F
+ ///
+ DisableBit = GetFunDisableBit (
+ Rmrr->DeviceScope[Index].PciPath[0],
+ Rmrr->DeviceScope[Index].PciPath[1]
+ );
+ NeedRemove = FALSE;
+ if ((DisableBit == 0xFF) ||
+ ((DisableBit == 0x80) &&
+ (MmPci32 (0, 0, Rmrr->DeviceScope[Index].PciPath[0], Rmrr->DeviceScope[Index].PciPath[1], 0x00) == 0xFFFFFFFF))
+ ){
+ NeedRemove = TRUE;
+ }
+ CopyMem (
+ &Rmrr->DeviceScope[ValidDeviceScopeNum],
+ &Rmrr->DeviceScope[Index],
+ sizeof (EFI_ACPI_DEV_SCOPE_STRUCTURE)
+ );
+ 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->Length = Length;
+ } else {
+ Rmrr->Length = 0;
+ }
+}
+
+VOID
+DmarTableUpdate (
+ IN OUT EFI_ACPI_DESCRIPTION_HEADER *TableHeader,
+ IN OUT EFI_ACPI_TABLE_VERSION *Version
+ )
+/**
+ Update the DMAR table
+
+ @param[in, out] TableHeader - The table to be set
+ @param[in, out] Version - Version to publish
+**/
+{
+ EFI_ACPI_DMAR_TABLE *DmarTable;
+ EFI_ACPI_DMAR_TABLE TempDmarTable;
+ UINTN Offset;
+ UINTN StructureLen;
+ UINT16 IgdMode;
+ UINT16 GttMode;
+ UINT32 IgdMemSize;
+ UINT32 GttMemSize;
+
+ IgdMemSize = 0;
+ GttMemSize = 0;
+ DmarTable = (EFI_ACPI_DMAR_TABLE *) TableHeader;
+
+ if (mInterruptRemappingSupport) {
+ DmarTable->Flags |= BIT0; ///< Set INTR_REMAP bit (BIT 0) if interrupt remapping is supported
+ }
+ ///
+ /// Find IGD memsize
+ ///
+ IgdMode = (McD0PciCfg16 (R_SA_GGC) & B_SA_GGC_GMS_MASK) >> 3;
+ if (IgdMode <= V_SA_GGC_GMS_512MB) {
+ IgdMemSize = IgdMode * 32 * (1024) * (1024);
+ } else {
+ IgdMemSize = 0;
+ }
+ ///
+ /// Find GTT mem size
+ ///
+ GttMode = (McD0PciCfg16 (R_SA_GGC) & B_SA_GGC_GGMS_MASK) >> 8;
+ if (GttMode <= V_SA_GGC_GGMS_2MB) {
+ GttMemSize = GttMode * (1024) * (1024);
+ } else {
+ GttMemSize = 0;
+ }
+
+ DmarTable->RmrrIgd.RmrBaseAddress = (McD0PciCfg32 (R_SA_TOLUD) &~(0x01)) - IgdMemSize - GttMemSize;
+ DmarTable->RmrrIgd.RmrLimitAddress = DmarTable->RmrrIgd.RmrBaseAddress + IgdMemSize + GttMemSize - 1;
+ DEBUG ((EFI_D_INFO, "RMRR Base address IGD %016lX\n", DmarTable->RmrrIgd.RmrBaseAddress));
+ DEBUG ((EFI_D_INFO, "RMRR Limit address IGD %016lX\n", DmarTable->RmrrIgd.RmrLimitAddress));
+
+ DmarTable->RmrrUsb.RmrBaseAddress = mDxePlatformSaPolicy->Vtd->RmrrUsbBaseAddress[0];
+ DmarTable->RmrrUsb.RmrLimitAddress = mDxePlatformSaPolicy->Vtd->RmrrUsbBaseAddress[1];
+
+ ///
+ /// Convert to 4KB alignment.
+ ///
+ DmarTable->RmrrUsb.RmrBaseAddress &= ~0xFFF;
+ DmarTable->RmrrUsb.RmrLimitAddress &= ~0xFFF;
+ DmarTable->RmrrUsb.RmrLimitAddress += 0x1000 - 1;
+
+ DEBUG ((EFI_D_INFO, "RMRR Base address USB %016lX\n", DmarTable->RmrrUsb.RmrBaseAddress));
+ DEBUG ((EFI_D_INFO, "RMRR Limit address USB %016lX\n", DmarTable->RmrrUsb.RmrLimitAddress));
+
+ ///
+ /// @todo check if this check is still needed.
+ ///
+ if (DmarTable->RmrrUsb.RmrBaseAddress == 0) {
+ DEBUG ((EFI_D_WARN, "BUGBUG: RmrrUsb.RmrBaseAddress is 0 - To be fixed\n"));
+ }
+ ///
+ /// Update DRHD structures of DmarTable
+ ///
+ DmarTable->DrhdEngine1.RegisterBaseAddress = (McMmio32 (R_SA_MCHBAR_VTD1_OFFSET) &~1);
+ DmarTable->DrhdEngine2.RegisterBaseAddress = (McMmio32 (R_SA_MCHBAR_VTD2_OFFSET) &~1);
+
+ DEBUG ((EFI_D_INFO, "VTD base address1 %x\n", DmarTable->DrhdEngine1.RegisterBaseAddress));
+ DEBUG ((EFI_D_INFO, "VTD base address2 %x\n", DmarTable->DrhdEngine2.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.DrhdEngine2);
+
+ ///
+ /// Update RMRR structures of temp DMAR table
+ ///
+ UpdateRMRR ((VOID *) &TempDmarTable.RmrrUsb);
+ UpdateRMRR ((VOID *) &TempDmarTable.RmrrIgd);
+
+ ///
+ /// Remove unused device scope or entire DRHD structures
+ ///
+ Offset = (UINTN) (&TempDmarTable.DrhdEngine1);
+ if (TempDmarTable.DrhdEngine1.Length != 0) {
+ Offset += TempDmarTable.DrhdEngine1.Length;
+ }
+ if (TempDmarTable.DrhdEngine2.Length != 0) {
+ StructureLen = TempDmarTable.DrhdEngine2.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.DrhdEngine2, TempDmarTable.DrhdEngine2.Length);
+ Offset += StructureLen;
+ }
+ ///
+ /// Remove unused device scope or entire RMRR structures
+ ///
+ if (TempDmarTable.RmrrUsb.Length != 0) {
+ StructureLen = TempDmarTable.RmrrUsb.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.RmrrUsb, TempDmarTable.RmrrUsb.Length);
+ Offset += StructureLen;
+ }
+ if (TempDmarTable.RmrrIgd.Length != 0) {
+ StructureLen = TempDmarTable.RmrrIgd.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.RmrrIgd, TempDmarTable.RmrrIgd.Length);
+ Offset += StructureLen;
+ }
+#ifdef SERIAL_IO_FLAG
+ ///
+ /// Include necessary ANDD structures. If not PchLp, remove all ANDD.
+ ///
+ if (mPchSeries == PchLp){
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoI2c0 != 0) {
+ StructureLen = TempDmarTable.AnddI2C0.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddI2C0, TempDmarTable.AnddI2C0.Length);
+ Offset += StructureLen;
+ }
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoI2c1 != 0) {
+ StructureLen = TempDmarTable.AnddI2C1.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddI2C1, TempDmarTable.AnddI2C1.Length);
+ Offset += StructureLen;
+ }
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoSpi0 != 0) {
+ StructureLen = TempDmarTable.AnddSpi0.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddSpi0, TempDmarTable.AnddSpi0.Length);
+ Offset += StructureLen;
+ }
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoSpi1 != 0) {
+ StructureLen = TempDmarTable.AnddSpi1.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddSpi1, TempDmarTable.AnddSpi1.Length);
+ Offset += StructureLen;
+ }
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoUart0 != 0) {
+ StructureLen = TempDmarTable.AnddUa00.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddUa00, TempDmarTable.AnddUa00.Length);
+ Offset += StructureLen;
+ }
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoUart1 != 0) {
+ StructureLen = TempDmarTable.AnddUa01.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddUa01, TempDmarTable.AnddUa01.Length);
+ Offset += StructureLen;
+ }
+ if (mDxePlatformPchPolicy->DeviceEnabling->SerialIoSdio != 0) {
+ StructureLen = TempDmarTable.AnddSdhc.Length;
+ CopyMem ((VOID *) Offset, (VOID *) &TempDmarTable.AnddSdhc, TempDmarTable.AnddSdhc.Length);
+ Offset += StructureLen;
+ }
+ }
+#endif
+ Offset = Offset - (UINTN) &TempDmarTable;
+ TempDmarTable.Header.Checksum = (UINT8) (TempDmarTable.Header.Checksum + TempDmarTable.Header.Length - Offset);
+ TempDmarTable.Header.Length = (UINT32) Offset;
+ ///
+ /// Replace DMAR table with rebuilt table TempDmarTable
+ ///
+ CopyMem ((VOID *) DmarTable, (VOID *) &TempDmarTable, TempDmarTable.Header.Length);
+}
+
+EFI_STATUS
+WaForVc0RemappingEngine (
+ UINT64 MchBar
+ )
+/**
+ Workaround for VC0 remapping engine
+
+ @param[in] MchBar - MCHBAR address
+
+ @retval EFI_SUCCESS - successed.
+**/
+{
+ UINT16 DeviceId;
+ UINT32 Vc0RemapEngineBase;
+ UINT32 Data32Or;
+ UINT32 Data32And;
+
+ DeviceId = PchLpcPciCfg16(R_PCH_LPC_DEVICE_ID);
+ Vc0RemapEngineBase = Mmio32(MchBar, R_SA_MCHBAR_VTD2_OFFSET) & 0xFFFFFFFE;
+
+ ///
+ /// Disable VTD SuperPage policy when iGfx is enabled
+ ///
+ if (McD2PciCfg16(R_SA_IGD_VID) != 0xFFFF) {
+ Data32And = (UINT32)~(BIT25);
+ Data32Or = 0;
+ Mmio32And(Vc0RemapEngineBase, 0xFF0, Data32And);
+ SCRIPT_MEM_READ_WRITE
+ (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (Vc0RemapEngineBase + 0xFF0),
+ &Data32Or, ///< Data to be ORed
+ &Data32And ///< Data to be ANDed
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+VOID
+UpdateDmarExitPmAuth (
+ VOID
+ )
+/**
+ ExitPmAuth routine for update DMAR
+**/
+{
+ 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_VOLUME_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;
+
+ ///
+ /// Locate PCH platform policy protocol and PCH series to support feature enabling/disabling
+ ///
+ Status = gBS->LocateProtocol (
+ &gDxePchPlatformPolicyProtocolGuid,
+ NULL,
+ (VOID**) &mDxePlatformPchPolicy
+ );
+ ASSERT_EFI_ERROR (Status);
+ mPchSeries = GetPchSeries();
+
+ if ((!mDxePlatformSaPolicy->Vtd->VtdEnable) || (McD0PciCfg32 (R_SA_MC_CAPID0_A_OFFSET) & BIT23)) {
+ DEBUG ((EFI_D_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,
+ &gEfiFirmwareVolumeProtocolGuid,
+ 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],
+ &gEfiFirmwareVolumeProtocolGuid,
+ (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
+ ///
+ switch (((EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable)->Signature) {
+
+ case EFI_ACPI_VTD_DMAR_TABLE_SIGNATURE:
+ VtdAcpiTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ DmarTableUpdate (VtdAcpiTable, &Version);
+ break;
+
+ default:
+ break;
+ }
+ ///
+ /// Increment the instance
+ ///
+ Instance++;
+ CurrentTable = NULL;
+ }
+ }
+ ///
+ /// Update the VTD table in the ACPI tables.
+ ///
+ AcpiTableHandle = 0;
+ if (VtdAcpiTable != NULL) {
+ Status = AcpiTable->InstallAcpiTable (
+ AcpiTable,
+ VtdAcpiTable,
+ VtdAcpiTable->Length,
+ &AcpiTableHandle
+ );
+ }
+}
+
+EFI_STATUS
+VtdInit (
+ IN DXE_PLATFORM_SA_POLICY_PROTOCOL *DxePlatformSaPolicy
+ )
+/**
+@brief
+ 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] DxePlatformSaPolicy - SA DxePlatformPolicy protocol
+
+ @retval EFI_SUCCESS - Vtd initialization complete
+ @exception EFI_UNSUPPORTED - Vtd is not enabled by policy
+**/
+{
+ EFI_STATUS Status;
+ UINTN i;
+ UINT64 MchBar;
+ UINT32 Data32Or;
+ UINT32 Data32And;
+ UINT32 VtdBase;
+ UINT32 VtBarReg [SA_VTD_ENGINE_NUMBER];
+ CPU_FAMILY CpuFamilyId;
+ CPU_STEPPING CpuStepping;
+
+ mInterruptRemappingSupport = FALSE;
+ mPchRootComplexBar = MmPci32 (0, 0, 31, 0, 0xF0) &~BIT0;
+ MchBar = McD0PciCfg64 (R_SA_MCHBAR) &~BIT0;
+
+ VtBarReg[0] = R_SA_MCHBAR_VTD1_OFFSET;
+ VtBarReg[1] = R_SA_MCHBAR_VTD2_OFFSET;
+ mDxePlatformSaPolicy = DxePlatformSaPolicy;
+
+ ///
+ /// Check SA supports VTD and VTD is enabled in setup menu
+ ///
+ if ((!mDxePlatformSaPolicy->Vtd->VtdEnable) || (McD0PciCfg32 (R_SA_MC_CAPID0_A_OFFSET) & BIT23)) {
+ DEBUG ((EFI_D_WARN, "VTd disabled or no capability!\n"));
+ return EFI_UNSUPPORTED;
+ }
+ DEBUG ((EFI_D_INFO, "VTd enabled\n"));
+
+ ///
+ /// 14.1 Program Remap Engine Base Address
+ /// Configure VTD1 BAR
+ ///
+ i = 0;
+
+ ///
+ /// Skip GFXVTBAR if IGD is disabled
+ ///
+ if (McD2PciCfg16 (R_SA_IGD_VID) != 0xFFFF) {
+ Data32Or = mDxePlatformSaPolicy->Vtd->BaseAddress[i];
+ Data32Or |= 0x1;
+ Mmio32 (MchBar, R_SA_MCHBAR_VTD1_OFFSET) = Data32Or;
+ SCRIPT_MEM_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (MchBar + R_SA_MCHBAR_VTD1_OFFSET),
+ 1,
+ &Data32Or
+ );
+ i++;
+ }
+
+ ///
+ /// Configure VTD2 BAR
+ ///
+ Data32Or = mDxePlatformSaPolicy->Vtd->BaseAddress[i];
+ Data32Or |= 0x1;
+ Mmio32 (MchBar, R_SA_MCHBAR_VTD2_OFFSET) = Data32Or;
+ SCRIPT_MEM_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (MchBar + R_SA_MCHBAR_VTD2_OFFSET),
+ 1,
+ &Data32Or
+ );
+
+ ///
+ /// Workaround for VC0 remapping engine
+ ///
+ Status = WaForVc0RemappingEngine (MchBar);
+ ASSERT_EFI_ERROR (Status);
+
+ for (i = 0; i < SA_VTD_ENGINE_NUMBER; i++) {
+ VtdBase = Mmio32 (MchBar, VtBarReg[i]) & 0xfffffffe;
+
+ ///
+ /// skip if the VT bar is 0
+ ///
+ if (VtdBase == 0) {
+ continue;
+ }
+
+ CpuFamilyId = GetCpuFamily();
+ CpuStepping = GetCpuStepping();
+
+ if ((CpuFamilyId == EnumCpuHsw) || (CpuFamilyId == EnumCpuHswUlt) || (CpuFamilyId == EnumCpuCrw)
+ ) {
+ Data32And = (UINT32) ~(BIT15+BIT16+BIT17+BIT18+BIT19); ///< mask out 19:15
+ Data32Or = BIT15;
+ if (i == 1) {
+ Mmio32AndThenOr (VtdBase, 0xF04, Data32And, Data32Or);
+ SCRIPT_MEM_READ_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (VtdBase + 0xF04),
+ &Data32Or, ///< Data to be ORed
+ &Data32And ///< Data to be ANDed
+ );
+ }
+ }
+ ///
+ /// 14.2 Set the remap engine policy bits
+ ///
+ Data32And = 0x0; ///< mask out all bits
+ Data32Or = 0;
+
+
+ if ((CpuFamilyId == EnumCpuHsw) || (CpuFamilyId == EnumCpuHswUlt) || (CpuFamilyId == EnumCpuCrw)) {
+ if (i == 0) {
+ Data32Or |= 0x02100000;
+ }
+ if (i == 1) {
+ if (McD2PciCfg16 (R_SA_IGD_VID) != 0xFFFF) {
+ Data32Or |= 0x000A5003;
+ } else {
+ Data32Or |= 0x020A5003;
+ }
+ }
+ }
+
+ ///
+ /// Set lock bit
+ ///
+ Data32Or |= BIT31;
+
+
+ Mmio32AndThenOr (VtdBase, 0xFF0, Data32And, Data32Or);
+ SCRIPT_MEM_READ_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (VtdBase + 0xFF0),
+ &Data32Or, ///< Data to be ORed
+ &Data32And ///< Data to be ANDed
+ );
+
+ ///
+ /// Check IR status
+ ///
+ if ((Mmio32 (VtdBase, VTD_ECAP_REG) & IR) && !(mInterruptRemappingSupport)) {
+ mInterruptRemappingSupport = TRUE;
+ }
+ }
+
+ return EFI_SUCCESS;
+}