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