Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,7 @@ NonCoherentPciIoMap (
VOID *AllocAddress;
EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
BOOLEAN Bounce;
BOOLEAN MappingIsCached;

if ((HostAddress == NULL) ||
(NumberOfBytes == NULL) ||
Expand All @@ -1288,113 +1289,130 @@ NonCoherentPciIoMap (
return EFI_INVALID_PARAMETER;
}

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.
// A bounce buffer will be needed if this device does not support 64-bit DMA
// addressing and the HostAddress >= 4 GB.
//
Bounce = ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&
(EFI_PHYSICAL_ADDRESS)(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);
}
//
// Check whether the host address refers to an uncached mapping. This is
// required for common buffer mappings.
//
Status = gDS->GetMemorySpaceDescriptor (
(EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
&GcdDescriptor
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}

if (Bounce) {
if (Operation == EfiPciIoOperationBusMasterCommonBuffer) {
Status = EFI_DEVICE_ERROR;
goto FreeMapInfo;
}
MappingIsCached = ((GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0);

Status = NonCoherentPciIoAllocateBuffer (
This,
AllocateAnyPages,
EfiBootServicesData,
EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
&AllocAddress,
EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE
);
if (EFI_ERROR (Status)) {
goto FreeMapInfo;
if (!MappingIsCached) {
if (Bounce) {
//
// Bounce buffering from an uncached mapping is very inefficient, and
// should never be needed, given that NonCoherentPciIoAllocateBuffer ()
// will take the EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE attribute in
// account too.
//
ASSERT (MappingIsCached || !Bounce);
return EFI_DEVICE_ERROR;
}

//
// No bounce buffering or cache maintenance is needed for uncached mappings
// that are in DMA range for the device, regardless of the DMA direction.
//
*Mapping = (VOID *)MAX_ADDRESS;
return EFI_SUCCESS;
}

switch (Operation) {
case EfiPciIoOperationBusMasterRead:
//
// Bounce buffering is never needed for streaming non-coherent outbound
// DMA involving cached memory, given that the bus master will only read
// from the region, and so there is no need for the potentially
// destructive cache invalidation that might otherwise result in
// corruption of unrelated adjacent memory.
//
break;

case EfiPciIoOperationBusMasterWrite:
//
// For streaming non-coherent inbound DMA involving cached memory, no
// bounce buffering is needed unless the buffer is misaligned wrt the
// CPU's DMA buffer alignment, as cache lines that are shared with
// unrelated adjacent data might be corrupted by the invalidation
// performed on unmap.
//
AlignMask = mCpu->DmaBufferAlignment - 1;
if ((((UINTN)HostAddress | *NumberOfBytes) & AlignMask) != 0) {
Bounce = TRUE;
}

break;

case EfiPciIoOperationBusMasterCommonBuffer:
// Non-coherent common buffer DMA requires uncached mappings.
ASSERT (!MappingIsCached);
return EFI_DEVICE_ERROR;

default:
ASSERT (FALSE);
}

MapInfo = AllocatePool (sizeof *MapInfo);
if (MapInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}

MapInfo->HostAddress = HostAddress;
MapInfo->Operation = Operation;
MapInfo->NumberOfBytes = *NumberOfBytes;

if (Bounce) {
AllocAddress = AllocatePool (
ALIGN_VALUE (
MapInfo->NumberOfBytes,
mCpu->DmaBufferAlignment
) + mCpu->DmaBufferAlignment - 8
);
MapInfo->AllocAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress;
*DeviceAddress = ALIGN_VALUE (
MapInfo->AllocAddress,
mCpu->DmaBufferAlignment
);
if (Operation == EfiPciIoOperationBusMasterRead) {
gBS->CopyMem (AllocAddress, HostAddress, *NumberOfBytes);
gBS->CopyMem ((VOID *)(UINTN)*DeviceAddress, 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
);
}

//
// 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,
*DeviceAddress,
ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment),
EfiCpuFlushTypeWriteBack
);

*Mapping = MapInfo;
return EFI_SUCCESS;

FreeMapInfo:
FreePool (MapInfo);

return Status;
}

/**
Expand All @@ -1415,46 +1433,42 @@ NonCoherentPciIoUnmap (
)
{
NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;
VOID *AllocAddress;
VOID *BufferAddress;

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
);
if (Mapping == (VOID *)MAX_ADDRESS) {
return EFI_SUCCESS;
}

MapInfo = Mapping;
AllocAddress = (VOID *)(UINTN)MapInfo->AllocAddress;
if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) {
if (AllocAddress == NULL) {
BufferAddress = MapInfo->HostAddress;
} else {
BufferAddress = ALIGN_POINTER (AllocAddress, mCpu->DmaBufferAlignment);
}

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
);
mCpu->FlushDataCache (
mCpu,
(EFI_PHYSICAL_ADDRESS)(UINTN)BufferAddress,
ALIGN_VALUE (MapInfo->NumberOfBytes, mCpu->DmaBufferAlignment),
EfiCpuFlushTypeInvalidate
);

if (MapInfo->HostAddress != BufferAddress) {
gBS->CopyMem (MapInfo->HostAddress, BufferAddress, MapInfo->NumberOfBytes);
}
}

if (AllocAddress != NULL) {
FreePool (AllocAddress);
}

FreePool (MapInfo);
return EFI_SUCCESS;
}
Expand Down
Loading