REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the OvmfPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Andrew Fish <afish@apple.com>
		
			
				
	
	
		
			1087 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1087 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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.
 | |
| 
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| **/
 | |
| 
 | |
| #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) {
 | |
|     //
 | |
|     // Suppress invalid "nullptr dereference" compiler/analyzer warnings: the
 | |
|     // only way for "PciCap->Key.Instance" to be positive here is for it to
 | |
|     // have been assigned *from* dereferencing "InstanceZero" above.
 | |
|     //
 | |
|     ASSERT (InstanceZero != NULL);
 | |
| 
 | |
|     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,
 | |
|                  (UINT16)ExtendedCapHdr.CapabilityId,
 | |
|                  ExtendedCapHdrOffset,
 | |
|                  (UINT8)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;
 | |
| 
 | |
|   ASSERT (Info != NULL);
 | |
| 
 | |
|   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
 | |
|                       );
 | |
| }
 |