18.5.8 DMA Bus Master Common Buffer Operation
A PCI driver uses common buffers when a memory region requires simultaneous
access by both the processor and a PCI bus master. A common buffer is typically
allocated in the Start()
service and freed in the Stop()
service. This
mechanism is very different from the bus master read and bus master write
operations where the PCI driver transfers the ownership of a memory region from
the processor to the bus master and back to the processor.
The general algorithm for allocating a common buffer in the Start()
follows:
Call
AllocateBuffer()
to allocate a common buffer.Call
Map()
with an Operation ofEfiPciOperationBusMasterCommonBuffer
.Program the DMA bus master with the DeviceAddress returned by
Map()
.The common buffer can now be accessed equally by the processor (using HostAddress) and the DMA bus master (using DeviceAddress) .
The general algorithm for freeing a common buffer in the Stop()
service is as
follows:
- Call
Unmap()
. - CallFreeBuffer()
.
The example below shows an example function the Start()
service calls to
set up a common buffer operation for a specific PCI controller. The function
accesses the PCI controller through the PciIo parameter. The function
allocates a common buffer of Length bytes and returns the address of the
common buffer in HostAddress.
A mapping is created for the common buffer and returned in the parameter
Mapping. The MMIO register at offset 0x18 of BAR #1 is the start address of
the common buffer from the PCI controller's perspective. The services of the
PCI I/O Protocol used in this example include AllocateBuffer()
, Map()
, and
Mem.Write()
. This example is for a 32-bit PCI bus master. A 64-bit PCI bus
master requires two 32-bit MMIO registers to specify the start address, and the
EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE
attribute must be set in the Driver
Binding Protocol Start()
service of the PCI driver.
Example 180-Allocate bus master common buffer
#include <Uefi.h>
#include <Protocol/PciIo.h>
EFI_STATUS
EFIAPI
SetupCommonBuffer (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT8 **HostAddress,
IN UINTN Length,
OUT VOID **Mapping
)
{
EFI_STATUS Status;
UINTN NumberOfBytes;
EFI_PHYSICAL_ADDRESS DeviceAddress;
UINT32 DmaStartAddress;
//
// Allocate a common buffer from anywhere in system memory of
// type EfiBootServicesData.
//
Status = PciIo->AllocateBuffer (
PciIo, // This
AllocateAnyPages, // Type
EfiBootServicesData, // MemoryType EFI_SIZE_TO_PAGES
(Length), // Pages
(VOID **)HostAddress, // HostAddress
0 // Attributes
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Call Map() to retrieve the DeviceAddress to use for the bus
// master common buffer operation. If the Map() function cannot
// support a DMA operation for the entire length, then return an
// error.
//
NumberOfBytes = Length;
Status = PciIo->Map (
PciIo, // This
EfiPciIoOperationBusMasterCommonBuffer, // Operation
(VOID *)*HostAddress, // HostAddress
&NumberOfBytes, // NumberOfBytes
&DeviceAddress, // DeviceAddress
Mapping // Mapping
);
if (!EFI_ERROR (Status) && NumberOfBytes != Length) {
PciIo->Unmap (PciIo, *Mapping);
Status = EFI_OUT_OF_RESOURCES;
}
if (EFI_ERROR (Status)) {
PciIo->FreeBuffer (
PciIo,
EFI_SIZE_TO_PAGES (Length),
(VOID *)*HostAddress
);
return Status;
}
//
// Write the DMA start address to MMIO Register offset 0x18 of Bar #1
//
DmaStartAddress = (UINT32)DeviceAddress;
Status = PciIo->Mem.Write (
PciIo, // This
EfiPciIoWidthUint32, // Width
1, // BarIndex
0x18, // Offset
1, // Count
&DmaStartAddress // Buffer
);
if (EFI_ERROR (Status)) {
PciIo->Unmap (PciIo, *Mapping);
PciIo->FreeBuffer (
PciIo,
EFI_SIZE_TO_PAGES (Length),
(VOID *)*HostAddress
);
}
return Status;
}
This example shows a function the Stop()
service calls to free a common
buffer for a PCI controller. The function accesses the PCI controller through
the services of the PciIo parameter and uses them to free the common buffer
specified by HostAddress and Length. This function undoes the mapping and
frees the common buffer. The services of the PCI I/O Protocol used in this
example include Unmap()
and FreeBuffer()
.
Example 181-Free bus master common buffer
#include <Uefi.h>
#include <Protocol/PciIo.h>
EFI_STATUS
EFIAPI
TearDownCommonBuffer (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT8 *HostAddress,
IN UINTN Length,
IN VOID *Mapping
)
{
EFI_STATUS Status;
Status = PciIo->Unmap (PciIo, Mapping);
if (EFI_ERROR (Status)) {
return Status;
}
Status = PciIo->FreeBuffer (
PciIo,
EFI_SIZE_TO_PAGES (Length),
(VOID *)HostAddress
);
return Status;
}