From: "Ard Biesheuvel" <ard.biesheuvel@linaro.org>
To: devel@edk2.groups.io
Cc: leif.lindholm@linaro.org, Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [PATCH 1/2] EmbeddedPkg/NonCoherentDmaLib: implement support for DMA range limits
Date: Thu, 21 Nov 2019 09:32:26 +0100 [thread overview]
Message-ID: <20191121083227.2850-2-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <20191121083227.2850-1-ard.biesheuvel@linaro.org>
Implement support for driving peripherals with limited DMA ranges to
NonCoherentDmaLib, by adding a device address limit, and taking it,
along with the device offset, into account when allocating or mapping
DMA buffers.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
EmbeddedPkg/EmbeddedPkg.dec | 6 +
EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c | 176 +++++++++++++++++---
EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf | 1 +
3 files changed, 158 insertions(+), 25 deletions(-)
diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
index 8812a6db7c30..69922802f473 100644
--- a/EmbeddedPkg/EmbeddedPkg.dec
+++ b/EmbeddedPkg/EmbeddedPkg.dec
@@ -186,6 +186,12 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
#
gEmbeddedTokenSpaceGuid.PcdDmaDeviceOffset|0x0|UINT64|0x0000058
+ #
+ # Highest address value supported by the device for DMA addressing. Note
+ # that this value should be strictly greater than PcdDmaDeviceOffset.
+ #
+ gEmbeddedTokenSpaceGuid.PcdDmaDeviceLimit|0xFFFFFFFFFFFFFFFF|UINT64|0x000005A
+
#
# Selection between DT and ACPI as a default
#
diff --git a/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c b/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c
index 78220f6358aa..dd3d111e7eb9 100644
--- a/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c
+++ b/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c
@@ -40,6 +40,8 @@ typedef struct {
STATIC EFI_CPU_ARCH_PROTOCOL *mCpu;
STATIC LIST_ENTRY UncachedAllocationList;
+STATIC PHYSICAL_ADDRESS mDmaHostAddressLimit;
+
STATIC
PHYSICAL_ADDRESS
HostToDeviceAddress (
@@ -49,6 +51,102 @@ HostToDeviceAddress (
return (PHYSICAL_ADDRESS)(UINTN)Address + PcdGet64 (PcdDmaDeviceOffset);
}
+/**
+ Allocates one or more 4KB pages of a certain memory type at a specified
+ alignment.
+
+ Allocates the number of 4KB pages specified by Pages of a certain memory type
+ with an alignment specified by Alignment. The allocated buffer is returned.
+ If Pages is 0, then NULL is returned. If there is not enough memory at the
+ specified alignment remaining to satisfy the request, then NULL is returned.
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+ If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
+
+ @param MemoryType The type of memory to allocate.
+ @param Pages The number of 4 KB pages to allocate.
+ @param Alignment The requested alignment of the allocation.
+ Must be a power of two.
+ If Alignment is zero, then byte alignment is
+ used.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+STATIC
+VOID *
+InternalAllocateAlignedPages (
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ IN UINTN Alignment
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Memory;
+ UINTN AlignedMemory;
+ UINTN AlignmentMask;
+ UINTN UnalignedPages;
+ UINTN RealPages;
+
+ //
+ // Alignment must be a power of two or zero.
+ //
+ ASSERT ((Alignment & (Alignment - 1)) == 0);
+
+ if (Pages == 0) {
+ return NULL;
+ }
+ if (Alignment > EFI_PAGE_SIZE) {
+ //
+ // Calculate the total number of pages since alignment is larger than page
+ // size.
+ //
+ AlignmentMask = Alignment - 1;
+ RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
+ //
+ // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not
+ // overflow.
+ //
+ ASSERT (RealPages > Pages);
+
+ Memory = mDmaHostAddressLimit;
+ Status = gBS->AllocatePages (AllocateMaxAddress, MemoryType, RealPages,
+ &Memory);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ AlignedMemory = ((UINTN)Memory + AlignmentMask) & ~AlignmentMask;
+ UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN)Memory);
+ if (UnalignedPages > 0) {
+ //
+ // Free first unaligned page(s).
+ //
+ Status = gBS->FreePages (Memory, UnalignedPages);
+ ASSERT_EFI_ERROR (Status);
+ }
+ Memory = AlignedMemory + EFI_PAGES_TO_SIZE (Pages);
+ UnalignedPages = RealPages - Pages - UnalignedPages;
+ if (UnalignedPages > 0) {
+ //
+ // Free last unaligned page(s).
+ //
+ Status = gBS->FreePages (Memory, UnalignedPages);
+ ASSERT_EFI_ERROR (Status);
+ }
+ } else {
+ //
+ // Do not over-allocate pages in this case.
+ //
+ Memory = mDmaHostAddressLimit;
+ Status = gBS->AllocatePages (AllocateMaxAddress, MemoryType, Pages,
+ &Memory);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ AlignedMemory = (UINTN)Memory;
+ }
+ return (VOID *)AlignedMemory;
+}
+
/**
Provides the DMA controller-specific addresses needed to access system memory.
@@ -111,7 +209,22 @@ DmaMap (
return EFI_OUT_OF_RESOURCES;
}
- if (Operation != MapOperationBusMasterRead &&
+ if (((UINTN)HostAddress + *NumberOfBytes) > mDmaHostAddressLimit) {
+
+ if (Operation == MapOperationBusMasterCommonBuffer) {
+ goto CommonBufferError;
+ }
+
+ AllocSize = ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment);
+ Map->BufferAddress = InternalAllocateAlignedPages (EfiBootServicesData,
+ EFI_SIZE_TO_PAGES (AllocSize),
+ mCpu->DmaBufferAlignment);
+ if (Map->BufferAddress == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeMapInfo;
+ }
+ *DeviceAddress = HostToDeviceAddress (Map->BufferAddress);
+ } else if (Operation != MapOperationBusMasterRead &&
((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) ||
((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0))) {
@@ -128,12 +241,7 @@ DmaMap (
// on uncached buffers.
//
if (Operation == MapOperationBusMasterCommonBuffer) {
- DEBUG ((DEBUG_ERROR,
- "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
- "supported\non memory regions that were allocated using "
- "DmaAllocateBuffer ()\n", __FUNCTION__));
- Status = EFI_UNSUPPORTED;
- goto FreeMapInfo;
+ goto CommonBufferError;
}
//
@@ -154,14 +262,6 @@ DmaMap (
Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment);
*DeviceAddress = HostToDeviceAddress (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;
}
@@ -184,13 +284,13 @@ DmaMap (
(GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) == 0);
DEBUG_CODE_END ();
-
- // Flush the Data Cache (should not have any effect if the memory region is
- // uncached)
- mCpu->FlushDataCache (mCpu, (UINTN)HostAddress, *NumberOfBytes,
- EfiCpuFlushTypeWriteBackInvalidate);
}
+ // Flush the Data Cache (should not have any effect if the memory region is
+ // uncached)
+ mCpu->FlushDataCache (mCpu, (UINTN)HostAddress, *NumberOfBytes,
+ EfiCpuFlushTypeWriteBack);
+
Map->HostAddress = (UINTN)HostAddress;
Map->NumberOfBytes = *NumberOfBytes;
Map->Operation = Operation;
@@ -199,6 +299,12 @@ DmaMap (
return EFI_SUCCESS;
+CommonBufferError:
+ DEBUG ((DEBUG_ERROR,
+ "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
+ "supported\non memory regions that were allocated using "
+ "DmaAllocateBuffer ()\n", __FUNCTION__));
+ Status = EFI_UNSUPPORTED;
FreeMapInfo:
FreePool (Map);
@@ -229,6 +335,7 @@ DmaUnmap (
MAP_INFO_INSTANCE *Map;
EFI_STATUS Status;
VOID *Buffer;
+ UINTN AllocSize;
if (Mapping == NULL) {
ASSERT (FALSE);
@@ -238,7 +345,18 @@ DmaUnmap (
Map = (MAP_INFO_INSTANCE *)Mapping;
Status = EFI_SUCCESS;
- if (Map->DoubleBuffer) {
+ if (((UINTN)Map->HostAddress + Map->NumberOfBytes) > mDmaHostAddressLimit) {
+
+ mCpu->FlushDataCache (mCpu, (UINTN)Map->BufferAddress, Map->NumberOfBytes,
+ EfiCpuFlushTypeInvalidate);
+
+ CopyMem ((VOID *)(UINTN)Map->HostAddress, Map->BufferAddress,
+ Map->NumberOfBytes);
+
+ AllocSize = ALIGN_VALUE (Map->NumberOfBytes, mCpu->DmaBufferAlignment);
+ FreePages (Map->BufferAddress, EFI_SIZE_TO_PAGES (AllocSize));
+ } else if (Map->DoubleBuffer) {
+
ASSERT (Map->Operation == MapOperationBusMasterWrite);
if (Map->Operation != MapOperationBusMasterWrite) {
@@ -335,10 +453,9 @@ DmaAllocateAlignedBuffer (
return EFI_INVALID_PARAMETER;
}
- if (MemoryType == EfiBootServicesData) {
- Allocation = AllocateAlignedPages (Pages, Alignment);
- } else if (MemoryType == EfiRuntimeServicesData) {
- Allocation = AllocateAlignedRuntimePages (Pages, Alignment);
+ if (MemoryType == EfiBootServicesData ||
+ MemoryType == EfiRuntimeServicesData) {
+ Allocation = InternalAllocateAlignedPages (MemoryType, Pages, Alignment);
} else {
return EFI_INVALID_PARAMETER;
}
@@ -479,6 +596,15 @@ NonCoherentDmaLibConstructor (
{
InitializeListHead (&UncachedAllocationList);
+ //
+ // Ensure that the combination of DMA addressing offset and limit produces
+ // a sane value.
+ //
+ ASSERT (PcdGet64 (PcdDmaDeviceLimit) > PcdGet64 (PcdDmaDeviceOffset));
+
+ mDmaHostAddressLimit = PcdGet64 (PcdDmaDeviceLimit) -
+ PcdGet64 (PcdDmaDeviceOffset);
+
// Get the Cpu protocol for later use
return gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
}
diff --git a/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf b/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
index 2db751afee91..1a21cfe4ff23 100644
--- a/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
+++ b/EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
@@ -38,6 +38,7 @@ [Protocols]
[Pcd]
gEmbeddedTokenSpaceGuid.PcdDmaDeviceOffset
+ gEmbeddedTokenSpaceGuid.PcdDmaDeviceLimit
[Depex]
gEfiCpuArchProtocolGuid
--
2.17.1
next prev parent reply other threads:[~2019-11-21 8:32 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-11-21 8:32 [PATCH 0/2] EmbeddedPkg: support for RPi4 PCI and platform DMA Ard Biesheuvel
2019-11-21 8:32 ` Ard Biesheuvel [this message]
2019-11-25 12:46 ` [PATCH 1/2] EmbeddedPkg/NonCoherentDmaLib: implement support for DMA range limits Leif Lindholm
2019-11-25 12:52 ` Ard Biesheuvel
2019-11-25 12:58 ` Leif Lindholm
2019-11-25 13:13 ` Ard Biesheuvel
2019-11-25 14:38 ` Leif Lindholm
2019-11-21 8:32 ` [PATCH 2/2] EmbeddedPkg: implement EDK2 IoMmu protocol wrapping DmaLib Ard Biesheuvel
2019-11-25 12:49 ` Leif Lindholm
2019-11-25 12:53 ` Ard Biesheuvel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191121083227.2850-2-ard.biesheuvel@linaro.org \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox