public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: edk2-devel@lists.01.org, leif.lindholm@linaro.org
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [PATCH 5/5] EmbeddedPkg/PlatformPciIoDxe: add support for non-coherent DMA
Date: Mon, 31 Oct 2016 18:13:10 +0000	[thread overview]
Message-ID: <1477937590-10361-6-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1477937590-10361-1-git-send-email-ard.biesheuvel@linaro.org>

Add support for bounce buffering using uncached mappings when DMA mappings
are not aligned to the CPU's DMA buffer alignment.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.c      | 204 ++++++++++++++++++++
 EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.h      |   5 +
 EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.c   |  17 +-
 EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.inf |   2 +
 4 files changed, 221 insertions(+), 7 deletions(-)

diff --git a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.c b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.c
index 97ed19353347..658d096c73c1 100644
--- a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.c
+++ b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.c
@@ -15,6 +15,8 @@
 
 #include "PlatformPciIo.h"
 
+#include <Library/DxeServicesTableLib.h>
+
 #include <Protocol/PciRootBridgeIo.h>
 
 typedef struct {
@@ -454,6 +456,201 @@ CoherentPciIoFreeBuffer (
   return EFI_SUCCESS;
 }
 
+STATIC
+EFI_STATUS
+NonCoherentPciIoFreeBuffer (
+  IN  EFI_PCI_IO_PROTOCOL         *This,
+  IN  UINTN                       Pages,
+  IN  VOID                        *HostAddress
+  )
+{
+  EFI_STATUS        Status;
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+                  EFI_PAGES_TO_SIZE (Pages),
+                  EFI_MEMORY_WB);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  FreePages (HostAddress, Pages);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+NonCoherentPciIoAllocateBuffer (
+  IN  EFI_PCI_IO_PROTOCOL         *This,
+  IN  EFI_ALLOCATE_TYPE           Type,
+  IN  EFI_MEMORY_TYPE             MemoryType,
+  IN  UINTN                       Pages,
+  OUT VOID                        **HostAddress,
+  IN  UINT64                      Attributes
+  )
+{
+  EFI_STATUS        Status;
+
+  Status = CoherentPciIoAllocateBuffer (This, Type, MemoryType, Pages,
+             HostAddress, Attributes);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  (EFI_PHYSICAL_ADDRESS)(UINTN)*HostAddress,
+                  EFI_PAGES_TO_SIZE (Pages),
+                  EFI_MEMORY_WC);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gCpu->FlushDataCache (
+                   gCpu,
+                   (EFI_PHYSICAL_ADDRESS)(UINTN)*HostAddress,
+                   EFI_PAGES_TO_SIZE (Pages),
+                   EfiCpuFlushTypeInvalidate);
+  if (EFI_ERROR (Status)) {
+    NonCoherentPciIoFreeBuffer (This, Pages, *HostAddress);
+  }
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+NonCoherentPciIoMap (
+  IN     EFI_PCI_IO_PROTOCOL            *This,
+  IN     EFI_PCI_IO_PROTOCOL_OPERATION  Operation,
+  IN     VOID                           *HostAddress,
+  IN OUT UINTN                          *NumberOfBytes,
+  OUT    EFI_PHYSICAL_ADDRESS           *DeviceAddress,
+  OUT    VOID                           **Mapping
+  )
+{
+  PLATFORM_PCI_IO_DEV               *Dev;
+  EFI_STATUS                        Status;
+  PLATFORM_PCI_IO_MAP_INFO          *MapInfo;
+  UINTN                             AlignMask;
+  VOID                              *AllocAddress;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   GcdDescriptor;
+  BOOLEAN                           UncachedMapping;
+
+  MapInfo = (PLATFORM_PCI_IO_MAP_INFO *)AllocatePool (sizeof *MapInfo);
+  if (MapInfo == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  MapInfo->HostAddress = HostAddress;
+  MapInfo->Operation = Operation;
+  MapInfo->NumberOfBytes = *NumberOfBytes;
+
+  //
+  // Bounce buffering is not possible for consistent mappings, so
+  // check we are mapping a cached buffer for consistent DMA
+  //
+  if (Operation == EfiPciIoOperationBusMasterCommonBuffer) {
+    Status = gDS->GetMemorySpaceDescriptor (
+                    (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+                    &GcdDescriptor);
+    if (!EFI_ERROR (Status)) {
+      UncachedMapping = (GcdDescriptor.Attributes & EFI_MEMORY_WC) != 0;
+    } else {
+      UncachedMapping = FALSE;
+    }
+  }
+
+  //
+  // If this device does not support 64-bit DMA addressing, we need to allocate
+  // a bounce buffer and copy over the data if HostAddress >= 4 GB. We also need
+  // to allocate a bounce buffer if the mapping is not aligned to the CPU's
+  // DMA buffer alignment.
+  //
+  Dev = PLATFORM_PCI_IO_DEV_FROM_PCI_IO(This);
+  AlignMask = gCpu->DmaBufferAlignment - 1;
+  if (((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&
+       (UINTN) HostAddress >= SIZE_4GB) ||
+       ((((UINTN) HostAddress || *NumberOfBytes) & AlignMask) != 0 &&
+        !UncachedMapping)) {
+
+    Status = NonCoherentPciIoAllocateBuffer (This, AllocateAnyPages,
+               EfiBootServicesData, EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+               &AllocAddress, 0);
+    if (EFI_ERROR (Status)) {
+      goto FreeMapInfo;
+    }
+    MapInfo->AllocAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress;
+    if (Operation == EfiPciIoOperationBusMasterRead) {
+      gBS->CopyMem (AllocAddress, 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.
+    //
+    gCpu->FlushDataCache (gCpu, (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+            *NumberOfBytes, EfiCpuFlushTypeWriteBack);
+  }
+
+  *Mapping = MapInfo;
+  return EFI_SUCCESS;
+
+FreeMapInfo:
+  FreePool (MapInfo);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+NonCoherentPciIoUnmap (
+  IN  EFI_PCI_IO_PROTOCOL          *This,
+  IN  VOID                         *Mapping
+  )
+{
+  PLATFORM_PCI_IO_MAP_INFO  *MapInfo;
+
+  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 *)MapInfo->AllocAddress,
+             MapInfo->NumberOfBytes);
+    }
+    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) {
+      gCpu->FlushDataCache (gCpu,
+              (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress,
+              MapInfo->NumberOfBytes, EfiCpuFlushTypeInvalidate);
+    }
+  }
+  FreePool (MapInfo);
+  return EFI_SUCCESS;
+}
 
 STATIC
 EFI_STATUS
@@ -646,4 +843,11 @@ InitializePciIoProtocol (
 
   // Copy protocol structure
   CopyMem(&PlatformPciIoDev->PciIo, &PciIoTemplate, sizeof PciIoTemplate);
+
+  if (PlatformPciIoDev->PlatformPciIo->DmaType == PlatformPciIoDmaNonCoherent) {
+    PlatformPciIoDev->PciIo.AllocateBuffer = NonCoherentPciIoAllocateBuffer;
+    PlatformPciIoDev->PciIo.FreeBuffer = NonCoherentPciIoFreeBuffer;
+    PlatformPciIoDev->PciIo.Map = NonCoherentPciIoMap;
+    PlatformPciIoDev->PciIo.Unmap = NonCoherentPciIoUnmap;
+  }
 }
diff --git a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.h b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.h
index 8fd8dc5e4a11..b7b792b85ae4 100644
--- a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.h
+++ b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIo.h
@@ -12,6 +12,8 @@
 
 **/
 
+#include <PiDxe.h>
+
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
 #include <Library/MemoryAllocationLib.h>
@@ -20,6 +22,7 @@
 
 #include <IndustryStandard/Pci.h>
 
+#include <Protocol/Cpu.h>
 #include <Protocol/PciIo.h>
 #include <Protocol/PlatformPciIo.h>
 
@@ -28,6 +31,8 @@
 #define PLATFORM_PCI_IO_DEV_FROM_PCI_IO(PciIoPointer) \
           CR (PciIoPointer, PLATFORM_PCI_IO_DEV, PciIo, PLATFORM_PCI_IO_SIG)
 
+extern EFI_CPU_ARCH_PROTOCOL      *gCpu;
+
 typedef struct {
   UINT32                  Signature;
   //
diff --git a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.c b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.c
index 7f3306e7e891..fa4719686a6d 100644
--- a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.c
+++ b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.c
@@ -17,6 +17,8 @@
 #include <Protocol/ComponentName.h>
 #include <Protocol/DriverBinding.h>
 
+EFI_CPU_ARCH_PROTOCOL      *gCpu;
+
 //
 // Probe, start and stop functions of this driver, called by the DXE core for
 // specific devices.
@@ -60,13 +62,9 @@ PlatformPciIoDriverBindingSupported (
   case PlatformPciIoDeviceEhci:
   case PlatformPciIoDeviceXhci:
   case PlatformPciIoDeviceAhci:
-    //
-    // Restricted to DMA coherent for now
-    //
-    if (PlatformPciIo->DmaType == PlatformPciIoDmaCoherent) {
-      Status = EFI_SUCCESS;
-      break;
-    }
+    Status = EFI_SUCCESS;
+    break;
+
   default:
     Status = EFI_UNSUPPORTED;
   }
@@ -257,6 +255,11 @@ PlatformPciIoDxeEntryPoint (
   IN EFI_SYSTEM_TABLE *SystemTable
   )
 {
+  EFI_STATUS      Status;
+
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
+  ASSERT_EFI_ERROR(Status);
+
   return EfiLibInstallDriverBindingComponentName2 (
            ImageHandle,
            SystemTable,
diff --git a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.inf b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.inf
index 2b0baf06732c..670dcc5a9ff2 100644
--- a/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.inf
+++ b/EmbeddedPkg/Drivers/PlatformPciIoDxe/PlatformPciIoDxe.inf
@@ -31,11 +31,13 @@ [Packages]
 [LibraryClasses]
   BaseMemoryLib
   DebugLib
+  DxeServicesTableLib
   MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
 
 [Protocols]
+  gEfiCpuArchProtocolGuid         ## CONSUMES
   gEfiPciIoProtocolGuid           ## BY_START
   gPlatformPciIoProtocolGuid      ## TO_START
-- 
2.7.4



  parent reply	other threads:[~2016-10-31 18:13 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-31 18:13 [PATCH 0/5] EmbeddedPkg: generic support for reusing PCI drivers for platform devices Ard Biesheuvel
2016-10-31 18:13 ` [PATCH 1/5] EmbeddedPkg: introduce platform PCI I/O protocol Ard Biesheuvel
2016-11-01 21:54   ` Leif Lindholm
2016-11-02 13:29     ` Ard Biesheuvel
2016-11-02 15:42       ` Leif Lindholm
2016-10-31 18:13 ` [PATCH 2/5] EmbeddedPkg: introduce platform PCI I/O registration library Ard Biesheuvel
2016-11-01 21:57   ` Leif Lindholm
2016-11-02 13:30     ` Ard Biesheuvel
2016-11-02 15:39       ` Leif Lindholm
2016-11-07 14:54       ` Evan Lloyd
2016-10-31 18:13 ` [PATCH 3/5] EmbeddedPkg: implement generic platform PCI I/O driver Ard Biesheuvel
2016-11-01 22:22   ` Leif Lindholm
2016-11-02 13:39     ` Ard Biesheuvel
2016-11-02 16:05       ` Leif Lindholm
2016-10-31 18:13 ` [PATCH 4/5] ArmPkg/CpuDxe: set DmaBufferAlignment according to CWG Ard Biesheuvel
2016-11-01 22:32   ` Leif Lindholm
2016-11-02 13:40     ` Ard Biesheuvel
2016-11-02 16:10       ` Leif Lindholm
2016-11-02 16:17         ` Ard Biesheuvel
2016-11-02 16:21           ` Leif Lindholm
2016-11-02 16:23             ` Ard Biesheuvel
2016-10-31 18:13 ` Ard Biesheuvel [this message]
2016-11-01 22:43   ` [PATCH 5/5] EmbeddedPkg/PlatformPciIoDxe: add support for non-coherent DMA Leif Lindholm
2016-11-02 13:43     ` Ard Biesheuvel
2016-11-02 16:17       ` Leif Lindholm
2016-11-01 21:45 ` [PATCH 0/5] EmbeddedPkg: generic support for reusing PCI drivers for platform devices Leif Lindholm

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=1477937590-10361-6-git-send-email-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