+/** @file\r
+\r
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials are licensed and made available under\r
+ the terms and conditions of the BSD License which accompanies this distribution.\r
+ The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiPei.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <IndustryStandard/Vtd.h>\r
+#include <Ppi/VTdInfo.h>\r
+\r
+#include "IntelVTdPmrPei.h"\r
+\r
+extern EDKII_VTD_INFO_PPI *mVTdInfoPpi;\r
+\r
+/**\r
+ Get protected low memory alignment.\r
+\r
+ @param VtdUnitBaseAddress The base address of the VTd engine.\r
+\r
+ @return protected low memory alignment.\r
+**/\r
+UINT32\r
+GetPlmrAlignment (\r
+ IN UINTN VtdUnitBaseAddress\r
+ )\r
+{\r
+ UINT32 Data32;\r
+\r
+ MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, 0xFFFFFFFF);\r
+ Data32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG);\r
+ Data32 = ~Data32 + 1;\r
+\r
+ return Data32;\r
+}\r
+\r
+/**\r
+ Get protected high memory alignment.\r
+\r
+ @param VtdUnitBaseAddress The base address of the VTd engine.\r
+\r
+ @return protected high memory alignment.\r
+**/\r
+UINT64\r
+GetPhmrAlignment (\r
+ IN UINTN VtdUnitBaseAddress\r
+ )\r
+{\r
+ UINT64 Data64;\r
+ UINT8 HostAddressWidth;\r
+\r
+ HostAddressWidth = mVTdInfoPpi->HostAddressWidth;\r
+\r
+ MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, 0xFFFFFFFFFFFFFFFF);\r
+ Data64 = MmioRead64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG);\r
+ Data64 = ~Data64 + 1;\r
+ Data64 = Data64 & (LShiftU64 (1, HostAddressWidth) - 1);\r
+\r
+ return Data64;\r
+}\r
+\r
+/**\r
+ Get protected low memory alignment.\r
+\r
+ @return protected low memory alignment.\r
+**/\r
+UINT32\r
+GetLowMemoryAlignment (\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINT32 Alignment;\r
+ UINT32 FinalAlignment;\r
+\r
+ FinalAlignment = 0;\r
+ for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) {\r
+ Alignment = GetPlmrAlignment ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]);\r
+ if (FinalAlignment < Alignment) {\r
+ FinalAlignment = Alignment;\r
+ }\r
+ }\r
+ return FinalAlignment;\r
+}\r
+\r
+/**\r
+ Get protected high memory alignment.\r
+\r
+ @return protected high memory alignment.\r
+**/\r
+UINT64\r
+GetHighMemoryAlignment (\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINT64 Alignment;\r
+ UINT64 FinalAlignment;\r
+\r
+ FinalAlignment = 0;\r
+ for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) {\r
+ Alignment = GetPhmrAlignment ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]);\r
+ if (FinalAlignment < Alignment) {\r
+ FinalAlignment = Alignment;\r
+ }\r
+ }\r
+ return FinalAlignment;\r
+}\r
+\r
+/**\r
+ Enable PMR in the VTd engine.\r
+\r
+ @param VtdUnitBaseAddress The base address of the VTd engine.\r
+\r
+ @retval EFI_SUCCESS The PMR is enabled.\r
+ @retval EFI_UNSUPPORTED The PMR is not supported.\r
+**/\r
+EFI_STATUS\r
+EnablePmr (\r
+ IN UINTN VtdUnitBaseAddress\r
+ )\r
+{\r
+ UINT32 Reg32;\r
+ VTD_CAP_REG CapReg;\r
+\r
+ CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);\r
+ if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);\r
+ if ((Reg32 & BIT0) == 0) {\r
+ MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, BIT31);\r
+ do {\r
+ Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);\r
+ } while((Reg32 & BIT0) == 0);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Disable PMR in the VTd engine.\r
+\r
+ @param VtdUnitBaseAddress The base address of the VTd engine.\r
+\r
+ @retval EFI_SUCCESS The PMR is disabled.\r
+ @retval EFI_UNSUPPORTED The PMR is not supported.\r
+**/\r
+EFI_STATUS\r
+DisablePmr (\r
+ IN UINTN VtdUnitBaseAddress\r
+ )\r
+{\r
+ UINT32 Reg32;\r
+ VTD_CAP_REG CapReg;\r
+\r
+ CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);\r
+ if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);\r
+ if ((Reg32 & BIT0) != 0) {\r
+ MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, 0x0);\r
+ do {\r
+ Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);\r
+ } while((Reg32 & BIT0) != 0);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set PMR region in the VTd engine.\r
+\r
+ @param VtdUnitBaseAddress The base address of the VTd engine.\r
+ @param LowMemoryBase The protected low memory region base.\r
+ @param LowMemoryLength The protected low memory region length.\r
+ @param HighMemoryBase The protected high memory region base.\r
+ @param HighMemoryLength The protected high memory region length.\r
+\r
+ @retval EFI_SUCCESS The PMR is set to protected region.\r
+ @retval EFI_UNSUPPORTED The PMR is not supported.\r
+**/\r
+EFI_STATUS\r
+SetPmrRegion (\r
+ IN UINTN VtdUnitBaseAddress,\r
+ IN UINT32 LowMemoryBase,\r
+ IN UINT32 LowMemoryLength,\r
+ IN UINT64 HighMemoryBase,\r
+ IN UINT64 HighMemoryLength\r
+ )\r
+{\r
+ VTD_CAP_REG CapReg;\r
+ UINT32 PlmrAlignment;\r
+ UINT64 PhmrAlignment;\r
+\r
+ DEBUG ((DEBUG_INFO, "VtdUnitBaseAddress - 0x%x\n", VtdUnitBaseAddress));\r
+\r
+ CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);\r
+ if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {\r
+ DEBUG ((DEBUG_ERROR, "PLMR/PHMR unsupported\n"));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ PlmrAlignment = GetPlmrAlignment (VtdUnitBaseAddress);\r
+ DEBUG ((DEBUG_INFO, "PlmrAlignment - 0x%x\n", PlmrAlignment));\r
+ PhmrAlignment = GetPhmrAlignment (VtdUnitBaseAddress);\r
+ DEBUG ((DEBUG_INFO, "PhmrAlignment - 0x%lx\n", PhmrAlignment));\r
+\r
+ if ((LowMemoryBase != ALIGN_VALUE(LowMemoryBase, PlmrAlignment)) ||\r
+ (LowMemoryLength != ALIGN_VALUE(LowMemoryLength, PlmrAlignment)) ||\r
+ (HighMemoryBase != ALIGN_VALUE(HighMemoryBase, PhmrAlignment)) ||\r
+ (HighMemoryLength != ALIGN_VALUE(HighMemoryLength, PhmrAlignment))) {\r
+ DEBUG ((DEBUG_ERROR, "PLMR/PHMR alignment issue\n"));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (LowMemoryBase == 0 && LowMemoryLength == 0) {\r
+ LowMemoryBase = 0xFFFFFFFF;\r
+ }\r
+ if (HighMemoryBase == 0 && HighMemoryLength == 0) {\r
+ HighMemoryBase = 0xFFFFFFFFFFFFFFFF;\r
+ }\r
+\r
+ MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, LowMemoryBase);\r
+ MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_LIMITE_REG, LowMemoryBase + LowMemoryLength - 1);\r
+ MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, HighMemoryBase);\r
+ MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_LIMITE_REG, HighMemoryBase + HighMemoryLength - 1);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set DMA protected region.\r
+\r
+ @param LowMemoryBase The protected low memory region base.\r
+ @param LowMemoryLength The protected low memory region length.\r
+ @param HighMemoryBase The protected high memory region base.\r
+ @param HighMemoryLength The protected high memory region length.\r
+\r
+ @retval EFI_SUCCESS The DMA protection is set.\r
+ @retval EFI_UNSUPPORTED The DMA protection is not set.\r
+**/\r
+EFI_STATUS\r
+SetDmaProtectedRange (\r
+ IN UINT32 LowMemoryBase,\r
+ IN UINT32 LowMemoryLength,\r
+ IN UINT64 HighMemoryBase,\r
+ IN UINT64 HighMemoryLength\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_STATUS Status;\r
+\r
+ DEBUG ((DEBUG_INFO, "SetDmaProtectedRange - [0x%x, 0x%x] [0x%lx, 0x%lx]\n", LowMemoryBase, LowMemoryLength, HighMemoryBase, HighMemoryLength));\r
+\r
+ for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) {\r
+ DisablePmr ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]);\r
+ Status = SetPmrRegion (\r
+ (UINTN)mVTdInfoPpi->VTdEngineAddress[Index],\r
+ LowMemoryBase,\r
+ LowMemoryLength,\r
+ HighMemoryBase,\r
+ HighMemoryLength\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ Status = EnablePmr ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Diable DMA protection.\r
+\r
+ @retval DMA protection is disabled.\r
+**/\r
+EFI_STATUS\r
+DisableDmaProtection (\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_STATUS Status;\r
+\r
+ DEBUG ((DEBUG_INFO, "DisableDmaProtection\n"));\r
+\r
+ for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) {\r
+ Status = DisablePmr ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r