From: "Philippe Mathieu-Daudé" <philmd@redhat.com>
To: devel@edk2.groups.io, ard.biesheuvel@linaro.org
Cc: leif.lindholm@linaro.org
Subject: Re: [edk2-devel] [PATCH v2 1/2] EmbeddedPkg/NonCoherentDmaLib: implement support for DMA range limits
Date: Tue, 26 Nov 2019 12:59:12 +0100 [thread overview]
Message-ID: <08876497-a2ce-4b55-6f50-f332ffd45ddd@redhat.com> (raw)
In-Reply-To: <ee6f5062-eeb1-fe89-ec8a-5ab2e6367b7a@redhat.com>
On 11/26/19 11:44 AM, Philippe Mathieu-Daudé wrote:
> On 11/26/19 12:12 AM, Ard Biesheuvel via Groups.Io wrote:
>> 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 | 165
>> ++++++++++++++++++--
>> EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf | 1 +
>> 3 files changed, 160 insertions(+), 12 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..115345765435 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);
The previous line made me think twice (due to the cast), but I can't
find a simpler way to write the same code.
>> + 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,30 @@ 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;
>> + }
>> +
>> + if (Map->Operation == MapOperationBusMasterRead) {
>> + CopyMem (Map->BufferAddress, (VOID *)(UINTN)Map->HostAddress,
>> + *NumberOfBytes);
>> + }
>> + mCpu->FlushDataCache (mCpu, (UINTN)Map->BufferAddress, AllocSize,
>> + EfiCpuFlushTypeWriteBack);
>> +
>> + *DeviceAddress = HostToDeviceAddress (Map->BufferAddress);
>> + } else if (Operation != MapOperationBusMasterRead &&
>> ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) ||
>> ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0))) {
>> @@ -128,12 +249,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;
>> }
>> //
>> @@ -199,6 +315,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 +351,7 @@ DmaUnmap (
>> MAP_INFO_INSTANCE *Map;
>> EFI_STATUS Status;
>> VOID *Buffer;
>> + UINTN AllocSize;
>> if (Mapping == NULL) {
>> ASSERT (FALSE);
>> @@ -238,7 +361,17 @@ DmaUnmap (
>> Map = (MAP_INFO_INSTANCE *)Mapping;
>> Status = EFI_SUCCESS;
>> - if (Map->DoubleBuffer) {
>> + if (((UINTN)Map->HostAddress + Map->NumberOfBytes) >
>> mDmaHostAddressLimit) {
>> + AllocSize = ALIGN_VALUE (Map->NumberOfBytes,
>> mCpu->DmaBufferAlignment);
>> + if (Map->Operation == MapOperationBusMasterWrite) {
>> + mCpu->FlushDataCache (mCpu, (UINTN)Map->BufferAddress, AllocSize,
>> + EfiCpuFlushTypeInvalidate);
>> + CopyMem ((VOID *)(UINTN)Map->HostAddress, Map->BufferAddress,
>> + Map->NumberOfBytes);
>> + }
>> + FreePages (Map->BufferAddress, EFI_SIZE_TO_PAGES (AllocSize));
>> + } else if (Map->DoubleBuffer) {
>> +
>> ASSERT (Map->Operation == MapOperationBusMasterWrite);
>> if (Map->Operation != MapOperationBusMasterWrite) {
>> @@ -335,10 +468,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 +611,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
>>
>
> For the 82% of the patch I understood:
> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>
Leif explained me that "I've reviewed it, but don't fully understand it,
but I think it's valuable" translate into a Acked-by on EDK2, so please
replace the previous Reviewed-by by:
Acked-by: Philippe Mathieu-Daude <philmd@redhat.com>
Thanks,
Phil.
next prev parent reply other threads:[~2019-11-26 11:59 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-11-25 23:12 [PATCH v2 0/2] EmbeddedPkg: support for RPi4 PCI and platform DMA Ard Biesheuvel
2019-11-25 23:12 ` [PATCH v2 1/2] EmbeddedPkg/NonCoherentDmaLib: implement support for DMA range limits Ard Biesheuvel
2019-11-26 10:28 ` [edk2-devel] " Pete Batard
2019-11-26 10:44 ` Philippe Mathieu-Daudé
2019-11-26 11:59 ` Philippe Mathieu-Daudé [this message]
2019-11-26 12:02 ` Ard Biesheuvel
2019-11-26 12:04 ` Leif Lindholm
2019-11-26 12:11 ` Ard Biesheuvel
2019-11-26 12:22 ` Leif Lindholm
2019-11-25 23:12 ` [PATCH v2 2/2] EmbeddedPkg: implement EDK2 IoMmu protocol wrapping DmaLib Ard Biesheuvel
2019-11-26 8:19 ` [edk2-devel] " Philippe Mathieu-Daudé
2019-11-26 10:28 ` Pete Batard
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=08876497-a2ce-4b55-6f50-f332ffd45ddd@redhat.com \
--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