From 3f5ed3fa13c66d66e1a7c5df9a78311f9a0ed991 Mon Sep 17 00:00:00 2001 From: Jiewen Yao Date: Fri, 1 Sep 2017 16:30:46 +0800 Subject: [PATCH] IntelSiliconPkg: Add IntelVTdPmrPei. This PEIM is to produce IOMMU_PPI, so that PEI device driver can have better DAM management. This PEIM will setup VTD PMR register to protect most DRAM. It allocates a big chunk DMA buffer in the entrypoint, and only use this buffer for DMA. Any other region is DMA protected. Cc: Star Zeng Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Jiewen Yao Reviewed-by: Star Zeng --- IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmr.c | 314 +++++++++ .../IntelVTdPmrPei/IntelVTdPmrPei.c | 615 ++++++++++++++++++ .../IntelVTdPmrPei/IntelVTdPmrPei.h | 68 ++ .../IntelVTdPmrPei/IntelVTdPmrPei.inf | 59 ++ .../IntelVTdPmrPei/IntelVTdPmrPei.uni | 20 + .../IntelVTdPmrPei/IntelVTdPmrPeiExtra.uni | 20 + 6 files changed, 1096 insertions(+) create mode 100644 IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmr.c create mode 100644 IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.c create mode 100644 IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.h create mode 100644 IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.inf create mode 100644 IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.uni create mode 100644 IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPeiExtra.uni diff --git a/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmr.c b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmr.c new file mode 100644 index 0000000000..ef08e2991f --- /dev/null +++ b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmr.c @@ -0,0 +1,314 @@ +/** @file + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials are licensed and made available under + the terms and conditions of the BSD License which accompanies this distribution. + The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include + +#include "IntelVTdPmrPei.h" + +extern EDKII_VTD_INFO_PPI *mVTdInfoPpi; + +/** + Get protected low memory alignment. + + @param VtdUnitBaseAddress The base address of the VTd engine. + + @return protected low memory alignment. +**/ +UINT32 +GetPlmrAlignment ( + IN UINTN VtdUnitBaseAddress + ) +{ + UINT32 Data32; + + MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, 0xFFFFFFFF); + Data32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG); + Data32 = ~Data32 + 1; + + return Data32; +} + +/** + Get protected high memory alignment. + + @param VtdUnitBaseAddress The base address of the VTd engine. + + @return protected high memory alignment. +**/ +UINT64 +GetPhmrAlignment ( + IN UINTN VtdUnitBaseAddress + ) +{ + UINT64 Data64; + UINT8 HostAddressWidth; + + HostAddressWidth = mVTdInfoPpi->HostAddressWidth; + + MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, 0xFFFFFFFFFFFFFFFF); + Data64 = MmioRead64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG); + Data64 = ~Data64 + 1; + Data64 = Data64 & (LShiftU64 (1, HostAddressWidth) - 1); + + return Data64; +} + +/** + Get protected low memory alignment. + + @return protected low memory alignment. +**/ +UINT32 +GetLowMemoryAlignment ( + VOID + ) +{ + UINTN Index; + UINT32 Alignment; + UINT32 FinalAlignment; + + FinalAlignment = 0; + for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) { + Alignment = GetPlmrAlignment ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]); + if (FinalAlignment < Alignment) { + FinalAlignment = Alignment; + } + } + return FinalAlignment; +} + +/** + Get protected high memory alignment. + + @return protected high memory alignment. +**/ +UINT64 +GetHighMemoryAlignment ( + VOID + ) +{ + UINTN Index; + UINT64 Alignment; + UINT64 FinalAlignment; + + FinalAlignment = 0; + for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) { + Alignment = GetPhmrAlignment ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]); + if (FinalAlignment < Alignment) { + FinalAlignment = Alignment; + } + } + return FinalAlignment; +} + +/** + Enable PMR in the VTd engine. + + @param VtdUnitBaseAddress The base address of the VTd engine. + + @retval EFI_SUCCESS The PMR is enabled. + @retval EFI_UNSUPPORTED The PMR is not supported. +**/ +EFI_STATUS +EnablePmr ( + IN UINTN VtdUnitBaseAddress + ) +{ + UINT32 Reg32; + VTD_CAP_REG CapReg; + + CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG); + if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) { + return EFI_UNSUPPORTED; + } + + Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG); + if ((Reg32 & BIT0) == 0) { + MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, BIT31); + do { + Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG); + } while((Reg32 & BIT0) == 0); + } + + return EFI_SUCCESS; +} + +/** + Disable PMR in the VTd engine. + + @param VtdUnitBaseAddress The base address of the VTd engine. + + @retval EFI_SUCCESS The PMR is disabled. + @retval EFI_UNSUPPORTED The PMR is not supported. +**/ +EFI_STATUS +DisablePmr ( + IN UINTN VtdUnitBaseAddress + ) +{ + UINT32 Reg32; + VTD_CAP_REG CapReg; + + CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG); + if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) { + return EFI_UNSUPPORTED; + } + + Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG); + if ((Reg32 & BIT0) != 0) { + MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, 0x0); + do { + Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG); + } while((Reg32 & BIT0) != 0); + } + + return EFI_SUCCESS; +} + +/** + Set PMR region in the VTd engine. + + @param VtdUnitBaseAddress The base address of the VTd engine. + @param LowMemoryBase The protected low memory region base. + @param LowMemoryLength The protected low memory region length. + @param HighMemoryBase The protected high memory region base. + @param HighMemoryLength The protected high memory region length. + + @retval EFI_SUCCESS The PMR is set to protected region. + @retval EFI_UNSUPPORTED The PMR is not supported. +**/ +EFI_STATUS +SetPmrRegion ( + IN UINTN VtdUnitBaseAddress, + IN UINT32 LowMemoryBase, + IN UINT32 LowMemoryLength, + IN UINT64 HighMemoryBase, + IN UINT64 HighMemoryLength + ) +{ + VTD_CAP_REG CapReg; + UINT32 PlmrAlignment; + UINT64 PhmrAlignment; + + DEBUG ((DEBUG_INFO, "VtdUnitBaseAddress - 0x%x\n", VtdUnitBaseAddress)); + + CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG); + if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) { + DEBUG ((DEBUG_ERROR, "PLMR/PHMR unsupported\n")); + return EFI_UNSUPPORTED; + } + + PlmrAlignment = GetPlmrAlignment (VtdUnitBaseAddress); + DEBUG ((DEBUG_INFO, "PlmrAlignment - 0x%x\n", PlmrAlignment)); + PhmrAlignment = GetPhmrAlignment (VtdUnitBaseAddress); + DEBUG ((DEBUG_INFO, "PhmrAlignment - 0x%lx\n", PhmrAlignment)); + + if ((LowMemoryBase != ALIGN_VALUE(LowMemoryBase, PlmrAlignment)) || + (LowMemoryLength != ALIGN_VALUE(LowMemoryLength, PlmrAlignment)) || + (HighMemoryBase != ALIGN_VALUE(HighMemoryBase, PhmrAlignment)) || + (HighMemoryLength != ALIGN_VALUE(HighMemoryLength, PhmrAlignment))) { + DEBUG ((DEBUG_ERROR, "PLMR/PHMR alignment issue\n")); + return EFI_UNSUPPORTED; + } + + if (LowMemoryBase == 0 && LowMemoryLength == 0) { + LowMemoryBase = 0xFFFFFFFF; + } + if (HighMemoryBase == 0 && HighMemoryLength == 0) { + HighMemoryBase = 0xFFFFFFFFFFFFFFFF; + } + + MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, LowMemoryBase); + MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_LIMITE_REG, LowMemoryBase + LowMemoryLength - 1); + MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, HighMemoryBase); + MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_LIMITE_REG, HighMemoryBase + HighMemoryLength - 1); + + return EFI_SUCCESS; +} + +/** + Set DMA protected region. + + @param LowMemoryBase The protected low memory region base. + @param LowMemoryLength The protected low memory region length. + @param HighMemoryBase The protected high memory region base. + @param HighMemoryLength The protected high memory region length. + + @retval EFI_SUCCESS The DMA protection is set. + @retval EFI_UNSUPPORTED The DMA protection is not set. +**/ +EFI_STATUS +SetDmaProtectedRange ( + IN UINT32 LowMemoryBase, + IN UINT32 LowMemoryLength, + IN UINT64 HighMemoryBase, + IN UINT64 HighMemoryLength + ) +{ + UINTN Index; + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "SetDmaProtectedRange - [0x%x, 0x%x] [0x%lx, 0x%lx]\n", LowMemoryBase, LowMemoryLength, HighMemoryBase, HighMemoryLength)); + + for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) { + DisablePmr ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]); + Status = SetPmrRegion ( + (UINTN)mVTdInfoPpi->VTdEngineAddress[Index], + LowMemoryBase, + LowMemoryLength, + HighMemoryBase, + HighMemoryLength + ); + if (EFI_ERROR(Status)) { + return Status; + } + Status = EnablePmr ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]); + if (EFI_ERROR(Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Diable DMA protection. + + @retval DMA protection is disabled. +**/ +EFI_STATUS +DisableDmaProtection ( + VOID + ) +{ + UINTN Index; + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "DisableDmaProtection\n")); + + for (Index = 0; Index < mVTdInfoPpi->VTdEngineCount; Index++) { + Status = DisablePmr ((UINTN)mVTdInfoPpi->VTdEngineAddress[Index]); + if (EFI_ERROR(Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} diff --git a/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.c b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.c new file mode 100644 index 0000000000..d118b7ef26 --- /dev/null +++ b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.c @@ -0,0 +1,615 @@ +/** @file + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials are licensed and made available under + the terms and conditions of the BSD License which accompanies this distribution. + The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "IntelVTdPmrPei.h" + +#define TOTAL_DMA_BUFFER_SIZE SIZE_4MB + +EDKII_VTD_INFO_PPI *mVTdInfoPpi; +UINTN mDmaBufferBase; +UINTN mDmaBufferSize = TOTAL_DMA_BUFFER_SIZE; +UINTN mDmaBufferCurrentTop; +UINTN mDmaBufferCurrentBottom; + +#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P') +typedef struct { + UINT32 Signature; + EDKII_IOMMU_OPERATION Operation; + UINTN NumberOfBytes; + EFI_PHYSICAL_ADDRESS HostAddress; + EFI_PHYSICAL_ADDRESS DeviceAddress; +} MAP_INFO; + +/** + + PEI Memory Layout: + + +------------------+ <------- EfiMemoryTop + | PEI allocated | + =========== +==================+ + ^ | Commom Buf | + | | -------------- | + DMA Buffer | * DMA FREE * | + | | -------------- | + V | Read/Write Buf | + =========== +==================+ + | PEI allocated | + | -------------- | <------- EfiFreeMemoryTop + | * PEI FREE * | + | -------------- | <------- EfiFreeMemoryBottom + | hob | + | -------------- | + | Stack | + +------------------+ <------- EfiMemoryBottom / Stack Bottom + + +------------------+ + | Mem Alloc Hob | + +------------------+ + +**/ + + +/** + Set IOMMU attribute for a system memory. + + If the IOMMU PPI exists, the system memory cannot be used + for DMA by default. + + When a device requests a DMA access for a system memory, + the device driver need use SetAttribute() to update the IOMMU + attribute to request DMA access (read and/or write). + + @param[in] This The PPI instance pointer. + @param[in] Mapping The mapping value returned from Map(). + @param[in] IoMmuAccess The IOMMU access. + + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @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 Mapping. + @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 +EFIAPI +PeiIoMmuSetAttribute ( + IN EDKII_IOMMU_PPI *This, + IN VOID *Mapping, + IN UINT64 IoMmuAccess + ) +{ + return EFI_SUCCESS; +} + +/** + Provides the controller-specific addresses required to access system memory from a + DMA bus master. + + @param This The PPI instance pointer. + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +EFIAPI +PeiIoMmuMap ( + IN EDKII_IOMMU_PPI *This, + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + MAP_INFO *MapInfo; + UINTN Length; + + if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { + *DeviceAddress = (UINTN)HostAddress; + *Mapping = 0; + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_VERBOSE, "PeiIoMmuMap - HostAddress - 0x%x, NumberOfBytes - %x\n", HostAddress, *NumberOfBytes)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentTop - %x\n", mDmaBufferCurrentTop)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentBottom - %x\n", mDmaBufferCurrentBottom)); + + Length = *NumberOfBytes + sizeof(MAP_INFO); + if (Length > mDmaBufferCurrentTop - mDmaBufferCurrentBottom) { + DEBUG ((DEBUG_ERROR, "PeiIoMmuMap - OUT_OF_RESOURCE\n")); + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + + *DeviceAddress = mDmaBufferCurrentBottom; + mDmaBufferCurrentBottom += Length; + + MapInfo = (VOID *)(UINTN)(*DeviceAddress + *NumberOfBytes); + MapInfo->Signature = MAP_INFO_SIGNATURE; + MapInfo->Operation = Operation; + MapInfo->NumberOfBytes = *NumberOfBytes; + MapInfo->HostAddress = (UINTN)HostAddress; + MapInfo->DeviceAddress = *DeviceAddress; + *Mapping = MapInfo; + DEBUG ((DEBUG_VERBOSE, " Op(%x):DeviceAddress - %x, Mapping - %x\n", Operation, (UINTN)*DeviceAddress, MapInfo)); + + // + // If this is a read operation from the Bus Master's point of view, + // then copy the contents of the real buffer into the mapped buffer + // so the Bus Master can read the contents of the real buffer. + // + if (Operation == EdkiiIoMmuOperationBusMasterRead || + Operation == EdkiiIoMmuOperationBusMasterRead64) { + CopyMem ( + (VOID *) (UINTN) MapInfo->DeviceAddress, + (VOID *) (UINTN) MapInfo->HostAddress, + MapInfo->NumberOfBytes + ); + } + + return EFI_SUCCESS; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This The PPI instance pointer. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. +**/ +EFI_STATUS +EFIAPI +PeiIoMmuUnmap ( + IN EDKII_IOMMU_PPI *This, + IN VOID *Mapping + ) +{ + MAP_INFO *MapInfo; + UINTN Length; + + if (Mapping == NULL) { + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_VERBOSE, "PeiIoMmuUnmap - Mapping - %x\n", Mapping)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentTop - %x\n", mDmaBufferCurrentTop)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentBottom - %x\n", mDmaBufferCurrentBottom)); + + MapInfo = Mapping; + ASSERT (MapInfo->Signature == MAP_INFO_SIGNATURE); + DEBUG ((DEBUG_VERBOSE, " Op(%x):DeviceAddress - %x, NumberOfBytes - %x\n", MapInfo->Operation, (UINTN)MapInfo->DeviceAddress, MapInfo->NumberOfBytes)); + + // + // If this is a write operation from the Bus Master's point of view, + // then copy the contents of the mapped buffer into the real buffer + // so the processor can read the contents of the real buffer. + // + if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite || + MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) { + CopyMem ( + (VOID *) (UINTN) MapInfo->HostAddress, + (VOID *) (UINTN) MapInfo->DeviceAddress, + MapInfo->NumberOfBytes + ); + } + + Length = MapInfo->NumberOfBytes + sizeof(MAP_INFO); + if (mDmaBufferCurrentBottom == MapInfo->DeviceAddress + Length) { + mDmaBufferCurrentBottom -= Length; + } + + return EFI_SUCCESS; +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param This The PPI instance pointer. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +EFIAPI +PeiIoMmuAllocateBuffer ( + IN EDKII_IOMMU_PPI *This, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT VOID **HostAddress, + IN UINT64 Attributes + ) +{ + UINTN Length; + + DEBUG ((DEBUG_VERBOSE, "PeiIoMmuAllocateBuffer - page - %x\n", Pages)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentTop - %x\n", mDmaBufferCurrentTop)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentBottom - %x\n", mDmaBufferCurrentBottom)); + + Length = EFI_PAGES_TO_SIZE(Pages); + if (Length > mDmaBufferCurrentTop - mDmaBufferCurrentBottom) { + DEBUG ((DEBUG_ERROR, "PeiIoMmuAllocateBuffer - OUT_OF_RESOURCE\n")); + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + *HostAddress = (VOID *)(UINTN)(mDmaBufferCurrentTop - Length); + mDmaBufferCurrentTop -= Length; + + DEBUG ((DEBUG_VERBOSE, "PeiIoMmuAllocateBuffer - allocate - %x\n", *HostAddress)); + return EFI_SUCCESS; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This The PPI instance pointer. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +EFIAPI +PeiIoMmuFreeBuffer ( + IN EDKII_IOMMU_PPI *This, + IN UINTN Pages, + IN VOID *HostAddress + ) +{ + UINTN Length; + + DEBUG ((DEBUG_VERBOSE, "PeiIoMmuFreeBuffer - page - %x, HostAddr - %x\n", Pages, HostAddress)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentTop - %x\n", mDmaBufferCurrentTop)); + DEBUG ((DEBUG_VERBOSE, " mDmaBufferCurrentBottom - %x\n", mDmaBufferCurrentBottom)); + + Length = EFI_PAGES_TO_SIZE(Pages); + if ((UINTN)HostAddress == mDmaBufferCurrentTop) { + mDmaBufferCurrentTop += Length; + } + + return EFI_SUCCESS; +} + +EDKII_IOMMU_PPI mIoMmuPpi = { + EDKII_IOMMU_PPI_REVISION, + PeiIoMmuSetAttribute, + PeiIoMmuMap, + PeiIoMmuUnmap, + PeiIoMmuAllocateBuffer, + PeiIoMmuFreeBuffer, +}; + +CONST EFI_PEI_PPI_DESCRIPTOR mIoMmuPpiList = { + EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gEdkiiIoMmuPpiGuid, + (VOID *) &mIoMmuPpi +}; + +#define MEMORY_ATTRIBUTE_MASK (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED | \ + EFI_RESOURCE_ATTRIBUTE_16_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_32_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_64_BIT_IO \ + ) + +#define TESTED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED) + +#define INITIALIZED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED) + +#define PRESENT_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT) + +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mResourceTypeShortName[] = { + "Mem", + "MMIO", + "I/O", + "FD", + "MM Port I/O", + "Reserved Mem", + "Reserved I/O", +}; + +/** + Return the short name of resource type. + + @param Type resource type. + + @return the short name of resource type. +**/ +CHAR8 * +ShortNameOfResourceType ( + IN UINT32 Type + ) +{ + if (Type < sizeof(mResourceTypeShortName) / sizeof(mResourceTypeShortName[0])) { + return mResourceTypeShortName[Type]; + } else { + return "Unknown"; + } +} + +/** + Dump resource hob. + + @param HobList the HOB list. +**/ +VOID +DumpResourceHob ( + IN VOID *HobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + + DEBUG ((DEBUG_VERBOSE, "Resource Descriptor HOBs\n")); + for (Hob.Raw = HobList; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + ResourceHob = Hob.ResourceDescriptor; + DEBUG ((DEBUG_VERBOSE, + " BA=%016lx L=%016lx Attr=%08x ", + ResourceHob->PhysicalStart, + ResourceHob->ResourceLength, + ResourceHob->ResourceAttribute + )); + DEBUG ((DEBUG_VERBOSE, ShortNameOfResourceType(ResourceHob->ResourceType))); + switch (ResourceHob->ResourceType) { + case EFI_RESOURCE_SYSTEM_MEMORY: + if ((ResourceHob->ResourceAttribute & EFI_RESOURCE_ATTRIBUTE_PERSISTENT) != 0) { + DEBUG ((DEBUG_VERBOSE, " (Persistent)")); + } else if ((ResourceHob->ResourceAttribute & EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE) != 0) { + DEBUG ((DEBUG_VERBOSE, " (MoreReliable)")); + } else if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) == TESTED_MEMORY_ATTRIBUTES) { + DEBUG ((DEBUG_VERBOSE, " (Tested)")); + } else if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) == INITIALIZED_MEMORY_ATTRIBUTES) { + DEBUG ((DEBUG_VERBOSE, " (Init)")); + } else if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) == PRESENT_MEMORY_ATTRIBUTES) { + DEBUG ((DEBUG_VERBOSE, " (Present)")); + } else { + DEBUG ((DEBUG_VERBOSE, " (Unknown)")); + } + break; + default: + break; + } + DEBUG ((DEBUG_VERBOSE, "\n")); + } + } +} + +/** + Dump PHIT hob. + + @param HobList the HOB list. +**/ +VOID +DumpPhitHob ( + IN VOID *HobList + ) +{ + EFI_HOB_HANDOFF_INFO_TABLE *PhitHob; + + PhitHob = HobList; + ASSERT(GET_HOB_TYPE(HobList) == EFI_HOB_TYPE_HANDOFF); + DEBUG ((DEBUG_VERBOSE, "PHIT HOB\n")); + DEBUG ((DEBUG_VERBOSE, " PhitHob - 0x%x\n", PhitHob)); + DEBUG ((DEBUG_VERBOSE, " BootMode - 0x%x\n", PhitHob->BootMode)); + DEBUG ((DEBUG_VERBOSE, " EfiMemoryTop - 0x%016lx\n", PhitHob->EfiMemoryTop)); + DEBUG ((DEBUG_VERBOSE, " EfiMemoryBottom - 0x%016lx\n", PhitHob->EfiMemoryBottom)); + DEBUG ((DEBUG_VERBOSE, " EfiFreeMemoryTop - 0x%016lx\n", PhitHob->EfiFreeMemoryTop)); + DEBUG ((DEBUG_VERBOSE, " EfiFreeMemoryBottom - 0x%016lx\n", PhitHob->EfiFreeMemoryBottom)); + DEBUG ((DEBUG_VERBOSE, " EfiEndOfHobList - 0x%lx\n", PhitHob->EfiEndOfHobList)); +} + +/** + Get the highest memory. + + @param HobList the HOB list. + + @return the highest memory. +**/ +UINT64 +GetTopMemory ( + IN VOID *HobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + UINT64 TopMemory; + UINT64 ResourceTop; + + TopMemory = 0; + for (Hob.Raw = HobList; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + ResourceHob = Hob.ResourceDescriptor; + switch (ResourceHob->ResourceType) { + case EFI_RESOURCE_SYSTEM_MEMORY: + ResourceTop = ResourceHob->PhysicalStart + ResourceHob->ResourceLength; + if (TopMemory < ResourceTop) { + TopMemory = ResourceTop; + } + break; + default: + break; + } + DEBUG ((DEBUG_VERBOSE, "\n")); + } + } + return TopMemory; +} + +/** + Initialize DMA protection. + + @param DmaBufferSize the DMA buffer size + @param DmaBufferBase the DMA buffer base + + @retval EFI_SUCCESS the DMA protection is initialized. + @retval EFI_OUT_OF_RESOURCES no enough resource to initialize DMA protection. +**/ +EFI_STATUS +InitDmaProtection ( + IN UINTN DmaBufferSize, + OUT UINTN *DmaBufferBase + ) +{ + EFI_STATUS Status; + VOID *HobList; + EFI_HOB_HANDOFF_INFO_TABLE *PhitHob; + UINT32 LowMemoryAlignment; + UINT64 HighMemoryAlignment; + UINTN MemoryAlignment; + UINTN LowBottom; + UINTN LowTop; + UINTN HighBottom; + UINT64 HighTop; + + HobList = GetHobList (); + DumpPhitHob (HobList); + DumpResourceHob (HobList); + + PhitHob = HobList; + + ASSERT (PhitHob->EfiMemoryBottom < PhitHob->EfiMemoryTop); + + LowMemoryAlignment = GetLowMemoryAlignment (); + HighMemoryAlignment = GetHighMemoryAlignment (); + if (LowMemoryAlignment < HighMemoryAlignment) { + MemoryAlignment = (UINTN)HighMemoryAlignment; + } else { + MemoryAlignment = LowMemoryAlignment; + } + ASSERT (DmaBufferSize == ALIGN_VALUE(DmaBufferSize, MemoryAlignment)); + *DmaBufferBase = (UINTN)AllocateAlignedPages (EFI_SIZE_TO_PAGES(DmaBufferSize), MemoryAlignment); + if (*DmaBufferBase == 0) { + DEBUG ((DEBUG_INFO, " InitDmaProtection : OutOfResource\n")); + return EFI_OUT_OF_RESOURCES; + } + + LowBottom = 0; + LowTop = *DmaBufferBase; + HighBottom = *DmaBufferBase + DmaBufferSize; + HighTop = GetTopMemory (HobList); + + Status = SetDmaProtectedRange ( + (UINT32)LowBottom, + (UINT32)(LowTop - LowBottom), + HighBottom, + HighTop - HighBottom + ); + + if (EFI_ERROR(Status)) { + FreePages ((VOID *)*DmaBufferBase, EFI_SIZE_TO_PAGES(DmaBufferSize)); + } + + return Status; +} + +/** + Initializes the Intel VTd PMR PEIM. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS Usb bot driver is successfully initialized. + @retval EFI_OUT_OF_RESOURCES Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +IntelVTdPmrInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + if ((PcdGet8(PcdVTdPolicyPropertyMask) & BIT0) == 0) { + return EFI_UNSUPPORTED; + } + + Status = PeiServicesLocatePpi ( + &gEdkiiVTdInfoPpiGuid, + 0, + NULL, + (VOID **)&mVTdInfoPpi + ); + ASSERT_EFI_ERROR(Status); + + // + // Find a pre-memory in resource hob as DMA buffer + // Mark PEI memory to be DMA protected. + // + Status = InitDmaProtection (mDmaBufferSize, &mDmaBufferBase); + if (EFI_ERROR(Status)) { + return Status; + } + + DEBUG ((DEBUG_INFO, " DmaBufferBase : 0x%x\n", mDmaBufferBase)); + DEBUG ((DEBUG_INFO, " DmaBufferSize : 0x%x\n", mDmaBufferSize)); + + mDmaBufferCurrentTop = mDmaBufferBase + mDmaBufferSize; + mDmaBufferCurrentBottom = mDmaBufferBase; + + // + // Install PPI. + // + Status = PeiServicesInstallPpi (&mIoMmuPpiList); + ASSERT_EFI_ERROR(Status); + + return Status; +} + diff --git a/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.h b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.h new file mode 100644 index 0000000000..aa5926a766 --- /dev/null +++ b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.h @@ -0,0 +1,68 @@ +/** @file + The definition for DMA access Library. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __DMA_ACCESS_LIB_H__ +#define __DMA_ACCESS_LIB_H__ + +/** + Set DMA protected region. + + @param LowMemoryBase The protected low memory region base. + @param LowMemoryLength The protected low memory region length. + @param HighMemoryBase The protected high memory region base. + @param HighMemoryLength The protected high memory region length. + + @retval EFI_SUCCESS The DMA protection is set. + @retval EFI_UNSUPPORTED The DMA protection is not set. +**/ +EFI_STATUS +SetDmaProtectedRange ( + IN UINT32 LowMemoryBase, + IN UINT32 LowMemoryLength, + IN UINT64 HighMemoryBase, + IN UINT64 HighMemoryLength + ); + +/** + Diable DMA protection. + + @retval DMA protection is disabled. +**/ +EFI_STATUS +DisableDmaProtection ( + VOID + ); + +/** + Get protected low memory alignment. + + @return protected low memory alignment. +**/ +UINT32 +GetLowMemoryAlignment ( + VOID + ); + +/** + Get protected high memory alignment. + + @return protected high memory alignment. +**/ +UINT64 +GetHighMemoryAlignment ( + VOID + ); + +#endif + diff --git a/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.inf b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.inf new file mode 100644 index 0000000000..86cd7d1618 --- /dev/null +++ b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.inf @@ -0,0 +1,59 @@ +## @file +# Component INF file for the Intel VTd PMR PEIM. +# +# This driver initializes VTd engine based upon EDKII_VTD_INFO_PPI +# and provide DMA protection in PEI. +# +# Copyright (c) 2017, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = IntelVTdPmrPei + MODULE_UNI_FILE = IntelVTdPmrPei.uni + FILE_GUID = F906769F-4AED-4A0D-8C7C-FF21B9D1051A + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = IntelVTdPmrInitialize + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelSiliconPkg/IntelSiliconPkg.dec + +[Sources] + IntelVTdPmrPei.c + IntelVTdPmrPei.h + IntelVTdPmr.c + +[LibraryClasses] + DebugLib + BaseMemoryLib + BaseLib + PeimEntryPoint + PeiServicesLib + HobLib + IoLib + +[Ppis] + gEdkiiIoMmuPpiGuid ## PRODUCES + gEdkiiVTdInfoPpiGuid ## CONSUMES + +[Pcd] + gIntelSiliconPkgTokenSpaceGuid.PcdVTdPolicyPropertyMask ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND + gEdkiiVTdInfoPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + IntelVTdPmrPeiExtra.uni + diff --git a/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.uni b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.uni new file mode 100644 index 0000000000..11508a4fd4 --- /dev/null +++ b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPei.uni @@ -0,0 +1,20 @@ +// /** @file +// IntelVTdPmrPei Module Localized Abstract and Description Content +// +// Copyright (c) 2017, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials are +// licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Intel VTd PMR PEI Driver." + +#string STR_MODULE_DESCRIPTION #language en-US "This driver initializes VTd engine based upon EDKII_VTD_INFO_PPI and provide DMA protection to device in PEI." + diff --git a/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPeiExtra.uni b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPeiExtra.uni new file mode 100644 index 0000000000..c6b2dec9cd --- /dev/null +++ b/IntelSiliconPkg/IntelVTdPmrPei/IntelVTdPmrPeiExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// IntelVTdPmrPei Localized Strings and Content +// +// Copyright (c) 2017, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials are +// licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Intel VTd PMR PEI Driver" + + -- 2.39.2