Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions fboss/platform/platform_manager/ConfigValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "fboss/platform/platform_manager/ConfigValidator.h"

#include <set>

#include <folly/logging/xlog.h>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/drop.hpp>
Expand Down Expand Up @@ -311,6 +313,7 @@ bool ConfigValidator::isValidI2cAdaptersFromCpu(
static const re2::RE2 kCpuBusNameRegex{"CPU_BUS@\\d+"};
bool hasVirtual = false;
bool hasExact = false;
std::set<std::string> seen;
for (const auto& name : i2cAdaptersFromCpu) {
if (re2::RE2::FullMatch(name, kCpuBusNameRegex)) {
if (name != "CPU_BUS@0" && name != "CPU_BUS@1") {
Expand All @@ -320,6 +323,10 @@ bool ConfigValidator::isValidI2cAdaptersFromCpu(
name);
return false;
}
if (!seen.insert(name).second) {
XLOG(ERR) << fmt::format("Duplicate virtual bus name '{}'", name);
return false;
}
hasVirtual = true;
} else {
hasExact = true;
Expand Down
78 changes: 76 additions & 2 deletions fboss/platform/platform_manager/I2cExplorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ namespace {

const re2::RE2 kI2cMuxChannelRegex{"channel-\\d+"};
const re2::RE2 kCpuBusPattern{"CPU_BUS@(\\d+)"};
const re2::RE2 kAmdI2cPlatformDevRegex{"AMDI0010:\\d+"};
constexpr auto kI2cDevCreationWaitSecs = std::chrono::seconds(5);
constexpr auto kCpuI2cBusNumsWaitSecs = 1;

// AMD FireRange DesignWare I2C: ACPI firmware_node/path → CPU_BUS index.
const std::map<std::string, int> kAmdAcpiPathToBusIndex = {
{R"(\_SB_.I2CA)", 1},
{R"(\_SB_.I2CB)", 0},
};

std::string getI2cAdapterName(const fs::path& busPath) {
auto nameFile = busPath / "name";
if (!fs::exists(nameFile)) {
Expand Down Expand Up @@ -95,9 +102,76 @@ std::map<std::string, uint16_t> I2cExplorer::getBusNums(
}

std::map<std::string, uint16_t> I2cExplorer::resolveAmdCpuBusNums(
const std::vector<std::string>& /* i2cAdaptersFromCpu */) {
const std::vector<std::string>& i2cAdaptersFromCpu) {
// AMD FireRange DesignWare I2C buses share identical adapter names, so
// we identify them by their ACPI firmware_node/path under
// /sys/devices/platform/AMDI0010:*.
// \_SB_.I2CA → CPU_BUS@1
// \_SB_.I2CB → CPU_BUS@0
const auto platformRoot = fs::path("/sys/devices/platform");
constexpr int maxRetries = 10;

std::map<std::string, uint16_t> busNums;

for (int attempt = 1; attempt <= maxRetries; attempt++) {
XLOG(INFO) << "Resolving AMD CPU_BUS names -- Attempt #" << attempt;
busNums.clear();

for (const auto& dirEntry : fs::directory_iterator(platformRoot)) {
if (!re2::RE2::FullMatch(
dirEntry.path().filename().string(), kAmdI2cPlatformDevRegex)) {
continue;
}
auto fwPathFile = dirEntry.path() / "firmware_node" / "path";
if (!fs::exists(fwPathFile)) {
continue;
}
std::string acpiPath;
if (!folly::readFile(fwPathFile.string().c_str(), acpiPath)) {
continue;
}
acpiPath = folly::trimWhitespace(acpiPath).str();

auto it = kAmdAcpiPathToBusIndex.find(acpiPath);
if (it == kAmdAcpiPathToBusIndex.end()) {
continue;
}
auto virtualName = fmt::format("CPU_BUS@{}", it->second);
if (std::find(
i2cAdaptersFromCpu.begin(),
i2cAdaptersFromCpu.end(),
virtualName) == i2cAdaptersFromCpu.end()) {
continue;
}

// Find the i2c adapter child (i2c-N) under this platform device.
for (const auto& child : fs::directory_iterator(dirEntry.path())) {
if (re2::RE2::FullMatch(
child.path().filename().string(), kI2cBusNameRegex)) {
auto busNum = extractBusNumFromPath(child.path());
XLOG(INFO) << fmt::format(
"Resolved {} -> i2c-{} (ACPI: {})",
virtualName,
busNum,
acpiPath);
busNums[virtualName] = busNum;
break;
}
}
}

if (busNums.size() == i2cAdaptersFromCpu.size()) {
return busNums;
}
sleep(kCpuI2cBusNumsWaitSecs);
}
throw std::runtime_error(
"CPU_BUS virtual bus resolution is not yet implemented for AMD CPUs");
fmt::format(
"Failed to resolve AMD CPU_BUS names over {}s. "
"Resolved {}/{} entries",
kCpuI2cBusNumsWaitSecs * maxRetries,
busNums.size(),
i2cAdaptersFromCpu.size()));
}

std::map<std::string, uint16_t> I2cExplorer::resolveIntelCpuBusNums(
Expand Down
5 changes: 3 additions & 2 deletions fboss/platform/platform_manager/I2cExplorer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ class I2cExplorer {
std::map<std::string, uint16_t> getBusNums(
const std::vector<std::string>& i2cAdaptersFromCpu);

// Resolve virtual "CPU_BUS@0" name to a kernel bus number on AMD CPUs.
// Not yet implemented — throws at runtime.
// Resolve virtual "CPU_BUS@N" names to kernel bus numbers on AMD CPUs.
// Scans /sys/devices/platform/AMDI0010:* and reads firmware_node/path
// to map ACPI paths to bus indices (\_SB_.I2CA → @1, \_SB_.I2CB → @0).
std::map<std::string, uint16_t> resolveAmdCpuBusNums(
const std::vector<std::string>& i2cAdaptersFromCpu);

Expand Down
10 changes: 6 additions & 4 deletions fboss/platform/platform_manager/platform_manager_config.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -743,12 +743,14 @@ struct PlatformConfig {
12: map<string, PmUnitConfig> pmUnitConfigs;

// List of the i2c buses created from the CPU. Entries can use either:
// (a) Virtual name "CPU_BUS@0" — resolved at runtime by detecting the
// (a) Virtual names "CPU_BUS@N" — resolved at runtime by detecting the
// CPU vendor (via folly::CpuId) and scanning sysfs for the
// corresponding adapter:
// - Intel: matches "SMBus I801 adapter at <offset>" (one per
// unit). Only CPU_BUS@0 is supported today.
// - AMD: not yet implemented (throws at runtime).
// - Intel: matches "SMBus I801 adapter at <offset>" by adapter
// name. Only CPU_BUS@0 is supported today.
// - AMD: identifies DesignWare I2C buses via ACPI
// firmware_node/path under /sys/devices/platform/AMDI0010:*.
// CPU_BUS@0 maps to \_SB_.I2CB, CPU_BUS@1 to \_SB_.I2CA.
// (b) Exact adapter name matching /sys/bus/i2c/devices/i2c-N/name
// (e.g. "SMBus I801 adapter at 5000").
// All entries in a single config must use the same style.
Expand Down
10 changes: 7 additions & 3 deletions fboss/platform/platform_manager/tests/ConfigValidatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@ TEST(ConfigValidatorTest, InvalidRootSlotType) {
}

TEST(ConfigValidatorTest, I2cAdaptersFromCpuValidation) {
// CPU_BUS@0 — valid
// CPU_BUS@0 — valid (Intel single-bus or AMD first bus)
EXPECT_TRUE(ConfigValidator().isValidI2cAdaptersFromCpu({"CPU_BUS@0"}));

// CPU_BUS@1 — valid
// CPU_BUS@1 — valid (AMD second bus)
EXPECT_TRUE(ConfigValidator().isValidI2cAdaptersFromCpu({"CPU_BUS@1"}));

// CPU_BUS@0 + CPU_BUS@1 — valid (two-bus config)
// CPU_BUS@0 + CPU_BUS@1 — valid (AMD two-bus config)
EXPECT_TRUE(
ConfigValidator().isValidI2cAdaptersFromCpu({"CPU_BUS@0", "CPU_BUS@1"}));

Expand All @@ -126,6 +126,10 @@ TEST(ConfigValidatorTest, I2cAdaptersFromCpuValidation) {
// CPU_BUS@2 — invalid (only @0 and @1 supported)
EXPECT_FALSE(ConfigValidator().isValidI2cAdaptersFromCpu({"CPU_BUS@2"}));

// Duplicate CPU_BUS@0 — invalid
EXPECT_FALSE(
ConfigValidator().isValidI2cAdaptersFromCpu({"CPU_BUS@0", "CPU_BUS@0"}));

// Mixed styles — invalid
EXPECT_FALSE(
ConfigValidator().isValidI2cAdaptersFromCpu(
Expand Down
Loading