--- /dev/null
+/** @file\r
+ Library class to work with PCI capabilities in PCI config space.\r
+\r
+ Provides functions to parse capabilities lists, and to locate, describe, read\r
+ and write capabilities. PCI config space access is abstracted away.\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_LIB_H__\r
+#define __PCI_CAP_LIB_H__\r
+\r
+#include <Uefi/UefiBaseType.h>\r
+\r
+//\r
+// Base structure for representing a PCI device -- down to the PCI function\r
+// level -- for the purposes of this library class. This is a forward\r
+// declaration that is completed below. Concrete implementations are supposed\r
+// to inherit and extend this type.\r
+//\r
+typedef struct PCI_CAP_DEV PCI_CAP_DEV;\r
+\r
+/**\r
+ Read the config space of a given PCI device (both normal and extended).\r
+\r
+ PCI_CAP_DEV_READ_CONFIG performs as few config space accesses as possible\r
+ (without attempting 64-bit wide accesses).\r
+\r
+ PCI_CAP_DEV_READ_CONFIG returns an unspecified error if accessing Size bytes\r
+ from SourceOffset exceeds the config space limit of the PCI device. Fewer\r
+ than Size bytes may have been read in this case.\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 space to\r
+ DestinationBuffer.\r
+\r
+ @return Unspecified error codes. Fewer than Size bytes may\r
+ have been read.\r
+**/\r
+typedef\r
+RETURN_STATUS\r
+(EFIAPI *PCI_CAP_DEV_READ_CONFIG) (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN UINT16 SourceOffset,\r
+ OUT VOID *DestinationBuffer,\r
+ IN UINT16 Size\r
+ );\r
+\r
+/**\r
+ Write the config space of a given PCI device (both normal and extended).\r
+\r
+ PCI_CAP_DEV_WRITE_CONFIG performs as few config space accesses as possible\r
+ (without attempting 64-bit wide accesses).\r
+\r
+ PCI_CAP_DEV_WRITE_CONFIG returns an unspecified error if accessing Size bytes\r
+ at DestinationOffset exceeds the config space limit of the PCI device. Fewer\r
+ than Size bytes may have been written in this case.\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 SourceBuffer to\r
+ config space.\r
+\r
+ @return Unspecified error codes. Fewer than Size bytes may\r
+ have been written.\r
+**/\r
+typedef\r
+RETURN_STATUS\r
+(EFIAPI *PCI_CAP_DEV_WRITE_CONFIG) (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN UINT16 DestinationOffset,\r
+ IN VOID *SourceBuffer,\r
+ IN UINT16 Size\r
+ );\r
+\r
+//\r
+// Complete the PCI_CAP_DEV type here. The base abstraction only requires\r
+// config space accessors.\r
+//\r
+struct PCI_CAP_DEV {\r
+ PCI_CAP_DEV_READ_CONFIG ReadConfig;\r
+ PCI_CAP_DEV_WRITE_CONFIG WriteConfig;\r
+};\r
+\r
+//\r
+// Opaque data structure representing parsed PCI Capabilities Lists.\r
+//\r
+typedef struct PCI_CAP_LIST PCI_CAP_LIST;\r
+\r
+//\r
+// Opaque data structure representing a PCI Capability in a parsed Capability\r
+// List.\r
+//\r
+typedef struct PCI_CAP PCI_CAP;\r
+\r
+//\r
+// Distinguishes whether a Capability ID is 8-bit wide and interpreted in\r
+// normal config space, or 16-bit wide and interpreted in extended config\r
+// space. Capability ID definitions are relative to domain.\r
+//\r
+typedef enum {\r
+ PciCapNormal,\r
+ PciCapExtended\r
+} PCI_CAP_DOMAIN;\r
+\r
+//\r
+// Public data structure that PciCapGetInfo() fills in about a PCI_CAP object.\r
+//\r
+typedef struct {\r
+ PCI_CAP_DOMAIN Domain;\r
+ UINT16 CapId;\r
+ //\r
+ // The capability identified by Domain and CapId may have multiple instances\r
+ // in config space. NumInstances provides the total count of occurrences of\r
+ // the capability. It is always positive.\r
+ //\r
+ UINT16 NumInstances;\r
+ //\r
+ // Instance is the serial number, in capabilities list traversal order (not\r
+ // necessarily config space offset order), of the one capability instance\r
+ // that PciCapGetInfo() is reporting about. Instance is always smaller than\r
+ // NumInstances.\r
+ //\r
+ UINT16 Instance;\r
+ //\r
+ // The offset in config space at which the capability header of the\r
+ // capability instance starts.\r
+ //\r
+ UINT16 Offset;\r
+ //\r
+ // The deduced maximum size of the capability instance, including the\r
+ // capability header. This hint is an upper bound, calculated -- without\r
+ // regard to the internal structure of the capability -- from (a) the next\r
+ // lowest offset in configuration space that is known to be used by another\r
+ // capability, and (b) from the end of the config space identified by Domain,\r
+ // whichever is lower.\r
+ //\r
+ UINT16 MaxSizeHint;\r
+ //\r
+ // The version number of the capability instance. Always zero when Domain is\r
+ // PciCapNormal.\r
+ //\r
+ UINT8 Version;\r
+} PCI_CAP_INFO;\r
+\r
+\r
+/**\r
+ Parse the capabilities lists (both normal and extended, as applicable) of a\r
+ PCI device.\r
+\r
+ If the PCI device has no capabilities, that per se will not fail\r
+ PciCapListInit(); an empty capabilities list will be represented.\r
+\r
+ If the PCI device is found to be PCI Express, then an attempt will be made to\r
+ parse the extended capabilities list as well. If the first extended config\r
+ space access -- via PciDevice->ReadConfig() with SourceOffset=0x100 and\r
+ Size=4 -- fails, that per se will not fail PciCapListInit(); the device will\r
+ be assumed to have no extended capabilities.\r
+\r
+ @param[in] PciDevice Implementation-specific unique representation of the\r
+ PCI device in the PCI hierarchy.\r
+\r
+ @param[out] CapList Opaque data structure that holds an in-memory\r
+ representation of the parsed capabilities lists of\r
+ PciDevice.\r
+\r
+ @retval RETURN_SUCCESS The capabilities lists have been parsed from\r
+ config space.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @retval RETURN_DEVICE_ERROR A loop or some other kind of invalid pointer\r
+ was detected in the capabilities lists of\r
+ PciDevice.\r
+\r
+ @return Error codes propagated from\r
+ PciDevice->ReadConfig().\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapListInit (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ OUT PCI_CAP_LIST **CapList\r
+ );\r
+\r
+\r
+/**\r
+ Free the resources used by CapList.\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object to free, originally produced by\r
+ PciCapListInit().\r
+**/\r
+VOID\r
+EFIAPI\r
+PciCapListUninit (\r
+ IN PCI_CAP_LIST *CapList\r
+ );\r
+\r
+\r
+/**\r
+ Locate a capability instance in the parsed capabilities lists.\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
+\r
+ @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
+ interpreted in normal config space, or 16-bit wide and\r
+ interpreted in extended config space. Capability ID\r
+ definitions are relative to domain.\r
+\r
+ @param[in] CapId Capability identifier to look up.\r
+\r
+ @param[in] Instance Domain and CapId may identify a multi-instance\r
+ capability. When Instance is zero, the first instance of\r
+ the capability is located (in list traversal order --\r
+ which may not mean increasing config space offset\r
+ order). Higher Instance values locate subsequent\r
+ instances of the same capability (in list traversal\r
+ order).\r
+\r
+ @param[out] Cap The capability instance that matches the search\r
+ criteria. Cap is owned by CapList and becomes invalid\r
+ when CapList is freed with PciCapListUninit().\r
+ PciCapListFindCap() may be called with Cap set to NULL,\r
+ in order to test the existence of a specific capability\r
+ instance.\r
+\r
+ @retval RETURN_SUCCESS The capability instance identified by (Domain,\r
+ CapId, Instance) has been found.\r
+\r
+ @retval RETURN_NOT_FOUND The requested (Domain, CapId, Instance) capability\r
+ instance does not exist.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapListFindCap (\r
+ IN PCI_CAP_LIST *CapList,\r
+ IN PCI_CAP_DOMAIN Domain,\r
+ IN UINT16 CapId,\r
+ IN UINT16 Instance,\r
+ OUT PCI_CAP **Cap OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ Locate the first instance of the capability given by (Domain, CapId) such\r
+ that the instance's Version is greater than or equal to MinVersion.\r
+\r
+ This is a convenience function that may save client code calls to\r
+ PciCapListFindCap() and PciCapGetInfo().\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
+\r
+ @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
+ interpreted in normal config space, or 16-bit wide and\r
+ interpreted in extended config space. Capability ID\r
+ definitions are relative to domain.\r
+\r
+ @param[in] CapId Capability identifier to look up.\r
+\r
+ @param[in] MinVersion The minimum version that the capability instance is\r
+ required to have. Note that all capability instances\r
+ in Domain=PciCapNormal have Version=0.\r
+\r
+ @param[out] Cap The first capability instance that matches the search\r
+ criteria. Cap is owned by CapList and becomes invalid\r
+ when CapList is freed with PciCapListUninit().\r
+ PciCapListFindCapVersion() may be called with Cap set\r
+ to NULL, in order just to test whether the search\r
+ criteria are satisfiable.\r
+\r
+ @retval RETURN_SUCCESS The first capability instance matching (Domain,\r
+ CapId, MinVersion) has been located.\r
+\r
+ @retval RETURN_NOT_FOUND No capability instance matches (Domain, CapId,\r
+ MinVersion).\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapListFindCapVersion (\r
+ IN PCI_CAP_LIST *CapList,\r
+ IN PCI_CAP_DOMAIN Domain,\r
+ IN UINT16 CapId,\r
+ IN UINT8 MinVersion,\r
+ OUT PCI_CAP **Cap OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ Get information about a PCI Capability instance.\r
+\r
+ @param[in] Cap The capability instance to get info about, located with\r
+ PciCapListFindCap*().\r
+\r
+ @param[out] Info A PCI_CAP_INFO structure that describes the properties of\r
+ Cap.\r
+\r
+ @retval RETURN_SUCCESS Fields of Info have been set.\r
+\r
+ @return Unspecified error codes, if filling in Info failed\r
+ for some reason.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapGetInfo (\r
+ IN PCI_CAP *Cap,\r
+ OUT PCI_CAP_INFO *Info\r
+ );\r
+\r
+\r
+/**\r
+ Read a slice of a capability instance.\r
+\r
+ The function performs as few config space accesses as possible (without\r
+ attempting 64-bit wide accesses). PciCapRead() performs bounds checking on\r
+ SourceOffsetInCap and Size, and only invokes PciDevice->ReadConfig() if the\r
+ requested transfer falls within Cap.\r
+\r
+ @param[in] PciDevice Implementation-specific unique representation\r
+ of the PCI device in the PCI hierarchy.\r
+\r
+ @param[in] Cap The capability instance to read, located with\r
+ PciCapListFindCap*().\r
+\r
+ @param[in] SourceOffsetInCap Source offset relative to the capability\r
+ header to start reading from. A zero value\r
+ refers to the first byte of the capability\r
+ header.\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 Cap to\r
+ DestinationBuffer.\r
+\r
+ @retval RETURN_BAD_BUFFER_SIZE Reading Size bytes starting from\r
+ SourceOffsetInCap would not (entirely) be\r
+ contained within Cap, as suggested by\r
+ PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
+ read.\r
+\r
+ @return Error codes propagated from\r
+ PciDevice->ReadConfig(). Fewer than Size\r
+ bytes may have been read.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapRead (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN PCI_CAP *Cap,\r
+ IN UINT16 SourceOffsetInCap,\r
+ OUT VOID *DestinationBuffer,\r
+ IN UINT16 Size\r
+ );\r
+\r
+\r
+/**\r
+ Write a slice of a capability instance.\r
+\r
+ The function performs as few config space accesses as possible (without\r
+ attempting 64-bit wide accesses). PciCapWrite() performs bounds checking on\r
+ DestinationOffsetInCap and Size, and only invokes PciDevice->WriteConfig() if\r
+ the requested transfer falls within Cap.\r
+\r
+ @param[in] PciDevice Implementation-specific unique\r
+ representation of the PCI device in the\r
+ PCI hierarchy.\r
+\r
+ @param[in] Cap The capability instance to write, located\r
+ with PciCapListFindCap*().\r
+\r
+ @param[in] DestinationOffsetInCap Destination offset relative to the\r
+ capability header to start writing at. A\r
+ zero value refers to the first byte of the\r
+ capability header.\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 Cap.\r
+\r
+ @retval RETURN_BAD_BUFFER_SIZE Writing Size bytes starting at\r
+ DestinationOffsetInCap would not (entirely)\r
+ be contained within Cap, as suggested by\r
+ PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
+ written.\r
+\r
+ @return Error codes propagated from\r
+ PciDevice->WriteConfig(). Fewer than Size\r
+ bytes may have been written.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapWrite (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN PCI_CAP *Cap,\r
+ IN UINT16 DestinationOffsetInCap,\r
+ IN VOID *SourceBuffer,\r
+ IN UINT16 Size\r
+ );\r
+\r
+#endif // __PCI_CAP_LIB_H__\r
--- /dev/null
+/** @file\r
+ Work with PCI capabilities in PCI config space.\r
+\r
+ Provides functions to parse capabilities lists, and to locate, describe, read\r
+ and write capabilities. PCI config space access is abstracted away.\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/PciExpress21.h>\r
+\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+#include "BasePciCapLib.h"\r
+\r
+\r
+/**\r
+ Compare a standalone PCI_CAP_KEY against a PCI_CAP containing an embedded\r
+ PCI_CAP_KEY.\r
+\r
+ @param[in] PciCapKey Pointer to the bare PCI_CAP_KEY.\r
+\r
+ @param[in] PciCap Pointer to the PCI_CAP with the embedded PCI_CAP_KEY.\r
+\r
+ @retval <0 If PciCapKey compares less than PciCap->Key.\r
+\r
+ @retval 0 If PciCapKey compares equal to PciCap->Key.\r
+\r
+ @retval >0 If PciCapKey compares greater than PciCap->Key.\r
+**/\r
+STATIC\r
+INTN\r
+EFIAPI\r
+ComparePciCapKey (\r
+ IN CONST VOID *PciCapKey,\r
+ IN CONST VOID *PciCap\r
+ )\r
+{\r
+ CONST PCI_CAP_KEY *Key1;\r
+ CONST PCI_CAP_KEY *Key2;\r
+\r
+ Key1 = PciCapKey;\r
+ Key2 = &((CONST PCI_CAP *)PciCap)->Key;\r
+\r
+ if (Key1->Domain < Key2->Domain) {\r
+ return -1;\r
+ }\r
+ if (Key1->Domain > Key2->Domain) {\r
+ return 1;\r
+ }\r
+ if (Key1->CapId < Key2->CapId) {\r
+ return -1;\r
+ }\r
+ if (Key1->CapId > Key2->CapId) {\r
+ return 1;\r
+ }\r
+ if (Key1->Instance < Key2->Instance) {\r
+ return -1;\r
+ }\r
+ if (Key1->Instance > Key2->Instance) {\r
+ return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+/**\r
+ Compare two PCI_CAP objects based on PCI_CAP.Key.\r
+\r
+ @param[in] PciCap1 Pointer to the first PCI_CAP.\r
+\r
+ @param[in] PciCap2 Pointer to the second PCI_CAP.\r
+\r
+ @retval <0 If PciCap1 compares less than PciCap2.\r
+\r
+ @retval 0 If PciCap1 compares equal to PciCap2.\r
+\r
+ @retval >0 If PciCap1 compares greater than PciCap2.\r
+**/\r
+STATIC\r
+INTN\r
+EFIAPI\r
+ComparePciCap (\r
+ IN CONST VOID *PciCap1,\r
+ IN CONST VOID *PciCap2\r
+ )\r
+{\r
+ CONST PCI_CAP_KEY *PciCap1Key;\r
+\r
+ PciCap1Key = &((CONST PCI_CAP *)PciCap1)->Key;\r
+ return ComparePciCapKey (PciCap1Key, PciCap2);\r
+}\r
+\r
+\r
+/**\r
+ Compare the standalone UINT16 config space offset of a capability header\r
+ against a PCI_CAP containing an embedded Offset.\r
+\r
+ @param[in] CapHdrOffset Pointer to the bare UINT16 config space offset.\r
+\r
+ @param[in] PciCap Pointer to the PCI_CAP with the embedded Offset.\r
+\r
+ @retval <0 If CapHdrOffset compares less than PciCap->Offset.\r
+\r
+ @retval 0 If CapHdrOffset compares equal to PciCap->Offset.\r
+\r
+ @retval >0 If CapHdrOffset compares greater than PciCap->Offset.\r
+**/\r
+STATIC\r
+INTN\r
+EFIAPI\r
+ComparePciCapOffsetKey (\r
+ IN CONST VOID *CapHdrOffset,\r
+ IN CONST VOID *PciCap\r
+ )\r
+{\r
+ UINT16 Offset1;\r
+ UINT16 Offset2;\r
+\r
+ Offset1 = *(CONST UINT16 *)CapHdrOffset;\r
+ Offset2 = ((CONST PCI_CAP *)PciCap)->Offset;\r
+ //\r
+ // Note: both Offset1 and Offset2 are promoted to INT32 below, and the\r
+ // subtraction takes place between INT32 values.\r
+ //\r
+ return Offset1 - Offset2;\r
+}\r
+\r
+\r
+/**\r
+ Compare two PCI_CAP objects based on PCI_CAP.Offset.\r
+\r
+ @param[in] PciCap1 Pointer to the first PCI_CAP.\r
+\r
+ @param[in] PciCap2 Pointer to the second PCI_CAP.\r
+\r
+ @retval <0 If PciCap1 compares less than PciCap2.\r
+\r
+ @retval 0 If PciCap1 compares equal to PciCap2.\r
+\r
+ @retval >0 If PciCap1 compares greater than PciCap2.\r
+**/\r
+STATIC\r
+INTN\r
+EFIAPI\r
+ComparePciCapOffset (\r
+ IN CONST VOID *PciCap1,\r
+ IN CONST VOID *PciCap2\r
+ )\r
+{\r
+ UINT16 Offset1;\r
+ UINT16 Offset2;\r
+\r
+ Offset1 = ((CONST PCI_CAP *)PciCap1)->Offset;\r
+ Offset2 = ((CONST PCI_CAP *)PciCap2)->Offset;\r
+ //\r
+ // Note: both Offset1 and Offset2 are promoted to INT32 below, and the\r
+ // subtraction takes place between INT32 values.\r
+ //\r
+ return Offset1 - Offset2;\r
+}\r
+\r
+\r
+/**\r
+ Insert a new instance of the PCI capability given by (Domain, CapId) in\r
+ CapList.\r
+\r
+ @param[in,out] CapList The PCI_CAP_LIST into which the new PCI_CAP\r
+ should be inserted. CapList will own the new\r
+ PCI_CAP structure.\r
+\r
+ @param[in,out] CapHdrOffsets Link the new PCI_CAP structure into the\r
+ (non-owning) CapHdrOffsets collection as well.\r
+ CapHdrOffsets orders the PCI_CAP structures\r
+ based on the PCI_CAP.Offset member, and enables\r
+ the calculation of PCI_CAP.MaxSizeHint.\r
+\r
+ @param[in] Domain Whether the capability is normal or extended.\r
+\r
+ @param[in] CapId Capability ID (specific to Domain).\r
+\r
+ @param[in] Offset Config space offset at which the standard\r
+ header of the capability starts. The caller is\r
+ responsible for ensuring that Offset be DWORD\r
+ aligned. The caller is also responsible for\r
+ ensuring that Offset be within the config space\r
+ identified by Domain.\r
+\r
+ @param[in] Version The version number of the capability. The\r
+ caller is responsible for passing 0 as Version\r
+ if Domain is PciCapNormal.\r
+\r
+ @retval RETURN_SUCCESS Insertion successful.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @retval RETURN_DEVICE_ERROR A PCI_CAP with Offset is already linked by\r
+ CapHdrOffsets. This indicates a loop in the\r
+ capabilities list being parsed.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+InsertPciCap (\r
+ IN OUT PCI_CAP_LIST *CapList,\r
+ IN OUT ORDERED_COLLECTION *CapHdrOffsets,\r
+ IN PCI_CAP_DOMAIN Domain,\r
+ IN UINT16 CapId,\r
+ IN UINT16 Offset,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ PCI_CAP *PciCap;\r
+ RETURN_STATUS Status;\r
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
+ PCI_CAP *InstanceZero;\r
+\r
+ ASSERT ((Offset & 0x3) == 0);\r
+ ASSERT (Offset < (Domain == PciCapNormal ?\r
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET));\r
+ ASSERT (Domain == PciCapExtended || Version == 0);\r
+\r
+ //\r
+ // Set InstanceZero to suppress incorrect compiler/analyzer warnings.\r
+ //\r
+ InstanceZero = NULL;\r
+\r
+ //\r
+ // Allocate PciCap, and populate it assuming it is the first occurrence of\r
+ // (Domain, CapId). Note that PciCap->MaxSizeHint is not assigned the final\r
+ // value just yet.\r
+ //\r
+ PciCap = AllocatePool (sizeof *PciCap);\r
+ if (PciCap == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+ PciCap->Key.Domain = Domain;\r
+ PciCap->Key.CapId = CapId;\r
+ PciCap->Key.Instance = 0;\r
+ PciCap->NumInstancesUnion.NumInstances = 1;\r
+ PciCap->Offset = Offset;\r
+ PciCap->MaxSizeHint = 0;\r
+ PciCap->Version = Version;\r
+\r
+ //\r
+ // Add PciCap to CapList.\r
+ //\r
+ Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry,\r
+ PciCap);\r
+ if (RETURN_ERROR (Status)) {\r
+ if (Status == RETURN_OUT_OF_RESOURCES) {\r
+ goto FreePciCap;\r
+ }\r
+ ASSERT (Status == RETURN_ALREADY_STARTED);\r
+ //\r
+ // PciCap is not the first instance of (Domain, CapId). Add it as a new\r
+ // instance, taking the current instance count from Instance#0. Note that\r
+ // we don't bump the instance count maintained in Instance#0 just yet, to\r
+ // keep rollback on errors simple.\r
+ //\r
+ InstanceZero = OrderedCollectionUserStruct (PciCapEntry);\r
+ PciCap->Key.Instance = InstanceZero->NumInstancesUnion.NumInstances;\r
+ PciCap->NumInstancesUnion.InstanceZero = InstanceZero;\r
+\r
+ ASSERT (PciCap->Key.Instance > 0);\r
+ Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry,\r
+ PciCap);\r
+ if (Status == RETURN_OUT_OF_RESOURCES) {\r
+ goto FreePciCap;\r
+ }\r
+ }\r
+ //\r
+ // At this point, PciCap has been inserted in CapList->Capabilities, either\r
+ // with Instance==0 or with Instance>0. PciCapEntry is the iterator that\r
+ // links PciCap.\r
+ //\r
+ ASSERT_RETURN_ERROR (Status);\r
+\r
+ //\r
+ // Link PciCap into CapHdrOffsets too, to order it globally based on config\r
+ // space offset. Note that partial overlaps between capability headers is not\r
+ // possible: Offset is DWORD aligned, normal capability headers are 16-bit\r
+ // wide, and extended capability headers are 32-bit wide. Therefore any two\r
+ // capability headers either are distinct or start at the same offset\r
+ // (implying a loop in the respective capabilities list).\r
+ //\r
+ Status = OrderedCollectionInsert (CapHdrOffsets, NULL, PciCap);\r
+ if (RETURN_ERROR (Status)) {\r
+ if (Status == RETURN_ALREADY_STARTED) {\r
+ //\r
+ // Loop found; map return status accordingly.\r
+ //\r
+ Status = RETURN_DEVICE_ERROR;\r
+ }\r
+ goto DeletePciCapFromCapList;\r
+ }\r
+\r
+ //\r
+ // Now we can bump the instance count maintained in Instance#0, if PciCap is\r
+ // not the first instance of (Domain, CapId).\r
+ //\r
+ if (PciCap->Key.Instance > 0) {\r
+ InstanceZero->NumInstancesUnion.NumInstances++;\r
+ }\r
+ return RETURN_SUCCESS;\r
+\r
+DeletePciCapFromCapList:\r
+ OrderedCollectionDelete (CapList->Capabilities, PciCapEntry, NULL);\r
+\r
+FreePciCap:\r
+ FreePool (PciCap);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Calculate the MaxSizeHint member for a PCI_CAP object.\r
+\r
+ CalculatePciCapMaxSizeHint() may only be called once all capability instances\r
+ have been successfully processed by InsertPciCap().\r
+\r
+ @param[in,out] PciCap The PCI_CAP object for which to calculate the\r
+ MaxSizeHint member. The caller is responsible for\r
+ passing a PCI_CAP object that has been created by a\r
+ successful invocation of InsertPciCap().\r
+\r
+ @param[in] NextPciCap If NextPciCap is NULL, then the caller is responsible\r
+ for PciCap to represent the capability instance with\r
+ the highest header offset in all config space. If\r
+ NextPciCap is not NULL, then the caller is responsible\r
+ for (a) having created NextPciCap with a successful\r
+ invocation of InsertPciCap(), and (b) NextPciCap being\r
+ the direct successor of PciCap in config space offset\r
+ order, as ordered by ComparePciCapOffset().\r
+**/\r
+STATIC\r
+VOID\r
+CalculatePciCapMaxSizeHint (\r
+ IN OUT PCI_CAP *PciCap,\r
+ IN PCI_CAP *NextPciCap OPTIONAL\r
+ )\r
+{\r
+ UINT16 ConfigSpaceSize;\r
+\r
+ ConfigSpaceSize = (PciCap->Key.Domain == PciCapNormal ?\r
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
+ //\r
+ // The following is guaranteed by the interface contract on\r
+ // CalculatePciCapMaxSizeHint().\r
+ //\r
+ ASSERT (NextPciCap == NULL || PciCap->Offset < NextPciCap->Offset);\r
+ //\r
+ // The following is guaranteed by the interface contract on InsertPciCap().\r
+ //\r
+ ASSERT (PciCap->Offset < ConfigSpaceSize);\r
+ //\r
+ // Thus we can safely subtract PciCap->Offset from either of\r
+ // - ConfigSpaceSize\r
+ // - and NextPciCap->Offset (if NextPciCap is not NULL).\r
+ //\r
+ // PciCap extends from PciCap->Offset to NextPciCap->Offset (if any), except\r
+ // it cannot cross config space boundary.\r
+ //\r
+ if (NextPciCap == NULL || NextPciCap->Offset >= ConfigSpaceSize) {\r
+ PciCap->MaxSizeHint = ConfigSpaceSize - PciCap->Offset;\r
+ return;\r
+ }\r
+ PciCap->MaxSizeHint = NextPciCap->Offset - PciCap->Offset;\r
+}\r
+\r
+\r
+/**\r
+ Debug dump a PCI_CAP_LIST object at the DEBUG_VERBOSE level.\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object to dump.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+DebugDumpPciCapList (\r
+ IN PCI_CAP_LIST *CapList\r
+ )\r
+{\r
+ DEBUG_CODE_BEGIN ();\r
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
+\r
+ for (PciCapEntry = OrderedCollectionMin (CapList->Capabilities);\r
+ PciCapEntry != NULL;\r
+ PciCapEntry = OrderedCollectionNext (PciCapEntry)) {\r
+ PCI_CAP *PciCap;\r
+ RETURN_STATUS Status;\r
+ PCI_CAP_INFO Info;\r
+\r
+ PciCap = OrderedCollectionUserStruct (PciCapEntry);\r
+ Status = PciCapGetInfo (PciCap, &Info);\r
+ //\r
+ // PciCapGetInfo() cannot fail in this library instance.\r
+ //\r
+ ASSERT_RETURN_ERROR (Status);\r
+\r
+ DEBUG ((DEBUG_VERBOSE,\r
+ "%a:%a: %a 0x%04x %03u/%03u v0x%x @0x%03x+0x%03x\n", gEfiCallerBaseName,\r
+ __FUNCTION__, (Info.Domain == PciCapNormal ? "Norm" : "Extd"),\r
+ Info.CapId, Info.Instance, Info.NumInstances, Info.Version, Info.Offset,\r
+ Info.MaxSizeHint));\r
+ }\r
+ DEBUG_CODE_END ();\r
+}\r
+\r
+\r
+/**\r
+ Empty a collection of PCI_CAP structures, optionally releasing the referenced\r
+ PCI_CAP structures themselves. Release the collection at last.\r
+\r
+ @param[in,out] PciCapCollection The collection to empty and release.\r
+\r
+ @param[in] FreePciCap TRUE if the PCI_CAP structures linked by\r
+ PciCapCollection should be released. When\r
+ FALSE, the caller is responsible for\r
+ retaining at least one reference to each\r
+ PCI_CAP structure originally linked by\r
+ PciCapCollection.\r
+**/\r
+STATIC\r
+VOID\r
+EmptyAndUninitPciCapCollection (\r
+ IN OUT ORDERED_COLLECTION *PciCapCollection,\r
+ IN BOOLEAN FreePciCap\r
+ )\r
+{\r
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
+ ORDERED_COLLECTION_ENTRY *NextEntry;\r
+\r
+ for (PciCapEntry = OrderedCollectionMin (PciCapCollection);\r
+ PciCapEntry != NULL;\r
+ PciCapEntry = NextEntry) {\r
+ PCI_CAP *PciCap;\r
+\r
+ NextEntry = OrderedCollectionNext (PciCapEntry);\r
+ OrderedCollectionDelete (PciCapCollection, PciCapEntry, (VOID **)&PciCap);\r
+ if (FreePciCap) {\r
+ FreePool (PciCap);\r
+ }\r
+ }\r
+ OrderedCollectionUninit (PciCapCollection);\r
+}\r
+\r
+\r
+/**\r
+ Parse the capabilities lists (both normal and extended, as applicable) of a\r
+ PCI device.\r
+\r
+ If the PCI device has no capabilities, that per se will not fail\r
+ PciCapListInit(); an empty capabilities list will be represented.\r
+\r
+ If the PCI device is found to be PCI Express, then an attempt will be made to\r
+ parse the extended capabilities list as well. If the first extended config\r
+ space access -- via PciDevice->ReadConfig() with SourceOffset=0x100 and\r
+ Size=4 -- fails, that per se will not fail PciCapListInit(); the device will\r
+ be assumed to have no extended capabilities.\r
+\r
+ @param[in] PciDevice Implementation-specific unique representation of the\r
+ PCI device in the PCI hierarchy.\r
+\r
+ @param[out] CapList Opaque data structure that holds an in-memory\r
+ representation of the parsed capabilities lists of\r
+ PciDevice.\r
+\r
+ @retval RETURN_SUCCESS The capabilities lists have been parsed from\r
+ config space.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @retval RETURN_DEVICE_ERROR A loop or some other kind of invalid pointer\r
+ was detected in the capabilities lists of\r
+ PciDevice.\r
+\r
+ @return Error codes propagated from\r
+ PciDevice->ReadConfig().\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapListInit (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ OUT PCI_CAP_LIST **CapList\r
+ )\r
+{\r
+ PCI_CAP_LIST *OutCapList;\r
+ RETURN_STATUS Status;\r
+ ORDERED_COLLECTION *CapHdrOffsets;\r
+ UINT16 PciStatusReg;\r
+ BOOLEAN DeviceIsExpress;\r
+ ORDERED_COLLECTION_ENTRY *OffsetEntry;\r
+\r
+ //\r
+ // Allocate the output structure.\r
+ //\r
+ OutCapList = AllocatePool (sizeof *OutCapList);\r
+ if (OutCapList == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // The OutCapList->Capabilities collection owns the PCI_CAP structures and\r
+ // orders them based on PCI_CAP.Key.\r
+ //\r
+ OutCapList->Capabilities = OrderedCollectionInit (ComparePciCap,\r
+ ComparePciCapKey);\r
+ if (OutCapList->Capabilities == NULL) {\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto FreeOutCapList;\r
+ }\r
+\r
+ //\r
+ // The (temporary) CapHdrOffsets collection only references PCI_CAP\r
+ // structures, and orders them based on PCI_CAP.Offset.\r
+ //\r
+ CapHdrOffsets = OrderedCollectionInit (ComparePciCapOffset,\r
+ ComparePciCapOffsetKey);\r
+ if (CapHdrOffsets == NULL) {\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto FreeCapabilities;\r
+ }\r
+\r
+ //\r
+ // Whether the device is PCI Express depends on the normal capability with\r
+ // identifier EFI_PCI_CAPABILITY_ID_PCIEXP.\r
+ //\r
+ DeviceIsExpress = FALSE;\r
+\r
+ //\r
+ // Check whether a normal capabilities list is present. If there's none,\r
+ // that's not an error; we'll just return OutCapList->Capabilities empty.\r
+ //\r
+ Status = PciDevice->ReadConfig (PciDevice, PCI_PRIMARY_STATUS_OFFSET,\r
+ &PciStatusReg, sizeof PciStatusReg);\r
+ if (RETURN_ERROR (Status)) {\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+ if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
+ UINT8 NormalCapHdrOffset;\r
+\r
+ //\r
+ // Fetch the start offset of the normal capabilities list.\r
+ //\r
+ Status = PciDevice->ReadConfig (PciDevice, PCI_CAPBILITY_POINTER_OFFSET,\r
+ &NormalCapHdrOffset, sizeof NormalCapHdrOffset);\r
+ if (RETURN_ERROR (Status)) {\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+\r
+ //\r
+ // Traverse the normal capabilities list.\r
+ //\r
+ NormalCapHdrOffset &= 0xFC;\r
+ while (NormalCapHdrOffset > 0) {\r
+ EFI_PCI_CAPABILITY_HDR NormalCapHdr;\r
+\r
+ Status = PciDevice->ReadConfig (PciDevice, NormalCapHdrOffset,\r
+ &NormalCapHdr, sizeof NormalCapHdr);\r
+ if (RETURN_ERROR (Status)) {\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+\r
+ Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapNormal,\r
+ NormalCapHdr.CapabilityID, NormalCapHdrOffset, 0);\r
+ if (RETURN_ERROR (Status)) {\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+\r
+ if (NormalCapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP) {\r
+ DeviceIsExpress = TRUE;\r
+ }\r
+ NormalCapHdrOffset = NormalCapHdr.NextItemPtr & 0xFC;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If the device has been found PCI Express, attempt to traverse the extended\r
+ // capabilities list. It starts right after the normal config space.\r
+ //\r
+ if (DeviceIsExpress) {\r
+ UINT16 ExtendedCapHdrOffset;\r
+\r
+ ExtendedCapHdrOffset = PCI_MAX_CONFIG_OFFSET;\r
+ while (ExtendedCapHdrOffset > 0) {\r
+ PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER ExtendedCapHdr;\r
+\r
+ Status = PciDevice->ReadConfig (PciDevice, ExtendedCapHdrOffset,\r
+ &ExtendedCapHdr, sizeof ExtendedCapHdr);\r
+ //\r
+ // If the first extended config space access fails, assume the device has\r
+ // no extended capabilities. If the first extended config space access\r
+ // succeeds but we read an "all bits zero" extended capability header,\r
+ // that means (by spec) the device has no extended capabilities.\r
+ //\r
+ if (ExtendedCapHdrOffset == PCI_MAX_CONFIG_OFFSET &&\r
+ (RETURN_ERROR (Status) ||\r
+ IsZeroBuffer (&ExtendedCapHdr, sizeof ExtendedCapHdr))) {\r
+ break;\r
+ }\r
+ if (RETURN_ERROR (Status)) {\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+\r
+ Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapExtended,\r
+ ExtendedCapHdr.CapabilityId, ExtendedCapHdrOffset,\r
+ ExtendedCapHdr.CapabilityVersion);\r
+ if (RETURN_ERROR (Status)) {\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+\r
+ ExtendedCapHdrOffset = ExtendedCapHdr.NextCapabilityOffset & 0xFFC;\r
+ if (ExtendedCapHdrOffset > 0 &&\r
+ ExtendedCapHdrOffset < PCI_MAX_CONFIG_OFFSET) {\r
+ //\r
+ // Invalid capability pointer.\r
+ //\r
+ Status = RETURN_DEVICE_ERROR;\r
+ goto FreeCapHdrOffsets;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Both capabilities lists have been parsed; compute the PCI_CAP.MaxSizeHint\r
+ // members if at least one capability has been found. In parallel, evacuate\r
+ // the CapHdrOffsets collection.\r
+ //\r
+ // At first, set OffsetEntry to the iterator of the PCI_CAP object with the\r
+ // lowest Offset (if such exists).\r
+ //\r
+ OffsetEntry = OrderedCollectionMin (CapHdrOffsets);\r
+ if (OffsetEntry != NULL) {\r
+ ORDERED_COLLECTION_ENTRY *NextOffsetEntry;\r
+ PCI_CAP *PciCap;\r
+\r
+ //\r
+ // Initialize NextOffsetEntry to the iterator of the PCI_CAP object with\r
+ // the second lowest Offset (if such exists).\r
+ //\r
+ NextOffsetEntry = OrderedCollectionNext (OffsetEntry);\r
+ //\r
+ // Calculate MaxSizeHint for all PCI_CAP objects except the one with the\r
+ // highest Offset.\r
+ //\r
+ while (NextOffsetEntry != NULL) {\r
+ PCI_CAP *NextPciCap;\r
+\r
+ OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);\r
+ NextPciCap = OrderedCollectionUserStruct (NextOffsetEntry);\r
+ CalculatePciCapMaxSizeHint (PciCap, NextPciCap);\r
+\r
+ OffsetEntry = NextOffsetEntry;\r
+ NextOffsetEntry = OrderedCollectionNext (OffsetEntry);\r
+ }\r
+ //\r
+ // Calculate MaxSizeHint for the PCI_CAP object with the highest Offset.\r
+ //\r
+ OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);\r
+ CalculatePciCapMaxSizeHint (PciCap, NULL);\r
+ }\r
+ ASSERT (OrderedCollectionIsEmpty (CapHdrOffsets));\r
+ OrderedCollectionUninit (CapHdrOffsets);\r
+\r
+ DebugDumpPciCapList (OutCapList);\r
+ *CapList = OutCapList;\r
+ return RETURN_SUCCESS;\r
+\r
+FreeCapHdrOffsets:\r
+ EmptyAndUninitPciCapCollection (CapHdrOffsets, FALSE);\r
+\r
+FreeCapabilities:\r
+ EmptyAndUninitPciCapCollection (OutCapList->Capabilities, TRUE);\r
+\r
+FreeOutCapList:\r
+ FreePool (OutCapList);\r
+\r
+ ASSERT (RETURN_ERROR (Status));\r
+ DEBUG ((DEBUG_ERROR, "%a:%a: %r\n", gEfiCallerBaseName, __FUNCTION__,\r
+ Status));\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Free the resources used by CapList.\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object to free, originally produced by\r
+ PciCapListInit().\r
+**/\r
+VOID\r
+EFIAPI\r
+PciCapListUninit (\r
+ IN PCI_CAP_LIST *CapList\r
+ )\r
+{\r
+ EmptyAndUninitPciCapCollection (CapList->Capabilities, TRUE);\r
+ FreePool (CapList);\r
+}\r
+\r
+\r
+/**\r
+ Locate a capability instance in the parsed capabilities lists.\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
+\r
+ @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
+ interpreted in normal config space, or 16-bit wide and\r
+ interpreted in extended config space. Capability ID\r
+ definitions are relative to domain.\r
+\r
+ @param[in] CapId Capability identifier to look up.\r
+\r
+ @param[in] Instance Domain and CapId may identify a multi-instance\r
+ capability. When Instance is zero, the first instance of\r
+ the capability is located (in list traversal order --\r
+ which may not mean increasing config space offset\r
+ order). Higher Instance values locate subsequent\r
+ instances of the same capability (in list traversal\r
+ order).\r
+\r
+ @param[out] Cap The capability instance that matches the search\r
+ criteria. Cap is owned by CapList and becomes invalid\r
+ when CapList is freed with PciCapListUninit().\r
+ PciCapListFindCap() may be called with Cap set to NULL,\r
+ in order to test the existence of a specific capability\r
+ instance.\r
+\r
+ @retval RETURN_SUCCESS The capability instance identified by (Domain,\r
+ CapId, Instance) has been found.\r
+\r
+ @retval RETURN_NOT_FOUND The requested (Domain, CapId, Instance) capability\r
+ instance does not exist.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapListFindCap (\r
+ IN PCI_CAP_LIST *CapList,\r
+ IN PCI_CAP_DOMAIN Domain,\r
+ IN UINT16 CapId,\r
+ IN UINT16 Instance,\r
+ OUT PCI_CAP **Cap OPTIONAL\r
+ )\r
+{\r
+ PCI_CAP_KEY Key;\r
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
+\r
+ Key.Domain = Domain;\r
+ Key.CapId = CapId;\r
+ Key.Instance = Instance;\r
+\r
+ PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);\r
+ if (PciCapEntry == NULL) {\r
+ return RETURN_NOT_FOUND;\r
+ }\r
+ if (Cap != NULL) {\r
+ *Cap = OrderedCollectionUserStruct (PciCapEntry);\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Locate the first instance of the capability given by (Domain, CapId) such\r
+ that the instance's Version is greater than or equal to MinVersion.\r
+\r
+ This is a convenience function that may save client code calls to\r
+ PciCapListFindCap() and PciCapGetInfo().\r
+\r
+ @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
+\r
+ @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
+ interpreted in normal config space, or 16-bit wide and\r
+ interpreted in extended config space. Capability ID\r
+ definitions are relative to domain.\r
+\r
+ @param[in] CapId Capability identifier to look up.\r
+\r
+ @param[in] MinVersion The minimum version that the capability instance is\r
+ required to have. Note that all capability instances\r
+ in Domain=PciCapNormal have Version=0.\r
+\r
+ @param[out] Cap The first capability instance that matches the search\r
+ criteria. Cap is owned by CapList and becomes invalid\r
+ when CapList is freed with PciCapListUninit().\r
+ PciCapListFindCapVersion() may be called with Cap set\r
+ to NULL, in order just to test whether the search\r
+ criteria are satisfiable.\r
+\r
+ @retval RETURN_SUCCESS The first capability instance matching (Domain,\r
+ CapId, MinVersion) has been located.\r
+\r
+ @retval RETURN_NOT_FOUND No capability instance matches (Domain, CapId,\r
+ MinVersion).\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapListFindCapVersion (\r
+ IN PCI_CAP_LIST *CapList,\r
+ IN PCI_CAP_DOMAIN Domain,\r
+ IN UINT16 CapId,\r
+ IN UINT8 MinVersion,\r
+ OUT PCI_CAP **Cap OPTIONAL\r
+ )\r
+{\r
+ PCI_CAP_KEY Key;\r
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
+\r
+ //\r
+ // Start the version checks at Instance#0 of (Domain, CapId).\r
+ //\r
+ Key.Domain = Domain;\r
+ Key.CapId = CapId;\r
+ Key.Instance = 0;\r
+\r
+ for (PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);\r
+ PciCapEntry != NULL;\r
+ PciCapEntry = OrderedCollectionNext (PciCapEntry)) {\r
+ PCI_CAP *PciCap;\r
+\r
+ PciCap = OrderedCollectionUserStruct (PciCapEntry);\r
+ //\r
+ // PCI_CAP.Key ordering keeps instances of the same (Domain, CapId)\r
+ // adjacent to each other, so stop searching if either Domain or CapId\r
+ // changes.\r
+ //\r
+ if (PciCap->Key.Domain != Domain || PciCap->Key.CapId != CapId) {\r
+ break;\r
+ }\r
+ if (PciCap->Version >= MinVersion) {\r
+ //\r
+ // Match found.\r
+ //\r
+ if (Cap != NULL) {\r
+ *Cap = PciCap;\r
+ }\r
+ return RETURN_SUCCESS;\r
+ }\r
+ }\r
+ return RETURN_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+ Get information about a PCI Capability instance.\r
+\r
+ @param[in] Cap The capability instance to get info about, located with\r
+ PciCapListFindCap*().\r
+\r
+ @param[out] Info A PCI_CAP_INFO structure that describes the properties of\r
+ Cap.\r
+\r
+ @retval RETURN_SUCCESS Fields of Info have been set.\r
+\r
+ @return Unspecified error codes, if filling in Info failed\r
+ for some reason.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapGetInfo (\r
+ IN PCI_CAP *Cap,\r
+ OUT PCI_CAP_INFO *Info\r
+ )\r
+{\r
+ PCI_CAP *InstanceZero;\r
+\r
+ ASSERT (Info != NULL);\r
+\r
+ InstanceZero = (Cap->Key.Instance == 0 ? Cap :\r
+ Cap->NumInstancesUnion.InstanceZero);\r
+\r
+ Info->Domain = Cap->Key.Domain;\r
+ Info->CapId = Cap->Key.CapId;\r
+ Info->NumInstances = InstanceZero->NumInstancesUnion.NumInstances;\r
+ Info->Instance = Cap->Key.Instance;\r
+ Info->Offset = Cap->Offset;\r
+ Info->MaxSizeHint = Cap->MaxSizeHint;\r
+ Info->Version = Cap->Version;\r
+\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Read a slice of a capability instance.\r
+\r
+ The function performs as few config space accesses as possible (without\r
+ attempting 64-bit wide accesses). PciCapRead() performs bounds checking on\r
+ SourceOffsetInCap and Size, and only invokes PciDevice->ReadConfig() if the\r
+ requested transfer falls within Cap.\r
+\r
+ @param[in] PciDevice Implementation-specific unique representation\r
+ of the PCI device in the PCI hierarchy.\r
+\r
+ @param[in] Cap The capability instance to read, located with\r
+ PciCapListFindCap*().\r
+\r
+ @param[in] SourceOffsetInCap Source offset relative to the capability\r
+ header to start reading from. A zero value\r
+ refers to the first byte of the capability\r
+ header.\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 Cap to\r
+ DestinationBuffer.\r
+\r
+ @retval RETURN_BAD_BUFFER_SIZE Reading Size bytes starting from\r
+ SourceOffsetInCap would not (entirely) be\r
+ contained within Cap, as suggested by\r
+ PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
+ read.\r
+\r
+ @return Error codes propagated from\r
+ PciDevice->ReadConfig(). Fewer than Size\r
+ bytes may have been read.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapRead (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN PCI_CAP *Cap,\r
+ IN UINT16 SourceOffsetInCap,\r
+ OUT VOID *DestinationBuffer,\r
+ IN UINT16 Size\r
+ )\r
+{\r
+ //\r
+ // Note: all UINT16 values are promoted to INT32 below, and addition and\r
+ // comparison take place between INT32 values.\r
+ //\r
+ if (SourceOffsetInCap + Size > Cap->MaxSizeHint) {\r
+ return RETURN_BAD_BUFFER_SIZE;\r
+ }\r
+ return PciDevice->ReadConfig (PciDevice, Cap->Offset + SourceOffsetInCap,\r
+ DestinationBuffer, Size);\r
+}\r
+\r
+\r
+/**\r
+ Write a slice of a capability instance.\r
+\r
+ The function performs as few config space accesses as possible (without\r
+ attempting 64-bit wide accesses). PciCapWrite() performs bounds checking on\r
+ DestinationOffsetInCap and Size, and only invokes PciDevice->WriteConfig() if\r
+ the requested transfer falls within Cap.\r
+\r
+ @param[in] PciDevice Implementation-specific unique\r
+ representation of the PCI device in the\r
+ PCI hierarchy.\r
+\r
+ @param[in] Cap The capability instance to write, located\r
+ with PciCapListFindCap*().\r
+\r
+ @param[in] DestinationOffsetInCap Destination offset relative to the\r
+ capability header to start writing at. A\r
+ zero value refers to the first byte of the\r
+ capability header.\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 Cap.\r
+\r
+ @retval RETURN_BAD_BUFFER_SIZE Writing Size bytes starting at\r
+ DestinationOffsetInCap would not (entirely)\r
+ be contained within Cap, as suggested by\r
+ PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
+ written.\r
+\r
+ @return Error codes propagated from\r
+ PciDevice->WriteConfig(). Fewer than Size\r
+ bytes may have been written.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapWrite (\r
+ IN PCI_CAP_DEV *PciDevice,\r
+ IN PCI_CAP *Cap,\r
+ IN UINT16 DestinationOffsetInCap,\r
+ IN VOID *SourceBuffer,\r
+ IN UINT16 Size\r
+ )\r
+{\r
+ //\r
+ // Note: all UINT16 values are promoted to INT32 below, and addition and\r
+ // comparison take place between INT32 values.\r
+ //\r
+ if (DestinationOffsetInCap + Size > Cap->MaxSizeHint) {\r
+ return RETURN_BAD_BUFFER_SIZE;\r
+ }\r
+ return PciDevice->WriteConfig (PciDevice,\r
+ Cap->Offset + DestinationOffsetInCap, SourceBuffer,\r
+ Size);\r
+}\r