Message ID | 1461761909-22398-1-git-send-email-ard.biesheuvel@linaro.org |
---|---|
State | Superseded |
Headers | show |
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
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
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
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
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 --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
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