xref: /openbmc/pldm/requester/mctp_endpoint_discovery.cpp (revision 70eca96bebade89aceab0762e3615b816e29fba7)
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>({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 
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(), 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 
getEndpointUUIDProp(const std::string & service,const std::string & path)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 
getEndpointConnectivityProp(const std::string & path)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 
getAddedMctpInfos(sdbusplus::message_t & msg,MctpInfos & mctpInfos)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 
addToExistingMctpInfos(const MctpInfos & addedInfos)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 
removeFromExistingMctpInfos(MctpInfos & mctpInfos,MctpInfos & removedInfos)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 
propertiesChangedCb(sdbusplus::message_t & msg)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 
discoverEndpoints(sdbusplus::message_t & msg)351 void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg)
352 {
353     MctpInfos addedInfos;
354     getAddedMctpInfos(msg, addedInfos);
355     addToExistingMctpInfos(addedInfos);
356     handleMctpEndpoints(addedInfos);
357 }
358 
removeEndpoints(sdbusplus::message_t &)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 
handleMctpEndpoints(const MctpInfos & mctpInfos)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 
handleRemovedMctpEndpoints(const MctpInfos & mctpInfos)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 
updateMctpEndpointAvailability(const MctpInfo & mctpInfo,Availability availability)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 
getNameFromProperties(const utils::PropertyMap & properties)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 
constructMctpReactorObjectPath(const MctpInfo & mctpInfo)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 
searchConfigurationFor(const pldm::utils::DBusHandler & handler,MctpInfo & mctpInfo)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 
removeConfigs(const MctpInfos & removedInfos)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