diff options
author | raywu <raywu0301@gmail.com> | 2018-06-15 00:00:50 +0800 |
---|---|---|
committer | raywu <raywu0301@gmail.com> | 2018-06-15 00:00:50 +0800 |
commit | b7c51c9cf4864df6aabb99a1ae843becd577237c (patch) | |
tree | eebe9b0d0ca03062955223097e57da84dd618b9a /ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c | |
download | zprj-master.tar.xz |
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c')
-rw-r--r-- | ReferenceCode/Chipset/SystemAgent/SaInit/Dxe/VTd.c | 934 |
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; +} |