diff mbox

[edk2] ArmPlatformPkg: implement CpuIo2 protocol driver specific for PCI

Message ID 1461761909-22398-1-git-send-email-ard.biesheuvel@linaro.org
State Superseded
Headers show

Commit Message

Ard Biesheuvel April 27, 2016, 12:58 p.m. UTC
The CpuIo2 protocol is required by the generic PciHostBridgeDxe driver,
which relies on it to back its own I/O and MMIO operations.

Since ARM has no native I/O port equivalent, such accesses can only originate
from PCI drivers, and the PCI I/O space is translated to MMIO in this case.

So we can implement this protocol using MMIO operations only, and take the
PCI I/O translation offset into account when performing I/O port accesses.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

---

We will need this on Seattle to support PCI devices with I/O BARs when we move
to the generic PciHostBridgeDxe driver.

 ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c   | 559 ++++++++++++++++++++
 ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf |  53 ++
 2 files changed, 612 insertions(+)

-- 
2.7.4

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Comments

Laszlo Ersek April 27, 2016, 3:05 p.m. UTC | #1
On 04/27/16 14:58, Ard Biesheuvel wrote:
> The CpuIo2 protocol is required by the generic PciHostBridgeDxe driver,

> which relies on it to back its own I/O and MMIO operations.

> 

> Since ARM has no native I/O port equivalent, such accesses can only originate

> from PCI drivers, and the PCI I/O space is translated to MMIO in this case.

> 

> So we can implement this protocol using MMIO operations only, and take the

> PCI I/O translation offset into account when performing I/O port accesses.

> 

> Contributed-under: TianoCore Contribution Agreement 1.0

> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

> ---

> 

> We will need this on Seattle to support PCI devices with I/O BARs when we move

> to the generic PciHostBridgeDxe driver.


I welcome this patch. I've been silently waiting / hoping for it :)

It will also help rebasing ArmVirtPkg to the generic PciHostBridgeDxe
driver.

I assume you started with a copy of UefiCpuPkg/CpuIo2Dxe? (Just so Leif
can compare the drivers, for an easier review.)

Thanks!
Laszlo

>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c   | 559 ++++++++++++++++++++

>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf |  53 ++

>  2 files changed, 612 insertions(+)

> 

> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

> new file mode 100644

> index 000000000000..fecf6a87adce

> --- /dev/null

> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

> @@ -0,0 +1,559 @@

> +/** @file

> +  Produces the CPU I/O 2 Protocol.

> +

> +Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>

> +Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

> +

> +This program and the accompanying materials

> +are licensed and made available under the terms and conditions of the BSD License

> +which accompanies this distribution.  The full text of the license may be found at

> +http://opensource.org/licenses/bsd-license.php

> +

> +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

> +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

> +

> +**/

> +

> +#include <PiDxe.h>

> +

> +#include <Protocol/CpuIo2.h>

> +

> +#include <Library/BaseLib.h>

> +#include <Library/DebugLib.h>

> +#include <Library/IoLib.h>

> +#include <Library/PcdLib.h>

> +#include <Library/UefiBootServicesTableLib.h>

> +

> +#define MAX_IO_PORT_ADDRESS   0xFFFF

> +

> +//

> +// Handle for the CPU I/O 2 Protocol

> +//

> +STATIC EFI_HANDLE  mHandle = NULL;

> +

> +//

> +// Lookup table for increment values based on transfer widths

> +//

> +STATIC CONST UINT8 mInStride[] = {

> +  1, // EfiCpuIoWidthUint8

> +  2, // EfiCpuIoWidthUint16

> +  4, // EfiCpuIoWidthUint32

> +  8, // EfiCpuIoWidthUint64

> +  0, // EfiCpuIoWidthFifoUint8

> +  0, // EfiCpuIoWidthFifoUint16

> +  0, // EfiCpuIoWidthFifoUint32

> +  0, // EfiCpuIoWidthFifoUint64

> +  1, // EfiCpuIoWidthFillUint8

> +  2, // EfiCpuIoWidthFillUint16

> +  4, // EfiCpuIoWidthFillUint32

> +  8  // EfiCpuIoWidthFillUint64

> +};

> +

> +//

> +// Lookup table for increment values based on transfer widths

> +//

> +STATIC CONST UINT8 mOutStride[] = {

> +  1, // EfiCpuIoWidthUint8

> +  2, // EfiCpuIoWidthUint16

> +  4, // EfiCpuIoWidthUint32

> +  8, // EfiCpuIoWidthUint64

> +  1, // EfiCpuIoWidthFifoUint8

> +  2, // EfiCpuIoWidthFifoUint16

> +  4, // EfiCpuIoWidthFifoUint32

> +  8, // EfiCpuIoWidthFifoUint64

> +  0, // EfiCpuIoWidthFillUint8

> +  0, // EfiCpuIoWidthFillUint16

> +  0, // EfiCpuIoWidthFillUint32

> +  0  // EfiCpuIoWidthFillUint64

> +};

> +

> +/**

> +  Check parameters to a CPU I/O 2 Protocol service request.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.

> +  @param[in] Width          Signifies the width of the I/O or Memory operation.

> +  @param[in] Address        The base address of the I/O operation.

> +  @param[in] Count          The number of I/O operations to perform. The number of

> +                            bytes moved is Width size * Count, starting at Address.

> +  @param[in] Buffer         For read operations, the destination buffer to store the results.

> +                            For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The parameters for this request pass the checks.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +CpuIoCheckParameter (

> +  IN BOOLEAN                    MmioOperation,

> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN UINT64                     Address,

> +  IN UINTN                      Count,

> +  IN VOID                       *Buffer

> +  )

> +{

> +  UINT64  MaxCount;

> +  UINT64  Limit;

> +

> +  //

> +  // Check to see if Buffer is NULL

> +  //

> +  if (Buffer == NULL) {

> +    return EFI_INVALID_PARAMETER;

> +  }

> +

> +  //

> +  // Check to see if Width is in the valid range

> +  //

> +  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {

> +    return EFI_INVALID_PARAMETER;

> +  }

> +

> +  //

> +  // For FIFO type, the target address won't increase during the access,

> +  // so treat Count as 1

> +  //

> +  if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {

> +    Count = 1;

> +  }

> +

> +  //

> +  // Check to see if Width is in the valid range for I/O Port operations

> +  //

> +  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {

> +    return EFI_INVALID_PARAMETER;

> +  }

> +

> +  //

> +  // Check to see if Address is aligned

> +  //

> +  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {

> +    return EFI_UNSUPPORTED;

> +  }

> +

> +  //

> +  // Check to see if any address associated with this transfer exceeds the maximum

> +  // allowed address.  The maximum address implied by the parameters passed in is

> +  // Address + Size * Count.  If the following condition is met, then the transfer

> +  // is not supported.

> +  //

> +  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1

> +  //

> +  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count

> +  // can also be the maximum integer value supported by the CPU, this range

> +  // check must be adjusted to avoid all oveflow conditions.

> +  //

> +  // The following form of the range check is equivalent but assumes that

> +  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).

> +  //

> +  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);

> +  if (Count == 0) {

> +    if (Address > Limit) {

> +      return EFI_UNSUPPORTED;

> +    }

> +  } else {

> +    MaxCount = RShiftU64 (Limit, Width);

> +    if (MaxCount < (Count - 1)) {

> +      return EFI_UNSUPPORTED;

> +    }

> +    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {

> +      return EFI_UNSUPPORTED;

> +    }

> +  }

> +

> +  //

> +  // Check to see if Buffer is aligned

> +  //

> +  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {

> +    return EFI_UNSUPPORTED;

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Reads memory-mapped registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuMemoryServiceRead (

> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN  UINT64                     Address,

> +  IN  UINTN                      Count,

> +  OUT VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

> +      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);

> +    }

> +  }

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Writes memory-mapped registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuMemoryServiceWrite (

> +  IN EFI_CPU_IO2_PROTOCOL       *This,

> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN UINT64                     Address,

> +  IN UINTN                      Count,

> +  IN VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

> +      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));

> +    }

> +  }

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Reads I/O registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuIoServiceRead (

> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN  UINT64                     Address,

> +  IN  UINTN                      Count,

> +  OUT VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Address += PcdGet64 (PcdPciIoTranslation);

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +

> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

> +    }

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Write I/O registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuIoServiceWrite (

> +  IN EFI_CPU_IO2_PROTOCOL       *This,

> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN UINT64                     Address,

> +  IN UINTN                      Count,

> +  IN VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  //

> +  // Make sure the parameters are valid

> +  //

> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Address += PcdGet64 (PcdPciIoTranslation);

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +

> +  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

> +    }

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +//

> +// CPU I/O 2 Protocol instance

> +//

> +STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {

> +  {

> +    CpuMemoryServiceRead,

> +    CpuMemoryServiceWrite

> +  },

> +  {

> +    CpuIoServiceRead,

> +    CpuIoServiceWrite

> +  }

> +};

> +

> +

> +/**

> +  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.

> +

> +  @param[in] ImageHandle    The firmware allocated handle for the EFI image.

> +  @param[in] SystemTable    A pointer to the EFI System Table.

> +

> +  @retval EFI_SUCCESS       The entry point is executed successfully.

> +  @retval other             Some error occurs when executing this entry point.

> +

> +**/

> +EFI_STATUS

> +EFIAPI

> +ArmPciCpuIo2Initialize (

> +  IN EFI_HANDLE        ImageHandle,

> +  IN EFI_SYSTEM_TABLE  *SystemTable

> +  )

> +{

> +  EFI_STATUS Status;

> +

> +  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);

> +  Status = gBS->InstallMultipleProtocolInterfaces (

> +                  &mHandle,

> +                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,

> +                  NULL

> +                  );

> +  ASSERT_EFI_ERROR (Status);

> +

> +  return Status;

> +}

> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

> new file mode 100644

> index 000000000000..6513e9843121

> --- /dev/null

> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

> @@ -0,0 +1,53 @@

> +## @file

> +#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.

> +#

> +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>

> +# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

> +#

> +# This program and the accompanying materials

> +# are licensed and made available under the terms and conditions of the BSD License

> +# which accompanies this distribution.  The full text of the license may be found at

> +# http://opensource.org/licenses/bsd-license.php

> +#

> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

> +#

> +##

> +

> +[Defines]

> +  INF_VERSION                    = 0x00010005

> +  BASE_NAME                      = ArmPciCpuIo2Dxe

> +  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067

> +  MODULE_TYPE                    = DXE_DRIVER

> +  VERSION_STRING                 = 1.0

> +  ENTRY_POINT                    = ArmPciCpuIo2Initialize

> +

> +#

> +# The following information is for reference only and not required by the build tools.

> +#

> +#  VALID_ARCHITECTURES           = ARM AARCH64

> +#

> +

> +[Sources]

> +  ArmPciCpuIo2Dxe.c

> +

> +[Packages]

> +  MdePkg/MdePkg.dec

> +  ArmPlatformPkg/ArmPlatformPkg.dec

> +

> +[LibraryClasses]

> +  UefiDriverEntryPoint

> +  BaseLib

> +  DebugLib

> +  IoLib

> +  PcdLib

> +  UefiBootServicesTableLib

> +

> +[Pcd]

> +  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation

> +

> +[Protocols]

> +  gEfiCpuIo2ProtocolGuid                         ## PRODUCES

> +

> +[Depex]

> +  TRUE

> 


_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Leif Lindholm April 27, 2016, 3:44 p.m. UTC | #2
On Wed, Apr 27, 2016 at 02:58:29PM +0200, Ard Biesheuvel wrote:
> The CpuIo2 protocol is required by the generic PciHostBridgeDxe driver,

> which relies on it to back its own I/O and MMIO operations.

> 

> Since ARM has no native I/O port equivalent, such accesses can only originate

> from PCI drivers, and the PCI I/O space is translated to MMIO in this case.

> 

> So we can implement this protocol using MMIO operations only, and take the

> PCI I/O translation offset into account when performing I/O port accesses.


Some of these lines look a bit long?

> Contributed-under: TianoCore Contribution Agreement 1.0

> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

> ---

> 

> We will need this on Seattle to support PCI devices with I/O BARs when we move

> to the generic PciHostBridgeDxe driver.


So, I'm mostly happy with this.

First thing, the diff against the UefiCpuPkg original (thanks Laszlo
for saving me the effort of figuring out myself :) is pretty tiny.
Unless someone is planning to rename UefiCpuPkg to something
Xnn-specific, maybe the best thing to do would be to merge this into
the existing code? To consider for future consolidation if nothing
else.

>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c   | 559 ++++++++++++++++++++

>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf |  53 ++


Don't you mean ArmPkg/Drivers?

Other than that, I'm happy with this addition.

>  2 files changed, 612 insertions(+)

> 

> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

> new file mode 100644

> index 000000000000..fecf6a87adce

> --- /dev/null

> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

> @@ -0,0 +1,559 @@

> +/** @file

> +  Produces the CPU I/O 2 Protocol.

> +

> +Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>

> +Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

> +

> +This program and the accompanying materials

> +are licensed and made available under the terms and conditions of the BSD License

> +which accompanies this distribution.  The full text of the license may be found at

> +http://opensource.org/licenses/bsd-license.php

> +

> +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

> +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

> +

> +**/

> +

> +#include <PiDxe.h>

> +

> +#include <Protocol/CpuIo2.h>

> +

> +#include <Library/BaseLib.h>

> +#include <Library/DebugLib.h>

> +#include <Library/IoLib.h>

> +#include <Library/PcdLib.h>

> +#include <Library/UefiBootServicesTableLib.h>

> +

> +#define MAX_IO_PORT_ADDRESS   0xFFFF

> +

> +//

> +// Handle for the CPU I/O 2 Protocol

> +//

> +STATIC EFI_HANDLE  mHandle = NULL;

> +

> +//

> +// Lookup table for increment values based on transfer widths

> +//

> +STATIC CONST UINT8 mInStride[] = {

> +  1, // EfiCpuIoWidthUint8

> +  2, // EfiCpuIoWidthUint16

> +  4, // EfiCpuIoWidthUint32

> +  8, // EfiCpuIoWidthUint64

> +  0, // EfiCpuIoWidthFifoUint8

> +  0, // EfiCpuIoWidthFifoUint16

> +  0, // EfiCpuIoWidthFifoUint32

> +  0, // EfiCpuIoWidthFifoUint64

> +  1, // EfiCpuIoWidthFillUint8

> +  2, // EfiCpuIoWidthFillUint16

> +  4, // EfiCpuIoWidthFillUint32

> +  8  // EfiCpuIoWidthFillUint64

> +};

> +

> +//

> +// Lookup table for increment values based on transfer widths

> +//

> +STATIC CONST UINT8 mOutStride[] = {

> +  1, // EfiCpuIoWidthUint8

> +  2, // EfiCpuIoWidthUint16

> +  4, // EfiCpuIoWidthUint32

> +  8, // EfiCpuIoWidthUint64

> +  1, // EfiCpuIoWidthFifoUint8

> +  2, // EfiCpuIoWidthFifoUint16

> +  4, // EfiCpuIoWidthFifoUint32

> +  8, // EfiCpuIoWidthFifoUint64

> +  0, // EfiCpuIoWidthFillUint8

> +  0, // EfiCpuIoWidthFillUint16

> +  0, // EfiCpuIoWidthFillUint32

> +  0  // EfiCpuIoWidthFillUint64

> +};

> +

> +/**

> +  Check parameters to a CPU I/O 2 Protocol service request.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.

> +  @param[in] Width          Signifies the width of the I/O or Memory operation.

> +  @param[in] Address        The base address of the I/O operation.

> +  @param[in] Count          The number of I/O operations to perform. The number of

> +                            bytes moved is Width size * Count, starting at Address.

> +  @param[in] Buffer         For read operations, the destination buffer to store the results.

> +                            For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The parameters for this request pass the checks.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +CpuIoCheckParameter (

> +  IN BOOLEAN                    MmioOperation,

> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN UINT64                     Address,

> +  IN UINTN                      Count,

> +  IN VOID                       *Buffer

> +  )

> +{

> +  UINT64  MaxCount;

> +  UINT64  Limit;

> +

> +  //

> +  // Check to see if Buffer is NULL

> +  //

> +  if (Buffer == NULL) {

> +    return EFI_INVALID_PARAMETER;

> +  }

> +

> +  //

> +  // Check to see if Width is in the valid range

> +  //

> +  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {

> +    return EFI_INVALID_PARAMETER;

> +  }

> +

> +  //

> +  // For FIFO type, the target address won't increase during the access,

> +  // so treat Count as 1

> +  //

> +  if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {

> +    Count = 1;

> +  }

> +

> +  //

> +  // Check to see if Width is in the valid range for I/O Port operations

> +  //

> +  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {

> +    return EFI_INVALID_PARAMETER;

> +  }

> +

> +  //

> +  // Check to see if Address is aligned

> +  //

> +  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {

> +    return EFI_UNSUPPORTED;

> +  }

> +

> +  //

> +  // Check to see if any address associated with this transfer exceeds the maximum

> +  // allowed address.  The maximum address implied by the parameters passed in is

> +  // Address + Size * Count.  If the following condition is met, then the transfer

> +  // is not supported.

> +  //

> +  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1

> +  //

> +  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count

> +  // can also be the maximum integer value supported by the CPU, this range

> +  // check must be adjusted to avoid all oveflow conditions.

> +  //

> +  // The following form of the range check is equivalent but assumes that

> +  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).

> +  //

> +  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);

> +  if (Count == 0) {

> +    if (Address > Limit) {

> +      return EFI_UNSUPPORTED;

> +    }

> +  } else {

> +    MaxCount = RShiftU64 (Limit, Width);

> +    if (MaxCount < (Count - 1)) {

> +      return EFI_UNSUPPORTED;

> +    }

> +    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {

> +      return EFI_UNSUPPORTED;

> +    }

> +  }

> +

> +  //

> +  // Check to see if Buffer is aligned

> +  //

> +  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {

> +    return EFI_UNSUPPORTED;

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Reads memory-mapped registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuMemoryServiceRead (

> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN  UINT64                     Address,

> +  IN  UINTN                      Count,

> +  OUT VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

> +      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);

> +    }

> +  }

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Writes memory-mapped registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuMemoryServiceWrite (

> +  IN EFI_CPU_IO2_PROTOCOL       *This,

> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN UINT64                     Address,

> +  IN UINTN                      Count,

> +  IN VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

> +      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));

> +    }

> +  }

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Reads I/O registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuIoServiceRead (

> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN  UINT64                     Address,

> +  IN  UINTN                      Count,

> +  OUT VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Address += PcdGet64 (PcdPciIoTranslation);

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +

> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

> +    }

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  Write I/O registers.

> +

> +  The I/O operations are carried out exactly as requested. The caller is responsible

> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> +  platform might require. For example on some platforms, width requests of

> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> +  be handled by the driver.

> +

> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> +  each of the Count operations that is performed.

> +

> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times on the same Address.

> +

> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> +  incremented for each of the Count operations that is performed. The read or

> +  write operation is performed Count times from the first element of Buffer.

> +

> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> +  @param[in]  Address  The base address of the I/O operation.

> +  @param[in]  Count    The number of I/O operations to perform. The number of

> +                       bytes moved is Width size * Count, starting at Address.

> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

> +                       For write operations, the source buffer from which to write data.

> +

> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> +                                 and Count is not valid for this PI system.

> +

> +**/

