xref: /openbmc/pldm/requester/mctp_endpoint_discovery.cpp (revision b249a645fdcd5d9feb0197d2a2fbe3187c369e31)
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 {
MctpDiscovery(sdbusplus::bus_t & bus,std::initializer_list<MctpDiscoveryHandlerIntf * > list)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),
__anon124d2f790202(sdbusplus::message_t& msg) 35         [this](sdbusplus::message_t& msg) { this->removeEndpoints(msg); }),
36     mctpEndpointPropChangedSignal(
37         bus, propertiesChangedNamespace(MCTPPath, MCTPInterfaceCC),
__anon124d2f790302(sdbusplus::message_t& msg) 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 
getMctpInfos(std::map<MctpInfo,Availability> & mctpInfoMap)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>({MCTPEndpoint::interface}));
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", MCTPEndpoint::interface);
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 
getMctpEndpointProps(const std::string & service,const std::string & path)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(), MCTPEndpoint::interface);
104 
105         if (properties.contains(MCTPEndpoint::property_names::network_id) &&
106             properties.contains(MCTPEndpoint::property_names::eid) &&
107             properties.contains(
108                 MCTPEndpoint::property_names::supported_message_types))
109         {
110             auto networkId = std::get<NetworkId>(
111                 properties.at(MCTPEndpoint::property_names::network_id));
112             auto eid = std::get<mctp_eid_t>(
113                 properties.at(MCTPEndpoint::property_names::eid));
114             auto types = std::get<std::vector<uint8_t>>(properties.at(
115                 MCTPEndpoint::property_names::supported_message_types));
116             return MctpEndpointProps(networkId, eid, types);
117         }
118     }
119     catch (const sdbusplus::exception_t& e)
120     {
121         error(
122             "Error reading MCTP Endpoint property at path '{PATH}' and service '{SERVICE}', error - {ERROR}",
123             "SERVICE", service, "PATH", path, "ERROR", e);
124         return MctpEndpointProps(0, MCTP_ADDR_ANY, {});
125     }
126 
127     return MctpEndpointProps(0, MCTP_ADDR_ANY, {});
128 }
129 
getEndpointUUIDProp(const std::string & service,const std::string & path)130 UUID MctpDiscovery::getEndpointUUIDProp(const std::string& service,
131                                         const std::string& path)
132 {
133     try
134     {
135         auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant(
136             service.c_str(), path.c_str(), CommonUUID::interface);
137 
138         if (properties.contains("UUID"))
139         {
140             return std::get<UUID>(properties.at("UUID"));
141         }
142     }
143     catch (const sdbusplus::exception_t& e)
144     {
145         error(
146             "Error reading Endpoint UUID property at path '{PATH}' and service '{SERVICE}', error - {ERROR}",
147             "SERVICE", service, "PATH", path, "ERROR", e);
148         return static_cast<UUID>(emptyUUID);
149     }
150 
151     return static_cast<UUID>(emptyUUID);
152 }
153 
getEndpointConnectivityProp(const std::string & path)154 Availability MctpDiscovery::getEndpointConnectivityProp(const std::string& path)
155 {
156     Availability available = false;
157     try
158     {
159         pldm::utils::PropertyValue propertyValue =
160             pldm::utils::DBusHandler().getDbusPropertyVariant(
161                 path.c_str(), MCTPConnectivityProp, MCTPInterfaceCC);
162         if (std::get<std::string>(propertyValue) == "Available")
163         {
164             available = true;
165         }
166     }
167     catch (const sdbusplus::exception_t& e)
168     {
169         error(
170             "Error reading Endpoint Connectivity property at path '{PATH}', error - {ERROR}",
171             "PATH", path, "ERROR", e);
172     }
173 
174     return available;
175 }
176 
getAddedMctpInfos(sdbusplus::message_t & msg,MctpInfos & mctpInfos)177 void MctpDiscovery::getAddedMctpInfos(sdbusplus::message_t& msg,
178                                       MctpInfos& mctpInfos)
179 {
180     using ObjectPath = sdbusplus::message::object_path;
181     ObjectPath objPath;
182     using Property = std::string;
183     using PropertyMap = std::map<Property, dbus::Value>;
184     std::map<std::string, PropertyMap> interfaces;
185     std::string uuid = emptyUUID;
186 
187     try
188     {
189         msg.read(objPath, interfaces);
190     }
191     catch (const sdbusplus::exception_t& e)
192     {
193         error(
194             "Error reading MCTP Endpoint added interface message, error - {ERROR}",
195             "ERROR", e);
196         return;
197     }
198     const Availability& availability = getEndpointConnectivityProp(objPath.str);
199 
200     /* Get UUID */
201     try
202     {
203         auto service = pldm::utils::DBusHandler().getService(
204             objPath.str.c_str(), CommonUUID::interface);
205         uuid = getEndpointUUIDProp(service, objPath.str);
206     }
207     catch (const sdbusplus::exception_t& e)
208     {
209         error("Error getting Endpoint UUID D-Bus interface, error - {ERROR}",
210               "ERROR", e);
211     }
212 
213     for (const auto& [intfName, properties] : interfaces)
214     {
215         if (intfName == MCTPEndpoint::interface)
216         {
217             if (properties.contains(MCTPEndpoint::property_names::network_id) &&
218                 properties.contains(MCTPEndpoint::property_names::eid) &&
219                 properties.contains(
220                     MCTPEndpoint::property_names::supported_message_types))
221             {
222                 auto networkId = std::get<NetworkId>(
223                     properties.at(MCTPEndpoint::property_names::network_id));
224                 auto eid = std::get<mctp_eid_t>(
225                     properties.at(MCTPEndpoint::property_names::eid));
226                 auto types = std::get<std::vector<uint8_t>>(properties.at(
227                     MCTPEndpoint::property_names::supported_message_types));
228 
229                 if (!availability)
230                 {
231                     // Log an error message here, but still add it to the
232                     // terminus
233                     error(
234                         "mctpd added a DEGRADED endpoint {EID} networkId {NET} to D-Bus",
235                         "NET", networkId, "EID", static_cast<unsigned>(eid));
236                 }
237                 if (std::find(types.begin(), types.end(), mctpTypePLDM) !=
238                     types.end())
239                 {
240                     info(
241                         "Adding Endpoint networkId '{NETWORK}' and EID '{EID}' UUID '{UUID}'",
242                         "NETWORK", networkId, "EID", eid, "UUID", uuid);
243                     auto mctpInfo =
244                         MctpInfo(eid, uuid, "", networkId, std::nullopt);
245                     searchConfigurationFor(pldm::utils::DBusHandler(),
246                                            mctpInfo);
247                     mctpInfos.emplace_back(std::move(mctpInfo));
248                 }
249             }
250         }
251     }
252 }
253 
addToExistingMctpInfos(const MctpInfos & addedInfos)254 void MctpDiscovery::addToExistingMctpInfos(const MctpInfos& addedInfos)
255 {
256     for (const auto& mctpInfo : addedInfos)
257     {
258         if (std::find(existingMctpInfos.begin(), existingMctpInfos.end(),
259                       mctpInfo) == existingMctpInfos.end())
260         {
261             existingMctpInfos.emplace_back(mctpInfo);
262         }
263     }
264 }
265 
removeFromExistingMctpInfos(MctpInfos & mctpInfos,MctpInfos & removedInfos)266 void MctpDiscovery::removeFromExistingMctpInfos(MctpInfos& mctpInfos,
267                                                 MctpInfos& removedInfos)
268 {
269     for (const auto& mctpInfo : existingMctpInfos)
270     {
271         if (std::find(mctpInfos.begin(), mctpInfos.end(), mctpInfo) ==
272             mctpInfos.end())
273         {
274             removedInfos.emplace_back(mctpInfo);
275         }
276     }
277     for (const auto& mctpInfo : removedInfos)
278     {
279         info("Removing Endpoint networkId '{NETWORK}' and  EID '{EID}'",
280              "NETWORK", std::get<3>(mctpInfo), "EID", std::get<0>(mctpInfo));
281         existingMctpInfos.erase(std::remove(existingMctpInfos.begin(),
282                                             existingMctpInfos.end(), mctpInfo),
283                                 existingMctpInfos.end());
284     }
285 }
286 
propertiesChangedCb(sdbusplus::message_t & msg)287 void MctpDiscovery::propertiesChangedCb(sdbusplus::message_t& msg)
288 {
289     using Interface = std::string;
290     using Property = std::string;
291     using Value = std::string;
292     using Properties = std::map<Property, std::variant<Value>>;
293 
294     Interface interface;
295     Properties properties;
296     std::string objPath{};
297     std::string service{};
298 
299     try
300     {
301         msg.read(interface, properties);
302         objPath = msg.get_path();
303     }
304     catch (const sdbusplus::exception_t& e)
305     {
306         error(
307             "Error handling Connectivity property changed message, error - {ERROR}",
308             "ERROR", e);
309         return;
310     }
311 
312     for (const auto& [key, valueVariant] : properties)
313     {
314         Value propVal = std::get<std::string>(valueVariant);
315         auto availability = (propVal == "Available") ? true : false;
316 
317         if (key == MCTPConnectivityProp)
318         {
319             try
320             {
321                 service = pldm::utils::DBusHandler().getService(
322                     objPath.c_str(), MCTPEndpoint::interface);
323             }
324             catch (const sdbusplus::exception_t& e)
325             {
326                 error(
327                     "Error getting service for path '{PATH}', error - {ERROR}",
328                     "PATH", objPath, "ERROR", e);
329                 return;
330             }
331             const MctpEndpointProps& epProps =
332                 getMctpEndpointProps(service, objPath);
333 
334             auto types = std::get<MCTPMsgTypes>(epProps);
335             if (!std::ranges::contains(types, mctpTypePLDM))
336             {
337                 return;
338             }
339             const UUID& uuid = getEndpointUUIDProp(service, objPath);
340 
341             MctpInfo mctpInfo(std::get<eid>(epProps), uuid, "",
342                               std::get<NetworkId>(epProps), std::nullopt);
343             searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo);
344             if (!std::ranges::contains(existingMctpInfos, mctpInfo))
345             {
346                 if (availability)
347                 {
348                     // The endpoint not in existingMctpInfos and is
349                     // available Add it to existingMctpInfos
350                     info(
351                         "Adding Endpoint networkId {NETWORK} ID {EID} by propertiesChanged signal",
352                         "NETWORK", std::get<3>(mctpInfo), "EID",
353                         unsigned(std::get<0>(mctpInfo)));
354                     addToExistingMctpInfos(MctpInfos(1, mctpInfo));
355                     handleMctpEndpoints(MctpInfos(1, mctpInfo));
356                 }
357             }
358             else
359             {
360                 // The endpoint already in existingMctpInfos
361                 updateMctpEndpointAvailability(mctpInfo, availability);
362             }
363         }
364     }
365 }
366 
discoverEndpoints(sdbusplus::message_t & msg)367 void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg)
368 {
369     MctpInfos addedInfos;
370     getAddedMctpInfos(msg, addedInfos);
371     addToExistingMctpInfos(addedInfos);
372     handleMctpEndpoints(addedInfos);
373 }
374 
removeEndpoints(sdbusplus::message_t &)375 void MctpDiscovery::removeEndpoints(sdbusplus::message_t&)
376 {
377     MctpInfos mctpInfos;
378     MctpInfos removedInfos;
379     std::map<MctpInfo, Availability> currentMctpInfoMap;
380     getMctpInfos(currentMctpInfoMap);
381     for (const auto& mapIt : currentMctpInfoMap)
382     {
383         mctpInfos.push_back(mapIt.first);
384     }
385     removeFromExistingMctpInfos(mctpInfos, removedInfos);
386     handleRemovedMctpEndpoints(removedInfos);
387     removeConfigs(removedInfos);
388 }
389 
handleMctpEndpoints(const MctpInfos & mctpInfos)390 void MctpDiscovery::handleMctpEndpoints(const MctpInfos& mctpInfos)
391 {
392     for (const auto& handler : handlers)
393     {
394         if (handler)
395         {
396             handler->handleConfigurations(configurations);
397             handler->handleMctpEndpoints(mctpInfos);
398         }
399     }
400 }
401 
handleRemovedMctpEndpoints(const MctpInfos & mctpInfos)402 void MctpDiscovery::handleRemovedMctpEndpoints(const MctpInfos& mctpInfos)
403 {
404     for (const auto& handler : handlers)
405     {
406         if (handler)
407         {
408             handler->handleRemovedMctpEndpoints(mctpInfos);
409         }
410     }
411 }
412 
updateMctpEndpointAvailability(const MctpInfo & mctpInfo,Availability availability)413 void MctpDiscovery::updateMctpEndpointAvailability(const MctpInfo& mctpInfo,
414                                                    Availability availability)
415 {
416     for (const auto& handler : handlers)
417     {
418         if (handler)
419         {
420             handler->updateMctpEndpointAvailability(mctpInfo, availability);
421         }
422     }
423 }
424 
getNameFromProperties(const utils::PropertyMap & properties)425 std::string MctpDiscovery::getNameFromProperties(
426     const utils::PropertyMap& properties)
427 {
428     if (!properties.contains("Name"))
429     {
430         error("Missing name property");
431         return "";
432     }
433     return std::get<std::string>(properties.at("Name"));
434 }
435 
constructMctpReactorObjectPath(const MctpInfo & mctpInfo)436 std::string MctpDiscovery::constructMctpReactorObjectPath(
437     const MctpInfo& mctpInfo)
438 {
439     const auto networkId = std::get<NetworkId>(mctpInfo);
440     const auto eid = std::get<pldm::eid>(mctpInfo);
441     return std::string{MCTPPath} + "/networks/" + std::to_string(networkId) +
442            "/endpoints/" + std::to_string(eid) + "/configured_by";
443 }
444 
searchConfigurationFor(const pldm::utils::DBusHandler & handler,MctpInfo & mctpInfo)445 void MctpDiscovery::searchConfigurationFor(
446     const pldm::utils::DBusHandler& handler, MctpInfo& mctpInfo)
447 {
448     const auto mctpReactorObjectPath = constructMctpReactorObjectPath(mctpInfo);
449     try
450     {
451         std::string associatedObjPath;
452         std::string associatedService;
453         std::string associatedInterface;
454         sdbusplus::message::object_path inventorySubtreePath(
455             inventorySubtreePathStr);
456 
457         //"/{board or chassis type}/{board or chassis}/{device}"
458         auto constexpr subTreeDepth = 3;
459         auto response = handler.getAssociatedSubTree(
460             mctpReactorObjectPath, inventorySubtreePath, subTreeDepth,
461             interfaceFilter);
462         if (response.empty())
463         {
464             warning("No associated subtree found for path {PATH}", "PATH",
465                     mctpReactorObjectPath);
466             return;
467         }
468         // Assume the first entry is the one we want
469         auto subTree = response.begin();
470         associatedObjPath = subTree->first;
471         auto associatedServiceProp = subTree->second;
472         if (associatedServiceProp.empty())
473         {
474             warning("No associated service found for path {PATH}", "PATH",
475                     mctpReactorObjectPath);
476             return;
477         }
478         // Assume the first entry is the one we want
479         auto entry = associatedServiceProp.begin();
480         associatedService = entry->first;
481         auto dBusIntfList = entry->second;
482         auto associatedInterfaceItr = std::find_if(
483             dBusIntfList.begin(), dBusIntfList.end(), [](const auto& intf) {
484                 return std::find(interfaceFilter.begin(), interfaceFilter.end(),
485                                  intf) != interfaceFilter.end();
486             });
487         if (associatedInterfaceItr == dBusIntfList.end())
488         {
489             error("No associated interface found for path {PATH}", "PATH",
490                   mctpReactorObjectPath);
491             return;
492         }
493         associatedInterface = *associatedInterfaceItr;
494         auto mctpTargetProperties = handler.getDbusPropertiesVariant(
495             associatedService.c_str(), associatedObjPath.c_str(),
496             associatedInterface.c_str());
497         auto name = getNameFromProperties(mctpTargetProperties);
498         if (!name.empty())
499         {
500             std::get<std::optional<std::string>>(mctpInfo) = name;
501         }
502         configurations.emplace(associatedObjPath, mctpInfo);
503     }
504     catch (const std::exception& e)
505     {
506         error(
507             "Error getting associated subtree for path {PATH}, error - {ERROR}",
508             "PATH", mctpReactorObjectPath, "ERROR", e);
509         return;
510     }
511 }
512 
removeConfigs(const MctpInfos & removedInfos)513 void MctpDiscovery::removeConfigs(const MctpInfos& removedInfos)
514 {
515     for (const auto& mctpInfo : removedInfos)
516     {
517         const auto eidToRemove = std::get<eid>(mctpInfo);
518         const auto netToRemove = std::get<NetworkId>(mctpInfo);
519 
520         std::erase_if(configurations, [eidToRemove,
521                                        netToRemove](const auto& config) {
522             const auto& [__, mctpInfo] = config;
523             const auto eidValue = std::get<eid>(mctpInfo);
524             const auto netValue = std::get<NetworkId>(mctpInfo);
525 
526             return eidValue == eidToRemove && netValue == netToRemove;
527         });
528     }
529 }
530 
531 } // namespace pldm
532