18.5.1 Map() Service Cautions

A common mistake in writing PCI drivers is omission of the use of the Map() service. On platforms with coherent PCI busses having a 1:1 mapping between CPU addresses and PCI DMA addresses, such as PCI implementations on many IA32, X64, and IPF systems, the omission of Map() may not produce any functional issues. However, if those same UEFI Driver sources are used on a platform is that not coherent, nor guarantees a 1:1 mapping between CPU addresses and PCI DMA addresses, the UEFI Driver may not function correctly, with the likely result being data corruption. For this reason, Map() must always be used when setting up a PCI DMA transfer.


TIP: Although omission of the Map() service may work on some platforms, use of Map() for DMA transaction is required and maximizes UEFI Driver compatibility.


The Map() service converts a system memory address to an address useful to a PCI device performing bus master DMA transactions. The device address returned is not related to the original system memory address. Some chipsets maintain a one-to-one mapping between system memory addresses and device addresses on the PCI bus. For this special case, the system memory address and device address are the same. However, a PCI driver cannot tell if it is executing on a platform with this one-to-one mapping. As a result, a PCI driver must make as few assumptions about the system architecture as possible. Avoiding assumptions means that a PCI driver must never use the device address that is returned from Map() to access the contents of the DMA buffer. Instead, this value should only be used to program the base address of the DMA transaction into the PCI controller. This programming is typically accomplished with one or more I/O or memory-mapped I/O write transactions to the PCI controller the PCI driver is managing.

The example below shows the function prototype for the Map() service of the PCI I/O Protocol. A PCI driver can use HostAddress to access the contents of the DMA buffer, but must never use the returned parameter DeviceAddress to access the contents of the DMA buffer.

Example 176-Map() Function
/**
  Provides the PCI controller-specific addresses needed to access system memory.

  @param  This                    A pointer to the EFI_PCI_IO_PROTOCOL instance.
  @param  Operation               Indicates if the bus master is going to read or write to system memory.
  @param  HostAddress             The system memory address to map to the PCI controller.
  @param  NumberOfBytes           On input the number of bytes to map. On output the number of bytes that
                                  were mapped. @param DeviceAddress The resulting map address for the bus
                                  master PCI controller to use to access the hosts HostAddress. 
  @param Mapping                  A resulting value to pass to Unmap().
  @retval  EFI_SUCCESS            The range was mapped for the returned NumberOfBytes.
  @retval  EFI_UNSUPPORTED        The HostAddress cannot be mapped as a common buffer.
  @retval  EFI_INVALID_PARAMETER  One or more parameters are invalid.
  @retval  EFI_OUT_OF_RESOURCES   The request could not be completed due to a lack of resources.
  @retval  EFI_DEVICE_ERROR       The system hardware could not map the requested address.
**/

typedef
EFI_STATUS
(EFIAPI * EFI_PCI_IO_PROTOCOL_MAP)(
  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
);