[edk2] [PATCH 11/11] MdeModulePkg/XhciPei: Support IoMmu.

Jiewen Yao posted 11 patches 7 years, 3 months ago
[edk2] [PATCH 11/11] MdeModulePkg/XhciPei: Support IoMmu.
Posted by Jiewen Yao 7 years, 3 months ago
Update XHCI driver to consume IOMMU_PPI to allocate DMA buffer.

If no IOMMU_PPI exists, this driver still calls PEI service
to allocate DMA buffer, with assumption that DRAM==DMA.

This is a compatible change.

Cc: Star Zeng <star.zeng@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jiewen Yao <jiewen.yao@intel.com>
---
 MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c    | 249 ++++++++++++++++++++
 MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c  |  55 +++--
 MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h  |   9 +-
 MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c   |  55 ++++-
 MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h   | 107 +++++++++
 MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf |   3 +
 MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c |  47 +++-
 MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h |   1 +
 8 files changed, 492 insertions(+), 34 deletions(-)

diff --git a/MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c b/MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c
new file mode 100644
index 0000000..6e2c1b5
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c
@@ -0,0 +1,249 @@
+/** @file
+The DMA memory help function.
+
+Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+
+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 "XhcPeim.h"
+
+EDKII_IOMMU_PPI  *mIoMmu;
+
+/**
+  Provides the controller-specific addresses required to access system memory from a
+  DMA bus master.
+
+  @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
+IoMmuMap (
+  IN  EDKII_IOMMU_OPERATION Operation,
+  IN VOID                   *HostAddress,
+  IN  OUT UINTN             *NumberOfBytes,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Attribute;
+
+  if (mIoMmu != NULL) {
+    Status = mIoMmu->Map (
+                       mIoMmu,
+                       Operation,
+                       HostAddress,
+                       NumberOfBytes,
+                       DeviceAddress,
+                       Mapping
+                       );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    switch (Operation) {
+    case EdkiiIoMmuOperationBusMasterRead:
+    case EdkiiIoMmuOperationBusMasterRead64:
+      Attribute = EDKII_IOMMU_ACCESS_READ;
+      break;
+    case EdkiiIoMmuOperationBusMasterWrite:
+    case EdkiiIoMmuOperationBusMasterWrite64:
+      Attribute = EDKII_IOMMU_ACCESS_WRITE;
+      break;
+    case EdkiiIoMmuOperationBusMasterCommonBuffer:
+    case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+      Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;
+      break;
+    default:
+      ASSERT(FALSE);
+      return EFI_INVALID_PARAMETER;
+    }
+    Status = mIoMmu->SetAttribute (
+                       mIoMmu,
+                       *Mapping,
+                       Attribute
+                       );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  } else {
+    *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+    *Mapping = NULL;
+    Status = EFI_SUCCESS;
+  }
+  return Status;
+}
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @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
+IoMmuUnmap (
+  IN VOID                  *Mapping
+  )
+{
+  EFI_STATUS  Status;
+
+  if (mIoMmu != NULL) {
+    Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0);
+    Status = mIoMmu->Unmap (mIoMmu, Mapping);
+  } else {
+    Status = EFI_SUCCESS;
+  }
+  return Status;
+}
+
+/**
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+  OperationBusMasterCommonBuffer64 mapping.
+
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory address of the
+                                allocated range.
+  @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 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
+IoMmuAllocateBuffer (
+  IN UINTN                  Pages,
+  OUT VOID                  **HostAddress,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  )
+{
+  EFI_STATUS            Status;
+  UINTN                 NumberOfBytes;
+  EFI_PHYSICAL_ADDRESS  HostPhyAddress;
+
+  *HostAddress = NULL;
+  *DeviceAddress = 0;
+
+  if (mIoMmu != NULL) {
+    Status = mIoMmu->AllocateBuffer (
+                       mIoMmu,
+                       EfiBootServicesData,
+                       Pages,
+                       HostAddress,
+                       0
+                       );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    NumberOfBytes = EFI_PAGES_TO_SIZE(Pages);
+    Status = mIoMmu->Map (
+                       mIoMmu,
+                       EdkiiIoMmuOperationBusMasterCommonBuffer,
+                       *HostAddress,
+                       &NumberOfBytes,
+                       DeviceAddress,
+                       Mapping
+                       );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    Status = mIoMmu->SetAttribute (
+                       mIoMmu,
+                       *Mapping,
+                       EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
+                       );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  } else {
+    Status = PeiServicesAllocatePages (
+               EfiBootServicesData,
+               Pages,
+               &HostPhyAddress
+               );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    *HostAddress = (VOID *)(UINTN)HostPhyAddress;
+    *DeviceAddress = HostPhyAddress;
+    *Mapping = NULL;
+  }
+  return Status;
+}
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated range.
+  @param  Mapping               The mapping value returned from Map().
+
+  @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
+IoMmuFreeBuffer (
+  IN UINTN                  Pages,
+  IN VOID                   *HostAddress,
+  IN VOID                   *Mapping
+  )
+{
+  EFI_STATUS  Status;
+
+  if (mIoMmu != NULL) {
+    Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0);
+    Status = mIoMmu->Unmap (mIoMmu, Mapping);
+    Status = mIoMmu->FreeBuffer (mIoMmu, Pages, HostAddress);
+  } else {
+    Status = EFI_SUCCESS;
+  }
+  return Status;
+}
+
+/**
+  Initialize IOMMU.
+**/
+VOID
+IoMmuInit (
+  VOID
+  )
+{
+  PeiServicesLocatePpi (
+    &gEdkiiIoMmuPpiGuid,
+    0,
+    NULL,
+    (VOID **)&mIoMmu
+    );
+}
+
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
index 6a3f3a5..5d0232c 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
+++ b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
@@ -31,6 +31,9 @@ UsbHcAllocMemBlock (
   )
 {
   USBHC_MEM_BLOCK       *Block;
+  VOID                  *BufHost;
+  VOID                  *Mapping;
+  EFI_PHYSICAL_ADDRESS  MappedAddr;
   EFI_STATUS            Status;
   UINTN                 PageNumber;
   EFI_PHYSICAL_ADDRESS  TempPtr;
@@ -71,18 +74,20 @@ UsbHcAllocMemBlock (
 
   Block->Bits = (UINT8 *) (UINTN) TempPtr;
 
-  Status = PeiServicesAllocatePages (
-             EfiBootServicesData,
+  Status = IoMmuAllocateBuffer (
              Pages,
-             &TempPtr
+             &BufHost,
+             &MappedAddr,
+             &Mapping
              );
   if (EFI_ERROR (Status)) {
     return NULL;
   }
-  ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (Pages));
+  ZeroMem ((VOID *) (UINTN) BufHost, EFI_PAGES_TO_SIZE (Pages));
 
-  Block->BufHost = (UINT8 *) (UINTN) TempPtr;;
-  Block->Buf = (UINT8 *) (UINTN) TempPtr;
+  Block->BufHost = (UINT8 *) (UINTN) BufHost;
+  Block->Buf = (UINT8 *) (UINTN) MappedAddr;
+  Block->Mapping  = Mapping;
   Block->Next = NULL;
 
   return Block;
@@ -102,6 +107,9 @@ UsbHcFreeMemBlock (
   )
 {
   ASSERT ((Pool != NULL) && (Block != NULL));
+
+  IoMmuFreeBuffer (EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping);
+
   //
   // No free memory in PEI.
   //
@@ -567,6 +575,7 @@ UsbHcFreeMem (
   @param  HostAddress           The system memory address to map to the PCI controller.
   @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           Success to allocate aligned pages.
   @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid.
@@ -578,13 +587,16 @@ UsbHcAllocateAlignedPages (
   IN UINTN                      Pages,
   IN UINTN                      Alignment,
   OUT VOID                      **HostAddress,
-  OUT EFI_PHYSICAL_ADDRESS      *DeviceAddress
+  OUT EFI_PHYSICAL_ADDRESS      *DeviceAddress,
+  OUT VOID                      **Mapping
   )
 {
   EFI_STATUS            Status;
-  EFI_PHYSICAL_ADDRESS  Memory;
+  VOID                  *Memory;
   UINTN                 AlignedMemory;
   UINTN                 AlignmentMask;
+  EFI_PHYSICAL_ADDRESS  DeviceMemory;
+  UINTN                 AlignedDeviceMemory;
   UINTN                 RealPages;
 
   //
@@ -611,32 +623,36 @@ UsbHcAllocateAlignedPages (
     //
     ASSERT (RealPages > Pages);
 
-    Status = PeiServicesAllocatePages (
-               EfiBootServicesData,
+    Status = IoMmuAllocateBuffer (
                Pages,
-               &Memory
+               &Memory,
+               &DeviceMemory,
+               Mapping
                );
     if (EFI_ERROR (Status)) {
       return EFI_OUT_OF_RESOURCES;
     }
     AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;
+    AlignedDeviceMemory = ((UINTN) DeviceMemory + AlignmentMask) & ~AlignmentMask;
   } else {
     //
     // Do not over-allocate pages in this case.
     //
-    Status = PeiServicesAllocatePages (
-               EfiBootServicesData,
+    Status = IoMmuAllocateBuffer (
                Pages,
-               &Memory
+               &Memory,
+               &DeviceMemory,
+               Mapping
                );
     if (EFI_ERROR (Status)) {
       return EFI_OUT_OF_RESOURCES;
     }
     AlignedMemory = (UINTN) Memory;
+    AlignedDeviceMemory = (UINTN) DeviceMemory;
   }
 
   *HostAddress = (VOID *) AlignedMemory;
-  *DeviceAddress = (EFI_PHYSICAL_ADDRESS) AlignedMemory;
+  *DeviceAddress = (EFI_PHYSICAL_ADDRESS) AlignedDeviceMemory;
 
   return EFI_SUCCESS;
 }
@@ -646,17 +662,18 @@ UsbHcAllocateAlignedPages (
 
   @param  HostAddress           The system memory address to map to the PCI controller.
   @param  Pages                 The number of pages to free.
+  @param  Mapping               The mapping value returned from Map().
 
 **/
 VOID
 UsbHcFreeAlignedPages (
   IN VOID               *HostAddress,
-  IN UINTN              Pages
+  IN UINTN              Pages,
+  IN VOID               *Mapping
   )
 {
   ASSERT (Pages != 0);
-  //
-  // No free memory in PEI.
-  //
+
+  IoMmuFreeBuffer (Pages, HostAddress, Mapping);
 }
 
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h
index c314e92..c315e6e 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h
+++ b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h
@@ -29,6 +29,7 @@ struct _USBHC_MEM_BLOCK {
   UINT8                 *Buf;
   UINT8                 *BufHost;
   UINTN                 BufLen; // Memory size in bytes
+  VOID                  *Mapping;
   USBHC_MEM_BLOCK       *Next;
 };
 
@@ -112,6 +113,7 @@ UsbHcGetHostAddrForPciAddr (
   @param  HostAddress           The system memory address to map to the PCI controller.
   @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           Success to allocate aligned pages.
   @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid.
@@ -123,7 +125,8 @@ UsbHcAllocateAlignedPages (
   IN UINTN                      Pages,
   IN UINTN                      Alignment,
   OUT VOID                      **HostAddress,
-  OUT EFI_PHYSICAL_ADDRESS      *DeviceAddress
+  OUT EFI_PHYSICAL_ADDRESS      *DeviceAddress,
+  OUT VOID                      **Mapping
   );
 
 /**
@@ -131,12 +134,14 @@ UsbHcAllocateAlignedPages (
 
   @param  HostAddress           The system memory address to map to the PCI controller.
   @param  Pages                 The number of pages to free.
+  @param  Mapping               The mapping value returned from Map().
 
 **/
 VOID
 UsbHcFreeAlignedPages (
   IN VOID               *HostAddress,
-  IN UINTN              Pages
+  IN UINTN              Pages,
+  IN VOID               *Mapping
   );
 
 #endif
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c
index 38f0d21..99f69f7 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c
+++ b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c
@@ -662,7 +662,8 @@ XhcPeiControlTransfer (
     if (EFI_ERROR(RecoveryStatus)) {
       DEBUG((EFI_D_ERROR, "XhcPeiControlTransfer: XhcPeiDequeueTrbFromEndpoint failed\n"));
     }
-    goto FREE_URB;
+    XhcPeiFreeUrb (Xhc, Urb);
+    goto ON_EXIT;
   } else {
     if (*TransferResult == EFI_USB_NOERROR) {
       Status = EFI_SUCCESS;
@@ -672,11 +673,17 @@ XhcPeiControlTransfer (
         DEBUG ((EFI_D_ERROR, "XhcPeiControlTransfer: XhcPeiRecoverHaltedEndpoint failed\n"));
       }
       Status = EFI_DEVICE_ERROR;
-      goto FREE_URB;
+      XhcPeiFreeUrb (Xhc, Urb);
+      goto ON_EXIT;
     } else {
-      goto FREE_URB;
+      XhcPeiFreeUrb (Xhc, Urb);
+      goto ON_EXIT;
     }
   }
+  //
+  // Unmap data before consume.
+  //
+  XhcPeiFreeUrb (Xhc, Urb);
 
   //
   // Hook Get_Descriptor request from UsbBus as we need evaluate context and configure endpoint.
@@ -704,7 +711,7 @@ XhcPeiControlTransfer (
       Xhc->UsbDevContext[SlotId].ConfDesc = AllocateZeroPool (Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations * sizeof (EFI_USB_CONFIG_DESCRIPTOR *));
       if (Xhc->UsbDevContext[SlotId].ConfDesc == NULL) {
         Status = EFI_OUT_OF_RESOURCES;
-        goto FREE_URB;
+        goto ON_EXIT;
       }
       if (Xhc->HcCParams.Data.Csz == 0) {
         Status = XhcPeiEvaluateContext (Xhc, SlotId, MaxPacket0);
@@ -722,7 +729,7 @@ XhcPeiControlTransfer (
         Xhc->UsbDevContext[SlotId].ConfDesc[Index] = AllocateZeroPool (*DataLength);
         if (Xhc->UsbDevContext[SlotId].ConfDesc[Index] == NULL) {
           Status = EFI_OUT_OF_RESOURCES;
-          goto FREE_URB;
+          goto ON_EXIT;
         }
         CopyMem (Xhc->UsbDevContext[SlotId].ConfDesc[Index], Data, *DataLength);
       }
@@ -844,9 +851,6 @@ XhcPeiControlTransfer (
     *(UINT32 *) Data = *(UINT32 *) &PortStatus;
   }
 
-FREE_URB:
-  XhcPeiFreeUrb (Xhc, Urb);
-
 ON_EXIT:
 
   if (EFI_ERROR (Status)) {
@@ -1399,6 +1403,34 @@ XhcPeiGetRootHubPortStatus (
 }
 
 /**
+  One notified function to stop the Host Controller at the end of PEI
+
+  @param[in]  PeiServices        Pointer to PEI Services Table.
+  @param[in]  NotifyDescriptor   Pointer to the descriptor for the Notification event that
+                                 caused this function to execute.
+  @param[in]  Ppi                Pointer to the PPI data associated with this function.
+
+  @retval     EFI_SUCCESS  The function completes successfully
+  @retval     others
+**/
+EFI_STATUS
+EFIAPI
+XhcEndOfPei (
+  IN EFI_PEI_SERVICES           **PeiServices,
+  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
+  IN VOID                       *Ppi
+  )
+{
+  PEI_XHC_DEV    *Xhc;
+
+  Xhc = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS_NOTIFY(NotifyDescriptor);
+
+  XhcPeiHaltHC (Xhc, XHC_GENERIC_TIMEOUT);
+
+  return EFI_SUCCESS;
+}
+
+/**
   @param FileHandle     Handle of the file being invoked.
   @param PeiServices    Describes the list of possible PEI Services.
 
@@ -1429,6 +1461,8 @@ XhcPeimEntry (
     return EFI_SUCCESS;
   }
 
+  IoMmuInit ();
+
   Status = PeiServicesLocatePpi (
              &gPeiUsbControllerPpiGuid,
              0,
@@ -1530,7 +1564,12 @@ XhcPeimEntry (
     XhcDev->PpiDescriptor.Guid = &gPeiUsb2HostControllerPpiGuid;
     XhcDev->PpiDescriptor.Ppi = &XhcDev->Usb2HostControllerPpi;
 
+    XhcDev->EndOfPeiNotifyList.Flags = (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
+    XhcDev->EndOfPeiNotifyList.Guid = &gEfiEndOfPeiSignalPpiGuid;
+    XhcDev->EndOfPeiNotifyList.Notify = XhcEndOfPei;
+
     PeiServicesInstallPpi (&XhcDev->PpiDescriptor);
+    PeiServicesNotifyPpi (&XhcDev->EndOfPeiNotifyList);
 
     Index++;
   }
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h
index 99f0396..e7a100f 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h
+++ b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h
@@ -21,6 +21,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 
 #include <Ppi/UsbController.h>
 #include <Ppi/Usb2HostController.h>
+#include <Ppi/IoMmu.h>
+#include <Ppi/EndOfPeiPhase.h>
 
 #include <Library/DebugLib.h>
 #include <Library/PeimEntryPoint.h>
@@ -153,6 +155,12 @@ struct _PEI_XHC_DEV {
   USBHC_MEM_POOL                    *MemPool;
 
   //
+  // EndOfPei callback is used to stop the XHC DMA operation
+  // after exit PEI phase.
+  //
+  EFI_PEI_NOTIFY_DESCRIPTOR         EndOfPeiNotifyList;
+
+  //
   // XHCI configuration data
   //
   UINT8                             CapLength;    ///< Capability Register Length
@@ -164,7 +172,9 @@ struct _PEI_XHC_DEV {
   UINT32                            PageSize;
   UINT32                            MaxScratchpadBufs;
   UINT64                            *ScratchBuf;
+  VOID                              *ScratchMap;
   UINT64                            *ScratchEntry;
+  UINTN                             *ScratchEntryMap;
   UINT64                            *DCBAA;
   UINT32                            MaxSlotsEn;
   //
@@ -184,6 +194,7 @@ struct _PEI_XHC_DEV {
 };
 
 #define PEI_RECOVERY_USB_XHC_DEV_FROM_THIS(a) CR (a, PEI_XHC_DEV, Usb2HostControllerPpi, USB_XHC_DEV_SIGNATURE)
+#define PEI_RECOVERY_USB_XHC_DEV_FROM_THIS_NOTIFY(a) CR (a, PEI_XHC_DEV, EndOfPeiNotifyList, USB_XHC_DEV_SIGNATURE)
 
 /**
   Initialize the memory management pool for the host controller.
@@ -242,4 +253,100 @@ UsbHcFreeMem (
   )
 ;
 
+
+/**
+  Initialize IOMMU.
+**/
+VOID
+IoMmuInit (
+  VOID
+  );
+
+/**
+  Provides the controller-specific addresses required to access system memory from a
+  DMA bus master.
+
+  @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
+IoMmuMap (
+  IN  EDKII_IOMMU_OPERATION Operation,
+  IN  VOID                  *HostAddress,
+  IN  OUT UINTN             *NumberOfBytes,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  );
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @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
+IoMmuUnmap (
+  IN VOID                  *Mapping
+  );
+
+/**
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+  OperationBusMasterCommonBuffer64 mapping.
+
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory address of the
+                                allocated range.
+  @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 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
+IoMmuAllocateBuffer (
+  IN UINTN                  Pages,
+  OUT VOID                  **HostAddress,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  );
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated range.
+  @param  Mapping               The mapping value returned from Map().
+
+  @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
+IoMmuFreeBuffer (
+  IN UINTN                  Pages,
+  IN VOID                   *HostAddress,
+  IN VOID                   *Mapping
+  );
+
 #endif
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf b/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf
index dc65f28..f307ea7 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf
+++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf
@@ -37,6 +37,7 @@
   XhcPeim.h
   XhciSched.c
   UsbHcMem.c
+  DmaMem.c
   XhciReg.h
   XhciSched.h
   UsbHcMem.h
@@ -56,6 +57,8 @@
 [Ppis]
   gPeiUsb2HostControllerPpiGuid                 ## PRODUCES
   gPeiUsbControllerPpiGuid                      ## CONSUMES
+  gEdkiiIoMmuPpiGuid                            ## CONSUMES
+  gEfiEndOfPeiSignalPpiGuid                     ## CONSUMES
 
 [Depex]
   gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c
index 3dd2b89..e5aee49 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c
+++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c
@@ -200,6 +200,8 @@ XhcPeiFreeUrb (
     return;
   }
 
+  IoMmuUnmap (Urb->DataMap);
+
   FreePool (Urb);
 }
 
@@ -227,6 +229,10 @@ XhcPeiCreateTransferTrb (
   UINTN                         TotalLen;
   UINTN                         Len;
   UINTN                         TrbNum;
+  EDKII_IOMMU_OPERATION         MapOp;
+  EFI_PHYSICAL_ADDRESS          PhyAddr;
+  VOID                          *Map;
+  EFI_STATUS                    Status;
 
   SlotId = XhcPeiBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr);
   if (SlotId == 0) {
@@ -249,7 +255,27 @@ XhcPeiCreateTransferTrb (
     EPType  = (UINT8) ((DEVICE_CONTEXT_64 *)OutputContext)->EP[Dci-1].EPType;
   }
 
-  Urb->DataPhy = Urb->Data;
+  //
+  // No need to remap.
+  //
+  if ((Urb->Data != NULL) && (Urb->DataMap == NULL)) {
+    if (((UINT8) (Urb->Ep.Direction)) == EfiUsbDataIn) {
+      MapOp = EdkiiIoMmuOperationBusMasterWrite;
+    } else {
+      MapOp = EdkiiIoMmuOperationBusMasterRead;
+    }
+
+    Len = Urb->DataLen;
+    Status = IoMmuMap (MapOp, Urb->Data, &Len, &PhyAddr, &Map);
+
+    if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {
+      DEBUG ((DEBUG_ERROR, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Urb->DataPhy  = (VOID *) ((UINTN) PhyAddr);
+    Urb->DataMap  = Map;
+  }
 
   //
   // Construct the TRB
@@ -2812,6 +2838,7 @@ XhcPeiInitSched (
   UINT64                *ScratchEntry;
   EFI_PHYSICAL_ADDRESS  ScratchEntryPhy;
   UINT32                Index;
+  UINTN                 *ScratchEntryMap;
   EFI_STATUS            Status;
 
   //
@@ -2848,6 +2875,13 @@ XhcPeiInitSched (
   ASSERT (MaxScratchpadBufs <= 1023);
   if (MaxScratchpadBufs != 0) {
     //
+    // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
+    //
+    ScratchEntryMap = AllocateZeroPool (sizeof (UINTN) * MaxScratchpadBufs);
+    ASSERT (ScratchEntryMap != NULL);
+    Xhc->ScratchEntryMap = ScratchEntryMap;
+
+    //
     // Allocate the buffer to record the host address for each entry
     //
     ScratchEntry = AllocateZeroPool (sizeof (UINT64) * MaxScratchpadBufs);
@@ -2859,7 +2893,8 @@ XhcPeiInitSched (
                EFI_SIZE_TO_PAGES (MaxScratchpadBufs * sizeof (UINT64)),
                Xhc->PageSize,
                (VOID **) &ScratchBuf,
-               &ScratchPhy
+               &ScratchPhy,
+               &Xhc->ScratchMap
                );
     ASSERT_EFI_ERROR (Status);
 
@@ -2875,7 +2910,8 @@ XhcPeiInitSched (
                  EFI_SIZE_TO_PAGES (Xhc->PageSize),
                  Xhc->PageSize,
                  (VOID **) &ScratchEntry[Index],
-                 &ScratchEntryPhy
+                 &ScratchEntryPhy,
+                 (VOID **) &ScratchEntryMap[Index]
                  );
       ASSERT_EFI_ERROR (Status);
       ZeroMem ((VOID *) (UINTN) ScratchEntry[Index], Xhc->PageSize);
@@ -2967,12 +3003,13 @@ XhcPeiFreeSched (
       //
       // Free Scratchpad Buffers
       //
-      UsbHcFreeAlignedPages ((VOID*) (UINTN) ScratchEntry[Index], EFI_SIZE_TO_PAGES (Xhc->PageSize));
+      UsbHcFreeAlignedPages ((VOID*) (UINTN) ScratchEntry[Index], EFI_SIZE_TO_PAGES (Xhc->PageSize), (VOID *) Xhc->ScratchEntryMap[Index]);
     }
     //
     // Free Scratchpad Buffer Array
     //
-    UsbHcFreeAlignedPages (Xhc->ScratchBuf, EFI_SIZE_TO_PAGES (Xhc->MaxScratchpadBufs * sizeof (UINT64)));
+    UsbHcFreeAlignedPages (Xhc->ScratchBuf, EFI_SIZE_TO_PAGES (Xhc->MaxScratchpadBufs * sizeof (UINT64)), Xhc->ScratchMap);
+    FreePool (Xhc->ScratchEntryMap);
     FreePool (Xhc->ScratchEntry);
   }
 
diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h
index b3d4c45..faf2e63 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h
+++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h
@@ -170,6 +170,7 @@ typedef struct _URB {
   VOID                              *Data;
   UINTN                             DataLen;
   VOID                              *DataPhy;
+  VOID                              *DataMap;
   EFI_ASYNC_USB_TRANSFER_CALLBACK   Callback;
   VOID                              *Context;
   //
-- 
2.7.4.windows.1

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel