diff --git a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c index 4daf51761ba0..1b726284d60a 100644 --- a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c +++ b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c @@ -1272,6 +1272,7 @@ NonCoherentPciIoMap ( VOID *AllocAddress; EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; BOOLEAN Bounce; + BOOLEAN MappingIsCached; if ((HostAddress == NULL) || (NumberOfBytes == NULL) || @@ -1288,113 +1289,130 @@ NonCoherentPciIoMap ( return EFI_INVALID_PARAMETER; } - MapInfo = AllocatePool (sizeof *MapInfo); - if (MapInfo == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - MapInfo->HostAddress = HostAddress; - MapInfo->Operation = Operation; - MapInfo->NumberOfBytes = *NumberOfBytes; - Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO (This); // - // If this device does not support 64-bit DMA addressing, we need to allocate - // a bounce buffer and copy over the data in case HostAddress >= 4 GB. + // A bounce buffer will be needed if this device does not support 64-bit DMA + // addressing and the HostAddress >= 4 GB. // Bounce = ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 && (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB); - if (!Bounce) { - switch (Operation) { - case EfiPciIoOperationBusMasterRead: - case EfiPciIoOperationBusMasterWrite: - // - // For streaming DMA, it is sufficient if the buffer is aligned to - // the CPUs DMA buffer alignment. - // - AlignMask = mCpu->DmaBufferAlignment - 1; - if ((((UINTN)HostAddress | *NumberOfBytes) & AlignMask) == 0) { - break; - } - - // fall through - - case EfiPciIoOperationBusMasterCommonBuffer: - // - // Check whether the host address refers to an uncached mapping. - // - Status = gDS->GetMemorySpaceDescriptor ( - (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, - &GcdDescriptor - ); - if (EFI_ERROR (Status) || - ((GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0)) - { - Bounce = TRUE; - } - - break; - - default: - ASSERT (FALSE); - } + // + // Check whether the host address refers to an uncached mapping. This is + // required for common buffer mappings. + // + Status = gDS->GetMemorySpaceDescriptor ( + (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, + &GcdDescriptor + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; } - if (Bounce) { - if (Operation == EfiPciIoOperationBusMasterCommonBuffer) { - Status = EFI_DEVICE_ERROR; - goto FreeMapInfo; - } + MappingIsCached = ((GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0); - Status = NonCoherentPciIoAllocateBuffer ( - This, - AllocateAnyPages, - EfiBootServicesData, - EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), - &AllocAddress, - EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE - ); - if (EFI_ERROR (Status)) { - goto FreeMapInfo; + if (!MappingIsCached) { + if (Bounce) { + // + // Bounce buffering from an uncached mapping is very inefficient, and + // should never be needed, given that NonCoherentPciIoAllocateBuffer () + // will take the EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE attribute in + // account too. + // + ASSERT (MappingIsCached || !Bounce); + return EFI_DEVICE_ERROR; } + // + // No bounce buffering or cache maintenance is needed for uncached mappings + // that are in DMA range for the device, regardless of the DMA direction. + // + *Mapping = (VOID *)MAX_ADDRESS; + return EFI_SUCCESS; + } + + switch (Operation) { + case EfiPciIoOperationBusMasterRead: + // + // Bounce buffering is never needed for streaming non-coherent outbound + // DMA involving cached memory, given that the bus master will only read + // from the region, and so there is no need for the potentially + // destructive cache invalidation that might otherwise result in + // corruption of unrelated adjacent memory. + // + break; + + case EfiPciIoOperationBusMasterWrite: + // + // For streaming non-coherent inbound DMA involving cached memory, no + // bounce buffering is needed unless the buffer is misaligned wrt the + // CPU's DMA buffer alignment, as cache lines that are shared with + // unrelated adjacent data might be corrupted by the invalidation + // performed on unmap. + // + AlignMask = mCpu->DmaBufferAlignment - 1; + if ((((UINTN)HostAddress | *NumberOfBytes) & AlignMask) != 0) { + Bounce = TRUE; + } + + break; + + case EfiPciIoOperationBusMasterCommonBuffer: + // Non-coherent common buffer DMA requires uncached mappings. + ASSERT (!MappingIsCached); + return EFI_DEVICE_ERROR; + + default: + ASSERT (FALSE); + } + + MapInfo = AllocatePool (sizeof *MapInfo); + if (MapInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MapInfo->HostAddress = HostAddress; + MapInfo->Operation = Operation; + MapInfo->NumberOfBytes = *NumberOfBytes; + + if (Bounce) { + AllocAddress = AllocatePool ( + ALIGN_VALUE ( + MapInfo->NumberOfBytes, + mCpu->DmaBufferAlignment + ) + mCpu->DmaBufferAlignment - 8 + ); MapInfo->AllocAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress; + *DeviceAddress = ALIGN_VALUE ( + MapInfo->AllocAddress, + mCpu->DmaBufferAlignment + ); if (Operation == EfiPciIoOperationBusMasterRead) { - gBS->CopyMem (AllocAddress, HostAddress, *NumberOfBytes); + gBS->CopyMem ((VOID *)(UINTN)*DeviceAddress, HostAddress, *NumberOfBytes); } - - *DeviceAddress = MapInfo->AllocAddress; } else { MapInfo->AllocAddress = 0; *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; - - // - // We are not using a bounce buffer: the mapping is sufficiently - // aligned to allow us to simply flush the caches. Note that cleaning - // the caches is necessary for both data directions: - // - for bus master read, we want the latest data to be present - // in main memory - // - for bus master write, we don't want any stale dirty cachelines that - // may be written back unexpectedly, and clobber the data written to - // main memory by the device. - // - mCpu->FlushDataCache ( - mCpu, - (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, - *NumberOfBytes, - EfiCpuFlushTypeWriteBack - ); } + // + // Cleaning the caches is necessary for both data directions: + // - for bus master read, we want the latest data to be present in main + // memory + // - for bus master write, we don't want any stale dirty cachelines that may + // be written back unexpectedly, and clobber the data written to main + // memory by the device. + // + mCpu->FlushDataCache ( + mCpu, + *DeviceAddress, + ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment), + EfiCpuFlushTypeWriteBack + ); + *Mapping = MapInfo; return EFI_SUCCESS; - -FreeMapInfo: - FreePool (MapInfo); - - return Status; } /** @@ -1415,46 +1433,42 @@ NonCoherentPciIoUnmap ( ) { NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo; + VOID *AllocAddress; + VOID *BufferAddress; if (Mapping == NULL) { return EFI_DEVICE_ERROR; } - MapInfo = Mapping; - if (MapInfo->AllocAddress != 0) { - // - // We are using a bounce buffer: copy back the data if necessary, - // and free the buffer. - // - if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) { - gBS->CopyMem ( - MapInfo->HostAddress, - (VOID *)(UINTN)MapInfo->AllocAddress, - MapInfo->NumberOfBytes - ); + if (Mapping == (VOID *)MAX_ADDRESS) { + return EFI_SUCCESS; + } + + MapInfo = Mapping; + AllocAddress = (VOID *)(UINTN)MapInfo->AllocAddress; + if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) { + if (AllocAddress == NULL) { + BufferAddress = MapInfo->HostAddress; + } else { + BufferAddress = ALIGN_POINTER (AllocAddress, mCpu->DmaBufferAlignment); } - NonCoherentPciIoFreeBuffer ( - This, - EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), - (VOID *)(UINTN)MapInfo->AllocAddress - ); - } else { - // - // We are *not* using a bounce buffer: if this is a bus master write, - // we have to invalidate the caches so the CPU will see the uncached - // data written by the device. - // - if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) { - mCpu->FlushDataCache ( - mCpu, - (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress, - MapInfo->NumberOfBytes, - EfiCpuFlushTypeInvalidate - ); + mCpu->FlushDataCache ( + mCpu, + (EFI_PHYSICAL_ADDRESS)(UINTN)BufferAddress, + ALIGN_VALUE (MapInfo->NumberOfBytes, mCpu->DmaBufferAlignment), + EfiCpuFlushTypeInvalidate + ); + + if (MapInfo->HostAddress != BufferAddress) { + gBS->CopyMem (MapInfo->HostAddress, BufferAddress, MapInfo->NumberOfBytes); } } + if (AllocAddress != NULL) { + FreePool (AllocAddress); + } + FreePool (MapInfo); return EFI_SUCCESS; }