xref: /openbmc/pldm/requester/mctp_endpoint_discovery.cpp (revision 75e00422df3f9a4c9341ca8085706b6eaa6605b7)
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), mctpEndpointAddedSignal(
30                   bus, interfacesAdded(MCTPPath),
31                   std::bind_front(&MctpDiscovery::discoverEndpoints, this)),
32     mctpEndpointRemovedSignal(
33         bus, interfacesRemoved(MCTPPath),
34         std::bind_front(&MctpDiscovery::removeEndpoints, this)),
35     mctpEndpointPropChangedSignal(
36         bus, propertiesChangedNamespace(MCTPPath, MCTPInterfaceCC),
37         std::bind_front(&MctpDiscovery::propertiesChangedCb, this)),
38     handlers(list)
39 {
40     std::map<MctpInfo, Availability> currentMctpInfoMap;
41     getMctpInfos(currentMctpInfoMap);
42     for (const auto& mapIt : currentMctpInfoMap)
43     {
44         if (mapIt.second)
45         {
46             // Only add the available endpoints to the terminus
47             // Let the propertiesChanged signal tells us when it comes back
48             // to Available again
49             addToExistingMctpInfos(MctpInfos(1, mapIt.first));
50         }
51     }
52     handleMctpEndpoints(existingMctpInfos);
53 }
54 
getMctpInfos(std::map<MctpInfo,Availability> & mctpInfoMap)55 void MctpDiscovery::getMctpInfos(std::map<MctpInfo, Availability>& mctpInfoMap)
56 {
57     // Find all implementations of the MCTP Endpoint interface
58     pldm::utils::GetSubTreeResponse mapperResponse;
59     try
60     {
61         mapperResponse = pldm::utils::DBusHandler().getSubtree(
62             MCTPPath, 0, std::vector<std::string>({MCTPInterface}));
63     }
64     catch (const sdbusplus::exception_t& e)
65     {
66         error(
67             "Failed to getSubtree call at path '{PATH}' and interface '{INTERFACE}', error - {ERROR} ",
68             "ERROR", e, "PATH", MCTPPath, "INTERFACE", MCTPInterface);
69         return;
70     }
71 
72     for (const auto& [path, services] : mapperResponse)
73     {
74         for (const auto& serviceIter : services)
75         {
76             const std::string& service = serviceIter.first;
77             const MctpEndpointProps& epProps =
78                 getMctpEndpointProps(service, path);
79             const UUID& uuid = getEndpointUUIDProp(service, path);
80             const Availability& availability =
81                 getEndpointConnectivityProp(path);
82             auto types = std::get<MCTPMsgTypes>(epProps);
83             if (std::find(types.begin(), types.end(), mctpTypePLDM) !=
84                 types.end())
85             {
86                 mctpInfoMap[MctpInfo(std::get<eid>(epProps), uuid, "",
87                                      std::get<NetworkId>(epProps))] =
88                     availability;
89             }
90         }
91     }
92 }
93 
getMctpEndpointProps(const std::string & service,const std::string & path)94 MctpEndpointProps MctpDiscovery::getMctpEndpointProps(
95     const std::string& service, const std::string& path)
96 {
97     try
98     {
99         auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant(
100             service.c_str(), path.c_str(), MCTPInterface);
101 
102         if (properties.contains("NetworkId") && properties.contains("EID") &&
103             properties.contains("SupportedMessageTypes"))
104         {
105             auto networkId = std::get<NetworkId>(properties.at("NetworkId"));
106             auto eid = std::get<mctp_eid_t>(properties.at("EID"));
107             auto types = std::get<std::vector<uint8_t>>(
108                 properties.at("SupportedMessageTypes"));
109             return MctpEndpointProps(networkId, eid, types);
110         }
111     }
112     catch (const sdbusplus::exception_t& e)
113     {
114         error(
115             "Error reading MCTP Endpoint property at path '{PATH}' and service '{SERVICE}', error - {ERROR}",
116             "SERVICE", service, "PATH", path, "ERROR", e);
117         return MctpEndpointProps(0, MCTP_ADDR_ANY, {});
118     }
119 
120     return MctpEndpointProps(0, MCTP_ADDR_ANY, {});
121 }
122 
getEndpointUUIDProp(const std::string & service,const std::string & path)123 UUID MctpDiscovery::getEndpointUUIDProp(const std::string& service,
124                                         const std::string& path)
125 {
126     try
127     {
128         auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant(
129             service.c_str(), path.c_str(), EndpointUUID);
130 
131         if (properties.contains("UUID"))
132         {
133             return std::get<UUID>(properties.at("UUID"));
134         }
135     }
136     catch (const sdbusplus::exception_t& e)
137     {
138         error(
139             "Error reading Endpoint UUID property at path '{PATH}' and service '{SERVICE}', error - {ERROR}",
140             "SERVICE", service, "PATH", path, "ERROR", e);
141         return static_cast<UUID>(emptyUUID);
142     }
143 
144     return static_cast<UUID>(emptyUUID);
145 }
146 
getEndpointConnectivityProp(const std::string & path)147 Availability MctpDiscovery::getEndpointConnectivityProp(const std::string& path)
148 {
149     Availability available = false;
150     try
151     {
152         pldm::utils::PropertyValue propertyValue =
153             pldm::utils::DBusHandler().getDbusPropertyVariant(
154                 path.c_str(), MCTPConnectivityProp, MCTPInterfaceCC);
155         if (std::get<std::string>(propertyValue) == "Available")
156         {
157             available = true;
158         }
159     }
160     catch (const sdbusplus::exception_t& e)
161     {
162         error(
163             "Error reading Endpoint Connectivity property at path '{PATH}', error - {ERROR}",
164             "PATH", path, "ERROR", e);
165     }
166 
167     return available;
168 }
169 
getAddedMctpInfos(sdbusplus::message_t & msg,MctpInfos & mctpInfos)170 void MctpDiscovery::getAddedMctpInfos(sdbusplus::message_t& msg,
171                                       MctpInfos& mctpInfos)
172 {
173     using ObjectPath = sdbusplus::message::object_path;
174     ObjectPath objPath;
175     using Property = std::string;
176     using PropertyMap = std::map<Property, dbus::Value>;
177     std::map<std::string, PropertyMap> interfaces;
178     std::string uuid = emptyUUID;
179 
180     try
181     {
182         msg.read(objPath, interfaces);
183     }
184     catch (const sdbusplus::exception_t& e)
185     {
186         error(
187             "Error reading MCTP Endpoint added interface message, error - {ERROR}",
188             "ERROR", e);
189         return;
190     }
191     const Availability& availability = getEndpointConnectivityProp(objPath.str);
192 
193     /* Get UUID */
194     try
195     {
196         auto service = pldm::utils::DBusHandler().getService(
197             objPath.str.c_str(), EndpointUUID);
198         uuid = getEndpointUUIDProp(service, objPath.str);
199     }
200     catch (const sdbusplus::exception_t& e)
201     {
202         error("Error getting Endpoint UUID D-Bus interface, error - {ERROR}",
203               "ERROR", e);
204     }
205 
206     for (const auto& [intfName, properties] : interfaces)
207     {
208         if (intfName == MCTPInterface)
209         {
210             if (properties.contains("NetworkId") &&
211                 properties.contains("EID") &&
212                 properties.contains("SupportedMessageTypes"))
213             {
214                 auto networkId =
215                     std::get<NetworkId>(properties.at("NetworkId"));
216                 auto eid = std::get<mctp_eid_t>(properties.at("EID"));
217                 auto types = std::get<std::vector<uint8_t>>(
218                     properties.at("SupportedMessageTypes"));
219 
220                 if (!availability)
221                 {
222                     // Log an error message here, but still add it to the
223                     // terminus
224                     error(
225                         "mctpd added a DEGRADED endpoint {EID} networkId {NET} to D-Bus",
226                         "NET", networkId, "EID", static_cast<unsigned>(eid));
227                 }
228                 if (std::find(types.begin(), types.end(), mctpTypePLDM) !=
229                     types.end())
230                 {
231                     info(
232                         "Adding Endpoint networkId '{NETWORK}' and EID '{EID}' UUID '{UUID}'",
233                         "NETWORK", networkId, "EID", eid, "UUID", uuid);
234                     mctpInfos.emplace_back(MctpInfo(eid, uuid, "", networkId));
235                 }
236             }
237         }
238     }
239 }
240 
addToExistingMctpInfos(const MctpInfos & addedInfos)241 void MctpDiscovery::addToExistingMctpInfos(const MctpInfos& addedInfos)
242 {
243     for (const auto& mctpInfo : addedInfos)
244     {
245         if (std::find(existingMctpInfos.begin(), existingMctpInfos.end(),
246                       mctpInfo) == existingMctpInfos.end())
247         {
248             existingMctpInfos.emplace_back(mctpInfo);
249         }
250     }
251 }
252 
removeFromExistingMctpInfos(MctpInfos & mctpInfos,MctpInfos & removedInfos)253 void MctpDiscovery::removeFromExistingMctpInfos(MctpInfos& mctpInfos,
254                                                 MctpInfos& removedInfos)
255 {
256     for (const auto& mctpInfo : existingMctpInfos)
257     {
258         if (std::find(mctpInfos.begin(), mctpInfos.end(), mctpInfo) ==
259             mctpInfos.end())
260         {
261             removedInfos.emplace_back(mctpInfo);
262         }
263     }
264     for (const auto& mctpInfo : removedInfos)
265     {
266         info("Removing Endpoint networkId '{NETWORK}' and  EID '{EID}'",
267              "NETWORK", std::get<3>(mctpInfo), "EID", std::get<0>(mctpInfo));
268         existingMctpInfos.erase(std::remove(existingMctpInfos.begin(),
269                                             existingMctpInfos.end(), mctpInfo),
270                                 existingMctpInfos.end());
271     }
272 }
273 
propertiesChangedCb(sdbusplus::message_t & msg)274 void MctpDiscovery::propertiesChangedCb(sdbusplus::message_t& msg)
275 {
276     using Interface = std::string;
277     using Property = std::string;
278     using Value = std::string;
279     using Properties = std::map<Property, std::variant<Value>>;
280 
281     Interface interface;
282     Properties properties;
283     std::string objPath{};
284     std::string service{};
285 
286     try
287     {
288         msg.read(interface, properties);
289         objPath = msg.get_path();
290     }
291     catch (const sdbusplus::exception_t& e)
292     {
293         error(
294             "Error handling Connectivity property changed message, error - {ERROR}",
295             "ERROR", e);
296         return;
297     }
298 
299     for (const auto& [key, valueVariant] : properties)
300     {
301         Value propVal = std::get<std::string>(valueVariant);
302         auto availability = (propVal == "Available") ? true : false;
303 
304         if (key == MCTPConnectivityProp)
305         {
306             service = pldm::utils::DBusHandler().getService(objPath.c_str(),
307                                                             MCTPInterface);
308             const MctpEndpointProps& epProps =
309                 getMctpEndpointProps(service, objPath);
310 
311             auto types = std::get<MCTPMsgTypes>(epProps);
312             if (!std::ranges::contains(types, mctpTypePLDM))
313             {
314                 return;
315             }
316             const UUID& uuid = getEndpointUUIDProp(service, objPath);
317 
318             MctpInfo mctpInfo(std::get<eid>(epProps), uuid, "",
319                               std::get<NetworkId>(epProps));
320             if (!std::ranges::contains(existingMctpInfos, mctpInfo))
321             {
322                 if (availability)
323                 {
324                     // The endpoint not in existingMctpInfos and is
325                     // available Add it to existingMctpInfos
326                     info(
327                         "Adding Endpoint networkId {NETWORK} ID {EID} by propertiesChanged signal",
328                         "NETWORK", std::get<3>(mctpInfo), "EID",
329                         unsigned(std::get<0>(mctpInfo)));
330                     addToExistingMctpInfos(MctpInfos(1, mctpInfo));
331                     handleMctpEndpoints(MctpInfos(1, mctpInfo));
332                 }
333             }
334             else
335             {
336                 // The endpoint already in existingMctpInfos
337                 updateMctpEndpointAvailability(mctpInfo, availability);
338             }
339         }
340     }
341 }
342 
discoverEndpoints(sdbusplus::message_t & msg)343 void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg)
344 {
345     MctpInfos addedInfos;
346     getAddedMctpInfos(msg, addedInfos);
347     addToExistingMctpInfos(addedInfos);
348     handleMctpEndpoints(addedInfos);
349 }
350 
removeEndpoints(sdbusplus::message_t &)351 void MctpDiscovery::removeEndpoints(sdbusplus::message_t&)
352 {
353     MctpInfos mctpInfos;
354     MctpInfos removedInfos;
355     std::map<MctpInfo, Availability> currentMctpInfoMap;
356     getMctpInfos(currentMctpInfoMap);
357     for (const auto& mapIt : currentMctpInfoMap)
358     {
359         mctpInfos.push_back(mapIt.first);
360     }
361     removeFromExistingMctpInfos(mctpInfos, removedInfos);
362     handleRemovedMctpEndpoints(removedInfos);
363 }
364 
handleMctpEndpoints(const MctpInfos & mctpInfos)365 void MctpDiscovery::handleMctpEndpoints(const MctpInfos& mctpInfos)
366 {
367     for (const auto& handler : handlers)
368     {
369         if (handler)
370         {
371             handler->handleMctpEndpoints(mctpInfos);
372         }
373     }
374 }
375 
handleRemovedMctpEndpoints(const MctpInfos & mctpInfos)376 void MctpDiscovery::handleRemovedMctpEndpoints(const MctpInfos& mctpInfos)
377 {
378     for (const auto& handler : handlers)
379     {
380         if (handler)
381         {
382             handler->handleRemovedMctpEndpoints(mctpInfos);
383         }
384     }
385 }
386 
updateMctpEndpointAvailability(const MctpInfo & mctpInfo,Availability availability)387 void MctpDiscovery::updateMctpEndpointAvailability(const MctpInfo& mctpInfo,
388                                                    Availability availability)
389 {
390     for (const auto& handler : handlers)
391     {
392         if (handler)
393         {
394             handler->updateMctpEndpointAvailability(mctpInfo, availability);
395         }
396     }
397 }
398 
399 } // namespace pldm
400