From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: edk2-devel@lists.01.org, leif.lindholm@linaro.org, ruiyu.ni@intel.com
Cc: mw@semihalf.com, liming.gao@intel.com, feng.tian@intel.com,
Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [PATCH v5 4/5] MdeModulePkg/NonDiscoverablePciDeviceDxe: add support for non-coherent DMA
Date: Mon, 5 Dec 2016 15:35:21 +0000 [thread overview]
Message-ID: <1480952122-27937-5-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1480952122-27937-1-git-send-email-ard.biesheuvel@linaro.org>
Add support for non-coherent DMA, either by performing explicit cache
maintenance when DMA mappings are aligned to the CPU's DMA buffer alignment,
or by bounce buffering via uncached mappings otherwise.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Tested-by: Marcin Wojtas <mw@semihalf.com>
---
MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.c | 14 +-
MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.inf | 2 +
MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c | 325 ++++++++++++++++++++
MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.h | 29 ++
4 files changed, 363 insertions(+), 7 deletions(-)
diff --git a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.c b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.c
index ee765d7a5d9c..0fcf2b2ec1bf 100644
--- a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.c
+++ b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.c
@@ -16,6 +16,8 @@
#include <Protocol/DriverBinding.h>
+EFI_CPU_ARCH_PROTOCOL *mCpu;
+
//
// We only support the following device types
//
@@ -69,14 +71,7 @@ NonDiscoverablePciDeviceSupported (
return Status;
}
- //
- // Restricted to DMA coherent for now
- //
Status = EFI_UNSUPPORTED;
- if (Device->DmaType != NonDiscoverableDeviceDmaTypeCoherent) {
- goto CloseProtocol;
- }
-
for (Idx = 0; Idx < ARRAY_SIZE (SupportedNonDiscoverableDevices); Idx++) {
if (CompareGuid (Device->Type, SupportedNonDiscoverableDevices [Idx])) {
Status = EFI_SUCCESS;
@@ -224,6 +219,11 @@ NonDiscoverablePciDeviceDxeEntryPoint (
IN EFI_SYSTEM_TABLE *SystemTable
)
{
+ EFI_STATUS Status;
+
+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
+ ASSERT_EFI_ERROR(Status);
+
return EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
diff --git a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.inf b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.inf
index 996fe310e0e3..5faa8945134c 100644
--- a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.inf
+++ b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.inf
@@ -32,6 +32,7 @@ [Packages]
[LibraryClasses]
BaseMemoryLib
DebugLib
+ DxeServicesTableLib
MemoryAllocationLib
UefiBootServicesTableLib
UefiDriverEntryPoint
@@ -40,6 +41,7 @@ [LibraryClasses]
[Protocols]
gEfiPciIoProtocolGuid ## BY_START
gEdkiiNonDiscoverableDeviceProtocolGuid ## TO_START
+ gEfiCpuArchProtocolGuid ## CONSUMES
[Guids]
gEdkiiNonDiscoverableAhciDeviceGuid
diff --git a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c
index 9d974e628c9d..54ace110f042 100644
--- a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c
+++ b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.c
@@ -15,6 +15,8 @@
#include "NonDiscoverablePciDeviceIo.h"
+#include <Library/DxeServicesTableLib.h>
+
#include <IndustryStandard/Acpi.h>
#include <Protocol/PciRootBridgeIo.h>
@@ -523,6 +525,320 @@ CoherentPciIoFreeBuffer (
return EFI_SUCCESS;
}
+STATIC
+EFI_STATUS
+NonCoherentPciIoFreeBuffer (
+ IN EFI_PCI_IO_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ )
+{
+ NON_DISCOVERABLE_PCI_DEVICE *Dev;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;
+ BOOLEAN Found;
+
+ Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);
+
+ Found = FALSE;
+
+ //
+ // Find the uncached allocation list entry associated
+ // with this allocation
+ //
+ for (Entry = Dev->UncachedAllocationList.ForwardLink;
+ Entry != &Dev->UncachedAllocationList;
+ Entry = Entry->ForwardLink) {
+
+ Alloc = BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION, List);
+ if (Alloc->HostAddress == HostAddress && Alloc->NumPages == Pages) {
+ //
+ // We are freeing the exact allocation we were given
+ // before by AllocateBuffer()
+ //
+ Found = TRUE;
+ break;
+ }
+ }
+
+ if (!Found) {
+ ASSERT_EFI_ERROR (EFI_NOT_FOUND);
+ return EFI_NOT_FOUND;
+ }
+
+ RemoveEntryList (&Alloc->List);
+
+ Status = gDS->SetMemorySpaceAttributes (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ Alloc->Attributes);
+ if (EFI_ERROR (Status)) {
+ goto FreeAlloc;
+ }
+
+ //
+ // If we fail to restore the original attributes, it is better to leak the
+ // memory than to return it to the heap
+ //
+ FreePages (HostAddress, Pages);
+
+FreeAlloc:
+ FreePool (Alloc);
+ return Status;
+}
+
+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
+ )
+{
+ NON_DISCOVERABLE_PCI_DEVICE *Dev;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
+ EFI_STATUS Status;
+ UINT64 MemType;
+ NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;
+ VOID *AllocAddress;
+
+ Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);
+
+ Status = CoherentPciIoAllocateBuffer (This, Type, MemoryType, Pages,
+ &AllocAddress, Attributes);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gDS->GetMemorySpaceDescriptor (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+ &GcdDescriptor);
+ if (EFI_ERROR (Status)) {
+ goto FreeBuffer;
+ }
+
+ if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto FreeBuffer;
+ }
+
+ //
+ // Set the preferred memory attributes
+ //
+ if ((Attributes & EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE) != 0 ||
+ (GcdDescriptor.Capabilities & EFI_MEMORY_UC) == 0) {
+ //
+ // Use write combining if it was requested, or if it is the only
+ // type supported by the region.
+ //
+ MemType = EFI_MEMORY_WC;
+ } else {
+ MemType = EFI_MEMORY_UC;
+ }
+
+ Alloc = AllocatePool (sizeof *Alloc);
+ if (Alloc == NULL) {
+ goto FreeBuffer;
+ }
+
+ Alloc->HostAddress = AllocAddress;
+ Alloc->NumPages = Pages;
+ Alloc->Attributes = GcdDescriptor.Attributes;
+
+ //
+ // Record this allocation in the linked list, so we
+ // can restore the memory space attributes later
+ //
+ InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List);
+
+ Status = gDS->SetMemorySpaceAttributes (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ MemType);
+ if (EFI_ERROR (Status)) {
+ goto RemoveList;
+ }
+
+ Status = mCpu->FlushDataCache (
+ mCpu,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ EfiCpuFlushTypeInvalidate);
+ if (EFI_ERROR (Status)) {
+ goto RemoveList;
+ }
+
+ *HostAddress = AllocAddress;
+
+ return EFI_SUCCESS;
+
+RemoveList:
+ RemoveEntryList (&Alloc->List);
+ FreePool (Alloc);
+
+FreeBuffer:
+ CoherentPciIoFreeBuffer (This, Pages, AllocAddress);
+ 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
+ )
+{
+ NON_DISCOVERABLE_PCI_DEVICE *Dev;
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;
+ UINTN AlignMask;
+ VOID *AllocAddress;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
+ BOOLEAN Bounce;
+
+ 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.
+ //
+ Bounce = ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&
+ (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);
+ }
+ }
+
+ if (Bounce) {
+ if (Operation == EfiPciIoOperationBusMasterCommonBuffer) {
+ Status = EFI_DEVICE_ERROR;
+ goto FreeMapInfo;
+ }
+
+ Status = NonCoherentPciIoAllocateBuffer (This, AllocateAnyPages,
+ EfiBootServicesData, EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+ &AllocAddress, EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE);
+ 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.
+ //
+ mCpu->FlushDataCache (mCpu, (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
+ )
+{
+ NON_DISCOVERABLE_PCI_DEVICE_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 *)(UINTN)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) {
+ mCpu->FlushDataCache (mCpu,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress,
+ MapInfo->NumberOfBytes, EfiCpuFlushTypeInvalidate);
+ }
+ }
+ FreePool (MapInfo);
+ return EFI_SUCCESS;
+}
STATIC
EFI_STATUS
@@ -707,12 +1023,21 @@ InitializePciIoProtocol (
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;
INTN Idx;
+ InitializeListHead (&Dev->UncachedAllocationList);
+
Dev->ConfigSpace.Hdr.VendorId = PCI_ID_VENDOR_UNKNOWN;
Dev->ConfigSpace.Hdr.DeviceId = PCI_ID_DEVICE_DONTCARE;
// Copy protocol structure
CopyMem(&Dev->PciIo, &PciIoTemplate, sizeof PciIoTemplate);
+ if (Dev->Device->DmaType == NonDiscoverableDeviceDmaTypeNonCoherent) {
+ Dev->PciIo.AllocateBuffer = NonCoherentPciIoAllocateBuffer;
+ Dev->PciIo.FreeBuffer = NonCoherentPciIoFreeBuffer;
+ Dev->PciIo.Map = NonCoherentPciIoMap;
+ Dev->PciIo.Unmap = NonCoherentPciIoUnmap;
+ }
+
if (CompareGuid (Dev->Device->Type, &gEdkiiNonDiscoverableAhciDeviceGuid)) {
Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_MASS_STORAGE_AHCI;
Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_MASS_STORAGE_SATADPA;
diff --git a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.h b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.h
index bc0a3d3258f9..449614862911 100644
--- a/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.h
+++ b/MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceIo.h
@@ -15,6 +15,8 @@
#ifndef __NON_DISCOVERABLE_PCI_DEVICE_IO_H__
#define __NON_DISCOVERABLE_PCI_DEVICE_IO_H__
+#include <PiDxe.h>
+
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
@@ -25,6 +27,7 @@
#include <Protocol/ComponentName.h>
#include <Protocol/NonDiscoverableDevice.h>
+#include <Protocol/Cpu.h>
#include <Protocol/PciIo.h>
#define NON_DISCOVERABLE_PCI_DEVICE_SIG SIGNATURE_32 ('P', 'P', 'I', 'D')
@@ -38,6 +41,27 @@
#define PCI_MAX_BARS 6
+extern EFI_CPU_ARCH_PROTOCOL *mCpu;
+
+typedef struct {
+ //
+ // The linked-list next pointer
+ //
+ LIST_ENTRY List;
+ //
+ // The address of the uncached allocation
+ //
+ VOID *HostAddress;
+ //
+ // The number of pages in the allocation
+ //
+ UINTN NumPages;
+ //
+ // The attributes of the allocation
+ //
+ UINT64 Attributes;
+} NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION;
+
typedef struct {
UINT32 Signature;
//
@@ -71,6 +95,11 @@ typedef struct {
// Whether this device has been enabled
//
BOOLEAN Enabled;
+ //
+ // Linked list to keep track of uncached allocations performed
+ // on behalf of this device
+ //
+ LIST_ENTRY UncachedAllocationList;
} NON_DISCOVERABLE_PCI_DEVICE;
VOID
--
2.7.4
next prev parent reply other threads:[~2016-12-05 15:35 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-05 15:35 [PATCH v5 0/5] MdeModulePkg: add support for non-discoverable devices Ard Biesheuvel
2016-12-05 15:35 ` [PATCH v5 1/5] MdeModulePkg: introduce non-discoverable device protocol Ard Biesheuvel
2016-12-05 15:35 ` [PATCH v5 2/5] MdeModulePkg: introduce helper library to register non-discoverable devices Ard Biesheuvel
2016-12-05 15:35 ` [PATCH v5 3/5] MdeModulePkg: implement generic PCI I/O driver for " Ard Biesheuvel
2016-12-05 15:35 ` Ard Biesheuvel [this message]
2016-12-05 15:35 ` [PATCH v5 5/5] Omap35xxPkg/PciEmulation: port to new non-discoverable device infrastructure Ard Biesheuvel
2016-12-05 16:23 ` Leif Lindholm
2016-12-05 18:57 ` [PATCH v5 0/5] MdeModulePkg: add support for non-discoverable devices Marcin Wojtas
2016-12-07 9:43 ` 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=1480952122-27937-5-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