> +STATIC

> +EFI_STATUS

> +EFIAPI

> +CpuIoServiceWrite (

> +  IN EFI_CPU_IO2_PROTOCOL       *This,

> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> +  IN UINT64                     Address,

> +  IN UINTN                      Count,

> +  IN VOID                       *Buffer

> +  )

> +{

> +  EFI_STATUS                 Status;

> +  UINT8                      InStride;

> +  UINT8                      OutStride;

> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> +  UINT8                      *Uint8Buffer;

> +

> +  //

> +  // Make sure the parameters are valid

> +  //

> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Address += PcdGet64 (PcdPciIoTranslation);

> +

> +  //

> +  // Select loop based on the width of the transfer

> +  //

> +  InStride = mInStride[Width];

> +  OutStride = mOutStride[Width];

> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> +

> +  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

> +    }

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +//

> +// CPU I/O 2 Protocol instance

> +//

> +STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {

> +  {

> +    CpuMemoryServiceRead,

> +    CpuMemoryServiceWrite

> +  },

> +  {

> +    CpuIoServiceRead,

> +    CpuIoServiceWrite

> +  }

> +};

> +

> +

> +/**

> +  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.

> +

> +  @param[in] ImageHandle    The firmware allocated handle for the EFI image.

> +  @param[in] SystemTable    A pointer to the EFI System Table.

> +

> +  @retval EFI_SUCCESS       The entry point is executed successfully.

> +  @retval other             Some error occurs when executing this entry point.

> +

> +**/

> +EFI_STATUS

> +EFIAPI

> +ArmPciCpuIo2Initialize (

> +  IN EFI_HANDLE        ImageHandle,

> +  IN EFI_SYSTEM_TABLE  *SystemTable

> +  )

> +{

> +  EFI_STATUS Status;

> +

> +  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);

> +  Status = gBS->InstallMultipleProtocolInterfaces (

> +                  &mHandle,

> +                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,

> +                  NULL

> +                  );

> +  ASSERT_EFI_ERROR (Status);

> +

> +  return Status;

> +}

> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

> new file mode 100644

> index 000000000000..6513e9843121

> --- /dev/null

> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

> @@ -0,0 +1,53 @@

> +## @file

> +#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.

> +#

> +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>

> +# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

> +#

> +# This program and the accompanying materials

> +# are licensed and made available under the terms and conditions of the BSD License

> +# which accompanies this distribution.  The full text of the license may be found at

> +# http://opensource.org/licenses/bsd-license.php

> +#

> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

> +#

> +##

> +

> +[Defines]

> +  INF_VERSION                    = 0x00010005

> +  BASE_NAME                      = ArmPciCpuIo2Dxe

> +  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067

> +  MODULE_TYPE                    = DXE_DRIVER

> +  VERSION_STRING                 = 1.0

> +  ENTRY_POINT                    = ArmPciCpuIo2Initialize

> +

> +#

> +# The following information is for reference only and not required by the build tools.

> +#

> +#  VALID_ARCHITECTURES           = ARM AARCH64

> +#

> +

> +[Sources]

> +  ArmPciCpuIo2Dxe.c

> +

> +[Packages]

> +  MdePkg/MdePkg.dec

> +  ArmPlatformPkg/ArmPlatformPkg.dec

> +

> +[LibraryClasses]

> +  UefiDriverEntryPoint

> +  BaseLib

> +  DebugLib

> +  IoLib

> +  PcdLib

> +  UefiBootServicesTableLib

> +

> +[Pcd]

> +  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation

> +

> +[Protocols]

> +  gEfiCpuIo2ProtocolGuid                         ## PRODUCES

> +

> +[Depex]

> +  TRUE

> -- 

> 2.7.4

> 

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Ard Biesheuvel April 27, 2016, 3:48 p.m. UTC | #3
On 27 April 2016 at 17:44, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> On Wed, Apr 27, 2016 at 02:58:29PM +0200, Ard Biesheuvel wrote:

>> The CpuIo2 protocol is required by the generic PciHostBridgeDxe driver,

>> which relies on it to back its own I/O and MMIO operations.

>>

>> Since ARM has no native I/O port equivalent, such accesses can only originate

>> from PCI drivers, and the PCI I/O space is translated to MMIO in this case.

>>

>> So we can implement this protocol using MMIO operations only, and take the

>> PCI I/O translation offset into account when performing I/O port accesses.

>

> Some of these lines look a bit long?

>


Will fix that

>> Contributed-under: TianoCore Contribution Agreement 1.0

>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

>> ---

>>

>> We will need this on Seattle to support PCI devices with I/O BARs when we move

>> to the generic PciHostBridgeDxe driver.

>

> So, I'm mostly happy with this.

>

> First thing, the diff against the UefiCpuPkg original (thanks Laszlo

> for saving me the effort of figuring out myself :) is pretty tiny.

> Unless someone is planning to rename UefiCpuPkg to something

> Xnn-specific, maybe the best thing to do would be to merge this into

> the existing code? To consider for future consolidation if nothing

> else.

>


Well, the concern is that the I/O space does not exist natively on
ARM, and so it will always be tied to the PCI implementation (and we
only need it because PciHostBridgeDxe invokes the protocol in its
implementation of the I/O accessors it exposes to PCI devices
drivers). This is different from UefiCpuPkg (of which I don't quite
understand the reason for existing in the first place tbh)

>>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c   | 559 ++++++++++++++++++++

>>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf |  53 ++

>

> Don't you mean ArmPkg/Drivers?

>


No. The IoTranslation PCD is defined by ArmPlatformPkg, so I didn't
think it was appropriate. Also, the ARM architecture does not have the
notion of I/O ports.

Also, I should probably rename the dir to match the .inf/.c names

> Other than that, I'm happy with this addition.

>


Thanks.


>>  2 files changed, 612 insertions(+)

>>

>> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

>> new file mode 100644

>> index 000000000000..fecf6a87adce

>> --- /dev/null

>> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

>> @@ -0,0 +1,559 @@

>> +/** @file

>> +  Produces the CPU I/O 2 Protocol.

>> +

>> +Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>

>> +Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

>> +

>> +This program and the accompanying materials

>> +are licensed and made available under the terms and conditions of the BSD License

>> +which accompanies this distribution.  The full text of the license may be found at

>> +http://opensource.org/licenses/bsd-license.php

>> +

>> +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

>> +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

>> +

>> +**/

>> +

>> +#include <PiDxe.h>

>> +

>> +#include <Protocol/CpuIo2.h>

>> +

>> +#include <Library/BaseLib.h>

>> +#include <Library/DebugLib.h>

>> +#include <Library/IoLib.h>

>> +#include <Library/PcdLib.h>

>> +#include <Library/UefiBootServicesTableLib.h>

>> +

>> +#define MAX_IO_PORT_ADDRESS   0xFFFF

>> +

>> +//

>> +// Handle for the CPU I/O 2 Protocol

>> +//

>> +STATIC EFI_HANDLE  mHandle = NULL;

>> +

>> +//

>> +// Lookup table for increment values based on transfer widths

>> +//

>> +STATIC CONST UINT8 mInStride[] = {

>> +  1, // EfiCpuIoWidthUint8

>> +  2, // EfiCpuIoWidthUint16

>> +  4, // EfiCpuIoWidthUint32

>> +  8, // EfiCpuIoWidthUint64

>> +  0, // EfiCpuIoWidthFifoUint8

>> +  0, // EfiCpuIoWidthFifoUint16

>> +  0, // EfiCpuIoWidthFifoUint32

>> +  0, // EfiCpuIoWidthFifoUint64

>> +  1, // EfiCpuIoWidthFillUint8

>> +  2, // EfiCpuIoWidthFillUint16

>> +  4, // EfiCpuIoWidthFillUint32

>> +  8  // EfiCpuIoWidthFillUint64

>> +};

>> +

>> +//

>> +// Lookup table for increment values based on transfer widths

>> +//

>> +STATIC CONST UINT8 mOutStride[] = {

>> +  1, // EfiCpuIoWidthUint8

>> +  2, // EfiCpuIoWidthUint16

>> +  4, // EfiCpuIoWidthUint32

>> +  8, // EfiCpuIoWidthUint64

>> +  1, // EfiCpuIoWidthFifoUint8

>> +  2, // EfiCpuIoWidthFifoUint16

>> +  4, // EfiCpuIoWidthFifoUint32

>> +  8, // EfiCpuIoWidthFifoUint64

>> +  0, // EfiCpuIoWidthFillUint8

>> +  0, // EfiCpuIoWidthFillUint16

>> +  0, // EfiCpuIoWidthFillUint32

>> +  0  // EfiCpuIoWidthFillUint64

>> +};

>> +

