public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: edk2-devel-01 <edk2-devel@lists.01.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>,
	Jordan Justen <jordan.l.justen@intel.com>,
	Liming Gao <liming.gao@intel.com>,
	Michael D Kinney <michael.d.kinney@intel.com>
Subject: [PATCH 1/7] MdePkg: introduce PciCapLib
Date: Fri,  4 May 2018 23:36:31 +0200	[thread overview]
Message-ID: <20180504213637.11266-2-lersek@redhat.com> (raw)
In-Reply-To: <20180504213637.11266-1-lersek@redhat.com>

Add a library class, and a BASE lib instance, to work more easily with PCI
capabilities in PCI config space. Functions are provided to parse
capabilities lists, and to locate, describe, read and write capabilities.
PCI config space access is abstracted away.

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Suggested-by: Jordan Justen <jordan.l.justen@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 MdePkg/MdePkg.dec                              |    4 +
 MdePkg/MdePkg.dsc                              |    1 +
 MdePkg/Library/BasePciCapLib/BasePciCapLib.inf |   37 +
 MdePkg/Include/Library/PciCapLib.h             |  429 +++++++++
 MdePkg/Library/BasePciCapLib/BasePciCapLib.h   |   60 ++
 MdePkg/Library/BasePciCapLib/BasePciCapLib.c   | 1007 ++++++++++++++++++++
 6 files changed, 1538 insertions(+)

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




  reply	other threads:[~2018-05-04 21:36 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-04 21:36 [PATCH 0/7] MdePkg, OvmfPkg, ArmVirtPkg: add and use PCI(E) Capabilities Library Laszlo Ersek
2018-05-04 21:36 ` Laszlo Ersek [this message]
2018-05-04 21:36 ` [PATCH 2/7] MdePkg: introduce PciCapPciSegmentLib Laszlo Ersek
2018-05-04 21:36 ` [PATCH 3/7] MdePkg: introduce PciCapPciIoLib Laszlo Ersek
2018-05-04 21:36 ` [PATCH 4/7] OvmfPkg: resolve PciCapLib, PciCapPciSegmentLib, PciCapPciIoLib Laszlo Ersek
2018-05-04 21:36 ` [PATCH 5/7] ArmVirtPkg: " Laszlo Ersek
2018-05-04 21:36 ` [PATCH 6/7] OvmfPkg/PciHotPlugInitDxe: convert to PciCapLib Laszlo Ersek
2018-05-04 21:36 ` [PATCH 7/7] OvmfPkg/Virtio10Dxe: " Laszlo Ersek
2018-05-14 13:15 ` [PATCH 0/7] MdePkg, OvmfPkg, ArmVirtPkg: add and use PCI(E) Capabilities Library Laszlo Ersek
2018-05-14 15:06   ` Gao, Liming
2018-05-14 17:19     ` Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180504213637.11266-2-lersek@redhat.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox