public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: Laszlo Ersek <lersek@redhat.com>
Cc: edk2-devel-01 <edk2-devel@lists.01.org>,
	Jordan Justen <jordan.l.justen@intel.com>
Subject: Re: [PATCH v2 1/7] OvmfPkg: introduce PciCapLib
Date: Thu, 24 May 2018 09:53:20 +0200	[thread overview]
Message-ID: <CAKv+Gu8iYBo9TedCG=b=0sp-rz_Dy90dOWvf94mGKqpVnnSi3w@mail.gmail.com> (raw)
In-Reply-To: <20180523202121.8125-2-lersek@redhat.com>

On 23 May 2018 at 22:21, Laszlo Ersek <lersek@redhat.com> wrote:
> 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>
> Suggested-by: Jordan Justen <jordan.l.justen@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>
> Notes:
>     v2:
>     - move library from MdePkg to OvmfPkg, for initial introduction
>
>  OvmfPkg/OvmfPkg.dec                             |    4 +
>  OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf |   38 +
>  OvmfPkg/Include/Library/PciCapLib.h             |  429 +++++++++
>  OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h   |   60 ++
>  OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c   | 1007 ++++++++++++++++++++
>  5 files changed, 1538 insertions(+)
>
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index c01a2ca7219a..74818a2e2a19 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -31,6 +31,10 @@ [LibraryClasses]
>    #
>    NvVarsFileLib|Include/Library/NvVarsFileLib.h
>
> +  ##  @libraryclass  Provides services to work with PCI capabilities in PCI
> +  #                  config space.
> +  PciCapLib|Include/Library/PciCapLib.h
> +
>    ##  @libraryclass  Access QEMU's firmware configuration interface
>    #
>    QemuFwCfgLib|Include/Library/QemuFwCfgLib.h
> diff --git a/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf b/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf
> new file mode 100644
> index 000000000000..9a7428a589c2
> --- /dev/null
> +++ b/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf
> @@ -0,0 +1,38 @@
> +## @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
> +  OvmfPkg/OvmfPkg.dec
> +
> +[LibraryClasses]
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  OrderedCollectionLib
> diff --git a/OvmfPkg/Include/Library/PciCapLib.h b/OvmfPkg/Include/Library/PciCapLib.h
> new file mode 100644
> index 000000000000..22a1ad624bd3
> --- /dev/null
> +++ b/OvmfPkg/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/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h b/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h
> new file mode 100644
> index 000000000000..e631745834d9
> --- /dev/null
> +++ b/OvmfPkg/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/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c b/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c
> new file mode 100644
> index 000000000000..6789359f0a54
> --- /dev/null
> +++ b/OvmfPkg/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;
> +

Move these out of the loop?

> +    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;
> +

and this one

> +    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;
> +

and this one

> +    //
> +    // 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;
> +

and this one.

Perhaps I am missing something? After four instances, it seems
deliberate rather than accidental :-)

> +      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;
> +

Nit: add

ASSERT (Info != NULL);

here?

I know it seems rather arbitrary to add it here and not anywhere else,
but PciCapGetInfo() is part of the API, and dereferencing Info [which
may be the result of e.g., a pool allocation] for writing is
particularly bad.

> +  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-24  7:53 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-23 20:21 [PATCH v2 0/7] OvmfPkg, ArmVirtPkg: add and use PCI(E) Capabilities Library Laszlo Ersek
2018-05-23 20:21 ` [PATCH v2 1/7] OvmfPkg: introduce PciCapLib Laszlo Ersek
2018-05-24  7:53   ` Ard Biesheuvel [this message]
2018-05-24 14:39     ` Laszlo Ersek
2018-05-24 14:41       ` Ard Biesheuvel
2018-05-24 17:25         ` Laszlo Ersek
2018-05-23 20:21 ` [PATCH v2 2/7] OvmfPkg: introduce PciCapPciSegmentLib Laszlo Ersek
2018-05-24  8:08   ` Ard Biesheuvel
2018-05-24 14:43     ` Laszlo Ersek
2018-05-24 14:55       ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 3/7] OvmfPkg: introduce PciCapPciIoLib Laszlo Ersek
2018-05-24  8:13   ` Ard Biesheuvel
2018-05-24 14:50     ` Laszlo Ersek
2018-05-24 14:54       ` Ard Biesheuvel
2018-05-24 14:54         ` Ard Biesheuvel
2018-05-24 17:22         ` Laszlo Ersek
2018-05-23 20:21 ` [PATCH v2 4/7] OvmfPkg: resolve PciCapLib, PciCapPciSegmentLib, PciCapPciIoLib Laszlo Ersek
2018-05-24  8:14   ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 5/7] ArmVirtPkg: " Laszlo Ersek
2018-05-24  8:14   ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 6/7] OvmfPkg/PciHotPlugInitDxe: convert to PciCapLib Laszlo Ersek
2018-05-24  8:15   ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 7/7] OvmfPkg/Virtio10Dxe: " Laszlo Ersek
2018-05-24  8:16   ` Ard Biesheuvel
2018-05-24 14:55     ` Laszlo Ersek
2018-05-24 20:04 ` [PATCH v2 0/7] OvmfPkg, ArmVirtPkg: add and use PCI(E) Capabilities Library 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='CAKv+Gu8iYBo9TedCG=b=0sp-rz_Dy90dOWvf94mGKqpVnnSi3w@mail.gmail.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