]> git.proxmox.com Git - mirror_edk2.git/blobdiff - IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.c
IntelSiliconPkg: Add VTd driver.
[mirror_edk2.git] / IntelSiliconPkg / IntelVTdDxe / IntelVTdDxe.c
diff --git a/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.c b/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.c
new file mode 100644 (file)
index 0000000..d22222d
--- /dev/null
@@ -0,0 +1,353 @@
+/** @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