1 #include "config.h" 2 3 #include "mctp_endpoint_discovery.hpp" 4 5 #include "common/types.hpp" 6 #include "common/utils.hpp" 7 8 #include <linux/mctp.h> 9 10 #include <phosphor-logging/lg2.hpp> 11 12 #include <algorithm> 13 #include <fstream> 14 #include <iostream> 15 #include <map> 16 #include <string> 17 #include <string_view> 18 #include <vector> 19 20 using namespace sdbusplus::bus::match::rules; 21 22 PHOSPHOR_LOG2_USING; 23 24 namespace pldm 25 { 26 MctpDiscovery::MctpDiscovery( 27 sdbusplus::bus_t& bus, 28 std::initializer_list<MctpDiscoveryHandlerIntf*> list) : 29 bus(bus), 30 mctpEndpointAddedSignal( 31 bus, interfacesAdded(MCTPPath), 32 [this](sdbusplus::message_t& msg) { this->discoverEndpoints(msg); }), 33 mctpEndpointRemovedSignal( 34 bus, interfacesRemoved(MCTPPath), 35 [this](sdbusplus::message_t& msg) { this->removeEndpoints(msg); }), 36 mctpEndpointPropChangedSignal( 37 bus, propertiesChangedNamespace(MCTPPath, MCTPInterfaceCC), 38 [this](sdbusplus::message_t& msg) { this->propertiesChangedCb(msg); }), 39 handlers(list) 40 { 41 std::map<MctpInfo, Availability> currentMctpInfoMap; 42 getMctpInfos(currentMctpInfoMap); 43 for (const auto& mapIt : currentMctpInfoMap) 44 { 45 if (mapIt.second) 46 { 47 // Only add the available endpoints to the terminus 48 // Let the propertiesChanged signal tells us when it comes back 49 // to Available again 50 addToExistingMctpInfos(MctpInfos(1, mapIt.first)); 51 } 52 } 53 handleMctpEndpoints(existingMctpInfos); 54 } 55 56 void MctpDiscovery::getMctpInfos(std::map<MctpInfo, Availability>& mctpInfoMap) 57 { 58 // Find all implementations of the MCTP Endpoint interface 59 pldm::utils::GetSubTreeResponse mapperResponse; 60 try 61 { 62 mapperResponse = pldm::utils::DBusHandler().getSubtree( 63 MCTPPath, 0, std::vector<std::string>({MCTPInterface})); 64 } 65 catch (const sdbusplus::exception_t& e) 66 { 67 error( 68 "Failed to getSubtree call at path '{PATH}' and interface '{INTERFACE}', error - {ERROR} ", 69 "ERROR", e, "PATH", MCTPPath, "INTERFACE", MCTPInterface); 70 return; 71 } 72 73 for (const auto& [path, services] : mapperResponse) 74 { 75 for (const auto& serviceIter : services) 76 { 77 const std::string& service = serviceIter.first; 78 const MctpEndpointProps& epProps = 79 getMctpEndpointProps(service, path); 80 const UUID& uuid = getEndpointUUIDProp(service, path); 81 const Availability& availability = 82 getEndpointConnectivityProp(path); 83 auto types = std::get<MCTPMsgTypes>(epProps); 84 if (std::find(types.begin(), types.end(), mctpTypePLDM) != 85 types.end()) 86 { 87 auto mctpInfo = 88 MctpInfo(std::get<eid>(epProps), uuid, "", 89 std::get<NetworkId>(epProps), std::nullopt); 90 searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); 91 mctpInfoMap[std::move(mctpInfo)] = availability; 92 } 93 } 94 } 95 } 96 97 MctpEndpointProps MctpDiscovery::getMctpEndpointProps( 98 const std::string& service, const std::string& path) 99 { 100 try 101 { 102 auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant( 103 service.c_str(), path.c_str(), MCTPInterface); 104 105 if (properties.contains("NetworkId") && properties.contains("EID") && 106 properties.contains("SupportedMessageTypes")) 107 { 108 auto networkId = std::get<NetworkId>(properties.at("NetworkId")); 109 auto eid = std::get<mctp_eid_t>(properties.at("EID")); 110 auto types = std::get<std::vector<uint8_t>>( 111 properties.at("SupportedMessageTypes")); 112 return MctpEndpointProps(networkId, eid, types); 113 } 114 } 115 catch (const sdbusplus::exception_t& e) 116 { 117 error( 118 "Error reading MCTP Endpoint property at path '{PATH}' and service '{SERVICE}', error - {ERROR}", 119 "SERVICE", service, "PATH", path, "ERROR", e); 120 return MctpEndpointProps(0, MCTP_ADDR_ANY, {}); 121 } 122 123 return MctpEndpointProps(0, MCTP_ADDR_ANY, {}); 124 } 125 126 UUID MctpDiscovery::getEndpointUUIDProp(const std::string& service, 127 const std::string& path) 128 { 129 try 130 { 131 auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant( 132 service.c_str(), path.c_str(), EndpointUUID); 133 134 if (properties.contains("UUID")) 135 { 136 return std::get<UUID>(properties.at("UUID")); 137 } 138 } 139 catch (const sdbusplus::exception_t& e) 140 { 141 error( 142 "Error reading Endpoint UUID property at path '{PATH}' and service '{SERVICE}', error - {ERROR}", 143 "SERVICE", service, "PATH", path, "ERROR", e); 144 return static_cast<UUID>(emptyUUID); 145 } 146 147 return static_cast<UUID>(emptyUUID); 148 } 149 150 Availability MctpDiscovery::getEndpointConnectivityProp(const std::string& path) 151 { 152 Availability available = false; 153 try 154 { 155 pldm::utils::PropertyValue propertyValue = 156 pldm::utils::DBusHandler().getDbusPropertyVariant( 157 path.c_str(), MCTPConnectivityProp, MCTPInterfaceCC); 158 if (std::get<std::string>(propertyValue) == "Available") 159 { 160 available = true; 161 } 162 } 163 catch (const sdbusplus::exception_t& e) 164 { 165 error( 166 "Error reading Endpoint Connectivity property at path '{PATH}', error - {ERROR}", 167 "PATH", path, "ERROR", e); 168 } 169 170 return available; 171 } 172 173 void MctpDiscovery::getAddedMctpInfos(sdbusplus::message_t& msg, 174 MctpInfos& mctpInfos) 175 { 176 using ObjectPath = sdbusplus::message::object_path; 177 ObjectPath objPath; 178 using Property = std::string; 179 using PropertyMap = std::map<Property, dbus::Value>; 180 std::map<std::string, PropertyMap> interfaces; 181 std::string uuid = emptyUUID; 182 183 try 184 { 185 msg.read(objPath, interfaces); 186 } 187 catch (const sdbusplus::exception_t& e) 188 { 189 error( 190 "Error reading MCTP Endpoint added interface message, error - {ERROR}", 191 "ERROR", e); 192 return; 193 } 194 const Availability& availability = getEndpointConnectivityProp(objPath.str); 195 196 /* Get UUID */ 197 try 198 { 199 auto service = pldm::utils::DBusHandler().getService( 200 objPath.str.c_str(), EndpointUUID); 201 uuid = getEndpointUUIDProp(service, objPath.str); 202 } 203 catch (const sdbusplus::exception_t& e) 204 { 205 error("Error getting Endpoint UUID D-Bus interface, error - {ERROR}", 206 "ERROR", e); 207 } 208 209 for (const auto& [intfName, properties] : interfaces) 210 { 211 if (intfName == MCTPInterface) 212 { 213 if (properties.contains("NetworkId") && 214 properties.contains("EID") && 215 properties.contains("SupportedMessageTypes")) 216 { 217 auto networkId = 218 std::get<NetworkId>(properties.at("NetworkId")); 219 auto eid = std::get<mctp_eid_t>(properties.at("EID")); 220 auto types = std::get<std::vector<uint8_t>>( 221 properties.at("SupportedMessageTypes")); 222 223 if (!availability) 224 { 225 // Log an error message here, but still add it to the 226 // terminus 227 error( 228 "mctpd added a DEGRADED endpoint {EID} networkId {NET} to D-Bus", 229 "NET", networkId, "EID", static_cast<unsigned>(eid)); 230 } 231 if (std::find(types.begin(), types.end(), mctpTypePLDM) != 232 types.end()) 233 { 234 info( 235 "Adding Endpoint networkId '{NETWORK}' and EID '{EID}' UUID '{UUID}'", 236 "NETWORK", networkId, "EID", eid, "UUID", uuid); 237 auto mctpInfo = 238 MctpInfo(eid, uuid, "", networkId, std::nullopt); 239 searchConfigurationFor(pldm::utils::DBusHandler(), 240 mctpInfo); 241 mctpInfos.emplace_back(std::move(mctpInfo)); 242 } 243 } 244 } 245 } 246 } 247 248 void MctpDiscovery::addToExistingMctpInfos(const MctpInfos& addedInfos) 249 { 250 for (const auto& mctpInfo : addedInfos) 251 { 252 if (std::find(existingMctpInfos.begin(), existingMctpInfos.end(), 253 mctpInfo) == existingMctpInfos.end()) 254 { 255 existingMctpInfos.emplace_back(mctpInfo); 256 } 257 } 258 } 259 260 void MctpDiscovery::removeFromExistingMctpInfos(MctpInfos& mctpInfos, 261 MctpInfos& removedInfos) 262 { 263 for (const auto& mctpInfo : existingMctpInfos) 264 { 265 if (std::find(mctpInfos.begin(), mctpInfos.end(), mctpInfo) == 266 mctpInfos.end()) 267 { 268 removedInfos.emplace_back(mctpInfo); 269 } 270 } 271 for (const auto& mctpInfo : removedInfos) 272 { 273 info("Removing Endpoint networkId '{NETWORK}' and EID '{EID}'", 274 "NETWORK", std::get<3>(mctpInfo), "EID", std::get<0>(mctpInfo)); 275 existingMctpInfos.erase(std::remove(existingMctpInfos.begin(), 276 existingMctpInfos.end(), mctpInfo), 277 existingMctpInfos.end()); 278 } 279 } 280 281 void MctpDiscovery::propertiesChangedCb(sdbusplus::message_t& msg) 282 { 283 using Interface = std::string; 284 using Property = std::string; 285 using Value = std::string; 286 using Properties = std::map<Property, std::variant<Value>>; 287 288 Interface interface; 289 Properties properties; 290 std::string objPath{}; 291 std::string service{}; 292 293 try 294 { 295 msg.read(interface, properties); 296 objPath = msg.get_path(); 297 } 298 catch (const sdbusplus::exception_t& e) 299 { 300 error( 301 "Error handling Connectivity property changed message, error - {ERROR}", 302 "ERROR", e); 303 return; 304 } 305 306 for (const auto& [key, valueVariant] : properties) 307 { 308 Value propVal = std::get<std::string>(valueVariant); 309 auto availability = (propVal == "Available") ? true : false; 310 311 if (key == MCTPConnectivityProp) 312 { 313 service = pldm::utils::DBusHandler().getService(objPath.c_str(), 314 MCTPInterface); 315 const MctpEndpointProps& epProps = 316 getMctpEndpointProps(service, objPath); 317 318 auto types = std::get<MCTPMsgTypes>(epProps); 319 if (!std::ranges::contains(types, mctpTypePLDM)) 320 { 321 return; 322 } 323 const UUID& uuid = getEndpointUUIDProp(service, objPath); 324 325 MctpInfo mctpInfo(std::get<eid>(epProps), uuid, "", 326 std::get<NetworkId>(epProps), std::nullopt); 327 searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); 328 if (!std::ranges::contains(existingMctpInfos, mctpInfo)) 329 { 330 if (availability) 331 { 332 // The endpoint not in existingMctpInfos and is 333 // available Add it to existingMctpInfos 334 info( 335 "Adding Endpoint networkId {NETWORK} ID {EID} by propertiesChanged signal", 336 "NETWORK", std::get<3>(mctpInfo), "EID", 337 unsigned(std::get<0>(mctpInfo))); 338 addToExistingMctpInfos(MctpInfos(1, mctpInfo)); 339 handleMctpEndpoints(MctpInfos(1, mctpInfo)); 340 } 341 } 342 else 343 { 344 // The endpoint already in existingMctpInfos 345 updateMctpEndpointAvailability(mctpInfo, availability); 346 } 347 } 348 } 349 } 350 351 void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg) 352 { 353 MctpInfos addedInfos; 354 getAddedMctpInfos(msg, addedInfos); 355 addToExistingMctpInfos(addedInfos); 356 handleMctpEndpoints(addedInfos); 357 } 358 359 void MctpDiscovery::removeEndpoints(sdbusplus::message_t&) 360 { 361 MctpInfos mctpInfos; 362 MctpInfos removedInfos; 363 std::map<MctpInfo, Availability> currentMctpInfoMap; 364 getMctpInfos(currentMctpInfoMap); 365 for (const auto& mapIt : currentMctpInfoMap) 366 { 367 mctpInfos.push_back(mapIt.first); 368 } 369 removeFromExistingMctpInfos(mctpInfos, removedInfos); 370 handleRemovedMctpEndpoints(removedInfos); 371 removeConfigs(removedInfos); 372 } 373 374 void MctpDiscovery::handleMctpEndpoints(const MctpInfos& mctpInfos) 375 { 376 for (const auto& handler : handlers) 377 { 378 if (handler) 379 { 380 handler->handleConfigurations(configurations); 381 handler->handleMctpEndpoints(mctpInfos); 382 } 383 } 384 } 385 386 void MctpDiscovery::handleRemovedMctpEndpoints(const MctpInfos& mctpInfos) 387 { 388 for (const auto& handler : handlers) 389 { 390 if (handler) 391 { 392 handler->handleRemovedMctpEndpoints(mctpInfos); 393 } 394 } 395 } 396 397 void MctpDiscovery::updateMctpEndpointAvailability(const MctpInfo& mctpInfo, 398 Availability availability) 399 { 400 for (const auto& handler : handlers) 401 { 402 if (handler) 403 { 404 handler->updateMctpEndpointAvailability(mctpInfo, availability); 405 } 406 } 407 } 408 409 std::string MctpDiscovery::getNameFromProperties( 410 const utils::PropertyMap& properties) 411 { 412 if (!properties.contains("Name")) 413 { 414 error("Missing name property"); 415 return ""; 416 } 417 return std::get<std::string>(properties.at("Name")); 418 } 419 420 std::string MctpDiscovery::constructMctpReactorObjectPath( 421 const MctpInfo& mctpInfo) 422 { 423 const auto networkId = std::get<NetworkId>(mctpInfo); 424 const auto eid = std::get<pldm::eid>(mctpInfo); 425 return std::string{MCTPPath} + "/networks/" + std::to_string(networkId) + 426 "/endpoints/" + std::to_string(eid) + "/configured_by"; 427 } 428 429 void MctpDiscovery::searchConfigurationFor( 430 const pldm::utils::DBusHandler& handler, MctpInfo& mctpInfo) 431 { 432 const auto mctpReactorObjectPath = constructMctpReactorObjectPath(mctpInfo); 433 try 434 { 435 std::string associatedObjPath; 436 std::string associatedService; 437 std::string associatedInterface; 438 sdbusplus::message::object_path inventorySubtreePath( 439 inventorySubtreePathStr); 440 441 //"/{board or chassis type}/{board or chassis}/{device}" 442 auto constexpr subTreeDepth = 3; 443 auto response = handler.getAssociatedSubTree( 444 mctpReactorObjectPath, inventorySubtreePath, subTreeDepth, 445 interfaceFilter); 446 if (response.empty()) 447 { 448 warning("No associated subtree found for path {PATH}", "PATH", 449 mctpReactorObjectPath); 450 return; 451 } 452 // Assume the first entry is the one we want 453 auto subTree = response.begin(); 454 associatedObjPath = subTree->first; 455 auto associatedServiceProp = subTree->second; 456 if (associatedServiceProp.empty()) 457 { 458 warning("No associated service found for path {PATH}", "PATH", 459 mctpReactorObjectPath); 460 return; 461 } 462 // Assume the first entry is the one we want 463 auto entry = associatedServiceProp.begin(); 464 associatedService = entry->first; 465 auto dBusIntfList = entry->second; 466 auto associatedInterfaceItr = std::find_if( 467 dBusIntfList.begin(), dBusIntfList.end(), [](const auto& intf) { 468 return std::find(interfaceFilter.begin(), interfaceFilter.end(), 469 intf) != interfaceFilter.end(); 470 }); 471 if (associatedInterfaceItr == dBusIntfList.end()) 472 { 473 error("No associated interface found for path {PATH}", "PATH", 474 mctpReactorObjectPath); 475 return; 476 } 477 associatedInterface = *associatedInterfaceItr; 478 auto mctpTargetProperties = handler.getDbusPropertiesVariant( 479 associatedService.c_str(), associatedObjPath.c_str(), 480 associatedInterface.c_str()); 481 auto name = getNameFromProperties(mctpTargetProperties); 482 if (!name.empty()) 483 { 484 std::get<std::optional<std::string>>(mctpInfo) = name; 485 } 486 configurations.emplace(associatedObjPath, mctpInfo); 487 } 488 catch (const std::exception& e) 489 { 490 error( 491 "Error getting associated subtree for path {PATH}, error - {ERROR}", 492 "PATH", mctpReactorObjectPath, "ERROR", e); 493 return; 494 } 495 } 496 497 void MctpDiscovery::removeConfigs(const MctpInfos& removedInfos) 498 { 499 for (const auto& mctpInfo : removedInfos) 500 { 501 auto eidToRemove = std::get<eid>(mctpInfo); 502 std::erase_if(configurations, [eidToRemove](const auto& config) { 503 auto& [__, mctpInfo] = config; 504 auto eidValue = std::get<eid>(mctpInfo); 505 return eidValue == eidToRemove; 506 }); 507 } 508 } 509 510 } // namespace pldm 511