From b575ca32c8b05af5c23f46728ccf4937f2889ba8 Mon Sep 17 00:00:00 2001 From: Jiewen Yao Date: Thu, 7 Sep 2017 16:06:27 +0800 Subject: [PATCH] MdeModulePkg/XhciPei: Support IoMmu. 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 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Jiewen Yao Reviewed-by: Star Zeng --- 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(-) create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c diff --git a/MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c b/MdeModulePkg/Bus/Pci/XhciPei/DmaMem.c new file mode 100644 index 0000000000..6e2c1b582b --- /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.
+ +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 6a3f3a5df3..5d0232ca56 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 c314e92004..c315e6e268 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 38f0d2184c..99f69f730b 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)) { @@ -1398,6 +1402,34 @@ XhcPeiGetRootHubPortStatus ( return EFI_SUCCESS; } +/** + 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 99f0396494..e7a100f297 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 #include +#include +#include #include #include @@ -152,6 +154,12 @@ struct _PEI_XHC_DEV { UINT32 UsbHostControllerBaseAddress; 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 // @@ -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 dc65f283bc..f307ea7646 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 3dd2b89097..e5aee4918b 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; // @@ -2847,6 +2874,13 @@ XhcPeiInitSched ( Xhc->MaxScratchpadBufs = MaxScratchpadBufs; 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 // @@ -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 b3d4c45614..faf2e63d51 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.39.2