| /** @file | |
| Plug an EFI_PCI_IO_PROTOCOL backend into PciCapLib, for config space access. | |
| Copyright (C) 2018, Red Hat, Inc. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Library/MemoryAllocationLib.h> | |
| #include "UefiPciCapPciIoLib.h" | |
| /** | |
| Transfer bytes between the config space of a given PCI device and a memory | |
| buffer. | |
| ProtoDevTransferConfig() performs as few config space accesses as possible | |
| (without attempting 64-bit wide accesses). | |
| @param[in] PciIo The EFI_PCI_IO_PROTOCOL representation of the | |
| PCI device. | |
| @param[in] TransferFunction The EFI_PCI_IO_PROTOCOL_CONFIG function that | |
| implements the transfer. The direction of the | |
| transfer is inherent to TransferFunction. | |
| TransferFunction() is required to return an | |
| unspecified error if any sub-transfer within | |
| Size bytes from ConfigOffset exceeds the config | |
| space limit of the PCI device. | |
| @param[in] ConfigOffset The offset in the config space of the PCI device | |
| at which the transfer should commence. | |
| @param[in,out] Buffer The memory buffer where the transfer should | |
| occur. | |
| @param[in] Size The number of bytes to transfer. | |
| @retval EFI_SUCCESS Size bytes have been transferred between config space | |
| and Buffer. | |
| @return Error codes propagated from TransferFunction(). Fewer | |
| than Size bytes may have been transferred. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| ProtoDevTransferConfig ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN EFI_PCI_IO_PROTOCOL_CONFIG TransferFunction, | |
| IN UINT16 ConfigOffset, | |
| IN OUT UINT8 *Buffer, | |
| IN UINT16 Size | |
| ) | |
| { | |
| while (Size > 0) { | |
| EFI_PCI_IO_PROTOCOL_WIDTH Width; | |
| UINT16 Count; | |
| EFI_STATUS Status; | |
| UINT16 Progress; | |
| // | |
| // Pick the largest access size that is allowed by the remaining transfer | |
| // Size and by the alignment of ConfigOffset. | |
| // | |
| // When the largest access size is available, transfer as many bytes as | |
| // possible in one iteration of the loop. Otherwise, transfer only one | |
| // unit, to improve the alignment. | |
| // | |
| if ((Size >= 4) && ((ConfigOffset & 3) == 0)) { | |
| Width = EfiPciIoWidthUint32; | |
| Count = Size >> Width; | |
| } else if ((Size >= 2) && ((ConfigOffset & 1) == 0)) { | |
| Width = EfiPciIoWidthUint16; | |
| Count = 1; | |
| } else { | |
| Width = EfiPciIoWidthUint8; | |
| Count = 1; | |
| } | |
| Status = TransferFunction (PciIo, Width, ConfigOffset, Count, Buffer); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Progress = Count << Width; | |
| ConfigOffset += Progress; | |
| Buffer += Progress; | |
| Size -= Progress; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Read the config space of a given PCI device (both normal and extended). | |
| ProtoDevReadConfig() performs as few config space accesses as possible | |
| (without attempting 64-bit wide accesses). | |
| ProtoDevReadConfig() returns an unspecified error if accessing Size bytes | |
| from SourceOffset exceeds the config space limit of the PCI device. Fewer | |
| than Size bytes may have been read in this case. | |
| @param[in] PciDevice Implementation-specific unique representation | |
| of the PCI device in the PCI hierarchy. | |
| @param[in] SourceOffset Source offset in the config space of the PCI | |
| device to start reading from. | |
| @param[out] DestinationBuffer Buffer to store the read data to. | |
| @param[in] Size The number of bytes to transfer. | |
| @retval RETURN_SUCCESS Size bytes have been transferred from config space to | |
| DestinationBuffer. | |
| @return Error codes propagated from | |
| EFI_PCI_IO_PROTOCOL.Pci.Read(). Fewer than Size bytes | |
| may have been read. | |
| **/ | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| ProtoDevReadConfig ( | |
| IN PCI_CAP_DEV *PciDevice, | |
| IN UINT16 SourceOffset, | |
| OUT VOID *DestinationBuffer, | |
| IN UINT16 Size | |
| ) | |
| { | |
| PROTO_DEV *ProtoDev; | |
| ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice); | |
| return ProtoDevTransferConfig ( | |
| ProtoDev->PciIo, | |
| ProtoDev->PciIo->Pci.Read, | |
| SourceOffset, | |
| DestinationBuffer, | |
| Size | |
| ); | |
| } | |
| /** | |
| Write the config space of a given PCI device (both normal and extended). | |
| ProtoDevWriteConfig() performs as few config space accesses as possible | |
| (without attempting 64-bit wide accesses). | |
| ProtoDevWriteConfig() returns an unspecified error if accessing Size bytes at | |
| DestinationOffset exceeds the config space limit of the PCI device. Fewer | |
| than Size bytes may have been written in this case. | |
| @param[in] PciDevice Implementation-specific unique representation | |
| of the PCI device in the PCI hierarchy. | |
| @param[in] DestinationOffset Destination offset in the config space of the | |
| PCI device to start writing at. | |
| @param[in] SourceBuffer Buffer to read the data to be stored from. | |
| @param[in] Size The number of bytes to transfer. | |
| @retval RETURN_SUCCESS Size bytes have been transferred from SourceBuffer to | |
| config space. | |
| @return Error codes propagated from | |
| EFI_PCI_IO_PROTOCOL.Pci.Write(). Fewer than Size | |
| bytes may have been written. | |
| **/ | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| ProtoDevWriteConfig ( | |
| IN PCI_CAP_DEV *PciDevice, | |
| IN UINT16 DestinationOffset, | |
| IN VOID *SourceBuffer, | |
| IN UINT16 Size | |
| ) | |
| { | |
| PROTO_DEV *ProtoDev; | |
| ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice); | |
| return ProtoDevTransferConfig ( | |
| ProtoDev->PciIo, | |
| ProtoDev->PciIo->Pci.Write, | |
| DestinationOffset, | |
| SourceBuffer, | |
| Size | |
| ); | |
| } | |
| /** | |
| Create a PCI_CAP_DEV object from an EFI_PCI_IO_PROTOCOL instance. The config | |
| space accessors are based upon EFI_PCI_IO_PROTOCOL.Pci.Read() and | |
| EFI_PCI_IO_PROTOCOL.Pci.Write(). | |
| @param[in] PciIo EFI_PCI_IO_PROTOCOL representation of the PCI device. | |
| @param[out] PciDevice The PCI_CAP_DEV object constructed as described above. | |
| PciDevice can be passed to the PciCapLib APIs. | |
| @retval EFI_SUCCESS PciDevice has been constructed and output. | |
| @retval EFI_OUT_OF_RESOURCES Memory allocation failed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PciCapPciIoDeviceInit ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| OUT PCI_CAP_DEV **PciDevice | |
| ) | |
| { | |
| PROTO_DEV *ProtoDev; | |
| ProtoDev = AllocatePool (sizeof *ProtoDev); | |
| if (ProtoDev == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| ProtoDev->Signature = PROTO_DEV_SIG; | |
| ProtoDev->PciIo = PciIo; | |
| ProtoDev->BaseDevice.ReadConfig = ProtoDevReadConfig; | |
| ProtoDev->BaseDevice.WriteConfig = ProtoDevWriteConfig; | |
| *PciDevice = &ProtoDev->BaseDevice; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Free the resources used by PciDevice. | |
| @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by | |
| PciCapPciIoDeviceInit(). | |
| **/ | |
| VOID | |
| EFIAPI | |
| PciCapPciIoDeviceUninit ( | |
| IN PCI_CAP_DEV *PciDevice | |
| ) | |
| { | |
| PROTO_DEV *ProtoDev; | |
| ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice); | |
| FreePool (ProtoDev); | |
| } |