+ 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