#include "config.h" #include "mctp_endpoint_discovery.hpp" #include "common/types.hpp" #include "common/utils.hpp" #include #include #include #include #include #include #include #include #include using namespace sdbusplus::bus::match::rules; PHOSPHOR_LOG2_USING; namespace pldm { MctpDiscovery::MctpDiscovery( sdbusplus::bus_t& bus, std::initializer_list list) : bus(bus), mctpEndpointAddedSignal( bus, interfacesAdded(MCTPPath), [this](sdbusplus::message_t& msg) { this->discoverEndpoints(msg); }), mctpEndpointRemovedSignal( bus, interfacesRemoved(MCTPPath), [this](sdbusplus::message_t& msg) { this->removeEndpoints(msg); }), mctpEndpointPropChangedSignal( bus, propertiesChangedNamespace(MCTPPath, MCTPInterfaceCC), [this](sdbusplus::message_t& msg) { this->propertiesChangedCb(msg); }), handlers(list) { std::map currentMctpInfoMap; getMctpInfos(currentMctpInfoMap); for (const auto& mapIt : currentMctpInfoMap) { if (mapIt.second) { // Only add the available endpoints to the terminus // Let the propertiesChanged signal tells us when it comes back // to Available again addToExistingMctpInfos(MctpInfos(1, mapIt.first)); } } handleMctpEndpoints(existingMctpInfos); } void MctpDiscovery::getMctpInfos(std::map& mctpInfoMap) { // Find all implementations of the MCTP Endpoint interface pldm::utils::GetSubTreeResponse mapperResponse; try { mapperResponse = pldm::utils::DBusHandler().getSubtree( MCTPPath, 0, std::vector({MCTPInterface})); } catch (const sdbusplus::exception_t& e) { error( "Failed to getSubtree call at path '{PATH}' and interface '{INTERFACE}', error - {ERROR} ", "ERROR", e, "PATH", MCTPPath, "INTERFACE", MCTPInterface); return; } for (const auto& [path, services] : mapperResponse) { for (const auto& serviceIter : services) { const std::string& service = serviceIter.first; const MctpEndpointProps& epProps = getMctpEndpointProps(service, path); const UUID& uuid = getEndpointUUIDProp(service, path); const Availability& availability = getEndpointConnectivityProp(path); auto types = std::get(epProps); if (std::find(types.begin(), types.end(), mctpTypePLDM) != types.end()) { auto mctpInfo = MctpInfo(std::get(epProps), uuid, "", std::get(epProps), std::nullopt); searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); mctpInfoMap[std::move(mctpInfo)] = availability; } } } } MctpEndpointProps MctpDiscovery::getMctpEndpointProps( const std::string& service, const std::string& path) { try { auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant( service.c_str(), path.c_str(), MCTPInterface); if (properties.contains("NetworkId") && properties.contains("EID") && properties.contains("SupportedMessageTypes")) { auto networkId = std::get(properties.at("NetworkId")); auto eid = std::get(properties.at("EID")); auto types = std::get>( properties.at("SupportedMessageTypes")); return MctpEndpointProps(networkId, eid, types); } } catch (const sdbusplus::exception_t& e) { error( "Error reading MCTP Endpoint property at path '{PATH}' and service '{SERVICE}', error - {ERROR}", "SERVICE", service, "PATH", path, "ERROR", e); return MctpEndpointProps(0, MCTP_ADDR_ANY, {}); } return MctpEndpointProps(0, MCTP_ADDR_ANY, {}); } UUID MctpDiscovery::getEndpointUUIDProp(const std::string& service, const std::string& path) { try { auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant( service.c_str(), path.c_str(), EndpointUUID); if (properties.contains("UUID")) { return std::get(properties.at("UUID")); } } catch (const sdbusplus::exception_t& e) { error( "Error reading Endpoint UUID property at path '{PATH}' and service '{SERVICE}', error - {ERROR}", "SERVICE", service, "PATH", path, "ERROR", e); return static_cast(emptyUUID); } return static_cast(emptyUUID); } Availability MctpDiscovery::getEndpointConnectivityProp(const std::string& path) { Availability available = false; try { pldm::utils::PropertyValue propertyValue = pldm::utils::DBusHandler().getDbusPropertyVariant( path.c_str(), MCTPConnectivityProp, MCTPInterfaceCC); if (std::get(propertyValue) == "Available") { available = true; } } catch (const sdbusplus::exception_t& e) { error( "Error reading Endpoint Connectivity property at path '{PATH}', error - {ERROR}", "PATH", path, "ERROR", e); } return available; } void MctpDiscovery::getAddedMctpInfos(sdbusplus::message_t& msg, MctpInfos& mctpInfos) { using ObjectPath = sdbusplus::message::object_path; ObjectPath objPath; using Property = std::string; using PropertyMap = std::map; std::map interfaces; std::string uuid = emptyUUID; try { msg.read(objPath, interfaces); } catch (const sdbusplus::exception_t& e) { error( "Error reading MCTP Endpoint added interface message, error - {ERROR}", "ERROR", e); return; } const Availability& availability = getEndpointConnectivityProp(objPath.str); /* Get UUID */ try { auto service = pldm::utils::DBusHandler().getService( objPath.str.c_str(), EndpointUUID); uuid = getEndpointUUIDProp(service, objPath.str); } catch (const sdbusplus::exception_t& e) { error("Error getting Endpoint UUID D-Bus interface, error - {ERROR}", "ERROR", e); } for (const auto& [intfName, properties] : interfaces) { if (intfName == MCTPInterface) { if (properties.contains("NetworkId") && properties.contains("EID") && properties.contains("SupportedMessageTypes")) { auto networkId = std::get(properties.at("NetworkId")); auto eid = std::get(properties.at("EID")); auto types = std::get>( properties.at("SupportedMessageTypes")); if (!availability) { // Log an error message here, but still add it to the // terminus error( "mctpd added a DEGRADED endpoint {EID} networkId {NET} to D-Bus", "NET", networkId, "EID", static_cast(eid)); } if (std::find(types.begin(), types.end(), mctpTypePLDM) != types.end()) { info( "Adding Endpoint networkId '{NETWORK}' and EID '{EID}' UUID '{UUID}'", "NETWORK", networkId, "EID", eid, "UUID", uuid); auto mctpInfo = MctpInfo(eid, uuid, "", networkId, std::nullopt); searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); mctpInfos.emplace_back(std::move(mctpInfo)); } } } } } void MctpDiscovery::addToExistingMctpInfos(const MctpInfos& addedInfos) { for (const auto& mctpInfo : addedInfos) { if (std::find(existingMctpInfos.begin(), existingMctpInfos.end(), mctpInfo) == existingMctpInfos.end()) { existingMctpInfos.emplace_back(mctpInfo); } } } void MctpDiscovery::removeFromExistingMctpInfos(MctpInfos& mctpInfos, MctpInfos& removedInfos) { for (const auto& mctpInfo : existingMctpInfos) { if (std::find(mctpInfos.begin(), mctpInfos.end(), mctpInfo) == mctpInfos.end()) { removedInfos.emplace_back(mctpInfo); } } for (const auto& mctpInfo : removedInfos) { info("Removing Endpoint networkId '{NETWORK}' and EID '{EID}'", "NETWORK", std::get<3>(mctpInfo), "EID", std::get<0>(mctpInfo)); existingMctpInfos.erase(std::remove(existingMctpInfos.begin(), existingMctpInfos.end(), mctpInfo), existingMctpInfos.end()); } } void MctpDiscovery::propertiesChangedCb(sdbusplus::message_t& msg) { using Interface = std::string; using Property = std::string; using Value = std::string; using Properties = std::map>; Interface interface; Properties properties; std::string objPath{}; std::string service{}; try { msg.read(interface, properties); objPath = msg.get_path(); } catch (const sdbusplus::exception_t& e) { error( "Error handling Connectivity property changed message, error - {ERROR}", "ERROR", e); return; } for (const auto& [key, valueVariant] : properties) { Value propVal = std::get(valueVariant); auto availability = (propVal == "Available") ? true : false; if (key == MCTPConnectivityProp) { service = pldm::utils::DBusHandler().getService(objPath.c_str(), MCTPInterface); const MctpEndpointProps& epProps = getMctpEndpointProps(service, objPath); auto types = std::get(epProps); if (!std::ranges::contains(types, mctpTypePLDM)) { return; } const UUID& uuid = getEndpointUUIDProp(service, objPath); MctpInfo mctpInfo(std::get(epProps), uuid, "", std::get(epProps), std::nullopt); searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); if (!std::ranges::contains(existingMctpInfos, mctpInfo)) { if (availability) { // The endpoint not in existingMctpInfos and is // available Add it to existingMctpInfos info( "Adding Endpoint networkId {NETWORK} ID {EID} by propertiesChanged signal", "NETWORK", std::get<3>(mctpInfo), "EID", unsigned(std::get<0>(mctpInfo))); addToExistingMctpInfos(MctpInfos(1, mctpInfo)); handleMctpEndpoints(MctpInfos(1, mctpInfo)); } } else { // The endpoint already in existingMctpInfos updateMctpEndpointAvailability(mctpInfo, availability); } } } } void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg) { MctpInfos addedInfos; getAddedMctpInfos(msg, addedInfos); addToExistingMctpInfos(addedInfos); handleMctpEndpoints(addedInfos); } void MctpDiscovery::removeEndpoints(sdbusplus::message_t&) { MctpInfos mctpInfos; MctpInfos removedInfos; std::map currentMctpInfoMap; getMctpInfos(currentMctpInfoMap); for (const auto& mapIt : currentMctpInfoMap) { mctpInfos.push_back(mapIt.first); } removeFromExistingMctpInfos(mctpInfos, removedInfos); handleRemovedMctpEndpoints(removedInfos); removeConfigs(removedInfos); } void MctpDiscovery::handleMctpEndpoints(const MctpInfos& mctpInfos) { for (const auto& handler : handlers) { if (handler) { handler->handleConfigurations(configurations); handler->handleMctpEndpoints(mctpInfos); } } } void MctpDiscovery::handleRemovedMctpEndpoints(const MctpInfos& mctpInfos) { for (const auto& handler : handlers) { if (handler) { handler->handleRemovedMctpEndpoints(mctpInfos); } } } void MctpDiscovery::updateMctpEndpointAvailability(const MctpInfo& mctpInfo, Availability availability) { for (const auto& handler : handlers) { if (handler) { handler->updateMctpEndpointAvailability(mctpInfo, availability); } } } std::string MctpDiscovery::getNameFromProperties( const utils::PropertyMap& properties) { if (!properties.contains("Name")) { error("Missing name property"); return ""; } return std::get(properties.at("Name")); } std::string MctpDiscovery::constructMctpReactorObjectPath( const MctpInfo& mctpInfo) { const auto networkId = std::get(mctpInfo); const auto eid = std::get(mctpInfo); return std::string{MCTPPath} + "/networks/" + std::to_string(networkId) + "/endpoints/" + std::to_string(eid) + "/configured_by"; } void MctpDiscovery::searchConfigurationFor( const pldm::utils::DBusHandler& handler, MctpInfo& mctpInfo) { const auto mctpReactorObjectPath = constructMctpReactorObjectPath(mctpInfo); try { std::string associatedObjPath; std::string associatedService; std::string associatedInterface; sdbusplus::message::object_path inventorySubtreePath( inventorySubtreePathStr); //"/{board or chassis type}/{board or chassis}/{device}" auto constexpr subTreeDepth = 3; auto response = handler.getAssociatedSubTree( mctpReactorObjectPath, inventorySubtreePath, subTreeDepth, interfaceFilter); if (response.empty()) { warning("No associated subtree found for path {PATH}", "PATH", mctpReactorObjectPath); return; } // Assume the first entry is the one we want auto subTree = response.begin(); associatedObjPath = subTree->first; auto associatedServiceProp = subTree->second; if (associatedServiceProp.empty()) { warning("No associated service found for path {PATH}", "PATH", mctpReactorObjectPath); return; } // Assume the first entry is the one we want auto entry = associatedServiceProp.begin(); associatedService = entry->first; auto dBusIntfList = entry->second; auto associatedInterfaceItr = std::find_if( dBusIntfList.begin(), dBusIntfList.end(), [](const auto& intf) { return std::find(interfaceFilter.begin(), interfaceFilter.end(), intf) != interfaceFilter.end(); }); if (associatedInterfaceItr == dBusIntfList.end()) { error("No associated interface found for path {PATH}", "PATH", mctpReactorObjectPath); return; } associatedInterface = *associatedInterfaceItr; auto mctpTargetProperties = handler.getDbusPropertiesVariant( associatedService.c_str(), associatedObjPath.c_str(), associatedInterface.c_str()); auto name = getNameFromProperties(mctpTargetProperties); if (!name.empty()) { std::get>(mctpInfo) = name; } configurations.emplace(associatedObjPath, mctpInfo); } catch (const std::exception& e) { error( "Error getting associated subtree for path {PATH}, error - {ERROR}", "PATH", mctpReactorObjectPath, "ERROR", e); return; } } void MctpDiscovery::removeConfigs(const MctpInfos& removedInfos) { for (const auto& mctpInfo : removedInfos) { const auto eidToRemove = std::get(mctpInfo); const auto netToRemove = std::get(mctpInfo); std::erase_if(configurations, [eidToRemove, netToRemove](const auto& config) { const auto& [__, mctpInfo] = config; const auto eidValue = std::get(mctpInfo); const auto netValue = std::get(mctpInfo); return eidValue == eidToRemove && netValue == netToRemove; }); } } } // namespace pldm