/** @file Copyright (c) 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "UsraAccessLib.h" #define MAX_IO_PORT_ADDRESS 0xFFFF // // Lookup table for increment values based on transfer widths // UINT8 mInStride[] = { 1, // UsraWidth8 2, // UsraWidth16 4, // UsraWidth32 8, // UsraWidth64 0, // UsraWidthFifo8 0, // UsraWidthFifo16 0, // UsraWidthFifo32 0, // UsraWidthFifo64 1, // UsraWidthFill8 2, // UsraWidthFill16 4, // UsraWidthFill32 8 // UsraWidthFill64 }; // // Lookup table for increment values based on transfer widths // UINT8 mOutStride[] = { 1, // UsraWidth8 2, // UsraWidth16 4, // UsraWidth32 8, // UsraWidth64 1, // UsraWidthFifo8 2, // UsraWidthFifo16 4, // UsraWidthFifo32 8, // UsraWidthFifo64 0, // UsraWidthFill8 0, // UsraWidthFill16 0, // UsraWidthFill32 0 // UsraWidthFill64 }; /** This API gets the Pcie address from the given USRA Address. @param[in] Global Global pointer @param[in] Virtual Virtual address @param[in] Address A pointer of the address of the USRA Address Structure @param[out] AlignedAddress A pointer of aligned address converted from USRA address @retval NONE **/ VOID GetPcieAccessAddress ( IN VOID *Global, IN BOOLEAN Virtual, IN USRA_ADDRESS *Address, OUT UINTN *AlignedAddress ) { INTN MmCfgBase; MmCfgBase = GetPcieSegMmcfgBaseAddress(Address); // TODO: add Error Check for NULL later *AlignedAddress = MmCfgBase + (UINTN)(Address->Attribute.RawData32[0] & 0x0fffffff); } /** This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register read operations. It transfers data from a register into a naturally aligned data buffer. @param[in] Address A pointer of the address of the USRA Address Structure to be read out @param[in] Buffer A pointer of buffer for the value read from the register @retval RETURN_SUCCESS The function completed successfully. **/ RETURN_STATUS PcieRegisterRead ( IN USRA_ADDRESS *Address, IN VOID *Buffer ) { UINTN AlignedAddress; GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress); UsraRegAlignedRead((UINT32)Address->Attribute.AccessWidth, AlignedAddress, Buffer); return RETURN_SUCCESS; } /** Check parameters to PcieBlkRegisterRead() function 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 UsraWidth64 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 Buffer is NULL. @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. @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 RETURN_STATUS CpuIoCheckParameter ( IN BOOLEAN MmioOperation, IN USRA_ACCESS_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 RETURN_INVALID_PARAMETER; } // // Check to see if Width is in the valid range // if ((UINT32)Width >= UsraWidthMaximum) { return RETURN_INVALID_PARAMETER; } // // For FIFO type, the target address won't increase during the access, // so treat Count as 1 // if (Width >= UsraWidthFifo8 && Width <= UsraWidthFifo64) { Count = 1; } // // Check to see if Width is in the valid range for I/O Port operations // Width = (USRA_ACCESS_WIDTH) (Width & 0x03); if (!MmioOperation && (Width == UsraWidth64)) { return RETURN_INVALID_PARAMETER; } // // Check to see if Address is aligned // if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) { return RETURN_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 RETURN_UNSUPPORTED; } } else { MaxCount = RShiftU64 (Limit, Width); if (MaxCount < (Count - 1)) { return RETURN_UNSUPPORTED; } if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { return RETURN_UNSUPPORTED; } } // // Check to see if Buffer is aligned // (IA-32 allows UINT64 and INT64 data types to be 32-bit aligned.) // if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width]) - 1))) != 0) { return RETURN_UNSUPPORTED; } return RETURN_SUCCESS; } /** This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie block silicon register read operations. It transfers data from a register into a naturally aligned data buffer. @param[in] Address A pointer of the address of the USRA Address Structure to be read out @param[in] Buffer A pointer of buffer for the value read from the register @retval RETURN_SUCCESS The function completed successfully. @retval Others Some error occurs when executing CpuIoCheckParameter function. **/ RETURN_STATUS PcieBlkRegisterRead ( IN USRA_ADDRESS *Address, IN VOID *Buffer ) { UINT8 InStride; UINT8 OutStride; RETURN_STATUS Status; UINTN AlignedAddress; UINT32 ReadCount = Address->PcieBlk.Count; UINT8 *UINT8Buffer; GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress); Status = CpuIoCheckParameter (TRUE, Address->Attribute.AccessWidth, AlignedAddress, ReadCount, Buffer); if (RETURN_ERROR (Status)) { return Status; } InStride = mInStride[Address->Attribute.AccessWidth]; OutStride = mOutStride[Address->Attribute.AccessWidth]; for (UINT8Buffer = Buffer; ReadCount > 0; AlignedAddress += InStride, UINT8Buffer += OutStride, ReadCount--) { UsraRegAlignedRead((USRA_ACCESS_WIDTH) (Address->Attribute.AccessWidth & 0x03), AlignedAddress, (VOID *)UINT8Buffer); } return RETURN_SUCCESS; } /** This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register write operations. It transfers data from a naturally aligned data buffer into a register. @param[in] Address A pointer of the address of the USRA Address Structure to be written @param[in] Buffer A pointer of buffer for the value write to the register @retval RETURN_SUCCESS The function completed successfully. **/ RETURN_STATUS PcieRegisterWrite ( IN USRA_ADDRESS *Address, OUT VOID *Buffer ) { UINTN AlignedAddress; GetPcieAccessAddress(NULL, 0, Address, &AlignedAddress); UsraRegAlignedWrite((UINT32)Address->Attribute.AccessWidth, AlignedAddress, Buffer); if (FeaturePcdGet (PcdUsraSupportS3)) { if(Address->Attribute.S3Enable) { S3BootScriptSaveMemWrite ((S3_BOOT_SCRIPT_LIB_WIDTH)Address->Attribute.AccessWidth, (UINT64)AlignedAddress, 1, Buffer); } } return RETURN_SUCCESS; } /** This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie block silicon register write operations. It transfers data from a naturally aligned data buffer into a register. @param[in] Address A pointer of the address of the USRA Address Structure to be written @param[in] Buffer A pointer of buffer for the value write to the register @retval RETURN_SUCCESS The function completed successfully. @retval Others Some error occurs when executing CpuIoCheckParameter function. **/ RETURN_STATUS PcieBlkRegisterWrite ( IN USRA_ADDRESS *Address, OUT VOID *Buffer ) { UINT8 InStride; UINT8 OutStride; RETURN_STATUS Status; UINTN AlignedAddress; UINT32 WriteCount = Address->PcieBlk.Count; UINT8 *UINT8Buffer; GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress); Status = CpuIoCheckParameter (TRUE, Address->Attribute.AccessWidth, AlignedAddress, WriteCount, Buffer); if (RETURN_ERROR (Status)) { return Status; } InStride = mInStride[Address->Attribute.AccessWidth]; OutStride = mOutStride[Address->Attribute.AccessWidth]; for (UINT8Buffer = Buffer; WriteCount > 0; AlignedAddress += InStride, UINT8Buffer += OutStride, WriteCount--) { UsraRegAlignedWrite((USRA_ACCESS_WIDTH) (Address->Attribute.AccessWidth & 0x03), AlignedAddress, (VOID *)UINT8Buffer); if (FeaturePcdGet (PcdUsraSupportS3)) { if(Address->Attribute.S3Enable) { S3BootScriptSaveMemWrite ((S3_BOOT_SCRIPT_LIB_WIDTH)(Address->Attribute.AccessWidth & 0x03), (UINT64)AlignedAddress, 1, (VOID *)UINT8Buffer); } } } return RETURN_SUCCESS; } /** This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register AND then OR operations. It read data from a register, And it with the AndBuffer, then Or it with the OrBuffer, and write the result back to the register @param[in] Address A pointer of the address of the silicon register to be modified @param[in] AndBuffer A pointer of buffer for the value used for AND operation A NULL pointer means no AND operation. RegisterModify() equivalents to RegisterOr() @param[in] OrBuffer A pointer of buffer for the value used for OR operation A NULL pointer means no OR operation. RegisterModify() equivalents to RegisterAnd() @retval RETURN_SUCCESS The function completed successfully. **/ RETURN_STATUS PcieRegisterModify ( IN USRA_ADDRESS *Address, IN VOID *AndBuffer, IN VOID *OrBuffer ) { UINT64 Data; UINT8 WidthTable[] = {1,2,4,8}; PcieRegisterRead(Address, &Data); DataAndOr (&Data, AndBuffer, OrBuffer, WidthTable[(UINT8)Address->Attribute.AccessWidth]); PcieRegisterWrite(Address, &Data); return RETURN_SUCCESS; }