diff --git a/ArmPkg/Library/ArmDmaLib/ArmDmaLib.c b/ArmPkg/Library/ArmDmaLib/ArmDmaLib.c index f4ee9e4c5e..566f77d03f 100644 --- a/ArmPkg/Library/ArmDmaLib/ArmDmaLib.c +++ b/ArmPkg/Library/ArmDmaLib/ArmDmaLib.c @@ -2,6 +2,7 @@ Generic ARM implementation of DmaLib.h Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
+ Copyright (c) 2015 - 2017, Linaro, Ltd. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -80,6 +81,7 @@ DmaMap ( MAP_INFO_INSTANCE *Map; VOID *Buffer; EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; + UINTN AllocSize; if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || Mapping == NULL ) { return EFI_INVALID_PARAMETER; @@ -104,8 +106,9 @@ DmaMap ( return EFI_OUT_OF_RESOURCES; } - if ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) || - ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0)) { + if (Operation != MapOperationBusMasterRead && + ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) || + ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0))) { // Get the cacheability of the region Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor); @@ -129,21 +132,31 @@ DmaMap ( } // - // If the buffer does not fill entire cache lines we must double buffer into - // uncached memory. Device (PCI) address becomes uncached page. + // If the buffer does not fill entire cache lines we must double buffer + // into a suitably aligned allocation that allows us to invalidate the + // cache without running the risk of corrupting adjacent unrelated data. + // Note that pool allocations are guaranteed to be 8 byte aligned, so + // we only have to add (alignment - 8) worth of padding. // - Map->DoubleBuffer = TRUE; - Status = DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (*NumberOfBytes), &Buffer); - if (EFI_ERROR (Status)) { + Map->DoubleBuffer = TRUE; + AllocSize = ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment) + + (mCpu->DmaBufferAlignment - 8); + Map->BufferAddress = AllocatePool (AllocSize); + if (Map->BufferAddress == NULL) { + Status = EFI_OUT_OF_RESOURCES; goto FreeMapInfo; } - if (Operation == MapOperationBusMasterRead) { - CopyMem (Buffer, HostAddress, *NumberOfBytes); - } - + Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment); *DeviceAddress = HostToDeviceAddress (ConvertToPhysicalAddress (Buffer)); - Map->BufferAddress = Buffer; + + // + // Get rid of any dirty cachelines covering the double buffer. This + // prevents them from being written back unexpectedly, potentially + // overwriting the data we receive from the device. + // + mCpu->FlushDataCache (mCpu, (UINTN)Buffer, *NumberOfBytes, + EfiCpuFlushTypeWriteBack); } else { Map->DoubleBuffer = FALSE; } @@ -207,6 +220,7 @@ DmaUnmap ( { MAP_INFO_INSTANCE *Map; EFI_STATUS Status; + VOID *Buffer; if (Mapping == NULL) { ASSERT (FALSE); @@ -217,17 +231,20 @@ DmaUnmap ( Status = EFI_SUCCESS; if (Map->DoubleBuffer) { - ASSERT (Map->Operation != MapOperationBusMasterCommonBuffer); + ASSERT (Map->Operation == MapOperationBusMasterWrite); - if (Map->Operation == MapOperationBusMasterCommonBuffer) { + if (Map->Operation != MapOperationBusMasterWrite) { Status = EFI_INVALID_PARAMETER; - } else if (Map->Operation == MapOperationBusMasterWrite) { - CopyMem ((VOID *)(UINTN)Map->HostAddress, Map->BufferAddress, - Map->NumberOfBytes); + } else { + Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment); + + mCpu->FlushDataCache (mCpu, (UINTN)Buffer, Map->NumberOfBytes, + EfiCpuFlushTypeInvalidate); + + CopyMem ((VOID *)(UINTN)Map->HostAddress, Buffer, Map->NumberOfBytes); + + FreePool (Map->BufferAddress); } - - DmaFreeBuffer (EFI_SIZE_TO_PAGES (Map->NumberOfBytes), Map->BufferAddress); - } else { if (Map->Operation == MapOperationBusMasterWrite) { //