--- /dev/null
+/** @file\r
+ Library class layered on top of PciCapLib that allows clients to plug a\r
+ PciSegmentLib backend into PciCapLib, for config space access.\r
+\r
+ Copyright (C) 2018, Red Hat, Inc.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#ifndef __PCI_CAP_PCI_SEGMENT_LIB_H__\r
+#define __PCI_CAP_PCI_SEGMENT_LIB_H__\r
+\r
+#include <Library/PciCapLib.h>\r
+\r
+\r
+/**\r
+ Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function\r
+ quadruplet. The config space accessors are based upon PciSegmentLib.\r
+\r
+ @param[in] MaxDomain If MaxDomain is PciCapExtended, then\r
+ PciDevice->ReadConfig() and PciDevice->WriteConfig()\r
+ will delegate extended config space accesses too to\r
+ PciSegmentReadBuffer() and PciSegmentWriteBuffer(),\r
+ respectively. Otherwise, PciDevice->ReadConfig() and\r
+ PciDevice->WriteConfig() will reject accesses to\r
+ extended config space with RETURN_UNSUPPORTED, without\r
+ calling PciSegmentReadBuffer() or\r
+ PciSegmentWriteBuffer(). By setting MaxDomain to\r
+ PciCapNormal, the platform can prevent undefined\r
+ PciSegmentLib behavior when the PCI root bridge under\r
+ the PCI device at Segment:Bus:Device.Function doesn't\r
+ support extended config space.\r
+\r
+ @param[in] Segment 16-bit wide segment number.\r
+\r
+ @param[in] Bus 8-bit wide bus number.\r
+\r
+ @param[in] Device 5-bit wide device number.\r
+\r
+ @param[in] Function 3-bit wide function number.\r
+\r
+ @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.\r
+ PciDevice can be passed to the PciCapLib APIs.\r
+\r
+ @retval RETURN_SUCCESS PciDevice has been constructed and output.\r
+\r
+ @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the\r
+ permitted number of bits.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapPciSegmentDeviceInit (\r
+ IN PCI_CAP_DOMAIN MaxDomain,\r
+ IN UINT16 Segment,\r
+ IN UINT8 Bus,\r
+ IN UINT8 Device,\r
+ IN UINT8 Function,\r
+ OUT PCI_CAP_DEV **PciDevice\r
+ );\r
+\r
+\r
+/**\r
+ Free the resources used by PciDevice.\r
+\r
+ @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by\r
+ PciCapPciSegmentDeviceInit().\r
+**/\r
+VOID\r
+EFIAPI\r
+PciCapPciSegmentDeviceUninit (\r
+ IN PCI_CAP_DEV *PciDevice\r
+ );\r
+\r
+#endif // __PCI_CAP_PCI_SEGMENT_LIB_H__\r
--- /dev/null
+/** @file\r
+ Plug a PciSegmentLib backend into PciCapLib, for config space access.\r
+\r
+ Copyright (C) 2018, Red Hat, Inc.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include <IndustryStandard/Pci23.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PciSegmentLib.h>\r
+\r
+#include "BasePciCapPciSegmentLib.h"\r
+\r
+\r
+/**\r
+ Read the config space of a given PCI device (both normal and extended).\r
+\r
+ SegmentDevReadConfig() performs as few config space accesses as possible\r
+ (without attempting 64-bit wide accesses).\r
+\r
+ @param[in] PciDevice Implementation-specific unique representation\r
+ of the PCI device in the PCI hierarchy.\r
+\r
+ @param[in] SourceOffset Source offset in the config space of the PCI\r
+ device to start reading from.\r
+\r
+ @param[out] DestinationBuffer Buffer to store the read data to.\r
+\r
+ @param[in] Size The number of bytes to transfer.\r
+\r
+ @retval RETURN_SUCCESS Size bytes have been transferred from config\r
+ space to DestinationBuffer.\r
+\r
+ @retval RETURN_UNSUPPORTED Accessing Size bytes from SourceOffset exceeds\r
+ the config space limit of the PCI device.\r
+ Although PCI_CAP_DEV_READ_CONFIG allows reading\r
+ fewer than Size bytes in this case,\r
+ SegmentDevReadConfig() will read none.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+EFIAPI\r
+SegmentDevReadConfig (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN UINT16 SourceOffset,\r
+ OUT VOID *DestinationBuffer,\r
+ IN UINT16 Size\r
+ )\r
+{\r
+ SEGMENT_DEV *SegmentDev;\r
+ UINT16 ConfigSpaceSize;\r
+ UINT64 SourceAddress;\r
+\r
+ SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
+ ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?\r
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
+ //\r
+ // Note that all UINT16 variables below are promoted to INT32, and the\r
+ // addition and the comparison is carried out in INT32.\r
+ //\r
+ if (SourceOffset + Size > ConfigSpaceSize) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,\r
+ SegmentDev->BusNr, SegmentDev->DeviceNr,\r
+ SegmentDev->FunctionNr, SourceOffset);\r
+ PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer);\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Write the config space of a given PCI device (both normal and extended).\r
+\r
+ SegmentDevWriteConfig() performs as few config space accesses as possible\r
+ (without attempting 64-bit wide accesses).\r
+\r
+ @param[in] PciDevice Implementation-specific unique representation\r
+ of the PCI device in the PCI hierarchy.\r
+\r
+ @param[in] DestinationOffset Destination offset in the config space of the\r
+ PCI device to start writing at.\r
+\r
+ @param[in] SourceBuffer Buffer to read the data to be stored from.\r
+\r
+ @param[in] Size The number of bytes to transfer.\r
+\r
+ @retval RETURN_SUCCESS Size bytes have been transferred from\r
+ SourceBuffer to config space.\r
+\r
+ @retval RETURN_UNSUPPORTED Accessing Size bytes at DestinationOffset exceeds\r
+ the config space limit of the PCI device.\r
+ Although PCI_CAP_DEV_WRITE_CONFIG allows writing\r
+ fewer than Size bytes in this case,\r
+ SegmentDevWriteConfig() will write none.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+EFIAPI\r
+SegmentDevWriteConfig (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN UINT16 DestinationOffset,\r
+ IN VOID *SourceBuffer,\r
+ IN UINT16 Size\r
+ )\r
+{\r
+ SEGMENT_DEV *SegmentDev;\r
+ UINT16 ConfigSpaceSize;\r
+ UINT64 DestinationAddress;\r
+\r
+ SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
+ ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?\r
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
+ //\r
+ // Note that all UINT16 variables below are promoted to INT32, and the\r
+ // addition and the comparison is carried out in INT32.\r
+ //\r
+ if (DestinationOffset + Size > ConfigSpaceSize) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,\r
+ SegmentDev->BusNr, SegmentDev->DeviceNr,\r
+ SegmentDev->FunctionNr, DestinationOffset);\r
+ PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer);\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function\r
+ quadruplet. The config space accessors are based upon PciSegmentLib.\r
+\r
+ @param[in] MaxDomain If MaxDomain is PciCapExtended, then\r
+ PciDevice->ReadConfig() and PciDevice->WriteConfig()\r
+ will delegate extended config space accesses too to\r
+ PciSegmentReadBuffer() and PciSegmentWriteBuffer(),\r
+ respectively. Otherwise, PciDevice->ReadConfig() and\r
+ PciDevice->WriteConfig() will reject accesses to\r
+ extended config space with RETURN_UNSUPPORTED, without\r
+ calling PciSegmentReadBuffer() or\r
+ PciSegmentWriteBuffer(). By setting MaxDomain to\r
+ PciCapNormal, the platform can prevent undefined\r
+ PciSegmentLib behavior when the PCI root bridge under\r
+ the PCI device at Segment:Bus:Device.Function doesn't\r
+ support extended config space.\r
+\r
+ @param[in] Segment 16-bit wide segment number.\r
+\r
+ @param[in] Bus 8-bit wide bus number.\r
+\r
+ @param[in] Device 5-bit wide device number.\r
+\r
+ @param[in] Function 3-bit wide function number.\r
+\r
+ @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.\r
+ PciDevice can be passed to the PciCapLib APIs.\r
+\r
+ @retval RETURN_SUCCESS PciDevice has been constructed and output.\r
+\r
+ @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the\r
+ permitted number of bits.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapPciSegmentDeviceInit (\r
+ IN PCI_CAP_DOMAIN MaxDomain,\r
+ IN UINT16 Segment,\r
+ IN UINT8 Bus,\r
+ IN UINT8 Device,\r
+ IN UINT8 Function,\r
+ OUT PCI_CAP_DEV **PciDevice\r
+ )\r
+{\r
+ SEGMENT_DEV *SegmentDev;\r
+\r
+ if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ SegmentDev = AllocatePool (sizeof *SegmentDev);\r
+ if (SegmentDev == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ SegmentDev->Signature = SEGMENT_DEV_SIG;\r
+ SegmentDev->MaxDomain = MaxDomain;\r
+ SegmentDev->SegmentNr = Segment;\r
+ SegmentDev->BusNr = Bus;\r
+ SegmentDev->DeviceNr = Device;\r
+ SegmentDev->FunctionNr = Function;\r
+ SegmentDev->BaseDevice.ReadConfig = SegmentDevReadConfig;\r
+ SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig;\r
+\r
+ *PciDevice = &SegmentDev->BaseDevice;\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Free the resources used by PciDevice.\r
+\r
+ @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by\r
+ PciCapPciSegmentDeviceInit().\r
+**/\r
+VOID\r
+EFIAPI\r
+PciCapPciSegmentDeviceUninit (\r
+ IN PCI_CAP_DEV *PciDevice\r
+ )\r
+{\r
+ SEGMENT_DEV *SegmentDev;\r
+\r
+ SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
+ FreePool (SegmentDev);\r
+}\r