>> +/**

>> +  Check parameters to a CPU I/O 2 Protocol service request.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.

>> +  @param[in] Width          Signifies the width of the I/O or Memory operation.

>> +  @param[in] Address        The base address of the I/O operation.

>> +  @param[in] Count          The number of I/O operations to perform. The number of

>> +                            bytes moved is Width size * Count, starting at Address.

>> +  @param[in] Buffer         For read operations, the destination buffer to store the results.

>> +                            For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The parameters for this request pass the checks.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +CpuIoCheckParameter (

>> +  IN BOOLEAN                    MmioOperation,

>> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN UINT64                     Address,

>> +  IN UINTN                      Count,

>> +  IN VOID                       *Buffer

>> +  )

>> +{

>> +  UINT64  MaxCount;

>> +  UINT64  Limit;

>> +

>> +  //

>> +  // Check to see if Buffer is NULL

>> +  //

>> +  if (Buffer == NULL) {

>> +    return EFI_INVALID_PARAMETER;

>> +  }

>> +

>> +  //

>> +  // Check to see if Width is in the valid range

>> +  //

>> +  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {

>> +    return EFI_INVALID_PARAMETER;

>> +  }

>> +

>> +  //

>> +  // For FIFO type, the target address won't increase during the access,

>> +  // so treat Count as 1

>> +  //

>> +  if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {

>> +    Count = 1;

>> +  }

>> +

>> +  //

>> +  // Check to see if Width is in the valid range for I/O Port operations

>> +  //

>> +  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {

>> +    return EFI_INVALID_PARAMETER;

>> +  }

>> +

>> +  //

>> +  // Check to see if Address is aligned

>> +  //

>> +  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {

>> +    return EFI_UNSUPPORTED;

>> +  }

>> +

>> +  //

>> +  // Check to see if any address associated with this transfer exceeds the maximum

>> +  // allowed address.  The maximum address implied by the parameters passed in is

>> +  // Address + Size * Count.  If the following condition is met, then the transfer

>> +  // is not supported.

>> +  //

>> +  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1

>> +  //

>> +  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count

>> +  // can also be the maximum integer value supported by the CPU, this range

>> +  // check must be adjusted to avoid all oveflow conditions.

>> +  //

>> +  // The following form of the range check is equivalent but assumes that

>> +  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).

>> +  //

>> +  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);

>> +  if (Count == 0) {

>> +    if (Address > Limit) {

>> +      return EFI_UNSUPPORTED;

>> +    }

>> +  } else {

>> +    MaxCount = RShiftU64 (Limit, Width);

>> +    if (MaxCount < (Count - 1)) {

>> +      return EFI_UNSUPPORTED;

>> +    }

>> +    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {

>> +      return EFI_UNSUPPORTED;

>> +    }

>> +  }

>> +

>> +  //

>> +  // Check to see if Buffer is aligned

>> +  //

>> +  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {

>> +    return EFI_UNSUPPORTED;

>> +  }

>> +

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Reads memory-mapped registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuMemoryServiceRead (

>> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN  UINT64                     Address,

>> +  IN  UINTN                      Count,

>> +  OUT VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

>> +      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);

>> +    }

>> +  }

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Writes memory-mapped registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuMemoryServiceWrite (

>> +  IN EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN UINT64                     Address,

>> +  IN UINTN                      Count,

>> +  IN VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

>> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

>> +      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));

>> +    }

>> +  }

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Reads I/O registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuIoServiceRead (

>> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN  UINT64                     Address,

>> +  IN  UINTN                      Count,

>> +  OUT VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  Address += PcdGet64 (PcdPciIoTranslation);

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +

>> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

>> +    }

>> +  }

>> +

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Write I/O registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuIoServiceWrite (

>> +  IN EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN UINT64                     Address,

>> +  IN UINTN                      Count,

>> +  IN VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  //

>> +  // Make sure the parameters are valid

>> +  //

>> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  Address += PcdGet64 (PcdPciIoTranslation);

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +

>> +  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

>> +    }

>> +  }

>> +

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +//

>> +// CPU I/O 2 Protocol instance

>> +//

>> +STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {

>> +  {

>> +    CpuMemoryServiceRead,

>> +    CpuMemoryServiceWrite

>> +  },

>> +  {

>> +    CpuIoServiceRead,

>> +    CpuIoServiceWrite

>> +  }

>> +};

>> +

>> +

>> +/**

>> +  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.

>> +

>> +  @param[in] ImageHandle    The firmware allocated handle for the EFI image.

>> +  @param[in] SystemTable    A pointer to the EFI System Table.

>> +

>> +  @retval EFI_SUCCESS       The entry point is executed successfully.

>> +  @retval other             Some error occurs when executing this entry point.

>> +

>> +**/

>> +EFI_STATUS

>> +EFIAPI

>> +ArmPciCpuIo2Initialize (

>> +  IN EFI_HANDLE        ImageHandle,

>> +  IN EFI_SYSTEM_TABLE  *SystemTable

>> +  )

>> +{

>> +  EFI_STATUS Status;

>> +

>> +  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);

>> +  Status = gBS->InstallMultipleProtocolInterfaces (

>> +                  &mHandle,

>> +                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,

>> +                  NULL

>> +                  );

>> +  ASSERT_EFI_ERROR (Status);

>> +

>> +  return Status;

>> +}

>> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

>> new file mode 100644

>> index 000000000000..6513e9843121

>> --- /dev/null

>> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

>> @@ -0,0 +1,53 @@

>> +## @file

>> +#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.

>> +#

>> +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>

>> +# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

>> +#

>> +# This program and the accompanying materials

>> +# are licensed and made available under the terms and conditions of the BSD License

>> +# which accompanies this distribution.  The full text of the license may be found at

>> +# http://opensource.org/licenses/bsd-license.php

>> +#

>> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

>> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

>> +#

>> +##

>> +

>> +[Defines]

>> +  INF_VERSION                    = 0x00010005

>> +  BASE_NAME                      = ArmPciCpuIo2Dxe

>> +  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067

>> +  MODULE_TYPE                    = DXE_DRIVER

>> +  VERSION_STRING                 = 1.0

>> +  ENTRY_POINT                    = ArmPciCpuIo2Initialize

>> +

>> +#

>> +# The following information is for reference only and not required by the build tools.

>> +#

>> +#  VALID_ARCHITECTURES           = ARM AARCH64

>> +#

>> +

>> +[Sources]

>> +  ArmPciCpuIo2Dxe.c

>> +

>> +[Packages]

>> +  MdePkg/MdePkg.dec

>> +  ArmPlatformPkg/ArmPlatformPkg.dec

>> +

>> +[LibraryClasses]

>> +  UefiDriverEntryPoint

>> +  BaseLib

>> +  DebugLib

>> +  IoLib

>> +  PcdLib

>> +  UefiBootServicesTableLib

>> +

>> +[Pcd]

>> +  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation

>> +

>> +[Protocols]

>> +  gEfiCpuIo2ProtocolGuid                         ## PRODUCES

>> +

>> +[Depex]

>> +  TRUE

>> --

>> 2.7.4

>>

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Laszlo Ersek April 27, 2016, 3:52 p.m. UTC | #4
On 04/27/16 17:44, Leif Lindholm wrote:
> On Wed, Apr 27, 2016 at 02:58:29PM +0200, Ard Biesheuvel wrote:

>> The CpuIo2 protocol is required by the generic PciHostBridgeDxe driver,

>> which relies on it to back its own I/O and MMIO operations.

>>

>> Since ARM has no native I/O port equivalent, such accesses can only originate

>> from PCI drivers, and the PCI I/O space is translated to MMIO in this case.

>>

>> So we can implement this protocol using MMIO operations only, and take the

>> PCI I/O translation offset into account when performing I/O port accesses.

> 

> Some of these lines look a bit long?

> 

>> Contributed-under: TianoCore Contribution Agreement 1.0

>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

>> ---

>>

>> We will need this on Seattle to support PCI devices with I/O BARs when we move

>> to the generic PciHostBridgeDxe driver.

> 

> So, I'm mostly happy with this.

> 

> First thing, the diff against the UefiCpuPkg original (thanks Laszlo

> for saving me the effort of figuring out myself :) is pretty tiny.

> Unless someone is planning to rename UefiCpuPkg to something

> Xnn-specific, maybe the best thing to do would be to merge this into

> the existing code? To consider for future consolidation if nothing

> else.


As far as I remember, opinions differ whether a single PCD is flexible
enough, in the general case, to control this mapping.

Since the protocol produced by this driver is supposed to be platform
and/or CPU dependent, I think it's fine to keep under ArmPlatformPkg,
separately. But that's just my opinion. :)

Thanks
Laszlo

> 

>>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c   | 559 ++++++++++++++++++++

>>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf |  53 ++

> 

> Don't you mean ArmPkg/Drivers?

> 

> Other than that, I'm happy with this addition.

> 

>>  2 files changed, 612 insertions(+)

>>

>> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

>> new file mode 100644

>> index 000000000000..fecf6a87adce

>> --- /dev/null

>> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

>> @@ -0,0 +1,559 @@

>> +/** @file

>> +  Produces the CPU I/O 2 Protocol.

>> +

>> +Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>

>> +Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

>> +

>> +This program and the accompanying materials

>> +are licensed and made available under the terms and conditions of the BSD License

>> +which accompanies this distribution.  The full text of the license may be found at

>> +http://opensource.org/licenses/bsd-license.php

>> +

>> +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

>> +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

>> +

>> +**/

>> +

>> +#include <PiDxe.h>

>> +

>> +#include <Protocol/CpuIo2.h>

>> +

>> +#include <Library/BaseLib.h>

>> +#include <Library/DebugLib.h>

