blob: 4f33c52d0c6c87d2ea79e6edb18c6f7454cb5600 [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Patrick Venture262276f2019-10-18 13:27:59 -070017#include "commandutils.hpp"
Patrick Venturec2a07d42020-05-30 16:35:03 -070018#include "types.hpp"
Patrick Venture262276f2019-10-18 13:27:59 -070019
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070020#include <boost/algorithm/string.hpp>
Jason M. Billsa9423b62018-11-29 10:25:16 -080021#include <boost/bimap.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070022#include <boost/container/flat_map.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070023#include <phosphor-logging/log.hpp>
24#include <sdbusplus/bus/match.hpp>
25
Patrick Venture262276f2019-10-18 13:27:59 -070026#include <cstdio>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070027#include <cstring>
Patrick Venture262276f2019-10-18 13:27:59 -070028#include <exception>
29#include <filesystem>
30#include <map>
Patrick Venture262276f2019-10-18 13:27:59 -070031#include <string>
Josh Lehan06aa21a2020-10-28 21:59:06 -070032#include <string_view>
Patrick Venture262276f2019-10-18 13:27:59 -070033#include <vector>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070034
35#pragma once
36
37struct CmpStrVersion
38{
39 bool operator()(std::string a, std::string b) const
40 {
41 return strverscmp(a.c_str(), b.c_str()) < 0;
42 }
43};
44
45using SensorSubTree = boost::container::flat_map<
46 std::string,
47 boost::container::flat_map<std::string, std::vector<std::string>>,
48 CmpStrVersion>;
49
Jason M. Billsa9423b62018-11-29 10:25:16 -080050using SensorNumMap = boost::bimap<int, std::string>;
51
Johnathan Mantey308c3a82020-07-22 11:50:54 -070052static constexpr uint16_t maxSensorsPerLUN = 255;
53static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
54static constexpr uint16_t lun1Sensor0 = 0x100;
55static constexpr uint16_t lun3Sensor0 = 0x300;
56static constexpr uint16_t invalidSensorNumber = 0xFFFF;
57static constexpr uint8_t reservedSensorNumber = 0xFF;
58
Jason M. Billsa9423b62018-11-29 10:25:16 -080059namespace details
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070060{
Josh Lehan06aa21a2020-10-28 21:59:06 -070061
62// Enable/disable the logging of stats instrumentation
63static constexpr bool enableInstrumentation = false;
64
65class IPMIStatsEntry
66{
67 private:
68 int numReadings = 0;
69 int numMissings = 0;
70 int numStreakRead = 0;
71 int numStreakMiss = 0;
72 double minValue = 0.0;
73 double maxValue = 0.0;
74 std::string sensorName;
75
76 public:
77 const std::string& getName(void) const
78 {
79 return sensorName;
80 }
81
82 void updateName(std::string_view name)
83 {
84 sensorName = name;
85 }
86
87 // Returns true if this is the first successful reading
88 // This is so the caller can log the coefficients used
89 bool updateReading(double reading, int raw)
90 {
91 if constexpr (!enableInstrumentation)
92 {
93 return false;
94 }
95
96 bool first = ((numReadings == 0) && (numMissings == 0));
97
98 // Sensors can use "nan" to indicate unavailable reading
99 if (!(std::isfinite(reading)))
100 {
101 // Only show this if beginning a new streak
102 if (numStreakMiss == 0)
103 {
104 std::cerr << "IPMI sensor " << sensorName
105 << ": Missing reading, byte=" << raw
106 << ", Reading counts good=" << numReadings
107 << " miss=" << numMissings
108 << ", Prior good streak=" << numStreakRead << "\n";
109 }
110
111 numStreakRead = 0;
112 ++numMissings;
113 ++numStreakMiss;
114
115 return first;
116 }
117
118 // Only show this if beginning a new streak and not the first time
119 if ((numStreakRead == 0) && (numReadings != 0))
120 {
121 std::cerr << "IPMI sensor " << sensorName
122 << ": Recovered reading, value=" << reading
123 << " byte=" << raw
124 << ", Reading counts good=" << numReadings
125 << " miss=" << numMissings
126 << ", Prior miss streak=" << numStreakMiss << "\n";
127 }
128
129 // Initialize min/max if the first successful reading
130 if (numReadings == 0)
131 {
132 std::cerr << "IPMI sensor " << sensorName
133 << ": First reading, value=" << reading << " byte=" << raw
134 << "\n";
135
136 minValue = reading;
137 maxValue = reading;
138 }
139
140 numStreakMiss = 0;
141 ++numReadings;
142 ++numStreakRead;
143
144 // Only provide subsequent output if new min/max established
145 if (reading < minValue)
146 {
147 std::cerr << "IPMI sensor " << sensorName
148 << ": Lowest reading, value=" << reading
149 << " byte=" << raw << "\n";
150
151 minValue = reading;
152 }
153
154 if (reading > maxValue)
155 {
156 std::cerr << "IPMI sensor " << sensorName
157 << ": Highest reading, value=" << reading
158 << " byte=" << raw << "\n";
159
160 maxValue = reading;
161 }
162
163 return first;
164 }
165};
166
167class IPMIStatsTable
168{
169 private:
170 std::vector<IPMIStatsEntry> entries;
171
172 private:
173 void padEntries(size_t index)
174 {
175 char hexbuf[16];
176
177 // Pad vector until entries[index] becomes a valid index
178 while (entries.size() <= index)
179 {
180 // As name not known yet, use human-readable hex as name
181 IPMIStatsEntry newEntry;
182 sprintf(hexbuf, "0x%02zX", entries.size());
183 newEntry.updateName(hexbuf);
184
185 entries.push_back(std::move(newEntry));
186 }
187 }
188
189 public:
190 void wipeTable(void)
191 {
192 entries.clear();
193 }
194
195 const std::string& getName(size_t index)
196 {
197 padEntries(index);
198 return entries[index].getName();
199 }
200
201 void updateName(size_t index, std::string_view name)
202 {
203 padEntries(index);
204 entries[index].updateName(name);
205 }
206
207 bool updateReading(size_t index, double reading, int raw)
208 {
209 padEntries(index);
210 return entries[index].updateReading(reading, raw);
211 }
212};
213
214// This object is global singleton, used from a variety of places
215inline IPMIStatsTable sdrStatsTable;
216
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800217inline static uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800218{
219 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800220 static uint16_t sensorUpdatedIndex = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700221 sd_bus* bus = NULL;
222 int ret = sd_bus_default_system(&bus);
223 if (ret < 0)
224 {
225 phosphor::logging::log<phosphor::logging::level::ERR>(
226 "Failed to connect to system bus",
227 phosphor::logging::entry("ERRNO=0x%X", -ret));
228 sd_bus_unref(bus);
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800229 return sensorUpdatedIndex;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700230 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500231 sdbusplus::bus_t dbus(bus);
232 static sdbusplus::bus::match_t sensorAdded(
Jason M. Billsa9423b62018-11-29 10:25:16 -0800233 dbus,
234 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
235 "sensors/'",
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500236 [](sdbusplus::message_t& m) { sensorTreePtr.reset(); });
Jason M. Billsa9423b62018-11-29 10:25:16 -0800237
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500238 static sdbusplus::bus::match_t sensorRemoved(
Jason M. Billsa9423b62018-11-29 10:25:16 -0800239 dbus,
240 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
241 "openbmc_project/sensors/'",
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500242 [](sdbusplus::message_t& m) { sensorTreePtr.reset(); });
Jason M. Billsa9423b62018-11-29 10:25:16 -0800243
Jason M. Billsa9423b62018-11-29 10:25:16 -0800244 if (sensorTreePtr)
245 {
246 subtree = sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800247 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800248 }
249
250 sensorTreePtr = std::make_shared<SensorSubTree>();
251
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700252 auto mapperCall =
253 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
254 "/xyz/openbmc_project/object_mapper",
255 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
Jason M. Bills52341e82018-11-28 17:34:43 -0800256 static constexpr const auto depth = 2;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700257 static constexpr std::array<const char*, 3> interfaces = {
258 "xyz.openbmc_project.Sensor.Value",
259 "xyz.openbmc_project.Sensor.Threshold.Warning",
260 "xyz.openbmc_project.Sensor.Threshold.Critical"};
261 mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
262
263 try
264 {
265 auto mapperReply = dbus.call(mapperCall);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800266 mapperReply.read(*sensorTreePtr);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700267 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500268 catch (const sdbusplus::exception_t& e)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700269 {
Jason M. Bills52341e82018-11-28 17:34:43 -0800270 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800271 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800272 }
273 subtree = sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800274 sensorUpdatedIndex++;
Josh Lehan06aa21a2020-10-28 21:59:06 -0700275 // The SDR is being regenerated, wipe the old stats
276 sdrStatsTable.wipeTable();
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800277 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800278}
279
280inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
281{
282 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
283 bool sensorNumMapUpated = false;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800284 static uint16_t prevSensorUpdatedIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800285 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800286 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800287 if (!sensorTree)
288 {
289 return sensorNumMapUpated;
290 }
291
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800292 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800293 {
294 sensorNumMap = sensorNumMapPtr;
295 return sensorNumMapUpated;
296 }
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800297 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800298
299 sensorNumMapPtr = std::make_shared<SensorNumMap>();
300
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700301 uint16_t sensorNum = 0;
302 uint16_t sensorIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800303 for (const auto& sensor : *sensorTree)
304 {
305 sensorNumMapPtr->insert(
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700306 SensorNumMap::value_type(sensorNum, sensor.first));
307 sensorIndex++;
308 if (sensorIndex == maxSensorsPerLUN)
309 {
310 sensorIndex = lun1Sensor0;
311 }
312 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
313 {
314 // Skip assigning LUN 0x2 any sensors
315 sensorIndex = lun3Sensor0;
316 }
317 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
318 {
319 // this is an error, too many IPMI sensors
320 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
321 }
322 sensorNum = sensorIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800323 }
324 sensorNumMap = sensorNumMapPtr;
325 sensorNumMapUpated = true;
326 return sensorNumMapUpated;
327}
328} // namespace details
329
330inline static bool getSensorSubtree(SensorSubTree& subtree)
331{
332 std::shared_ptr<SensorSubTree> sensorTree;
333 details::getSensorSubtree(sensorTree);
334 if (!sensorTree)
335 {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700336 return false;
337 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800338
339 subtree = *sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700340 return true;
341}
342
343struct CmpStr
344{
345 bool operator()(const char* a, const char* b) const
346 {
347 return std::strcmp(a, b) < 0;
348 }
349};
350
Jason M. Bills52341e82018-11-28 17:34:43 -0800351enum class SensorTypeCodes : uint8_t
352{
353 reserved = 0x0,
354 temperature = 0x1,
355 voltage = 0x2,
356 current = 0x3,
357 fan = 0x4,
358 other = 0xB,
359};
360
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700361const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
362 sensorTypes{{{"temperature", SensorTypeCodes::temperature},
363 {"voltage", SensorTypeCodes::voltage},
364 {"current", SensorTypeCodes::current},
365 {"fan_tach", SensorTypeCodes::fan},
James Feistf426f332019-01-04 12:48:16 -0800366 {"fan_pwm", SensorTypeCodes::fan},
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700367 {"power", SensorTypeCodes::other}}};
368
369inline static std::string getSensorTypeStringFromPath(const std::string& path)
370{
371 // get sensor type string from path, path is defined as
372 // /xyz/openbmc_project/sensors/<type>/label
373 size_t typeEnd = path.rfind("/");
Jason M. Bills360f5932018-11-14 09:08:54 -0800374 if (typeEnd == std::string::npos)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700375 {
Jason M. Bills360f5932018-11-14 09:08:54 -0800376 return path;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700377 }
Jason M. Bills360f5932018-11-14 09:08:54 -0800378 size_t typeStart = path.rfind("/", typeEnd - 1);
379 if (typeStart == std::string::npos)
380 {
381 return path;
382 }
383 // Start at the character after the '/'
384 typeStart++;
385 return path.substr(typeStart, typeEnd - typeStart);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700386}
387
388inline static uint8_t getSensorTypeFromPath(const std::string& path)
389{
390 uint8_t sensorType = 0;
391 std::string type = getSensorTypeStringFromPath(path);
392 auto findSensor = sensorTypes.find(type.c_str());
393 if (findSensor != sensorTypes.end())
394 {
395 sensorType = static_cast<uint8_t>(findSensor->second);
396 } // else default 0x0 RESERVED
397
398 return sensorType;
399}
400
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700401inline static uint16_t getSensorNumberFromPath(const std::string& path)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700402{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800403 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
404 details::getSensorNumMap(sensorNumMapPtr);
405 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700406 {
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700407 return invalidSensorNumber;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700408 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800409
410 try
411 {
412 return sensorNumMapPtr->right.at(path);
413 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500414 catch (const std::out_of_range& e)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800415 {
416 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700417 return invalidSensorNumber;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800418 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700419}
420
421inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
422{
423 // TODO: Add support for additional reading types as needed
424 return 0x1; // reading type = threshold
425}
426
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700427inline static std::string getPathFromSensorNumber(uint16_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700428{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800429 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
430 details::getSensorNumMap(sensorNumMapPtr);
431 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700432 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800433 return std::string();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700434 }
435
Jason M. Billsa9423b62018-11-29 10:25:16 -0800436 try
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700437 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800438 return sensorNumMapPtr->left.at(sensorNum);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700439 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500440 catch (const std::out_of_range& e)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800441 {
442 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
443 return std::string();
444 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700445}
Patrick Venture262276f2019-10-18 13:27:59 -0700446
447namespace ipmi
448{
449
450static inline std::map<std::string, std::vector<std::string>>
451 getObjectInterfaces(const char* path)
452{
453 std::map<std::string, std::vector<std::string>> interfacesResponse;
454 std::vector<std::string> interfaces;
455 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
456
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500457 sdbusplus::message_t getObjectMessage =
Patrick Venture262276f2019-10-18 13:27:59 -0700458 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
459 "/xyz/openbmc_project/object_mapper",
460 "xyz.openbmc_project.ObjectMapper", "GetObject");
461 getObjectMessage.append(path, interfaces);
462
463 try
464 {
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500465 sdbusplus::message_t response = dbus->call(getObjectMessage);
Patrick Venture262276f2019-10-18 13:27:59 -0700466 response.read(interfacesResponse);
467 }
468 catch (const std::exception& e)
469 {
470 phosphor::logging::log<phosphor::logging::level::ERR>(
471 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
472 phosphor::logging::entry("WHAT=%s", e.what()));
473 }
474
475 return interfacesResponse;
476}
477
478static inline std::map<std::string, DbusVariant>
479 getEntityManagerProperties(const char* path, const char* interface)
480{
481 std::map<std::string, DbusVariant> properties;
482 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
483
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500484 sdbusplus::message_t getProperties =
Patrick Venture262276f2019-10-18 13:27:59 -0700485 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
486 "org.freedesktop.DBus.Properties", "GetAll");
487 getProperties.append(interface);
488
489 try
490 {
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500491 sdbusplus::message_t response = dbus->call(getProperties);
Patrick Venture262276f2019-10-18 13:27:59 -0700492 response.read(properties);
493 }
494 catch (const std::exception& e)
495 {
496 phosphor::logging::log<phosphor::logging::level::ERR>(
497 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
498 phosphor::logging::entry("INTF=%s", interface),
499 phosphor::logging::entry("WHAT=%s", e.what()));
500 }
501
502 return properties;
503}
504
505static inline const std::string* getSensorConfigurationInterface(
506 const std::map<std::string, std::vector<std::string>>&
507 sensorInterfacesResponse)
508{
509 auto entityManagerService =
510 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
511 if (entityManagerService == sensorInterfacesResponse.end())
512 {
513 return nullptr;
514 }
515
516 // Find the fan configuration first (fans can have multiple configuration
517 // interfaces).
518 for (const auto& entry : entityManagerService->second)
519 {
520 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
521 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
522 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
523 {
524 return &entry;
525 }
526 }
527
528 for (const auto& entry : entityManagerService->second)
529 {
530 if (boost::algorithm::starts_with(entry,
531 "xyz.openbmc_project.Configuration."))
532 {
533 return &entry;
534 }
535 }
536
537 return nullptr;
538}
539
540// Follow Association properties for Sensor back to the Board dbus object to
541// check for an EntityId and EntityInstance property.
542static inline void updateIpmiFromAssociation(const std::string& path,
543 const SensorMap& sensorMap,
544 uint8_t& entityId,
545 uint8_t& entityInstance)
546{
547 namespace fs = std::filesystem;
548
549 auto sensorAssociationObject =
550 sensorMap.find("xyz.openbmc_project.Association.Definitions");
551 if (sensorAssociationObject == sensorMap.end())
552 {
553 if constexpr (debug)
554 {
555 std::fprintf(stderr, "path=%s, no association interface found\n",
556 path.c_str());
557 }
558
559 return;
560 }
561
562 auto associationObject =
563 sensorAssociationObject->second.find("Associations");
564 if (associationObject == sensorAssociationObject->second.end())
565 {
566 if constexpr (debug)
567 {
568 std::fprintf(stderr, "path=%s, no association records found\n",
569 path.c_str());
570 }
571
572 return;
573 }
574
575 std::vector<Association> associationValues =
576 std::get<std::vector<Association>>(associationObject->second);
577
578 // loop through the Associations looking for the right one:
579 for (const auto& entry : associationValues)
580 {
581 // forward, reverse, endpoint
582 const std::string& forward = std::get<0>(entry);
583 const std::string& reverse = std::get<1>(entry);
584 const std::string& endpoint = std::get<2>(entry);
585
586 // We only currently concern ourselves with chassis+all_sensors.
587 if (!(forward == "chassis" && reverse == "all_sensors"))
588 {
589 continue;
590 }
591
592 // the endpoint is the board entry provided by
593 // Entity-Manager. so let's grab its properties if it has
594 // the right interface.
595
596 // just try grabbing the properties first.
597 std::map<std::string, DbusVariant> ipmiProperties =
598 getEntityManagerProperties(
599 endpoint.c_str(),
600 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
601
602 auto entityIdProp = ipmiProperties.find("EntityId");
603 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
604 if (entityIdProp != ipmiProperties.end())
605 {
606 entityId =
607 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
608 }
609 if (entityInstanceProp != ipmiProperties.end())
610 {
611 entityInstance = static_cast<uint8_t>(
612 std::get<uint64_t>(entityInstanceProp->second));
613 }
614
615 // Now check the entity-manager entry for this sensor to see
616 // if it has its own value and use that instead.
617 //
618 // In theory, checking this first saves us from checking
619 // both, except in most use-cases identified, there won't be
620 // a per sensor override, so we need to always check both.
621 std::string sensorNameFromPath = fs::path(path).filename();
622
623 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
624
625 // Download the interfaces for the sensor from
626 // Entity-Manager to find the name of the configuration
627 // interface.
628 std::map<std::string, std::vector<std::string>>
629 sensorInterfacesResponse =
630 getObjectInterfaces(sensorConfigPath.c_str());
631
632 const std::string* configurationInterface =
633 getSensorConfigurationInterface(sensorInterfacesResponse);
634
635 // We didnt' find a configuration interface for this sensor, but we
636 // followed the Association property to get here, so we're done
637 // searching.
638 if (!configurationInterface)
639 {
640 break;
641 }
642
643 // We found a configuration interface.
644 std::map<std::string, DbusVariant> configurationProperties =
645 getEntityManagerProperties(sensorConfigPath.c_str(),
646 configurationInterface->c_str());
647
648 entityIdProp = configurationProperties.find("EntityId");
649 entityInstanceProp = configurationProperties.find("EntityInstance");
650 if (entityIdProp != configurationProperties.end())
651 {
652 entityId =
653 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
654 }
655 if (entityInstanceProp != configurationProperties.end())
656 {
657 entityInstance = static_cast<uint8_t>(
658 std::get<uint64_t>(entityInstanceProp->second));
659 }
660
661 // stop searching Association records.
662 break;
663 } // end for Association vectors.
664
665 if constexpr (debug)
666 {
667 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
668 path.c_str(), entityId, entityInstance);
669 }
670}
671
672} // namespace ipmi