--- /dev/null
+/** @file\r
+ Intel VTd driver.\r
+\r
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. 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 <PiDxe.h>\r
+\r
+#include <Protocol/IoMmu.h>\r
+#include <Protocol/PciIo.h>\r
+\r
+#include <Library/IoLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include "DmaProtection.h"\r
+\r
+/**\r
+ Provides the controller-specific addresses required to access system memory from a\r
+ DMA bus master.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Operation Indicates if the bus master is going to read or write to system memory.\r
+ @param HostAddress The system memory address to map to the PCI controller.\r
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
+ that were mapped.\r
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
+ access the hosts HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuMap (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EDKII_IOMMU_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ );\r
+\r
+/**\r
+ Completes the Map() operation and releases any corresponding resources.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The range was unmapped.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuUnmap (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN VOID *Mapping\r
+ );\r
+\r
+/**\r
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+ OperationBusMasterCommonBuffer64 mapping.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Type This parameter is not used and must be ignored.\r
+ @param MemoryType The type of memory to allocate, EfiBootServicesData or\r
+ EfiRuntimeServicesData.\r
+ @param Pages The number of pages to allocate.\r
+ @param HostAddress A pointer to store the base system memory address of the\r
+ allocated range.\r
+ @param Attributes The requested bit mask of attributes for the allocated range.\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were allocated.\r
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuAllocateBuffer (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN EFI_MEMORY_TYPE MemoryType,\r
+ IN UINTN Pages,\r
+ IN OUT VOID **HostAddress,\r
+ IN UINT64 Attributes\r
+ );\r
+\r
+/**\r
+ Frees memory that was allocated with AllocateBuffer().\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Pages The number of pages to free.\r
+ @param HostAddress The base system memory address of the allocated range.\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were freed.\r
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+ was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuFreeBuffer (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress\r
+ );\r
+\r
+/**\r
+ Convert the DeviceHandle to SourceId and Segment.\r
+\r
+ @param[in] DeviceHandle The device who initiates the DMA access request.\r
+ @param[out] Segment The Segment used to identify a VTd engine.\r
+ @param[out] SourceId The SourceId used to identify a VTd engine and table entry.\r
+\r
+ @retval EFI_SUCCESS The Segment and SourceId are returned.\r
+ @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
+ @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
+**/\r
+EFI_STATUS\r
+DeviceHandleToSourceId (\r
+ IN EFI_HANDLE DeviceHandle,\r
+ OUT UINT16 *Segment,\r
+ OUT VTD_SOURCE_ID *SourceId\r
+ )\r
+{\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ UINTN Seg;\r
+ UINTN Bus;\r
+ UINTN Dev;\r
+ UINTN Func;\r
+ EFI_STATUS Status;\r
+ EDKII_PLATFORM_VTD_DEVICE_INFO DeviceInfo;\r
+\r
+ Status = EFI_NOT_FOUND;\r
+ if (mPlatformVTdPolicy != NULL) {\r
+ Status = mPlatformVTdPolicy->GetDeviceId (mPlatformVTdPolicy, DeviceHandle, &DeviceInfo);\r
+ if (!EFI_ERROR(Status)) {\r
+ *Segment = DeviceInfo.Segment;\r
+ *SourceId = DeviceInfo.SourceId;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, (VOID **)&PciIo);\r
+ if (EFI_ERROR(Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ Status = PciIo->GetLocation (PciIo, &Seg, &Bus, &Dev, &Func);\r
+ if (EFI_ERROR(Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ *Segment = (UINT16)Seg;\r
+ SourceId->Bits.Bus = (UINT8)Bus;\r
+ SourceId->Bits.Device = (UINT8)Dev;\r
+ SourceId->Bits.Function = (UINT8)Func;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set IOMMU attribute for a system memory.\r
+\r
+ If the IOMMU protocol exists, the system memory cannot be used\r
+ for DMA by default.\r
+\r
+ When a device requests a DMA access for a system memory,\r
+ the device driver need use SetAttribute() to update the IOMMU\r
+ attribute to request DMA access (read and/or write).\r
+\r
+ The DeviceHandle is used to identify which device submits the request.\r
+ The IOMMU implementation need translate the device path to an IOMMU device ID,\r
+ and set IOMMU hardware register accordingly.\r
+ 1) DeviceHandle can be a standard PCI device.\r
+ The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
+ The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
+ The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
+ After the memory is used, the memory need set 0 to keep it being protected.\r
+ 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
+ The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.\r
+\r
+ @param[in] This The protocol instance pointer.\r
+ @param[in] DeviceHandle The device who initiates the DMA access request.\r
+ @param[in] DeviceAddress The base of device memory address to be used as the DMA memory.\r
+ @param[in] Length The length of device memory address to be used as the DMA memory.\r
+ @param[in] IoMmuAccess The IOMMU access.\r
+\r
+ @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.\r
+ @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
+ @retval EFI_INVALID_PARAMETER DeviceAddress is not IoMmu Page size aligned.\r
+ @retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned.\r
+ @retval EFI_INVALID_PARAMETER Length is 0.\r
+ @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access.\r
+ @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
+ @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+ @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by DeviceAddress and Length.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access.\r
+ @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+VTdSetAttribute (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_PHYSICAL_ADDRESS DeviceAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 IoMmuAccess\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT16 Segment;\r
+ VTD_SOURCE_ID SourceId;\r
+\r
+ DumpVtdIfError ();\r
+\r
+ Status = DeviceHandleToSourceId (DeviceHandle, &Segment, &SourceId);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuSetAttribute: "));\r
+ DEBUG ((DEBUG_VERBOSE, "PCI(S%x.B%x.D%x.F%x) ", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+ DEBUG ((DEBUG_VERBOSE, "(0x%lx~0x%lx) - %lx\n", DeviceAddress, Length, IoMmuAccess));\r
+\r
+ Status = SetAccessAttribute (Segment, SourceId, DeviceAddress, Length, IoMmuAccess);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Set IOMMU attribute for a system memory.\r
+\r
+ If the IOMMU protocol exists, the system memory cannot be used\r
+ for DMA by default.\r
+\r
+ When a device requests a DMA access for a system memory,\r
+ the device driver need use SetAttribute() to update the IOMMU\r
+ attribute to request DMA access (read and/or write).\r
+\r
+ The DeviceHandle is used to identify which device submits the request.\r
+ The IOMMU implementation need translate the device path to an IOMMU device ID,\r
+ and set IOMMU hardware register accordingly.\r
+ 1) DeviceHandle can be a standard PCI device.\r
+ The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
+ The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
+ The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
+ After the memory is used, the memory need set 0 to keep it being protected.\r
+ 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
+ The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.\r
+\r
+ @param[in] This The protocol instance pointer.\r
+ @param[in] DeviceHandle The device who initiates the DMA access request.\r
+ @param[in] Mapping The mapping value returned from Map().\r
+ @param[in] IoMmuAccess The IOMMU access.\r
+\r
+ @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.\r
+ @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+ @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access.\r
+ @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
+ @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+ @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by Mapping.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access.\r
+ @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuSetAttribute (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN VOID *Mapping,\r
+ IN UINT64 IoMmuAccess\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+ UINTN NumberOfPages;\r
+\r
+ Status = GetDeviceInfoFromMapping (Mapping, &DeviceAddress, &NumberOfPages);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ Status = VTdSetAttribute (\r
+ This,\r
+ DeviceHandle,\r
+ DeviceAddress,\r
+ EFI_PAGES_TO_SIZE(NumberOfPages),\r
+ IoMmuAccess\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+EDKII_IOMMU_PROTOCOL mIntelVTd = {\r
+ EDKII_IOMMU_PROTOCOL_REVISION,\r
+ IoMmuSetAttribute,\r
+ IoMmuMap,\r
+ IoMmuUnmap,\r
+ IoMmuAllocateBuffer,\r
+ IoMmuFreeBuffer,\r
+};\r
+\r
+/**\r
+ Initialize the VTd driver.\r
+\r
+ @param[in] ImageHandle ImageHandle of the loaded driver\r
+ @param[in] SystemTable Pointer to the System Table\r
+\r
+ @retval EFI_SUCCESS The Protocol is installed.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.\r
+ @retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IntelVTdInitialize (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+\r
+ InitializeDmaProtection ();\r
+\r
+ Handle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEdkiiIoMmuProtocolGuid, &mIntelVTd,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r