>> +#include <Library/IoLib.h>

>> +#include <Library/PcdLib.h>

>> +#include <Library/UefiBootServicesTableLib.h>

>> +

>> +#define MAX_IO_PORT_ADDRESS   0xFFFF

>> +

>> +//

>> +// Handle for the CPU I/O 2 Protocol

>> +//

>> +STATIC EFI_HANDLE  mHandle = NULL;

>> +

>> +//

>> +// Lookup table for increment values based on transfer widths

>> +//

>> +STATIC CONST UINT8 mInStride[] = {

>> +  1, // EfiCpuIoWidthUint8

>> +  2, // EfiCpuIoWidthUint16

>> +  4, // EfiCpuIoWidthUint32

>> +  8, // EfiCpuIoWidthUint64

>> +  0, // EfiCpuIoWidthFifoUint8

>> +  0, // EfiCpuIoWidthFifoUint16

>> +  0, // EfiCpuIoWidthFifoUint32

>> +  0, // EfiCpuIoWidthFifoUint64

>> +  1, // EfiCpuIoWidthFillUint8

>> +  2, // EfiCpuIoWidthFillUint16

>> +  4, // EfiCpuIoWidthFillUint32

>> +  8  // EfiCpuIoWidthFillUint64

>> +};

>> +

>> +//

>> +// Lookup table for increment values based on transfer widths

>> +//

>> +STATIC CONST UINT8 mOutStride[] = {

>> +  1, // EfiCpuIoWidthUint8

>> +  2, // EfiCpuIoWidthUint16

>> +  4, // EfiCpuIoWidthUint32

>> +  8, // EfiCpuIoWidthUint64

>> +  1, // EfiCpuIoWidthFifoUint8

>> +  2, // EfiCpuIoWidthFifoUint16

>> +  4, // EfiCpuIoWidthFifoUint32

>> +  8, // EfiCpuIoWidthFifoUint64

>> +  0, // EfiCpuIoWidthFillUint8

>> +  0, // EfiCpuIoWidthFillUint16

>> +  0, // EfiCpuIoWidthFillUint32

>> +  0  // EfiCpuIoWidthFillUint64

>> +};

>> +

>> +/**

>> +  Check parameters to a CPU I/O 2 Protocol service request.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.

>> +  @param[in] Width          Signifies the width of the I/O or Memory operation.

>> +  @param[in] Address        The base address of the I/O operation.

>> +  @param[in] Count          The number of I/O operations to perform. The number of

>> +                            bytes moved is Width size * Count, starting at Address.

>> +  @param[in] Buffer         For read operations, the destination buffer to store the results.

>> +                            For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The parameters for this request pass the checks.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +CpuIoCheckParameter (

>> +  IN BOOLEAN                    MmioOperation,

>> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN UINT64                     Address,

>> +  IN UINTN                      Count,

>> +  IN VOID                       *Buffer

>> +  )

>> +{

>> +  UINT64  MaxCount;

>> +  UINT64  Limit;

>> +

>> +  //

>> +  // Check to see if Buffer is NULL

>> +  //

>> +  if (Buffer == NULL) {

>> +    return EFI_INVALID_PARAMETER;

>> +  }

>> +

>> +  //

>> +  // Check to see if Width is in the valid range

>> +  //

>> +  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {

>> +    return EFI_INVALID_PARAMETER;

>> +  }

>> +

>> +  //

>> +  // For FIFO type, the target address won't increase during the access,

>> +  // so treat Count as 1

>> +  //

>> +  if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {

>> +    Count = 1;

>> +  }

>> +

>> +  //

>> +  // Check to see if Width is in the valid range for I/O Port operations

>> +  //

>> +  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {

>> +    return EFI_INVALID_PARAMETER;

>> +  }

>> +

>> +  //

>> +  // Check to see if Address is aligned

>> +  //

>> +  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {

>> +    return EFI_UNSUPPORTED;

>> +  }

>> +

>> +  //

>> +  // Check to see if any address associated with this transfer exceeds the maximum

>> +  // allowed address.  The maximum address implied by the parameters passed in is

>> +  // Address + Size * Count.  If the following condition is met, then the transfer

>> +  // is not supported.

>> +  //

>> +  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1

>> +  //

>> +  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count

>> +  // can also be the maximum integer value supported by the CPU, this range

>> +  // check must be adjusted to avoid all oveflow conditions.

>> +  //

>> +  // The following form of the range check is equivalent but assumes that

>> +  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).

>> +  //

>> +  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);

>> +  if (Count == 0) {

>> +    if (Address > Limit) {

>> +      return EFI_UNSUPPORTED;

>> +    }

>> +  } else {

>> +    MaxCount = RShiftU64 (Limit, Width);

>> +    if (MaxCount < (Count - 1)) {

>> +      return EFI_UNSUPPORTED;

>> +    }

>> +    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {

>> +      return EFI_UNSUPPORTED;

>> +    }

>> +  }

>> +

>> +  //

>> +  // Check to see if Buffer is aligned

>> +  //

>> +  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {

>> +    return EFI_UNSUPPORTED;

>> +  }

>> +

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Reads memory-mapped registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuMemoryServiceRead (

>> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN  UINT64                     Address,

>> +  IN  UINTN                      Count,

>> +  OUT VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

>> +      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);

>> +    }

>> +  }

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Writes memory-mapped registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuMemoryServiceWrite (

>> +  IN EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN UINT64                     Address,

>> +  IN UINTN                      Count,

>> +  IN VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

>> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

>> +      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));

>> +    }

>> +  }

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Reads I/O registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuIoServiceRead (

>> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN  UINT64                     Address,

>> +  IN  UINTN                      Count,

>> +  OUT VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  Address += PcdGet64 (PcdPciIoTranslation);

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +

>> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

>> +    }

>> +  }

>> +

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +/**

>> +  Write I/O registers.

>> +

>> +  The I/O operations are carried out exactly as requested. The caller is responsible

>> +  for satisfying any alignment and I/O width restrictions that a PI System on a

>> +  platform might require. For example on some platforms, width requests of

>> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

>> +  be handled by the driver.

>> +

>> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

>> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

>> +  each of the Count operations that is performed.

>> +

>> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

>> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times on the same Address.

>> +

>> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

>> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

>> +  incremented for each of the Count operations that is performed. The read or

>> +  write operation is performed Count times from the first element of Buffer.

>> +

>> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

>> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

>> +  @param[in]  Address  The base address of the I/O operation.

>> +  @param[in]  Count    The number of I/O operations to perform. The number of

>> +                       bytes moved is Width size * Count, starting at Address.

>> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

>> +                       For write operations, the source buffer from which to write data.

>> +

>> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

>> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

>> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

>> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

>> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

>> +                                 and Count is not valid for this PI system.

>> +

>> +**/

>> +STATIC

>> +EFI_STATUS

>> +EFIAPI

>> +CpuIoServiceWrite (

>> +  IN EFI_CPU_IO2_PROTOCOL       *This,

>> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

>> +  IN UINT64                     Address,

>> +  IN UINTN                      Count,

>> +  IN VOID                       *Buffer

>> +  )

>> +{

>> +  EFI_STATUS                 Status;

>> +  UINT8                      InStride;

>> +  UINT8                      OutStride;

>> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

>> +  UINT8                      *Uint8Buffer;

>> +

>> +  //

>> +  // Make sure the parameters are valid

>> +  //

>> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

>> +  if (EFI_ERROR (Status)) {

>> +    return Status;

>> +  }

>> +

>> +  Address += PcdGet64 (PcdPciIoTranslation);

>> +

>> +  //

>> +  // Select loop based on the width of the transfer

>> +  //

>> +  InStride = mInStride[Width];

>> +  OutStride = mOutStride[Width];

>> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

>> +

>> +  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

>> +    if (OperationWidth == EfiCpuIoWidthUint8) {

>> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

>> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

>> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

>> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

>> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

>> +    }

>> +  }

>> +

>> +  return EFI_SUCCESS;

>> +}

>> +

>> +//

>> +// CPU I/O 2 Protocol instance

>> +//

>> +STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {

>> +  {

>> +    CpuMemoryServiceRead,

>> +    CpuMemoryServiceWrite

>> +  },

>> +  {

>> +    CpuIoServiceRead,

>> +    CpuIoServiceWrite

>> +  }

>> +};

>> +

>> +

>> +/**

>> +  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.

>> +

>> +  @param[in] ImageHandle    The firmware allocated handle for the EFI image.

>> +  @param[in] SystemTable    A pointer to the EFI System Table.

>> +

>> +  @retval EFI_SUCCESS       The entry point is executed successfully.

>> +  @retval other             Some error occurs when executing this entry point.

>> +

>> +**/

>> +EFI_STATUS

>> +EFIAPI

>> +ArmPciCpuIo2Initialize (

>> +  IN EFI_HANDLE        ImageHandle,

>> +  IN EFI_SYSTEM_TABLE  *SystemTable

>> +  )

>> +{

>> +  EFI_STATUS Status;

>> +

>> +  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);

>> +  Status = gBS->InstallMultipleProtocolInterfaces (

>> +                  &mHandle,

>> +                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,

>> +                  NULL

>> +                  );

>> +  ASSERT_EFI_ERROR (Status);

>> +

>> +  return Status;

>> +}

>> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

>> new file mode 100644

>> index 000000000000..6513e9843121

>> --- /dev/null

>> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

>> @@ -0,0 +1,53 @@

>> +## @file

>> +#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.

>> +#

>> +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>

