blob: 645c52e06f6058928c01238e48d4d318750e1e15 [file]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_PCI_LIB_CPP_CAPABILITIES_H_
#define SRC_DEVICES_PCI_LIB_CPP_CAPABILITIES_H_
#include <lib/pci/constants.h>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include <hwreg/bitfields.h>
#include "src/devices/pci/lib/cpp/config.h"
namespace pci {
// General PCI/PCIe capability classes. Final calculated address
// for config corresponds to cfg's base plus cap's base along with
// the specific register's offset.
class Capability {
public:
using BaseClass = Capability;
using RegType = uint8_t;
// PCI Code and ID Assignment Specification Revision 1.9 section 2.
enum class Id : RegType {
/* 0x00 */ kNull = 0,
/* 0x01 */ kPciPowerManagement,
/* 0x02 */ kAgp,
/* 0x03 */ kVitalProductData,
/* 0x04 */ kSlotIdentification,
/* 0x05 */ kMsi,
/* 0x06 */ kCompactPciHotSwap,
/* 0x07 */ kPciX,
/* 0x08 */ kHyperTransport,
/* 0x09 */ kVendor,
/* 0x0a */ kDebugPort,
/* 0x0b */ kCompactPciCrc,
/* 0x0c */ kPciHotplug,
/* 0x0d */ kPciBridgeSubsystemVendorId,
/* 0x0e */ kAgp8x,
/* 0x0f */ kSecureDevice,
/* 0x10 */ kPciExpress,
/* 0x11 */ kMsiX,
/* 0x12 */ kSataDataNdxCfg,
/* 0x13 */ kAdvancedFeatures,
/* 0x14 */ kEnhancedAllocation,
/* 0x15 */ kFlatteningPortalBridge,
};
Capability(uint8_t id, uint8_t base, const char* addr = nullptr)
: id_(id), base_(base), addr_(addr) {}
virtual ~Capability() = default;
[[nodiscard]] uint8_t id() const { return id_; }
[[nodiscard]] uint8_t base() const { return base_; }
const char* addr() const { return addr_; }
private:
uint8_t id_;
uint8_t base_;
const char* addr_;
};
using CapabilityList = std::vector<std::unique_ptr<Capability>>;
static_assert(std::to_underlying(Capability::Id::kFlatteningPortalBridge) == 0x15);
struct CapabilityHdr {
uint8_t id;
uint8_t ptr;
};
inline bool ReadCapability(const Config& cfg, uint8_t offset, CapabilityHdr* header) {
offset &= kCapabilityPtrMask;
// Reject a capability starting in the last DWORD as a guard against overrunning config space.
if (offset == 0 || offset == kCapPtrMaxValid) {
return false;
}
// Read the id (at offset + 0x0) and pointer to the next cap (at offset + 1).
// The lower two bits must be masked off per PCI Local Bus Spec 6.7.
// In the case of PCIe, the ptr field also contains the revision number of
// the capability and that is handled by the extended capability parsing
// code.
header->id = cfg.Read(PciReg8(offset));
header->ptr = cfg.Read(PciReg8(static_cast<uint16_t>(offset + 1u))) & kCapabilityPtrMask;
return true;
}
inline std::optional<uint8_t> FindCapability(const Config& cfg, Capability::Id id) {
if (!config::Status.ReadFrom(&cfg).has_capabilities_list()) {
return std::nullopt;
}
CapabilityHdr hdr{};
uint8_t cap_offset =
static_cast<uint8_t>(cfg.Read(Config::kCapabilitiesPtr) & kCapabilityPtrMask);
// Bounded by kMaxStdCapabilities (the maximum number of 4-byte aligned standard capabilities
// in 192 bytes) to guard against capability pointer cycles.
for (size_t i = 0; i < kMaxStdCapabilities && cap_offset != 0; ++i) {
if (cap_offset < kCapPtrMinValid || cap_offset > kCapPtrMaxValid) {
return std::nullopt;
}
if (!ReadCapability(cfg, cap_offset, &hdr)) {
return std::nullopt;
}
if (hdr.id == std::to_underlying(id)) {
return cap_offset;
}
cap_offset = hdr.ptr;
}
return std::nullopt;
}
// General PCIe Extended capability classes. Final calculated address
// for capability register corresponds to cfg's base plus cap's base along with
// the specific register's offset.
class ExtCapability {
public:
using BaseClass = ExtCapability;
using RegType = uint16_t;
// PCI Code and ID Assignment Specification Revision 1.9 section 3.
// Extended capability IDs are a 16-bit field in the extended capability header.
enum class Id : RegType { // NOLINT(performance-enum-size)
/* 0x00 */ kNull = 0,
/* 0x01 */ kAdvancedErrorReporting,
/* 0x02 */ kVirtualChannelNoMFVC,
/* 0x03 */ kDeviceSerialNumber,
/* 0x04 */ kPowerBudgeting,
/* 0x05 */ kRootComplexLinkDeclaration,
/* 0x06 */ kRootComplexInternalLinkControl,
/* 0x07 */ kRootComplexEventCollectorEndpointAssociation,
/* 0x08 */ kMultiFunctionVirtualChannel,
/* 0x09 */ kVirtualChannel,
/* 0x0a */ kRCRB,
/* 0x0b */ kVendor,
/* 0x0c */ kCAC,
/* 0x0d */ kACS,
/* 0x0e */ kARI,
/* 0x0f */ kATS,
/* 0x10 */ kSR_IOV,
/* 0x11 */ kMR_IOV,
/* 0x12 */ kMulticast,
/* 0x13 */ kPRI,
/* 0x14 */ kEnhancedAllocation,
/* 0x15 */ kResizableBAR,
/* 0x16 */ kDynamicPowerAllocation,
/* 0x17 */ kTPHRequester,
/* 0x18 */ kLatencyToleranceReporting,
/* 0x19 */ kSecondaryPCIExpress,
/* 0x1a */ kPMUX,
/* 0x1b */ kPASID,
/* 0x1c */ kLNR,
/* 0x1d */ kDPC,
/* 0x1e */ kL1PMSubstates,
/* 0x1f */ kPrecisionTimeMeasurement,
/* 0x20 */ kMPCIe,
/* 0x21 */ kFRSQueueing,
/* 0x22 */ kReadinessTimeReporting,
/* 0x23 */ kDesignatedVendor,
/* 0x24 */ kVFResizableBAR,
/* 0x25 */ kDataLinkFeature,
/* 0x26 */ kPhysicalLayer16,
/* 0x27 */ kLaneMarginingAtReceiver,
/* 0x28 */ kHierarchyId,
/* 0x29 */ kNativePCIeEnclosure,
/* 0x2a */ kPhysicalLayer32,
/* 0x2b */ kAlternateProtocol,
/* 0x2c */ kSystemFirmwareIntermediary,
};
ExtCapability(uint16_t id, uint8_t version, uint16_t base)
: id_(id), base_(base), version_(version) {}
virtual ~ExtCapability() = default;
[[nodiscard]] uint16_t id() const { return id_; }
[[nodiscard]] uint16_t base() const { return base_; }
[[nodiscard]] uint8_t version() const { return version_; }
private:
uint16_t id_;
uint16_t base_;
uint8_t version_;
};
using ExtCapabilityList = std::vector<std::unique_ptr<ExtCapability>>;
static_assert(std::to_underlying(ExtCapability::Id::kSystemFirmwareIntermediary) == 0x2c);
// PCI Express Base Spec 7.6
struct ExtCapabilityIdReg : public hwreg::RegisterBase<ExtCapabilityIdReg, uint32_t> {
DEF_FIELD(31, 20, offset);
DEF_FIELD(19, 16, version);
DEF_FIELD(15, 0, id);
static auto Get(uint16_t offset) { return hwreg::RegisterAddr<ExtCapabilityIdReg>(offset); }
};
struct ExtCapabilityHdr {
uint16_t id;
uint16_t ptr;
uint8_t version;
};
inline bool ReadExtCapability(const Config& cfg, uint16_t offset, ExtCapabilityHdr* header) {
// Reject an extended capability starting in the last DWORD as a guard against overrunning
// extended config space.
if (offset == 0 || (offset & kExtCapabilityPtrMask) == kPcieExtCapPtrMaxValid) {
return false;
}
auto reg = ExtCapabilityIdReg::Get(offset).ReadFrom(&cfg);
uint16_t id = static_cast<uint16_t>(reg.id());
if (id == kInvalidExtCapabilityId) {
return false;
}
// Extended capabilities start with a 16 bit id, followed by a 4 bit version
// and a 12 bit pointer to the next offset. Like standard capabilities, the bottom
// 2 bits off the offset must be masked off.
header->id = id;
header->ptr = static_cast<uint16_t>(reg.offset() & kExtCapabilityPtrMask);
header->version = static_cast<uint8_t>(reg.version());
return true;
}
} // namespace pci
#endif // SRC_DEVICES_PCI_LIB_CPP_CAPABILITIES_H_