requester: support multi-host MCTP devices hot plug
Currently, pldmd listens for new MCTP endpoint exposed by mctpd, but
they only shows their EID, Network Id, and SupportedMessageTypes, which
cannot fulfill some requirements, e.g., get the device's name or check
the host slot number which contains the MCTP endpoint in multi-host
system.
In openbmc, the additional information are exposed by Entity Manager,
so that we add ConfigurationDiscoveryHandler, search for Entity
Manager's configuration when a new MCTP endpoint has been register by
mctpd.
Objects who want to obtain the configuration can include the
ConfigurationDiscoveryHandler as their attribute, get the configuration
when needed.
TESTED:
A series of unit test case to simulate the response of dbus call,
confirm that the configurations are successfully stored.
Change-Id: I59b8bf434576cbdea651848a8da11e5b870e2dfa
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
Signed-off-by: MandyMCHung <mandy.hung.wiwynn@gmail.com>
Signed-off-by: LioraGuo-wiwynn <liora.guo.wiwynn@gmail.com>
Signed-off-by: Unive Tien <unive.tien.wiwynn@gmail.com>
diff --git a/requester/configuration_discovery_handler.cpp b/requester/configuration_discovery_handler.cpp
new file mode 100644
index 0000000..8b4d956
--- /dev/null
+++ b/requester/configuration_discovery_handler.cpp
@@ -0,0 +1,178 @@
+#include "configuration_discovery_handler.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <algorithm>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+
+void ConfigurationDiscoveryHandler::handleMctpEndpoints(
+ const MctpInfos& newMctpInfos)
+{
+ for (const auto& newMctpInfo : newMctpInfos)
+ {
+ searchConfigurationFor(newMctpInfo);
+ }
+}
+
+void ConfigurationDiscoveryHandler::handleRemovedMctpEndpoints(
+ const MctpInfos& removedMctpInfos)
+{
+ for (const auto& removedMctpInfo : removedMctpInfos)
+ {
+ removeConfigByEid(std::get<0>(removedMctpInfo));
+ }
+}
+
+std::map<std::string, MctpEndpoint>&
+ ConfigurationDiscoveryHandler::getConfigurations()
+{
+ return configurations;
+}
+
+void ConfigurationDiscoveryHandler::searchConfigurationFor(MctpInfo mctpInfo)
+{
+ constexpr auto inventoryPath = "/xyz/openbmc_project/inventory/";
+ constexpr auto depthWithDownstreamDevices = std::ranges::count(
+ "/inventory/system/{BOARD_OR_CHASSIS}/{DEVICE}/{DOWNSTREAM_DEVICE}",
+ '/');
+
+ const std::vector<std::string> mctpEndpoint = {
+ "xyz.openbmc_project.Configuration.MCTPEndpoint"};
+
+ try
+ {
+ auto response = dBusIntf->getSubtree(
+ inventoryPath, depthWithDownstreamDevices, mctpEndpoint);
+
+ for (const auto& [objectPath, serviceMap] : response)
+ {
+ appendConfigIfEidMatch(std::get<eid>(mctpInfo), objectPath,
+ serviceMap);
+ }
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ error("{FUNC}: Failed to getSubtree with MCTPEndpoint, error={ERROR}",
+ "FUNC", std::string(__func__), "ERROR", e.what());
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ error("{FUNC}: Unpredicted error occured, error={ERROR}", "FUNC",
+ std::string(__func__), "ERROR", e.what());
+ return;
+ }
+}
+
+void ConfigurationDiscoveryHandler::appendConfigIfEidMatch(
+ uint8_t targetEid, const std::string& configPath,
+ const pldm::utils::MapperServiceMap& serviceMap)
+{
+ if (!configurations.contains(configPath))
+ {
+ const auto& serviceName = serviceMap.at(0).first;
+
+ /** mctpEndpointInterface should be
+ * "xyz.openbmc_project.Configuration.MCTPEndpoint".
+ */
+ const auto& mctpEndpointInterface = serviceMap.at(0).second.at(0);
+ try
+ {
+ auto response = dBusIntf->getAll(serviceName, configPath,
+ mctpEndpointInterface);
+ appendIfEidMatch(targetEid, configPath,
+ parseMctpEndpointFromResponse(response));
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ error(
+ "{FUNC}: Failed to getAll of interface={INTERFACE} in path={PATH}, error={ERROR}",
+ "FUNC", std::string(__func__), "INTERFACE",
+ mctpEndpointInterface, "PATH", configPath, "ERROR", e.what());
+ return;
+ }
+ catch (const NoSuchPropertiesException& e)
+ {
+ error("{FUNC}: Insufficient properties in {PATH}, error={ERROR}",
+ "FUNC", std::string(__func__), "PATH", configPath, "ERROR",
+ e.what());
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ error("{FUNC}: Unpredicted error occured, error={ERROR}", "FUNC",
+ std::string(__func__), "ERROR", e.what());
+ return;
+ }
+ }
+}
+
+MctpEndpoint ConfigurationDiscoveryHandler::parseMctpEndpointFromResponse(
+ const pldm::utils::PropertyMap& response)
+{
+ if (response.contains("Address") && response.contains("Bus") &&
+ response.contains("EndpointId") && response.contains("Name"))
+ {
+ auto addressArray =
+ std::get<std::vector<uint64_t>>(response.at("Address"));
+ uint64_t address = 0;
+
+ for (const auto& element : addressArray)
+ {
+ address = (address << 8) | element;
+ }
+
+ auto eid = std::get<uint64_t>(response.at("EndpointId"));
+ auto bus = std::get<uint64_t>(response.at("Bus"));
+ auto componentName = std::get<std::string>(response.at("Name"));
+ if (response.contains("IANA"))
+ {
+ auto iana = std::get<std::string>(response.at("IANA"));
+ return MctpEndpoint{address, eid, bus, componentName, iana};
+ }
+
+ return MctpEndpoint{address, eid, bus, componentName, std::nullopt};
+ }
+ else
+ {
+ std::vector<std::string> missingProperties{};
+ if (response.contains("Address"))
+ missingProperties.emplace_back("Address");
+ if (response.contains("Bus"))
+ missingProperties.emplace_back("Bus");
+ if (response.contains("EndpointId"))
+ missingProperties.emplace_back("EndpointId");
+ if (response.contains("Name"))
+ missingProperties.emplace_back("Name");
+
+ throw NoSuchPropertiesException(missingProperties);
+ }
+}
+
+void ConfigurationDiscoveryHandler::appendIfEidMatch(
+ uint8_t targetEid, const std::string& configPath,
+ const MctpEndpoint& endpoint)
+{
+ if (endpoint.EndpointId == targetEid)
+ {
+ configurations.emplace(configPath, endpoint);
+ }
+}
+
+void ConfigurationDiscoveryHandler::removeConfigByEid(uint8_t eid)
+{
+ for (const auto& [configDbusPath, mctpEndpoint] : configurations)
+ {
+ if (mctpEndpoint.EndpointId == eid)
+ {
+ configurations.erase(configDbusPath);
+ return;
+ }
+ }
+}
+
+} // namespace pldm