>> +# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

>> +#

>> +# This program and the accompanying materials

>> +# are licensed and made available under the terms and conditions of the BSD License

>> +# which accompanies this distribution.  The full text of the license may be found at

>> +# http://opensource.org/licenses/bsd-license.php

>> +#

>> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

>> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

>> +#

>> +##

>> +

>> +[Defines]

>> +  INF_VERSION                    = 0x00010005

>> +  BASE_NAME                      = ArmPciCpuIo2Dxe

>> +  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067

>> +  MODULE_TYPE                    = DXE_DRIVER

>> +  VERSION_STRING                 = 1.0

>> +  ENTRY_POINT                    = ArmPciCpuIo2Initialize

>> +

>> +#

>> +# The following information is for reference only and not required by the build tools.

>> +#

>> +#  VALID_ARCHITECTURES           = ARM AARCH64

>> +#

>> +

>> +[Sources]

>> +  ArmPciCpuIo2Dxe.c

>> +

>> +[Packages]

>> +  MdePkg/MdePkg.dec

>> +  ArmPlatformPkg/ArmPlatformPkg.dec

>> +

>> +[LibraryClasses]

>> +  UefiDriverEntryPoint

>> +  BaseLib

>> +  DebugLib

>> +  IoLib

>> +  PcdLib

>> +  UefiBootServicesTableLib

>> +

>> +[Pcd]

>> +  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation

>> +

>> +[Protocols]

>> +  gEfiCpuIo2ProtocolGuid                         ## PRODUCES

>> +

>> +[Depex]

>> +  TRUE

>> -- 

>> 2.7.4

>>

> _______________________________________________

> edk2-devel mailing list

> edk2-devel@lists.01.org

> https://lists.01.org/mailman/listinfo/edk2-devel

> 


_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Leif Lindholm April 27, 2016, 4:13 p.m. UTC | #5
On Wed, Apr 27, 2016 at 05:48:55PM +0200, Ard Biesheuvel wrote:
> >> So we can implement this protocol using MMIO operations only, and take the

> >> PCI I/O translation offset into account when performing I/O port accesses.

> >

> > Some of these lines look a bit long?

> 

> Will fix that


Thanks.

> > First thing, the diff against the UefiCpuPkg original (thanks Laszlo

> > for saving me the effort of figuring out myself :) is pretty tiny.

> > Unless someone is planning to rename UefiCpuPkg to something

> > Xnn-specific, maybe the best thing to do would be to merge this into

> > the existing code? To consider for future consolidation if nothing

> > else.

> >

> 

> Well, the concern is that the I/O space does not exist natively on

> ARM, and so it will always be tied to the PCI implementation (and we

> only need it because PciHostBridgeDxe invokes the protocol in its

> implementation of the I/O accessors it exposes to PCI devices

> drivers). This is different from UefiCpuPkg (of which I don't quite

> understand the reason for existing in the first place tbh)


I'm with you on that one.

> 

> >>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c   | 559 ++++++++++++++++++++

> >>  ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf |  53 ++

> >

> > Don't you mean ArmPkg/Drivers?

> >

> 

> No. The IoTranslation PCD is defined by ArmPlatformPkg, so I didn't

> think it was appropriate.


The PCD can move too.

ArmPlatformPkg is a historic mistake which should over time disperse
into ArmPkg, <whatever we do about platform code> and <whatever we do
about drivers>.

I really don't like new stuff being added there, apart from maybe
primecell device drivers pending <whatever we do about drivers>.

> Also, the ARM architecture does not have the

> notion of I/O ports.


No, but this isn't about I/O ports, it is about providing a
compatibility layer for software (and hardware?) that expects I/O
ports. Which will be generally required across very many ARM*
platforms.

It may well be that where it _should_ live is in MdePkg.

> Also, I should probably rename the dir to match the .inf/.c names

> 

> > Other than that, I'm happy with this addition.

> >

> 

> Thanks.

> 

> 

> >>  2 files changed, 612 insertions(+)

> >>

> >> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

> >> new file mode 100644

> >> index 000000000000..fecf6a87adce

> >> --- /dev/null

> >> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c

> >> @@ -0,0 +1,559 @@

> >> +/** @file

> >> +  Produces the CPU I/O 2 Protocol.

> >> +

> >> +Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>

> >> +Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

> >> +

> >> +This program and the accompanying materials

> >> +are licensed and made available under the terms and conditions of the BSD License

> >> +which accompanies this distribution.  The full text of the license may be found at

> >> +http://opensource.org/licenses/bsd-license.php

> >> +

> >> +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

> >> +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

> >> +

> >> +**/

> >> +

> >> +#include <PiDxe.h>

> >> +

> >> +#include <Protocol/CpuIo2.h>

> >> +

> >> +#include <Library/BaseLib.h>

> >> +#include <Library/DebugLib.h>

> >> +#include <Library/IoLib.h>

> >> +#include <Library/PcdLib.h>

> >> +#include <Library/UefiBootServicesTableLib.h>

> >> +

> >> +#define MAX_IO_PORT_ADDRESS   0xFFFF

> >> +

> >> +//

> >> +// Handle for the CPU I/O 2 Protocol

> >> +//

> >> +STATIC EFI_HANDLE  mHandle = NULL;

> >> +

> >> +//

> >> +// Lookup table for increment values based on transfer widths

> >> +//

> >> +STATIC CONST UINT8 mInStride[] = {

> >> +  1, // EfiCpuIoWidthUint8

> >> +  2, // EfiCpuIoWidthUint16

> >> +  4, // EfiCpuIoWidthUint32

> >> +  8, // EfiCpuIoWidthUint64

> >> +  0, // EfiCpuIoWidthFifoUint8

> >> +  0, // EfiCpuIoWidthFifoUint16

> >> +  0, // EfiCpuIoWidthFifoUint32

> >> +  0, // EfiCpuIoWidthFifoUint64

> >> +  1, // EfiCpuIoWidthFillUint8

> >> +  2, // EfiCpuIoWidthFillUint16

> >> +  4, // EfiCpuIoWidthFillUint32

> >> +  8  // EfiCpuIoWidthFillUint64

> >> +};

> >> +

> >> +//

> >> +// Lookup table for increment values based on transfer widths

> >> +//

> >> +STATIC CONST UINT8 mOutStride[] = {

> >> +  1, // EfiCpuIoWidthUint8

> >> +  2, // EfiCpuIoWidthUint16

> >> +  4, // EfiCpuIoWidthUint32

> >> +  8, // EfiCpuIoWidthUint64

> >> +  1, // EfiCpuIoWidthFifoUint8

> >> +  2, // EfiCpuIoWidthFifoUint16

> >> +  4, // EfiCpuIoWidthFifoUint32

> >> +  8, // EfiCpuIoWidthFifoUint64

> >> +  0, // EfiCpuIoWidthFillUint8

> >> +  0, // EfiCpuIoWidthFillUint16

> >> +  0, // EfiCpuIoWidthFillUint32

> >> +  0  // EfiCpuIoWidthFillUint64

> >> +};

> >> +

> >> +/**

> >> +  Check parameters to a CPU I/O 2 Protocol service request.

> >> +

> >> +  The I/O operations are carried out exactly as requested. The caller is responsible

> >> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> >> +  platform might require. For example on some platforms, width requests of

> >> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> >> +  be handled by the driver.

> >> +

> >> +  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.

> >> +  @param[in] Width          Signifies the width of the I/O or Memory operation.

> >> +  @param[in] Address        The base address of the I/O operation.

> >> +  @param[in] Count          The number of I/O operations to perform. The number of

> >> +                            bytes moved is Width size * Count, starting at Address.

> >> +  @param[in] Buffer         For read operations, the destination buffer to store the results.

> >> +                            For write operations, the source buffer from which to write data.

> >> +

> >> +  @retval EFI_SUCCESS            The parameters for this request pass the checks.

> >> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> >> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> >> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> >> +                                 and Count is not valid for this PI system.

> >> +

> >> +**/

> >> +STATIC

> >> +EFI_STATUS

> >> +CpuIoCheckParameter (

> >> +  IN BOOLEAN                    MmioOperation,

> >> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> >> +  IN UINT64                     Address,

> >> +  IN UINTN                      Count,

> >> +  IN VOID                       *Buffer

> >> +  )

> >> +{

> >> +  UINT64  MaxCount;

> >> +  UINT64  Limit;

> >> +

> >> +  //

> >> +  // Check to see if Buffer is NULL

> >> +  //

> >> +  if (Buffer == NULL) {

> >> +    return EFI_INVALID_PARAMETER;

> >> +  }

> >> +

> >> +  //

> >> +  // Check to see if Width is in the valid range

> >> +  //

> >> +  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {

> >> +    return EFI_INVALID_PARAMETER;

> >> +  }

> >> +

> >> +  //

> >> +  // For FIFO type, the target address won't increase during the access,

> >> +  // so treat Count as 1

> >> +  //

> >> +  if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {

> >> +    Count = 1;

> >> +  }

> >> +

> >> +  //

> >> +  // Check to see if Width is in the valid range for I/O Port operations

> >> +  //

> >> +  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> >> +  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {

> >> +    return EFI_INVALID_PARAMETER;

> >> +  }

> >> +

> >> +  //

> >> +  // Check to see if Address is aligned

> >> +  //

> >> +  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {

> >> +    return EFI_UNSUPPORTED;

> >> +  }

> >> +

> >> +  //

> >> +  // Check to see if any address associated with this transfer exceeds the maximum

> >> +  // allowed address.  The maximum address implied by the parameters passed in is

> >> +  // Address + Size * Count.  If the following condition is met, then the transfer

> >> +  // is not supported.

> >> +  //

> >> +  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1

> >> +  //

> >> +  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count

> >> +  // can also be the maximum integer value supported by the CPU, this range

> >> +  // check must be adjusted to avoid all oveflow conditions.

> >> +  //

> >> +  // The following form of the range check is equivalent but assumes that

> >> +  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).

