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