blob: ed283149a7324e302178bf0a4153d05db426f32a [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
Patrick Williams1bcced02024-08-16 15:20:24 -0400122 << ": Recovered reading, value=" << reading << " byte="
123 << raw << ", Reading counts good=" << numReadings
Josh Lehan06aa21a2020-10-28 21:59:06 -0700124 << " miss=" << numMissings
125 << ", Prior miss streak=" << numStreakMiss << "\n";
126 }
127
128 // Initialize min/max if the first successful reading
129 if (numReadings == 0)
130 {
131 std::cerr << "IPMI sensor " << sensorName
132 << ": First reading, value=" << reading << " byte=" << raw
133 << "\n";
134
135 minValue = reading;
136 maxValue = reading;
137 }
138
139 numStreakMiss = 0;
140 ++numReadings;
141 ++numStreakRead;
142
143 // Only provide subsequent output if new min/max established
144 if (reading < minValue)
145 {
146 std::cerr << "IPMI sensor " << sensorName
147 << ": Lowest reading, value=" << reading
148 << " byte=" << raw << "\n";
149
150 minValue = reading;
151 }
152
153 if (reading > maxValue)
154 {
155 std::cerr << "IPMI sensor " << sensorName
156 << ": Highest reading, value=" << reading
157 << " byte=" << raw << "\n";
158
159 maxValue = reading;
160 }
161
162 return first;
163 }
164};
165
166class IPMIStatsTable
167{
168 private:
169 std::vector<IPMIStatsEntry> entries;
170
171 private:
172 void padEntries(size_t index)
173 {
174 char hexbuf[16];
175
176 // Pad vector until entries[index] becomes a valid index
177 while (entries.size() <= index)
178 {
179 // As name not known yet, use human-readable hex as name
180 IPMIStatsEntry newEntry;
181 sprintf(hexbuf, "0x%02zX", entries.size());
182 newEntry.updateName(hexbuf);
183
184 entries.push_back(std::move(newEntry));
185 }
186 }
187
188 public:
189 void wipeTable(void)
190 {
191 entries.clear();
192 }
193
194 const std::string& getName(size_t index)
195 {
196 padEntries(index);
197 return entries[index].getName();
198 }
199
200 void updateName(size_t index, std::string_view name)
201 {
202 padEntries(index);
203 entries[index].updateName(name);
204 }
205
206 bool updateReading(size_t index, double reading, int raw)
207 {
208 padEntries(index);
209 return entries[index].updateReading(reading, raw);
210 }
211};
212
213// This object is global singleton, used from a variety of places
214inline IPMIStatsTable sdrStatsTable;
215
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800216inline static uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800217{
218 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800219 static uint16_t sensorUpdatedIndex = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700220 sd_bus* bus = NULL;
221 int ret = sd_bus_default_system(&bus);
222 if (ret < 0)
223 {
224 phosphor::logging::log<phosphor::logging::level::ERR>(
225 "Failed to connect to system bus",
226 phosphor::logging::entry("ERRNO=0x%X", -ret));
227 sd_bus_unref(bus);
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800228 return sensorUpdatedIndex;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700229 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500230 sdbusplus::bus_t dbus(bus);
231 static sdbusplus::bus::match_t sensorAdded(
Jason M. Billsa9423b62018-11-29 10:25:16 -0800232 dbus,
233 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
234 "sensors/'",
Vernon Mauerydcff1502022-09-28 11:12:46 -0700235 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Jason M. Billsa9423b62018-11-29 10:25:16 -0800236
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500237 static sdbusplus::bus::match_t sensorRemoved(
Jason M. Billsa9423b62018-11-29 10:25:16 -0800238 dbus,
239 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
240 "openbmc_project/sensors/'",
Vernon Mauerydcff1502022-09-28 11:12:46 -0700241 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Jason M. Billsa9423b62018-11-29 10:25:16 -0800242
Jason M. Billsa9423b62018-11-29 10:25:16 -0800243 if (sensorTreePtr)
244 {
245 subtree = sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800246 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800247 }
248
249 sensorTreePtr = std::make_shared<SensorSubTree>();
250
Patrick Williams1bcced02024-08-16 15:20:24 -0400251 auto mapperCall =
252 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
253 "/xyz/openbmc_project/object_mapper",
254 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
Jason M. Bills52341e82018-11-28 17:34:43 -0800255 static constexpr const auto depth = 2;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700256 static constexpr std::array<const char*, 3> interfaces = {
257 "xyz.openbmc_project.Sensor.Value",
258 "xyz.openbmc_project.Sensor.Threshold.Warning",
259 "xyz.openbmc_project.Sensor.Threshold.Critical"};
260 mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
261
262 try
263 {
264 auto mapperReply = dbus.call(mapperCall);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800265 mapperReply.read(*sensorTreePtr);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700266 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500267 catch (const sdbusplus::exception_t& e)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700268 {
Jason M. Bills52341e82018-11-28 17:34:43 -0800269 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800270 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800271 }
272 subtree = sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800273 sensorUpdatedIndex++;
Josh Lehan06aa21a2020-10-28 21:59:06 -0700274 // The SDR is being regenerated, wipe the old stats
275 sdrStatsTable.wipeTable();
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800276 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800277}
278
279inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
280{
281 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
282 bool sensorNumMapUpated = false;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800283 static uint16_t prevSensorUpdatedIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800284 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800285 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800286 if (!sensorTree)
287 {
288 return sensorNumMapUpated;
289 }
290
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800291 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800292 {
293 sensorNumMap = sensorNumMapPtr;
294 return sensorNumMapUpated;
295 }
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800296 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800297
298 sensorNumMapPtr = std::make_shared<SensorNumMap>();
299
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700300 uint16_t sensorNum = 0;
301 uint16_t sensorIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800302 for (const auto& sensor : *sensorTree)
303 {
304 sensorNumMapPtr->insert(
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700305 SensorNumMap::value_type(sensorNum, sensor.first));
306 sensorIndex++;
307 if (sensorIndex == maxSensorsPerLUN)
308 {
309 sensorIndex = lun1Sensor0;
310 }
311 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
312 {
313 // Skip assigning LUN 0x2 any sensors
314 sensorIndex = lun3Sensor0;
315 }
316 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
317 {
318 // this is an error, too many IPMI sensors
319 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
320 }
321 sensorNum = sensorIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800322 }
323 sensorNumMap = sensorNumMapPtr;
324 sensorNumMapUpated = true;
325 return sensorNumMapUpated;
326}
327} // namespace details
328
329inline static bool getSensorSubtree(SensorSubTree& subtree)
330{
331 std::shared_ptr<SensorSubTree> sensorTree;
332 details::getSensorSubtree(sensorTree);
333 if (!sensorTree)
334 {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700335 return false;
336 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800337
338 subtree = *sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700339 return true;
340}
341
342struct CmpStr
343{
344 bool operator()(const char* a, const char* b) const
345 {
346 return std::strcmp(a, b) < 0;
347 }
348};
349
Jason M. Bills52341e82018-11-28 17:34:43 -0800350enum class SensorTypeCodes : uint8_t
351{
352 reserved = 0x0,
353 temperature = 0x1,
354 voltage = 0x2,
355 current = 0x3,
356 fan = 0x4,
357 other = 0xB,
358};
359
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700360const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
361 sensorTypes{{{"temperature", SensorTypeCodes::temperature},
362 {"voltage", SensorTypeCodes::voltage},
363 {"current", SensorTypeCodes::current},
364 {"fan_tach", SensorTypeCodes::fan},
James Feistf426f332019-01-04 12:48:16 -0800365 {"fan_pwm", SensorTypeCodes::fan},
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700366 {"power", SensorTypeCodes::other}}};
367
368inline static std::string getSensorTypeStringFromPath(const std::string& path)
369{
370 // get sensor type string from path, path is defined as
371 // /xyz/openbmc_project/sensors/<type>/label
372 size_t typeEnd = path.rfind("/");
Jason M. Bills360f5932018-11-14 09:08:54 -0800373 if (typeEnd == std::string::npos)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700374 {
Jason M. Bills360f5932018-11-14 09:08:54 -0800375 return path;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700376 }
Jason M. Bills360f5932018-11-14 09:08:54 -0800377 size_t typeStart = path.rfind("/", typeEnd - 1);
378 if (typeStart == std::string::npos)
379 {
380 return path;
381 }
382 // Start at the character after the '/'
383 typeStart++;
384 return path.substr(typeStart, typeEnd - typeStart);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700385}
386
387inline static uint8_t getSensorTypeFromPath(const std::string& path)
388{
389 uint8_t sensorType = 0;
390 std::string type = getSensorTypeStringFromPath(path);
391 auto findSensor = sensorTypes.find(type.c_str());
392 if (findSensor != sensorTypes.end())
393 {
394 sensorType = static_cast<uint8_t>(findSensor->second);
395 } // else default 0x0 RESERVED
396
397 return sensorType;
398}
399
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700400inline static uint16_t getSensorNumberFromPath(const std::string& path)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700401{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800402 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
403 details::getSensorNumMap(sensorNumMapPtr);
404 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700405 {
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700406 return invalidSensorNumber;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700407 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800408
409 try
410 {
411 return sensorNumMapPtr->right.at(path);
412 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500413 catch (const std::out_of_range& e)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800414 {
415 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700416 return invalidSensorNumber;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800417 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700418}
419
Vernon Mauerydcff1502022-09-28 11:12:46 -0700420inline static uint8_t getSensorEventTypeFromPath(const std::string& /* path */)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700421{
422 // TODO: Add support for additional reading types as needed
423 return 0x1; // reading type = threshold
424}
425
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700426inline static std::string getPathFromSensorNumber(uint16_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700427{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800428 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
429 details::getSensorNumMap(sensorNumMapPtr);
430 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700431 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800432 return std::string();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700433 }
434
Jason M. Billsa9423b62018-11-29 10:25:16 -0800435 try
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700436 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800437 return sensorNumMapPtr->left.at(sensorNum);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700438 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500439 catch (const std::out_of_range& e)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800440 {
441 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
442 return std::string();
443 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700444}
Patrick Venture262276f2019-10-18 13:27:59 -0700445
446namespace ipmi
447{
448
449static inline std::map<std::string, std::vector<std::string>>
450 getObjectInterfaces(const char* path)
451{
452 std::map<std::string, std::vector<std::string>> interfacesResponse;
453 std::vector<std::string> interfaces;
454 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
455
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500456 sdbusplus::message_t getObjectMessage =
Patrick Venture262276f2019-10-18 13:27:59 -0700457 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
458 "/xyz/openbmc_project/object_mapper",
459 "xyz.openbmc_project.ObjectMapper", "GetObject");
460 getObjectMessage.append(path, interfaces);
461
462 try
463 {
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500464 sdbusplus::message_t response = dbus->call(getObjectMessage);
Patrick Venture262276f2019-10-18 13:27:59 -0700465 response.read(interfacesResponse);
466 }
467 catch (const std::exception& e)
468 {
469 phosphor::logging::log<phosphor::logging::level::ERR>(
470 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
471 phosphor::logging::entry("WHAT=%s", e.what()));
472 }
473
474 return interfacesResponse;
475}
476
477static inline std::map<std::string, DbusVariant>
478 getEntityManagerProperties(const char* path, const char* interface)
479{
480 std::map<std::string, DbusVariant> properties;
481 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
482
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500483 sdbusplus::message_t getProperties =
Patrick Venture262276f2019-10-18 13:27:59 -0700484 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
485 "org.freedesktop.DBus.Properties", "GetAll");
486 getProperties.append(interface);
487
488 try
489 {
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500490 sdbusplus::message_t response = dbus->call(getProperties);
Patrick Venture262276f2019-10-18 13:27:59 -0700491 response.read(properties);
492 }
493 catch (const std::exception& e)
494 {
495 phosphor::logging::log<phosphor::logging::level::ERR>(
496 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
497 phosphor::logging::entry("INTF=%s", interface),
498 phosphor::logging::entry("WHAT=%s", e.what()));
499 }
500
501 return properties;
502}
503
504static inline const std::string* getSensorConfigurationInterface(
505 const std::map<std::string, std::vector<std::string>>&
506 sensorInterfacesResponse)
507{
508 auto entityManagerService =
509 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
510 if (entityManagerService == sensorInterfacesResponse.end())
511 {
512 return nullptr;
513 }
514
515 // Find the fan configuration first (fans can have multiple configuration
516 // interfaces).
517 for (const auto& entry : entityManagerService->second)
518 {
519 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
520 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
521 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
522 {
523 return &entry;
524 }
525 }
526
527 for (const auto& entry : entityManagerService->second)
528 {
529 if (boost::algorithm::starts_with(entry,
530 "xyz.openbmc_project.Configuration."))
531 {
532 return &entry;
533 }
534 }
535
536 return nullptr;
537}
538
539// Follow Association properties for Sensor back to the Board dbus object to
540// check for an EntityId and EntityInstance property.
Patrick Williams1bcced02024-08-16 15:20:24 -0400541static inline void updateIpmiFromAssociation(
542 const std::string& path, const SensorMap& sensorMap, uint8_t& entityId,
543 uint8_t& entityInstance)
Patrick Venture262276f2019-10-18 13:27:59 -0700544{
545 namespace fs = std::filesystem;
546
547 auto sensorAssociationObject =
548 sensorMap.find("xyz.openbmc_project.Association.Definitions");
549 if (sensorAssociationObject == sensorMap.end())
550 {
551 if constexpr (debug)
552 {
553 std::fprintf(stderr, "path=%s, no association interface found\n",
554 path.c_str());
555 }
556
557 return;
558 }
559
560 auto associationObject =
561 sensorAssociationObject->second.find("Associations");
562 if (associationObject == sensorAssociationObject->second.end())
563 {
564 if constexpr (debug)
565 {
566 std::fprintf(stderr, "path=%s, no association records found\n",
567 path.c_str());
568 }
569
570 return;
571 }
572
573 std::vector<Association> associationValues =
574 std::get<std::vector<Association>>(associationObject->second);
575
576 // loop through the Associations looking for the right one:
577 for (const auto& entry : associationValues)
578 {
579 // forward, reverse, endpoint
580 const std::string& forward = std::get<0>(entry);
581 const std::string& reverse = std::get<1>(entry);
582 const std::string& endpoint = std::get<2>(entry);
583
584 // We only currently concern ourselves with chassis+all_sensors.
585 if (!(forward == "chassis" && reverse == "all_sensors"))
586 {
587 continue;
588 }
589
590 // the endpoint is the board entry provided by
591 // Entity-Manager. so let's grab its properties if it has
592 // the right interface.
593
594 // just try grabbing the properties first.
595 std::map<std::string, DbusVariant> ipmiProperties =
596 getEntityManagerProperties(
597 endpoint.c_str(),
598 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
599
600 auto entityIdProp = ipmiProperties.find("EntityId");
601 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
602 if (entityIdProp != ipmiProperties.end())
603 {
604 entityId =
605 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
606 }
607 if (entityInstanceProp != ipmiProperties.end())
608 {
609 entityInstance = static_cast<uint8_t>(
610 std::get<uint64_t>(entityInstanceProp->second));
611 }
612
613 // Now check the entity-manager entry for this sensor to see
614 // if it has its own value and use that instead.
615 //
616 // In theory, checking this first saves us from checking
617 // both, except in most use-cases identified, there won't be
618 // a per sensor override, so we need to always check both.
619 std::string sensorNameFromPath = fs::path(path).filename();
620
621 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
622
623 // Download the interfaces for the sensor from
624 // Entity-Manager to find the name of the configuration
625 // interface.
626 std::map<std::string, std::vector<std::string>>
627 sensorInterfacesResponse =
628 getObjectInterfaces(sensorConfigPath.c_str());
629
630 const std::string* configurationInterface =
631 getSensorConfigurationInterface(sensorInterfacesResponse);
632
633 // We didnt' find a configuration interface for this sensor, but we
634 // followed the Association property to get here, so we're done
635 // searching.
636 if (!configurationInterface)
637 {
638 break;
639 }
640
641 // We found a configuration interface.
642 std::map<std::string, DbusVariant> configurationProperties =
643 getEntityManagerProperties(sensorConfigPath.c_str(),
644 configurationInterface->c_str());
645
646 entityIdProp = configurationProperties.find("EntityId");
647 entityInstanceProp = configurationProperties.find("EntityInstance");
648 if (entityIdProp != configurationProperties.end())
649 {
650 entityId =
651 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
652 }
653 if (entityInstanceProp != configurationProperties.end())
654 {
655 entityInstance = static_cast<uint8_t>(
656 std::get<uint64_t>(entityInstanceProp->second));
657 }
658
659 // stop searching Association records.
660 break;
661 } // end for Association vectors.
662
663 if constexpr (debug)
664 {
665 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
666 path.c_str(), entityId, entityInstance);
667 }
668}
669
670} // namespace ipmi