> >> +  //

> >> +  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);

> >> +  if (Count == 0) {

> >> +    if (Address > Limit) {

> >> +      return EFI_UNSUPPORTED;

> >> +    }

> >> +  } else {

> >> +    MaxCount = RShiftU64 (Limit, Width);

> >> +    if (MaxCount < (Count - 1)) {

> >> +      return EFI_UNSUPPORTED;

> >> +    }

> >> +    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {

> >> +      return EFI_UNSUPPORTED;

> >> +    }

> >> +  }

> >> +

> >> +  //

> >> +  // Check to see if Buffer is aligned

> >> +  //

> >> +  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {

> >> +    return EFI_UNSUPPORTED;

> >> +  }

> >> +

> >> +  return EFI_SUCCESS;

> >> +}

> >> +

> >> +/**

> >> +  Reads memory-mapped registers.

> >> +

> >> +  The I/O operations are carried out exactly as requested. The caller is responsible

> >> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> >> +  platform might require. For example on some platforms, width requests of

> >> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> >> +  be handled by the driver.

> >> +

> >> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> >> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> >> +  each of the Count operations that is performed.

> >> +

> >> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> >> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times on the same Address.

> >> +

> >> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> >> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times from the first element of Buffer.

> >> +

> >> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> >> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> >> +  @param[in]  Address  The base address of the I/O operation.

> >> +  @param[in]  Count    The number of I/O operations to perform. The number of

> >> +                       bytes moved is Width size * Count, starting at Address.

> >> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

> >> +                       For write operations, the source buffer from which to write data.

> >> +

> >> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> >> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> >> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> >> +                                 and Count is not valid for this PI system.

> >> +

> >> +**/

> >> +STATIC

> >> +EFI_STATUS

> >> +EFIAPI

> >> +CpuMemoryServiceRead (

> >> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

> >> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> >> +  IN  UINT64                     Address,

> >> +  IN  UINTN                      Count,

> >> +  OUT VOID                       *Buffer

> >> +  )

> >> +{

> >> +  EFI_STATUS                 Status;

> >> +  UINT8                      InStride;

> >> +  UINT8                      OutStride;

> >> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> >> +  UINT8                      *Uint8Buffer;

> >> +

> >> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

> >> +  if (EFI_ERROR (Status)) {

> >> +    return Status;

> >> +  }

> >> +

> >> +  //

> >> +  // Select loop based on the width of the transfer

> >> +  //

> >> +  InStride = mInStride[Width];

> >> +  OutStride = mOutStride[Width];

> >> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> >> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> >> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> >> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> >> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> >> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

> >> +      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);

> >> +    }

> >> +  }

> >> +  return EFI_SUCCESS;

> >> +}

> >> +

> >> +/**

> >> +  Writes memory-mapped registers.

> >> +

> >> +  The I/O operations are carried out exactly as requested. The caller is responsible

> >> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> >> +  platform might require. For example on some platforms, width requests of

> >> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> >> +  be handled by the driver.

> >> +

> >> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> >> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> >> +  each of the Count operations that is performed.

> >> +

> >> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> >> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times on the same Address.

> >> +

> >> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> >> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times from the first element of Buffer.

> >> +

> >> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> >> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> >> +  @param[in]  Address  The base address of the I/O operation.

> >> +  @param[in]  Count    The number of I/O operations to perform. The number of

> >> +                       bytes moved is Width size * Count, starting at Address.

> >> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

> >> +                       For write operations, the source buffer from which to write data.

> >> +

> >> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> >> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> >> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> >> +                                 and Count is not valid for this PI system.

> >> +

> >> +**/

> >> +STATIC

> >> +EFI_STATUS

> >> +EFIAPI

> >> +CpuMemoryServiceWrite (

> >> +  IN EFI_CPU_IO2_PROTOCOL       *This,

> >> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> >> +  IN UINT64                     Address,

> >> +  IN UINTN                      Count,

> >> +  IN VOID                       *Buffer

> >> +  )

> >> +{

> >> +  EFI_STATUS                 Status;

> >> +  UINT8                      InStride;

> >> +  UINT8                      OutStride;

> >> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> >> +  UINT8                      *Uint8Buffer;

> >> +

> >> +  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);

> >> +  if (EFI_ERROR (Status)) {

> >> +    return Status;

> >> +  }

> >> +

> >> +  //

> >> +  // Select loop based on the width of the transfer

> >> +  //

> >> +  InStride = mInStride[Width];

> >> +  OutStride = mOutStride[Width];

> >> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> >> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> >> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> >> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> >> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> >> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint64) {

> >> +      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));

> >> +    }

> >> +  }

> >> +  return EFI_SUCCESS;

> >> +}

> >> +

> >> +/**

> >> +  Reads I/O registers.

> >> +

> >> +  The I/O operations are carried out exactly as requested. The caller is responsible

> >> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> >> +  platform might require. For example on some platforms, width requests of

> >> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> >> +  be handled by the driver.

> >> +

> >> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> >> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> >> +  each of the Count operations that is performed.

> >> +

> >> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> >> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times on the same Address.

> >> +

> >> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> >> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times from the first element of Buffer.

> >> +

> >> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> >> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> >> +  @param[in]  Address  The base address of the I/O operation.

> >> +  @param[in]  Count    The number of I/O operations to perform. The number of

> >> +                       bytes moved is Width size * Count, starting at Address.

> >> +  @param[out] Buffer   For read operations, the destination buffer to store the results.

> >> +                       For write operations, the source buffer from which to write data.

> >> +

> >> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> >> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> >> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> >> +                                 and Count is not valid for this PI system.

> >> +

> >> +**/

> >> +STATIC

> >> +EFI_STATUS

> >> +EFIAPI

> >> +CpuIoServiceRead (

> >> +  IN  EFI_CPU_IO2_PROTOCOL       *This,

> >> +  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> >> +  IN  UINT64                     Address,

> >> +  IN  UINTN                      Count,

> >> +  OUT VOID                       *Buffer

> >> +  )

> >> +{

> >> +  EFI_STATUS                 Status;

> >> +  UINT8                      InStride;

> >> +  UINT8                      OutStride;

> >> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> >> +  UINT8                      *Uint8Buffer;

> >> +

> >> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

> >> +  if (EFI_ERROR (Status)) {

> >> +    return Status;

> >> +  }

> >> +

> >> +  Address += PcdGet64 (PcdPciIoTranslation);

> >> +

> >> +  //

> >> +  // Select loop based on the width of the transfer

> >> +  //

> >> +  InStride = mInStride[Width];

> >> +  OutStride = mOutStride[Width];

> >> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> >> +

> >> +  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> >> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> >> +      *Uint8Buffer = MmioRead8 ((UINTN)Address);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> >> +      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> >> +      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);

> >> +    }

> >> +  }

> >> +

> >> +  return EFI_SUCCESS;

> >> +}

> >> +

> >> +/**

> >> +  Write I/O registers.

> >> +

> >> +  The I/O operations are carried out exactly as requested. The caller is responsible

> >> +  for satisfying any alignment and I/O width restrictions that a PI System on a

> >> +  platform might require. For example on some platforms, width requests of

> >> +  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will

> >> +  be handled by the driver.

> >> +

> >> +  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,

> >> +  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for

> >> +  each of the Count operations that is performed.

> >> +

> >> +  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,

> >> +  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times on the same Address.

> >> +

> >> +  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,

> >> +  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is

> >> +  incremented for each of the Count operations that is performed. The read or

> >> +  write operation is performed Count times from the first element of Buffer.

> >> +

> >> +  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.

> >> +  @param[in]  Width    Signifies the width of the I/O or Memory operation.

> >> +  @param[in]  Address  The base address of the I/O operation.

> >> +  @param[in]  Count    The number of I/O operations to perform. The number of

> >> +                       bytes moved is Width size * Count, starting at Address.

> >> +  @param[in]  Buffer   For read operations, the destination buffer to store the results.

> >> +                       For write operations, the source buffer from which to write data.

> >> +

> >> +  @retval EFI_SUCCESS            The data was read from or written to the PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.

> >> +  @retval EFI_INVALID_PARAMETER  Buffer is NULL.

> >> +  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.

> >> +  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,

> >> +                                 and Count is not valid for this PI system.

> >> +

> >> +**/

> >> +STATIC

> >> +EFI_STATUS

> >> +EFIAPI

> >> +CpuIoServiceWrite (

> >> +  IN EFI_CPU_IO2_PROTOCOL       *This,

> >> +  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,

> >> +  IN UINT64                     Address,

> >> +  IN UINTN                      Count,

> >> +  IN VOID                       *Buffer

> >> +  )

> >> +{

> >> +  EFI_STATUS                 Status;

> >> +  UINT8                      InStride;

> >> +  UINT8                      OutStride;

> >> +  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;

> >> +  UINT8                      *Uint8Buffer;

> >> +

> >> +  //

> >> +  // Make sure the parameters are valid

> >> +  //

> >> +  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);

> >> +  if (EFI_ERROR (Status)) {

> >> +    return Status;

> >> +  }

> >> +

