blob: ede39aae7317ba45bdbd2dec0ceb33dbd16abbfb [file] [log] [blame] [edit]
#include "pcie_bifurcation_server_3.hpp"
#include <nlohmann/json.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/exception.hpp>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <future>
#include <iostream>
#include <ranges>
#include <stdexcept>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
namespace google::pcie_bifurcation
{
static constexpr std::string_view fruInterface =
"xyz.openbmc_project.FruDevice";
static constexpr std::array<std::string_view, 2> fruInterfaces = {fruInterface};
static constexpr std::string_view kHostPresentDir = "/run/host_present";
static constexpr std::string_view kHPMPresentDir = "/run/hpm_present";
static constexpr std::string_view kHost1PresentPath = "/run/host_present/1";
static constexpr std::string_view kHPM1PresentPath = "/run/hpm_present/1";
SocketType getSocketType()
{
namespace fs = std::filesystem;
if (!fs::exists(kHostPresentDir) || !fs::exists(kHPMPresentDir))
{
return SocketType::TwoByOne;
}
if (fs::exists(kHost1PresentPath))
{
return SocketType::TwoByOne;
}
if (fs::exists(kHPM1PresentPath))
{
return SocketType::OneByTwo;
}
return SocketType::OneByOne;
}
Server3PcieBifurcation::Server3PcieBifurcation(
std::string_view pcieConfigFilePath) : socketType_(getSocketType())
{
parseConfig(std::string{pcieConfigFilePath});
try
{
auto sysBus = sdbusplus::bus::new_bus();
initBusAddressMap(sysBus);
}
catch (const sdbusplus::exception_t& e)
{
std::cerr << std::format("Failed to create new bus: {}\n", e.what());
}
}
void Server3PcieBifurcation::parseConfig(const std::string& pcieConfigFilePath)
{
std::ifstream pcieConfigFile(pcieConfigFilePath);
auto pcieConfigJson = nlohmann::json::parse(pcieConfigFile);
if (!pcieConfigJson.is_array())
{
std::cerr << "JsonData is not valid\n";
return;
}
for (const auto& hostConfigJson : pcieConfigJson)
{
auto hostId = hostConfigJson["cpu_id"].get<int>();
auto nSlots = hostConfigJson["num_slot"].get<int>();
auto outputPath = hostConfigJson["output_path"].get<std::string>();
std::cout << "Parsing host " << hostId << std::endl;
hostBifurcations_.emplace_back(hostId, nSlots,
hostConfigJson["slot_data"], outputPath);
std::cout << "Finished parsing host " << hostId << std::endl;
}
}
bool Server3PcieBifurcation::populatePCIeBifurcationData()
{
std::vector<std::vector<uint8_t>> mergedBifurcationData; // for 1x2 socket
for (auto& host : hostBifurcations_)
{
host.populateBifurcation();
auto data = host.aggregateEachSlot();
if (socketType_ == SocketType::OneByTwo)
{
mergedBifurcationData.insert(mergedBifurcationData.end(),
data.begin(), data.end());
}
else
{
host.writeBifurcationDataToFile(data);
}
}
if (socketType_ == SocketType::OneByTwo)
{
hostBifurcations_[0].writeBifurcationDataToFile(mergedBifurcationData);
}
return true;
}
void Server3PcieBifurcation::initBusAddressMap(sdbusplus::bus::bus& sysBus)
{
auto fruSubtree = sysBus.new_method_call(
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
fruSubtree.append("/xyz/openbmc_project/FruDevice", 2, fruInterfaces);
std::vector<std::string> mapperResponses;
try
{
auto responseMsg = sysBus.call(fruSubtree);
responseMsg.read(mapperResponses);
}
catch (const sdbusplus::exception_t& e)
{
std::cerr << std::format("Failed to get fruSubtree: {}\n", e.what());
return;
}
for (const auto& response : mapperResponses)
{
auto getProperty = sysBus.new_method_call(
"xyz.openbmc_project.FruDevice", response.c_str(),
"org.freedesktop.DBus.Properties", "GetAll");
getProperty.append(fruInterface);
std::map<std::string, std::variant<uint32_t, std::string>> properties;
try
{
auto resp = sysBus.call(getProperty);
resp.read(properties);
}
catch (const sdbusplus::exception_t& e)
{
std::cerr << std::format(
"Failed to GetAll FruDevice {}, error {}\n", response.c_str(),
e.what());
continue;
}
bool hasOverride = false;
uint32_t bus{0};
uint32_t addr{0};
std::string bifurcationStr;
for (const auto& [key, value] : properties)
{
if (key == "BUS" || key == "Bus")
{
bus = std::get<uint32_t>(value);
}
if (key == "ADDRESS" || key == "Address")
{
addr = std::get<uint32_t>(value);
}
if (key.starts_with("BOARD_INFO_AM") && !hasOverride)
{
bifurcationStr = std::get<std::string>(value);
}
// TODO(b/472373833) - remove this after swapping to use EVT2+
// cables.
if (key == "BOARD_PRODUCT_NAME")
{
// For these cables, the logical bifurcation are supposed to be
// x16 though physically they are x8x8 from the viewpoint of a
// PE slot.
std::string pn = std::get<std::string>(value);
if (pn.starts_with("x32_apec_to_4x8_mxio_P") ||
pn.starts_with("x16_cem_to_2x8_mxio_P"))
{
bifurcationStr = "PCIe-bifurcation:x16";
hasOverride = true;
}
}
}
for (auto& host : hostBifurcations_)
{
host.updateValuesIfAvailable(bus, addr, bifurcationStr);
}
}
}
} // namespace google::pcie_bifurcation