audk/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1087 lines
34 KiB
C
Raw Permalink Normal View History

/** @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) {
OvmfPkg/BasePciCapLib: suppress invalid "nullptr deref" warning RH covscan reports the following "nullptr deref" warning: > Error: CLANG_WARNING: > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:312:5: > warning: Dereference of null pointer > # InstanceZero->NumInstancesUnion.NumInstances++; > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:509:7: > note: Assuming 'OutCapList' is not equal to NULL > # if (OutCapList == NULL) { > # ^~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:509:3: > note: Taking false branch > # if (OutCapList == NULL) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:518:7: > note: Assuming the condition is false > # if (OutCapList->Capabilities == NULL) { > # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:518:3: > note: Taking false branch > # if (OutCapList->Capabilities == NULL) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:529:7: > note: Assuming 'CapHdrOffsets' is not equal to NULL > # if (CapHdrOffsets == NULL) { > # ^~~~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:529:3: > note: Taking false branch > # if (CapHdrOffsets == NULL) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:546:3: > note: Taking false branch > # if (RETURN_ERROR (Status)) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:549:7: > note: Assuming the condition is true > # if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) { > # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:549:3: > note: Taking true branch > # if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:557:5: > note: Taking false branch > # if (RETURN_ERROR (Status)) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:565:12: > note: Assuming 'NormalCapHdrOffset' is > 0 > # while (NormalCapHdrOffset > 0) { > # ^~~~~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:565:5: > note: Loop condition is true. Entering loop body > # while (NormalCapHdrOffset > 0) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:570:7: > note: Taking false branch > # if (RETURN_ERROR (Status)) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:574:16: > note: Calling 'InsertPciCap' > # Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapNormal, > # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:235:3: > note: Null pointer value stored to 'InstanceZero' > # InstanceZero = NULL; > # ^~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:243:7: > note: Assuming 'PciCap' is not equal to NULL > # if (PciCap == NULL) { > # ^~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:243:3: > note: Taking false branch > # if (PciCap == NULL) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:259:3: > note: Taking false branch > # if (RETURN_ERROR (Status)) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:297:3: > note: Taking false branch > # if (RETURN_ERROR (Status)) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:311:7: > note: Assuming the condition is true > # if (PciCap->Key.Instance > 0) { > # ^~~~~~~~~~~~~~~~~~~~~~~~ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:311:3: > note: Taking true branch > # if (PciCap->Key.Instance > 0) { > # ^ > edk2-89910a39dcfd/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c:312:5: > note: Dereference of null pointer > # InstanceZero->NumInstancesUnion.NumInstances++; > # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > # 310| // > # 311| if (PciCap->Key.Instance > 0) { > # 312|-> InstanceZero->NumInstancesUnion.NumInstances++; > # 313| } > # 314| return RETURN_SUCCESS; The warning is invalid: the flagged dereferencing of "InstanceZero" is gated by a condition that is only satisfied if we dereference "InstanceZero" *first*. (Perhaps the analyzer assumes that the OrderedCollectionInsert() call, just before line 259, can change the value of "PciCap->Key.Instance" via the last argument: 254 // 255 // Add PciCap to CapList. 256 // 257 Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry, 258 PciCap); 259 if (RETURN_ERROR (Status)) { That assumption is incorrect.) Add a comment and an ASSERT(). Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Jordan Justen <jordan.l.justen@intel.com> Bugzilla: https://bugzilla.tianocore.org/show_bug.cgi?id=1710 Issue: scan-0994.txt Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com> Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2019-04-12 19:00:52 +02:00
//
// 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,
__func__,
(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,
__func__,
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
);
}