> >> +  Address += PcdGet64 (PcdPciIoTranslation);

> >> +

> >> +  //

> >> +  // Select loop based on the width of the transfer

> >> +  //

> >> +  InStride = mInStride[Width];

> >> +  OutStride = mOutStride[Width];

> >> +  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);

> >> +

> >> +  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {

> >> +    if (OperationWidth == EfiCpuIoWidthUint8) {

> >> +      MmioWrite8 ((UINTN)Address, *Uint8Buffer);

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint16) {

> >> +      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));

> >> +    } else if (OperationWidth == EfiCpuIoWidthUint32) {

> >> +      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));

> >> +    }

> >> +  }

> >> +

> >> +  return EFI_SUCCESS;

> >> +}

> >> +

> >> +//

> >> +// CPU I/O 2 Protocol instance

> >> +//

> >> +STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {

> >> +  {

> >> +    CpuMemoryServiceRead,

> >> +    CpuMemoryServiceWrite

> >> +  },

> >> +  {

> >> +    CpuIoServiceRead,

> >> +    CpuIoServiceWrite

> >> +  }

> >> +};

> >> +

> >> +

> >> +/**

> >> +  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.

> >> +

> >> +  @param[in] ImageHandle    The firmware allocated handle for the EFI image.

> >> +  @param[in] SystemTable    A pointer to the EFI System Table.

> >> +

> >> +  @retval EFI_SUCCESS       The entry point is executed successfully.

> >> +  @retval other             Some error occurs when executing this entry point.

> >> +

> >> +**/

> >> +EFI_STATUS

> >> +EFIAPI

> >> +ArmPciCpuIo2Initialize (

> >> +  IN EFI_HANDLE        ImageHandle,

> >> +  IN EFI_SYSTEM_TABLE  *SystemTable

> >> +  )

> >> +{

> >> +  EFI_STATUS Status;

> >> +

> >> +  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);

> >> +  Status = gBS->InstallMultipleProtocolInterfaces (

> >> +                  &mHandle,

> >> +                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,

> >> +                  NULL

> >> +                  );

> >> +  ASSERT_EFI_ERROR (Status);

> >> +

> >> +  return Status;

> >> +}

> >> diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

> >> new file mode 100644

> >> index 000000000000..6513e9843121

> >> --- /dev/null

> >> +++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf

> >> @@ -0,0 +1,53 @@

> >> +## @file

> >> +#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.

> >> +#

> >> +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>

> >> +# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>

> >> +#

> >> +# This program and the accompanying materials

> >> +# are licensed and made available under the terms and conditions of the BSD License

> >> +# which accompanies this distribution.  The full text of the license may be found at

> >> +# http://opensource.org/licenses/bsd-license.php

> >> +#

> >> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

> >> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

> >> +#

> >> +##

> >> +

> >> +[Defines]

> >> +  INF_VERSION                    = 0x00010005

> >> +  BASE_NAME                      = ArmPciCpuIo2Dxe

> >> +  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067

> >> +  MODULE_TYPE                    = DXE_DRIVER

> >> +  VERSION_STRING                 = 1.0

> >> +  ENTRY_POINT                    = ArmPciCpuIo2Initialize

> >> +

> >> +#

> >> +# The following information is for reference only and not required by the build tools.

> >> +#

> >> +#  VALID_ARCHITECTURES           = ARM AARCH64

> >> +#

> >> +

> >> +[Sources]

> >> +  ArmPciCpuIo2Dxe.c

> >> +

> >> +[Packages]

> >> +  MdePkg/MdePkg.dec

> >> +  ArmPlatformPkg/ArmPlatformPkg.dec

> >> +

> >> +[LibraryClasses]

> >> +  UefiDriverEntryPoint

> >> +  BaseLib

> >> +  DebugLib

> >> +  IoLib

> >> +  PcdLib

> >> +  UefiBootServicesTableLib

> >> +

> >> +[Pcd]

> >> +  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation

> >> +

> >> +[Protocols]

> >> +  gEfiCpuIo2ProtocolGuid                         ## PRODUCES

> >> +

> >> +[Depex]

> >> +  TRUE

> >> --

> >> 2.7.4

> >>

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox

Patch

diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c
new file mode 100644
index 000000000000..fecf6a87adce
--- /dev/null
+++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.c
@@ -0,0 +1,559 @@ 
+/** @file
+  Produces the CPU I/O 2 Protocol.
+
+Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <PiDxe.h>
+
+#include <Protocol/CpuIo2.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define MAX_IO_PORT_ADDRESS   0xFFFF
+
+//
+// Handle for the CPU I/O 2 Protocol
+//
+STATIC EFI_HANDLE  mHandle = NULL;
+
+//
+// Lookup table for increment values based on transfer widths
+//
+STATIC CONST UINT8 mInStride[] = {
+  1, // EfiCpuIoWidthUint8
+  2, // EfiCpuIoWidthUint16
+  4, // EfiCpuIoWidthUint32
+  8, // EfiCpuIoWidthUint64
+  0, // EfiCpuIoWidthFifoUint8
+  0, // EfiCpuIoWidthFifoUint16
+  0, // EfiCpuIoWidthFifoUint32
+  0, // EfiCpuIoWidthFifoUint64
+  1, // EfiCpuIoWidthFillUint8
+  2, // EfiCpuIoWidthFillUint16
+  4, // EfiCpuIoWidthFillUint32
+  8  // EfiCpuIoWidthFillUint64
+};
+
+//
+// Lookup table for increment values based on transfer widths
+//
+STATIC CONST UINT8 mOutStride[] = {
+  1, // EfiCpuIoWidthUint8
+  2, // EfiCpuIoWidthUint16
+  4, // EfiCpuIoWidthUint32
+  8, // EfiCpuIoWidthUint64
+  1, // EfiCpuIoWidthFifoUint8
+  2, // EfiCpuIoWidthFifoUint16
+  4, // EfiCpuIoWidthFifoUint32
+  8, // EfiCpuIoWidthFifoUint64
+  0, // EfiCpuIoWidthFillUint8
+  0, // EfiCpuIoWidthFillUint16
+  0, // EfiCpuIoWidthFillUint32
+  0  // EfiCpuIoWidthFillUint64
+};
+
+/**
+  Check parameters to a CPU I/O 2 Protocol service request.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
+  @param[in] Width          Signifies the width of the I/O or Memory operation.
+  @param[in] Address        The base address of the I/O operation.
+  @param[in] Count          The number of I/O operations to perform. The number of
+                            bytes moved is Width size * Count, starting at Address.
+  @param[in] Buffer         For read operations, the destination buffer to store the results.
+                            For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The parameters for this request pass the checks.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+CpuIoCheckParameter (
+  IN BOOLEAN                    MmioOperation,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  UINT64  MaxCount;
+  UINT64  Limit;
+
+  //
+  // Check to see if Buffer is NULL
+  //
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check to see if Width is in the valid range
+  //
+  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // For FIFO type, the target address won't increase during the access,
+  // so treat Count as 1
+  //
+  if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
+    Count = 1;
+  }
+
+  //
+  // Check to see if Width is in the valid range for I/O Port operations
+  //
+  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check to see if Address is aligned
+  //
+  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check to see if any address associated with this transfer exceeds the maximum
+  // allowed address.  The maximum address implied by the parameters passed in is
+  // Address + Size * Count.  If the following condition is met, then the transfer
+  // is not supported.
+  //
+  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
+  //
+  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count
+  // can also be the maximum integer value supported by the CPU, this range
+  // check must be adjusted to avoid all oveflow conditions.
+  //
+  // The following form of the range check is equivalent but assumes that
+  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
+  //
+  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
+  if (Count == 0) {
+    if (Address > Limit) {
+      return EFI_UNSUPPORTED;
+    }
+  } else {
+    MaxCount = RShiftU64 (Limit, Width);
+    if (MaxCount < (Count - 1)) {
+      return EFI_UNSUPPORTED;
+    }
+    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  //
+  // Check to see if Buffer is aligned
+  //
+  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      *Uint8Buffer = MmioRead8 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint64) {
+      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Writes memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint64) {
+      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuIoServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Address += PcdGet64 (PcdPciIoTranslation);
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      *Uint8Buffer = MmioRead8 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Write I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuIoServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  //
+  // Make sure the parameters are valid
+  //
+  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Address += PcdGet64 (PcdPciIoTranslation);
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+
+  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+//
+// CPU I/O 2 Protocol instance
+//
+STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {
+  {
+    CpuMemoryServiceRead,
+    CpuMemoryServiceWrite
+  },
+  {
+    CpuIoServiceRead,
+    CpuIoServiceWrite
+  }
+};
+
+
+/**
+  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+ArmPciCpuIo2Initialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mHandle,
+                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf
new file mode 100644
index 000000000000..6513e9843121
--- /dev/null
+++ b/ArmPlatformPkg/Drivers/CpuIo2Dxe/ArmPciCpuIo2Dxe.inf
@@ -0,0 +1,53 @@ 
+## @file
+#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+#
+# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = ArmPciCpuIo2Dxe
+  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = ArmPciCpuIo2Initialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = ARM AARCH64
+#
+
+[Sources]
+  ArmPciCpuIo2Dxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  IoLib
+  PcdLib
+  UefiBootServicesTableLib
+
+[Pcd]
+  gArmPlatformTokenSpaceGuid.PcdPciIoTranslation
+
+[Protocols]
+  gEfiCpuIo2ProtocolGuid                         ## PRODUCES
+
+[Depex]
+  TRUE