| #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 |