blob: bf06a45804b9baaa143bf928f3c7a02c68081fbc [file] [log] [blame] [edit]
#include "host_bifurcation.hpp"
#include "bus_bifurcation.hpp"
#include "pcie_bifurcation_utils.hpp"
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/exception.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <format>
#include <fstream>
#include <functional>
#include <future>
#include <iostream>
#include <numeric>
#include <ranges>
#include <string_view>
#include <vector>
namespace google::pcie_bifurcation
{
namespace
{
inline bool isX16(const std::vector<uint8_t>& x)
{
return x.size() == 1 && x[0] == 16;
}
inline void duplicateBifurcationIfNecessary(
size_t expectedSlots, std::vector<std::vector<uint8_t>>& bifurcations)
{
if (expectedSlots == 2 && bifurcations.size() == 1)
{
bifurcations.emplace_back(bifurcations[0]);
}
}
std::vector<uint8_t>
mergeBifurcations(std::vector<std::vector<uint8_t>>& bifurcations,
bool hasX16, size_t expectedSlots)
{
constexpr uint8_t MAX_EACH_LANE = 16;
std::vector<uint8_t> mergeBifurcation;
if (hasX16)
{
if (!std::all_of(bifurcations.begin(), bifurcations.end(), isX16))
{
std::cerr << "Err: Mixed x16 and non-x16 bifurcation" << std::endl;
return {};
}
mergeBifurcation = {16};
}
else
{
duplicateBifurcationIfNecessary(expectedSlots, bifurcations);
for (const auto& bifurcation : bifurcations)
{
mergeBifurcation.insert(mergeBifurcation.end(), bifurcation.begin(),
bifurcation.end());
}
}
if (std::accumulate(mergeBifurcation.begin(), mergeBifurcation.end(), 0u) >
MAX_EACH_LANE)
{
std::cerr << "Err: Total lanes exceeded "
<< static_cast<int>(MAX_EACH_LANE) << std::endl;
return {};
}
return mergeBifurcation;
}
std::vector<uint8_t> aggregateABSlot(
const std::vector<int>& buses,
const std::unordered_map<int, BusPCIeBifurcation>& busBifurcationMap)
{
std::vector<std::vector<uint8_t>> bifurcations;
bool hasX16 = false;
for (auto bus : buses)
{
const auto& pcieBifurcation =
busBifurcationMap.at(bus).getBifurcation();
if (pcieBifurcation.empty())
{
continue;
}
hasX16 |= isX16(pcieBifurcation);
bifurcations.emplace_back(pcieBifurcation);
}
return mergeBifurcations(bifurcations, hasX16, buses.size());
}
} // namespace
constexpr static const char* kEntityManagerServiceName =
"xyz.openbmc_project.EntityManager";
constexpr static const char* kI2CDeviceInterfaceName =
"xyz.openbmc_project.Inventory.Decorator.I2CDevice";
HostBifurcation::HostBifurcation(int hostId, int numSlot,
const nlohmann::json& cpuPCIeData,
std::string outputPath) :
hostId_(hostId), numSlot_(numSlot), i2cBuses_(numSlot_),
outputPath_(std::move(outputPath))
{
for (const auto& slotConfig : cpuPCIeData)
{
auto slot = slotConfig["slot"].get<int>();
// In the event that a bus isn't provided, the value should be
// statically set. Instead of refactoring everything, provide a negative
// bus number to act as the identifier
auto bus =
(!slotConfig.contains("bus") && slotConfig.contains("static_value"))
? -1 - slot
: slotConfig["bus"].get<int>();
// Skip check that lanes add up to x8/x16 due to utility cluster
// irregularity.
bool skipLaneSum = (bus < 0);
std::string staticBifurcationStr;
if (slotConfig.contains("static_value"))
{
staticBifurcationStr =
slotConfig["static_value"].get<std::string>();
}
if (slot >= numSlot)
{
throw std::runtime_error("Slot index out of range");
}
i2cBuses_[slot].push_back(bus);
busBifurcationMap_.try_emplace(bus, slot, bus, staticBifurcationStr,
skipLaneSum);
}
try
{
getEntityManagerDbus();
}
catch (const sdbusplus::exception_t& e)
{
std::cerr << std::format("Failed to get EntityManager DBus: {}\n",
e.what());
}
}
void HostBifurcation::getEntityManagerDbus()
{
std::string mainboardPath = std::format("{}{}", MAIN_BOARD_PREFIX, hostId_);
auto sysBus = sdbusplus::bus::new_bus();
auto associations = physicalAssociations(sysBus, mainboardPath);
for (const std::string& path : associations)
{
auto getBus =
sysBus.new_method_call(kEntityManagerServiceName, path.c_str(),
"org.freedesktop.DBus.Properties", "Get");
getBus.append(kI2CDeviceInterfaceName, "Bus");
std::variant<uint64_t> busProp;
try
{
auto resp = sysBus.call(getBus);
resp.read(busProp);
}
catch (const sdbusplus::exception_t& e)
{
std::cerr << std::format(
"Failed to get i2c devices for path {}, error {}\n", path,
e.what());
continue;
}
auto bus = std::get<std::uint64_t>(busProp);
if (!busBifurcationMap_.contains(bus))
{
continue;
}
busBifurcationMap_[bus].updateEMPath(path);
}
}
void HostBifurcation::populateBifurcation()
{
std::vector<std::future<void>> slotPopulateTasks;
for (auto&& [bus, busBifur] : busBifurcationMap_)
{
slotPopulateTasks.push_back(
std::async(std::launch::async,
std::bind_front(&BusPCIeBifurcation::populateBifurcation,
&busBifur)));
}
for (auto& task : slotPopulateTasks)
{
task.get();
}
}
std::vector<std::vector<uint8_t>> HostBifurcation::aggregateEachSlot()
{
std::vector<std::vector<uint8_t>> bifurcationData;
for (auto&& buses : i2cBuses_)
{
bifurcationData.emplace_back(
aggregateABSlot(buses, busBifurcationMap_));
}
return bifurcationData;
}
void HostBifurcation::writeBifurcationDataToFile(
const std::vector<std::vector<uint8_t>>& bifurcationData)
{
std::vector<uint8_t> aggregatedBifurcationData;
aggregatedBifurcationData.push_back(bifurcationData.size());
for (const auto& slotData : bifurcationData)
{
aggregatedBifurcationData.push_back(slotData.size());
for (auto data : slotData)
{
aggregatedBifurcationData.push_back(data);
}
}
std::ofstream outputFile(outputPath_, std::ios::out | std::ios::binary |
std::ios::trunc);
if (!outputFile)
{
std::cerr << std::format("Failed to open file for writing: {}\n",
outputPath_);
return;
}
outputFile.write(reinterpret_cast<char*>(&aggregatedBifurcationData[0]),
aggregatedBifurcationData.size());
if (outputFile.fail())
{
std::cerr << std::format("Writing to file {} failed\n", outputPath_);
}
}
void HostBifurcation::updateValuesIfAvailable(int bus, int address,
std::string_view bifurcationStr)
{
if (!busBifurcationMap_.contains(bus))
{
return;
}
std::cout << "Got bus " << bus << ", addr " << address << ", bifurcation "
<< bifurcationStr << std::endl;
busBifurcationMap_[bus].updateAddress(address);
busBifurcationMap_[bus].updateValue(bifurcationStr);
}
} // namespace google::pcie_bifurcation