xref: /openbmc/intel-ipmi-oem/include/sdrutils.hpp (revision bd51e6a9ae450986d456afb83583443695fb4a65)
13f7c5e40SJason M. Bills /*
23f7c5e40SJason M. Bills // Copyright (c) 2018 Intel Corporation
33f7c5e40SJason M. Bills //
43f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License");
53f7c5e40SJason M. Bills // you may not use this file except in compliance with the License.
63f7c5e40SJason M. Bills // You may obtain a copy of the License at
73f7c5e40SJason M. Bills //
83f7c5e40SJason M. Bills //      http://www.apache.org/licenses/LICENSE-2.0
93f7c5e40SJason M. Bills //
103f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software
113f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS,
123f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f7c5e40SJason M. Bills // See the License for the specific language governing permissions and
143f7c5e40SJason M. Bills // limitations under the License.
153f7c5e40SJason M. Bills */
163f7c5e40SJason M. Bills 
17262276f4SPatrick Venture #include "commandutils.hpp"
18c2a07d4bSPatrick Venture #include "types.hpp"
19262276f4SPatrick Venture 
203f7c5e40SJason M. Bills #include <boost/algorithm/string.hpp>
21a9423b6eSJason M. Bills #include <boost/bimap.hpp>
223f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp>
23fcd2d3a9SJames Feist #include <phosphor-logging/log.hpp>
24fcd2d3a9SJames Feist #include <sdbusplus/bus/match.hpp>
25fcd2d3a9SJames Feist 
26262276f4SPatrick Venture #include <cstdio>
273f7c5e40SJason M. Bills #include <cstring>
28262276f4SPatrick Venture #include <exception>
29262276f4SPatrick Venture #include <filesystem>
30262276f4SPatrick Venture #include <map>
31262276f4SPatrick Venture #include <string>
3206aa21abSJosh Lehan #include <string_view>
33262276f4SPatrick Venture #include <vector>
343f7c5e40SJason M. Bills 
353f7c5e40SJason M. Bills #pragma once
363f7c5e40SJason M. Bills 
373f7c5e40SJason M. Bills struct CmpStrVersion
383f7c5e40SJason M. Bills {
393f7c5e40SJason M. Bills     bool operator()(std::string a, std::string b) const
403f7c5e40SJason M. Bills     {
413f7c5e40SJason M. Bills         return strverscmp(a.c_str(), b.c_str()) < 0;
423f7c5e40SJason M. Bills     }
433f7c5e40SJason M. Bills };
443f7c5e40SJason M. Bills 
453f7c5e40SJason M. Bills using SensorSubTree = boost::container::flat_map<
463f7c5e40SJason M. Bills     std::string,
473f7c5e40SJason M. Bills     boost::container::flat_map<std::string, std::vector<std::string>>,
483f7c5e40SJason M. Bills     CmpStrVersion>;
493f7c5e40SJason M. Bills 
50a9423b6eSJason M. Bills using SensorNumMap = boost::bimap<int, std::string>;
51a9423b6eSJason M. Bills 
52308c3a8bSJohnathan Mantey static constexpr uint16_t maxSensorsPerLUN = 255;
53308c3a8bSJohnathan Mantey static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
54308c3a8bSJohnathan Mantey static constexpr uint16_t lun1Sensor0 = 0x100;
55308c3a8bSJohnathan Mantey static constexpr uint16_t lun3Sensor0 = 0x300;
56308c3a8bSJohnathan Mantey static constexpr uint16_t invalidSensorNumber = 0xFFFF;
57308c3a8bSJohnathan Mantey static constexpr uint8_t reservedSensorNumber = 0xFF;
58308c3a8bSJohnathan Mantey 
59a9423b6eSJason M. Bills namespace details
603f7c5e40SJason M. Bills {
6106aa21abSJosh Lehan 
6206aa21abSJosh Lehan // Enable/disable the logging of stats instrumentation
6306aa21abSJosh Lehan static constexpr bool enableInstrumentation = false;
6406aa21abSJosh Lehan 
6506aa21abSJosh Lehan class IPMIStatsEntry
6606aa21abSJosh Lehan {
6706aa21abSJosh Lehan   private:
6806aa21abSJosh Lehan     int numReadings = 0;
6906aa21abSJosh Lehan     int numMissings = 0;
7006aa21abSJosh Lehan     int numStreakRead = 0;
7106aa21abSJosh Lehan     int numStreakMiss = 0;
7206aa21abSJosh Lehan     double minValue = 0.0;
7306aa21abSJosh Lehan     double maxValue = 0.0;
7406aa21abSJosh Lehan     std::string sensorName;
7506aa21abSJosh Lehan 
7606aa21abSJosh Lehan   public:
7706aa21abSJosh Lehan     const std::string& getName(void) const
7806aa21abSJosh Lehan     {
7906aa21abSJosh Lehan         return sensorName;
8006aa21abSJosh Lehan     }
8106aa21abSJosh Lehan 
8206aa21abSJosh Lehan     void updateName(std::string_view name)
8306aa21abSJosh Lehan     {
8406aa21abSJosh Lehan         sensorName = name;
8506aa21abSJosh Lehan     }
8606aa21abSJosh Lehan 
8706aa21abSJosh Lehan     // Returns true if this is the first successful reading
8806aa21abSJosh Lehan     // This is so the caller can log the coefficients used
8906aa21abSJosh Lehan     bool updateReading(double reading, int raw)
9006aa21abSJosh Lehan     {
9106aa21abSJosh Lehan         if constexpr (!enableInstrumentation)
9206aa21abSJosh Lehan         {
9306aa21abSJosh Lehan             return false;
9406aa21abSJosh Lehan         }
9506aa21abSJosh Lehan 
9606aa21abSJosh Lehan         bool first = ((numReadings == 0) && (numMissings == 0));
9706aa21abSJosh Lehan 
9806aa21abSJosh Lehan         // Sensors can use "nan" to indicate unavailable reading
9906aa21abSJosh Lehan         if (!(std::isfinite(reading)))
10006aa21abSJosh Lehan         {
10106aa21abSJosh Lehan             // Only show this if beginning a new streak
10206aa21abSJosh Lehan             if (numStreakMiss == 0)
10306aa21abSJosh Lehan             {
10406aa21abSJosh Lehan                 std::cerr << "IPMI sensor " << sensorName
10506aa21abSJosh Lehan                           << ": Missing reading, byte=" << raw
10606aa21abSJosh Lehan                           << ", Reading counts good=" << numReadings
10706aa21abSJosh Lehan                           << " miss=" << numMissings
10806aa21abSJosh Lehan                           << ", Prior good streak=" << numStreakRead << "\n";
10906aa21abSJosh Lehan             }
11006aa21abSJosh Lehan 
11106aa21abSJosh Lehan             numStreakRead = 0;
11206aa21abSJosh Lehan             ++numMissings;
11306aa21abSJosh Lehan             ++numStreakMiss;
11406aa21abSJosh Lehan 
11506aa21abSJosh Lehan             return first;
11606aa21abSJosh Lehan         }
11706aa21abSJosh Lehan 
11806aa21abSJosh Lehan         // Only show this if beginning a new streak and not the first time
11906aa21abSJosh Lehan         if ((numStreakRead == 0) && (numReadings != 0))
12006aa21abSJosh Lehan         {
12106aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
12206aa21abSJosh Lehan                       << ": Recovered reading, value=" << reading
12306aa21abSJosh Lehan                       << " byte=" << raw
12406aa21abSJosh Lehan                       << ", Reading counts good=" << numReadings
12506aa21abSJosh Lehan                       << " miss=" << numMissings
12606aa21abSJosh Lehan                       << ", Prior miss streak=" << numStreakMiss << "\n";
12706aa21abSJosh Lehan         }
12806aa21abSJosh Lehan 
12906aa21abSJosh Lehan         // Initialize min/max if the first successful reading
13006aa21abSJosh Lehan         if (numReadings == 0)
13106aa21abSJosh Lehan         {
13206aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
13306aa21abSJosh Lehan                       << ": First reading, value=" << reading << " byte=" << raw
13406aa21abSJosh Lehan                       << "\n";
13506aa21abSJosh Lehan 
13606aa21abSJosh Lehan             minValue = reading;
13706aa21abSJosh Lehan             maxValue = reading;
13806aa21abSJosh Lehan         }
13906aa21abSJosh Lehan 
14006aa21abSJosh Lehan         numStreakMiss = 0;
14106aa21abSJosh Lehan         ++numReadings;
14206aa21abSJosh Lehan         ++numStreakRead;
14306aa21abSJosh Lehan 
14406aa21abSJosh Lehan         // Only provide subsequent output if new min/max established
14506aa21abSJosh Lehan         if (reading < minValue)
14606aa21abSJosh Lehan         {
14706aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
14806aa21abSJosh Lehan                       << ": Lowest reading, value=" << reading
14906aa21abSJosh Lehan                       << " byte=" << raw << "\n";
15006aa21abSJosh Lehan 
15106aa21abSJosh Lehan             minValue = reading;
15206aa21abSJosh Lehan         }
15306aa21abSJosh Lehan 
15406aa21abSJosh Lehan         if (reading > maxValue)
15506aa21abSJosh Lehan         {
15606aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
15706aa21abSJosh Lehan                       << ": Highest reading, value=" << reading
15806aa21abSJosh Lehan                       << " byte=" << raw << "\n";
15906aa21abSJosh Lehan 
16006aa21abSJosh Lehan             maxValue = reading;
16106aa21abSJosh Lehan         }
16206aa21abSJosh Lehan 
16306aa21abSJosh Lehan         return first;
16406aa21abSJosh Lehan     }
16506aa21abSJosh Lehan };
16606aa21abSJosh Lehan 
16706aa21abSJosh Lehan class IPMIStatsTable
16806aa21abSJosh Lehan {
16906aa21abSJosh Lehan   private:
17006aa21abSJosh Lehan     std::vector<IPMIStatsEntry> entries;
17106aa21abSJosh Lehan 
17206aa21abSJosh Lehan   private:
17306aa21abSJosh Lehan     void padEntries(size_t index)
17406aa21abSJosh Lehan     {
17506aa21abSJosh Lehan         char hexbuf[16];
17606aa21abSJosh Lehan 
17706aa21abSJosh Lehan         // Pad vector until entries[index] becomes a valid index
17806aa21abSJosh Lehan         while (entries.size() <= index)
17906aa21abSJosh Lehan         {
18006aa21abSJosh Lehan             // As name not known yet, use human-readable hex as name
18106aa21abSJosh Lehan             IPMIStatsEntry newEntry;
18206aa21abSJosh Lehan             sprintf(hexbuf, "0x%02zX", entries.size());
18306aa21abSJosh Lehan             newEntry.updateName(hexbuf);
18406aa21abSJosh Lehan 
18506aa21abSJosh Lehan             entries.push_back(std::move(newEntry));
18606aa21abSJosh Lehan         }
18706aa21abSJosh Lehan     }
18806aa21abSJosh Lehan 
18906aa21abSJosh Lehan   public:
19006aa21abSJosh Lehan     void wipeTable(void)
19106aa21abSJosh Lehan     {
19206aa21abSJosh Lehan         entries.clear();
19306aa21abSJosh Lehan     }
19406aa21abSJosh Lehan 
19506aa21abSJosh Lehan     const std::string& getName(size_t index)
19606aa21abSJosh Lehan     {
19706aa21abSJosh Lehan         padEntries(index);
19806aa21abSJosh Lehan         return entries[index].getName();
19906aa21abSJosh Lehan     }
20006aa21abSJosh Lehan 
20106aa21abSJosh Lehan     void updateName(size_t index, std::string_view name)
20206aa21abSJosh Lehan     {
20306aa21abSJosh Lehan         padEntries(index);
20406aa21abSJosh Lehan         entries[index].updateName(name);
20506aa21abSJosh Lehan     }
20606aa21abSJosh Lehan 
20706aa21abSJosh Lehan     bool updateReading(size_t index, double reading, int raw)
20806aa21abSJosh Lehan     {
20906aa21abSJosh Lehan         padEntries(index);
21006aa21abSJosh Lehan         return entries[index].updateReading(reading, raw);
21106aa21abSJosh Lehan     }
21206aa21abSJosh Lehan };
21306aa21abSJosh Lehan 
21406aa21abSJosh Lehan // This object is global singleton, used from a variety of places
21506aa21abSJosh Lehan inline IPMIStatsTable sdrStatsTable;
21606aa21abSJosh Lehan 
21717eadbfbSKuiying Wang inline static uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
218a9423b6eSJason M. Bills {
219a9423b6eSJason M. Bills     static std::shared_ptr<SensorSubTree> sensorTreePtr;
22017eadbfbSKuiying Wang     static uint16_t sensorUpdatedIndex = 0;
2213f7c5e40SJason M. Bills     sd_bus* bus = NULL;
2223f7c5e40SJason M. Bills     int ret = sd_bus_default_system(&bus);
2233f7c5e40SJason M. Bills     if (ret < 0)
2243f7c5e40SJason M. Bills     {
2253f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
2263f7c5e40SJason M. Bills             "Failed to connect to system bus",
2273f7c5e40SJason M. Bills             phosphor::logging::entry("ERRNO=0x%X", -ret));
2283f7c5e40SJason M. Bills         sd_bus_unref(bus);
22917eadbfbSKuiying Wang         return sensorUpdatedIndex;
2303f7c5e40SJason M. Bills     }
2313f7c5e40SJason M. Bills     sdbusplus::bus::bus dbus(bus);
232a9423b6eSJason M. Bills     static sdbusplus::bus::match::match sensorAdded(
233a9423b6eSJason M. Bills         dbus,
234a9423b6eSJason M. Bills         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
235a9423b6eSJason M. Bills         "sensors/'",
236a9423b6eSJason M. Bills         [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
237a9423b6eSJason M. Bills 
238a9423b6eSJason M. Bills     static sdbusplus::bus::match::match sensorRemoved(
239a9423b6eSJason M. Bills         dbus,
240a9423b6eSJason M. Bills         "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
241a9423b6eSJason M. Bills         "openbmc_project/sensors/'",
242a9423b6eSJason M. Bills         [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
243a9423b6eSJason M. Bills 
244a9423b6eSJason M. Bills     if (sensorTreePtr)
245a9423b6eSJason M. Bills     {
246a9423b6eSJason M. Bills         subtree = sensorTreePtr;
24717eadbfbSKuiying Wang         return sensorUpdatedIndex;
248a9423b6eSJason M. Bills     }
249a9423b6eSJason M. Bills 
250a9423b6eSJason M. Bills     sensorTreePtr = std::make_shared<SensorSubTree>();
251a9423b6eSJason M. Bills 
2523f7c5e40SJason M. Bills     auto mapperCall =
2533f7c5e40SJason M. Bills         dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
2543f7c5e40SJason M. Bills                              "/xyz/openbmc_project/object_mapper",
2553f7c5e40SJason M. Bills                              "xyz.openbmc_project.ObjectMapper", "GetSubTree");
25652341e85SJason M. Bills     static constexpr const auto depth = 2;
2573f7c5e40SJason M. Bills     static constexpr std::array<const char*, 3> interfaces = {
2583f7c5e40SJason M. Bills         "xyz.openbmc_project.Sensor.Value",
2593f7c5e40SJason M. Bills         "xyz.openbmc_project.Sensor.Threshold.Warning",
2603f7c5e40SJason M. Bills         "xyz.openbmc_project.Sensor.Threshold.Critical"};
2613f7c5e40SJason M. Bills     mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
2623f7c5e40SJason M. Bills 
2633f7c5e40SJason M. Bills     try
2643f7c5e40SJason M. Bills     {
2653f7c5e40SJason M. Bills         auto mapperReply = dbus.call(mapperCall);
266a9423b6eSJason M. Bills         mapperReply.read(*sensorTreePtr);
2673f7c5e40SJason M. Bills     }
268*bd51e6a9SPatrick Williams     catch (const sdbusplus::exception_t& e)
2693f7c5e40SJason M. Bills     {
27052341e85SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
27117eadbfbSKuiying Wang         return sensorUpdatedIndex;
272a9423b6eSJason M. Bills     }
273a9423b6eSJason M. Bills     subtree = sensorTreePtr;
27417eadbfbSKuiying Wang     sensorUpdatedIndex++;
27506aa21abSJosh Lehan     // The SDR is being regenerated, wipe the old stats
27606aa21abSJosh Lehan     sdrStatsTable.wipeTable();
27717eadbfbSKuiying Wang     return sensorUpdatedIndex;
278a9423b6eSJason M. Bills }
279a9423b6eSJason M. Bills 
280a9423b6eSJason M. Bills inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
281a9423b6eSJason M. Bills {
282a9423b6eSJason M. Bills     static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
283a9423b6eSJason M. Bills     bool sensorNumMapUpated = false;
28417eadbfbSKuiying Wang     static uint16_t prevSensorUpdatedIndex = 0;
285a9423b6eSJason M. Bills     std::shared_ptr<SensorSubTree> sensorTree;
28617eadbfbSKuiying Wang     uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
287a9423b6eSJason M. Bills     if (!sensorTree)
288a9423b6eSJason M. Bills     {
289a9423b6eSJason M. Bills         return sensorNumMapUpated;
290a9423b6eSJason M. Bills     }
291a9423b6eSJason M. Bills 
29217eadbfbSKuiying Wang     if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
293a9423b6eSJason M. Bills     {
294a9423b6eSJason M. Bills         sensorNumMap = sensorNumMapPtr;
295a9423b6eSJason M. Bills         return sensorNumMapUpated;
296a9423b6eSJason M. Bills     }
29717eadbfbSKuiying Wang     prevSensorUpdatedIndex = curSensorUpdatedIndex;
298a9423b6eSJason M. Bills 
299a9423b6eSJason M. Bills     sensorNumMapPtr = std::make_shared<SensorNumMap>();
300a9423b6eSJason M. Bills 
301308c3a8bSJohnathan Mantey     uint16_t sensorNum = 0;
302308c3a8bSJohnathan Mantey     uint16_t sensorIndex = 0;
303a9423b6eSJason M. Bills     for (const auto& sensor : *sensorTree)
304a9423b6eSJason M. Bills     {
305a9423b6eSJason M. Bills         sensorNumMapPtr->insert(
306308c3a8bSJohnathan Mantey             SensorNumMap::value_type(sensorNum, sensor.first));
307308c3a8bSJohnathan Mantey         sensorIndex++;
308308c3a8bSJohnathan Mantey         if (sensorIndex == maxSensorsPerLUN)
309308c3a8bSJohnathan Mantey         {
310308c3a8bSJohnathan Mantey             sensorIndex = lun1Sensor0;
311308c3a8bSJohnathan Mantey         }
312308c3a8bSJohnathan Mantey         else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
313308c3a8bSJohnathan Mantey         {
314308c3a8bSJohnathan Mantey             // Skip assigning LUN 0x2 any sensors
315308c3a8bSJohnathan Mantey             sensorIndex = lun3Sensor0;
316308c3a8bSJohnathan Mantey         }
317308c3a8bSJohnathan Mantey         else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
318308c3a8bSJohnathan Mantey         {
319308c3a8bSJohnathan Mantey             // this is an error, too many IPMI sensors
320308c3a8bSJohnathan Mantey             throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
321308c3a8bSJohnathan Mantey         }
322308c3a8bSJohnathan Mantey         sensorNum = sensorIndex;
323a9423b6eSJason M. Bills     }
324a9423b6eSJason M. Bills     sensorNumMap = sensorNumMapPtr;
325a9423b6eSJason M. Bills     sensorNumMapUpated = true;
326a9423b6eSJason M. Bills     return sensorNumMapUpated;
327a9423b6eSJason M. Bills }
328a9423b6eSJason M. Bills } // namespace details
329a9423b6eSJason M. Bills 
330a9423b6eSJason M. Bills inline static bool getSensorSubtree(SensorSubTree& subtree)
331a9423b6eSJason M. Bills {
332a9423b6eSJason M. Bills     std::shared_ptr<SensorSubTree> sensorTree;
333a9423b6eSJason M. Bills     details::getSensorSubtree(sensorTree);
334a9423b6eSJason M. Bills     if (!sensorTree)
335a9423b6eSJason M. Bills     {
3363f7c5e40SJason M. Bills         return false;
3373f7c5e40SJason M. Bills     }
338a9423b6eSJason M. Bills 
339a9423b6eSJason M. Bills     subtree = *sensorTree;
3403f7c5e40SJason M. Bills     return true;
3413f7c5e40SJason M. Bills }
3423f7c5e40SJason M. Bills 
3433f7c5e40SJason M. Bills struct CmpStr
3443f7c5e40SJason M. Bills {
3453f7c5e40SJason M. Bills     bool operator()(const char* a, const char* b) const
3463f7c5e40SJason M. Bills     {
3473f7c5e40SJason M. Bills         return std::strcmp(a, b) < 0;
3483f7c5e40SJason M. Bills     }
3493f7c5e40SJason M. Bills };
3503f7c5e40SJason M. Bills 
35152341e85SJason M. Bills enum class SensorTypeCodes : uint8_t
35252341e85SJason M. Bills {
35352341e85SJason M. Bills     reserved = 0x0,
35452341e85SJason M. Bills     temperature = 0x1,
35552341e85SJason M. Bills     voltage = 0x2,
35652341e85SJason M. Bills     current = 0x3,
35752341e85SJason M. Bills     fan = 0x4,
35852341e85SJason M. Bills     other = 0xB,
35952341e85SJason M. Bills };
36052341e85SJason M. Bills 
3613f7c5e40SJason M. Bills const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
3623f7c5e40SJason M. Bills     sensorTypes{{{"temperature", SensorTypeCodes::temperature},
3633f7c5e40SJason M. Bills                  {"voltage", SensorTypeCodes::voltage},
3643f7c5e40SJason M. Bills                  {"current", SensorTypeCodes::current},
3653f7c5e40SJason M. Bills                  {"fan_tach", SensorTypeCodes::fan},
366f426f334SJames Feist                  {"fan_pwm", SensorTypeCodes::fan},
3673f7c5e40SJason M. Bills                  {"power", SensorTypeCodes::other}}};
3683f7c5e40SJason M. Bills 
3693f7c5e40SJason M. Bills inline static std::string getSensorTypeStringFromPath(const std::string& path)
3703f7c5e40SJason M. Bills {
3713f7c5e40SJason M. Bills     // get sensor type string from path, path is defined as
3723f7c5e40SJason M. Bills     // /xyz/openbmc_project/sensors/<type>/label
3733f7c5e40SJason M. Bills     size_t typeEnd = path.rfind("/");
374360f593bSJason M. Bills     if (typeEnd == std::string::npos)
3753f7c5e40SJason M. Bills     {
3763f7c5e40SJason M. Bills         return path;
3773f7c5e40SJason M. Bills     }
378360f593bSJason M. Bills     size_t typeStart = path.rfind("/", typeEnd - 1);
379360f593bSJason M. Bills     if (typeStart == std::string::npos)
380360f593bSJason M. Bills     {
381360f593bSJason M. Bills         return path;
382360f593bSJason M. Bills     }
383360f593bSJason M. Bills     // Start at the character after the '/'
384360f593bSJason M. Bills     typeStart++;
385360f593bSJason M. Bills     return path.substr(typeStart, typeEnd - typeStart);
386360f593bSJason M. Bills }
3873f7c5e40SJason M. Bills 
3883f7c5e40SJason M. Bills inline static uint8_t getSensorTypeFromPath(const std::string& path)
3893f7c5e40SJason M. Bills {
3903f7c5e40SJason M. Bills     uint8_t sensorType = 0;
3913f7c5e40SJason M. Bills     std::string type = getSensorTypeStringFromPath(path);
3923f7c5e40SJason M. Bills     auto findSensor = sensorTypes.find(type.c_str());
3933f7c5e40SJason M. Bills     if (findSensor != sensorTypes.end())
3943f7c5e40SJason M. Bills     {
3953f7c5e40SJason M. Bills         sensorType = static_cast<uint8_t>(findSensor->second);
3963f7c5e40SJason M. Bills     } // else default 0x0 RESERVED
3973f7c5e40SJason M. Bills 
3983f7c5e40SJason M. Bills     return sensorType;
3993f7c5e40SJason M. Bills }
4003f7c5e40SJason M. Bills 
401308c3a8bSJohnathan Mantey inline static uint16_t getSensorNumberFromPath(const std::string& path)
4023f7c5e40SJason M. Bills {
403a9423b6eSJason M. Bills     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
404a9423b6eSJason M. Bills     details::getSensorNumMap(sensorNumMapPtr);
405a9423b6eSJason M. Bills     if (!sensorNumMapPtr)
406a9423b6eSJason M. Bills     {
407308c3a8bSJohnathan Mantey         return invalidSensorNumber;
408a9423b6eSJason M. Bills     }
4093f7c5e40SJason M. Bills 
410a9423b6eSJason M. Bills     try
4113f7c5e40SJason M. Bills     {
412a9423b6eSJason M. Bills         return sensorNumMapPtr->right.at(path);
413a9423b6eSJason M. Bills     }
414*bd51e6a9SPatrick Williams     catch (const std::out_of_range& e)
4153f7c5e40SJason M. Bills     {
416a9423b6eSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
417308c3a8bSJohnathan Mantey         return invalidSensorNumber;
4183f7c5e40SJason M. Bills     }
4193f7c5e40SJason M. Bills }
4203f7c5e40SJason M. Bills 
4213f7c5e40SJason M. Bills inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
4223f7c5e40SJason M. Bills {
4233f7c5e40SJason M. Bills     // TODO: Add support for additional reading types as needed
4243f7c5e40SJason M. Bills     return 0x1; // reading type = threshold
4253f7c5e40SJason M. Bills }
4263f7c5e40SJason M. Bills 
427308c3a8bSJohnathan Mantey inline static std::string getPathFromSensorNumber(uint16_t sensorNum)
4283f7c5e40SJason M. Bills {
429a9423b6eSJason M. Bills     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
430a9423b6eSJason M. Bills     details::getSensorNumMap(sensorNumMapPtr);
431a9423b6eSJason M. Bills     if (!sensorNumMapPtr)
4323f7c5e40SJason M. Bills     {
433a9423b6eSJason M. Bills         return std::string();
4343f7c5e40SJason M. Bills     }
4353f7c5e40SJason M. Bills 
436a9423b6eSJason M. Bills     try
4373f7c5e40SJason M. Bills     {
438a9423b6eSJason M. Bills         return sensorNumMapPtr->left.at(sensorNum);
439a9423b6eSJason M. Bills     }
440*bd51e6a9SPatrick Williams     catch (const std::out_of_range& e)
4413f7c5e40SJason M. Bills     {
442a9423b6eSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
443a9423b6eSJason M. Bills         return std::string();
4443f7c5e40SJason M. Bills     }
4453f7c5e40SJason M. Bills }
446262276f4SPatrick Venture 
447262276f4SPatrick Venture namespace ipmi
448262276f4SPatrick Venture {
449262276f4SPatrick Venture 
450262276f4SPatrick Venture static inline std::map<std::string, std::vector<std::string>>
451262276f4SPatrick Venture     getObjectInterfaces(const char* path)
452262276f4SPatrick Venture {
453262276f4SPatrick Venture     std::map<std::string, std::vector<std::string>> interfacesResponse;
454262276f4SPatrick Venture     std::vector<std::string> interfaces;
455262276f4SPatrick Venture     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
456262276f4SPatrick Venture 
457262276f4SPatrick Venture     sdbusplus::message::message getObjectMessage =
458262276f4SPatrick Venture         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
459262276f4SPatrick Venture                               "/xyz/openbmc_project/object_mapper",
460262276f4SPatrick Venture                               "xyz.openbmc_project.ObjectMapper", "GetObject");
461262276f4SPatrick Venture     getObjectMessage.append(path, interfaces);
462262276f4SPatrick Venture 
463262276f4SPatrick Venture     try
464262276f4SPatrick Venture     {
465262276f4SPatrick Venture         sdbusplus::message::message response = dbus->call(getObjectMessage);
466262276f4SPatrick Venture         response.read(interfacesResponse);
467262276f4SPatrick Venture     }
468262276f4SPatrick Venture     catch (const std::exception& e)
469262276f4SPatrick Venture     {
470262276f4SPatrick Venture         phosphor::logging::log<phosphor::logging::level::ERR>(
471262276f4SPatrick Venture             "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
472262276f4SPatrick Venture             phosphor::logging::entry("WHAT=%s", e.what()));
473262276f4SPatrick Venture     }
474262276f4SPatrick Venture 
475262276f4SPatrick Venture     return interfacesResponse;
476262276f4SPatrick Venture }
477262276f4SPatrick Venture 
478262276f4SPatrick Venture static inline std::map<std::string, DbusVariant>
479262276f4SPatrick Venture     getEntityManagerProperties(const char* path, const char* interface)
480262276f4SPatrick Venture {
481262276f4SPatrick Venture     std::map<std::string, DbusVariant> properties;
482262276f4SPatrick Venture     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
483262276f4SPatrick Venture 
484262276f4SPatrick Venture     sdbusplus::message::message getProperties =
485262276f4SPatrick Venture         dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
486262276f4SPatrick Venture                               "org.freedesktop.DBus.Properties", "GetAll");
487262276f4SPatrick Venture     getProperties.append(interface);
488262276f4SPatrick Venture 
489262276f4SPatrick Venture     try
490262276f4SPatrick Venture     {
491262276f4SPatrick Venture         sdbusplus::message::message response = dbus->call(getProperties);
492262276f4SPatrick Venture         response.read(properties);
493262276f4SPatrick Venture     }
494262276f4SPatrick Venture     catch (const std::exception& e)
495262276f4SPatrick Venture     {
496262276f4SPatrick Venture         phosphor::logging::log<phosphor::logging::level::ERR>(
497262276f4SPatrick Venture             "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
498262276f4SPatrick Venture             phosphor::logging::entry("INTF=%s", interface),
499262276f4SPatrick Venture             phosphor::logging::entry("WHAT=%s", e.what()));
500262276f4SPatrick Venture     }
501262276f4SPatrick Venture 
502262276f4SPatrick Venture     return properties;
503262276f4SPatrick Venture }
504262276f4SPatrick Venture 
505262276f4SPatrick Venture static inline const std::string* getSensorConfigurationInterface(
506262276f4SPatrick Venture     const std::map<std::string, std::vector<std::string>>&
507262276f4SPatrick Venture         sensorInterfacesResponse)
508262276f4SPatrick Venture {
509262276f4SPatrick Venture     auto entityManagerService =
510262276f4SPatrick Venture         sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
511262276f4SPatrick Venture     if (entityManagerService == sensorInterfacesResponse.end())
512262276f4SPatrick Venture     {
513262276f4SPatrick Venture         return nullptr;
514262276f4SPatrick Venture     }
515262276f4SPatrick Venture 
516262276f4SPatrick Venture     // Find the fan configuration first (fans can have multiple configuration
517262276f4SPatrick Venture     // interfaces).
518262276f4SPatrick Venture     for (const auto& entry : entityManagerService->second)
519262276f4SPatrick Venture     {
520262276f4SPatrick Venture         if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
521262276f4SPatrick Venture             entry == "xyz.openbmc_project.Configuration.I2CFan" ||
522262276f4SPatrick Venture             entry == "xyz.openbmc_project.Configuration.NuvotonFan")
523262276f4SPatrick Venture         {
524262276f4SPatrick Venture             return &entry;
525262276f4SPatrick Venture         }
526262276f4SPatrick Venture     }
527262276f4SPatrick Venture 
528262276f4SPatrick Venture     for (const auto& entry : entityManagerService->second)
529262276f4SPatrick Venture     {
530262276f4SPatrick Venture         if (boost::algorithm::starts_with(entry,
531262276f4SPatrick Venture                                           "xyz.openbmc_project.Configuration."))
532262276f4SPatrick Venture         {
533262276f4SPatrick Venture             return &entry;
534262276f4SPatrick Venture         }
535262276f4SPatrick Venture     }
536262276f4SPatrick Venture 
537262276f4SPatrick Venture     return nullptr;
538262276f4SPatrick Venture }
539262276f4SPatrick Venture 
540262276f4SPatrick Venture // Follow Association properties for Sensor back to the Board dbus object to
541262276f4SPatrick Venture // check for an EntityId and EntityInstance property.
542262276f4SPatrick Venture static inline void updateIpmiFromAssociation(const std::string& path,
543262276f4SPatrick Venture                                              const SensorMap& sensorMap,
544262276f4SPatrick Venture                                              uint8_t& entityId,
545262276f4SPatrick Venture                                              uint8_t& entityInstance)
546262276f4SPatrick Venture {
547262276f4SPatrick Venture     namespace fs = std::filesystem;
548262276f4SPatrick Venture 
549262276f4SPatrick Venture     auto sensorAssociationObject =
550262276f4SPatrick Venture         sensorMap.find("xyz.openbmc_project.Association.Definitions");
551262276f4SPatrick Venture     if (sensorAssociationObject == sensorMap.end())
552262276f4SPatrick Venture     {
553262276f4SPatrick Venture         if constexpr (debug)
554262276f4SPatrick Venture         {
555262276f4SPatrick Venture             std::fprintf(stderr, "path=%s, no association interface found\n",
556262276f4SPatrick Venture                          path.c_str());
557262276f4SPatrick Venture         }
558262276f4SPatrick Venture 
559262276f4SPatrick Venture         return;
560262276f4SPatrick Venture     }
561262276f4SPatrick Venture 
562262276f4SPatrick Venture     auto associationObject =
563262276f4SPatrick Venture         sensorAssociationObject->second.find("Associations");
564262276f4SPatrick Venture     if (associationObject == sensorAssociationObject->second.end())
565262276f4SPatrick Venture     {
566262276f4SPatrick Venture         if constexpr (debug)
567262276f4SPatrick Venture         {
568262276f4SPatrick Venture             std::fprintf(stderr, "path=%s, no association records found\n",
569262276f4SPatrick Venture                          path.c_str());
570262276f4SPatrick Venture         }
571262276f4SPatrick Venture 
572262276f4SPatrick Venture         return;
573262276f4SPatrick Venture     }
574262276f4SPatrick Venture 
575262276f4SPatrick Venture     std::vector<Association> associationValues =
576262276f4SPatrick Venture         std::get<std::vector<Association>>(associationObject->second);
577262276f4SPatrick Venture 
578262276f4SPatrick Venture     // loop through the Associations looking for the right one:
579262276f4SPatrick Venture     for (const auto& entry : associationValues)
580262276f4SPatrick Venture     {
581262276f4SPatrick Venture         // forward, reverse, endpoint
582262276f4SPatrick Venture         const std::string& forward = std::get<0>(entry);
583262276f4SPatrick Venture         const std::string& reverse = std::get<1>(entry);
584262276f4SPatrick Venture         const std::string& endpoint = std::get<2>(entry);
585262276f4SPatrick Venture 
586262276f4SPatrick Venture         // We only currently concern ourselves with chassis+all_sensors.
587262276f4SPatrick Venture         if (!(forward == "chassis" && reverse == "all_sensors"))
588262276f4SPatrick Venture         {
589262276f4SPatrick Venture             continue;
590262276f4SPatrick Venture         }
591262276f4SPatrick Venture 
592262276f4SPatrick Venture         // the endpoint is the board entry provided by
593262276f4SPatrick Venture         // Entity-Manager. so let's grab its properties if it has
594262276f4SPatrick Venture         // the right interface.
595262276f4SPatrick Venture 
596262276f4SPatrick Venture         // just try grabbing the properties first.
597262276f4SPatrick Venture         std::map<std::string, DbusVariant> ipmiProperties =
598262276f4SPatrick Venture             getEntityManagerProperties(
599262276f4SPatrick Venture                 endpoint.c_str(),
600262276f4SPatrick Venture                 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
601262276f4SPatrick Venture 
602262276f4SPatrick Venture         auto entityIdProp = ipmiProperties.find("EntityId");
603262276f4SPatrick Venture         auto entityInstanceProp = ipmiProperties.find("EntityInstance");
604262276f4SPatrick Venture         if (entityIdProp != ipmiProperties.end())
605262276f4SPatrick Venture         {
606262276f4SPatrick Venture             entityId =
607262276f4SPatrick Venture                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
608262276f4SPatrick Venture         }
609262276f4SPatrick Venture         if (entityInstanceProp != ipmiProperties.end())
610262276f4SPatrick Venture         {
611262276f4SPatrick Venture             entityInstance = static_cast<uint8_t>(
612262276f4SPatrick Venture                 std::get<uint64_t>(entityInstanceProp->second));
613262276f4SPatrick Venture         }
614262276f4SPatrick Venture 
615262276f4SPatrick Venture         // Now check the entity-manager entry for this sensor to see
616262276f4SPatrick Venture         // if it has its own value and use that instead.
617262276f4SPatrick Venture         //
618262276f4SPatrick Venture         // In theory, checking this first saves us from checking
619262276f4SPatrick Venture         // both, except in most use-cases identified, there won't be
620262276f4SPatrick Venture         // a per sensor override, so we need to always check both.
621262276f4SPatrick Venture         std::string sensorNameFromPath = fs::path(path).filename();
622262276f4SPatrick Venture 
623262276f4SPatrick Venture         std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
624262276f4SPatrick Venture 
625262276f4SPatrick Venture         // Download the interfaces for the sensor from
626262276f4SPatrick Venture         // Entity-Manager to find the name of the configuration
627262276f4SPatrick Venture         // interface.
628262276f4SPatrick Venture         std::map<std::string, std::vector<std::string>>
629262276f4SPatrick Venture             sensorInterfacesResponse =
630262276f4SPatrick Venture                 getObjectInterfaces(sensorConfigPath.c_str());
631262276f4SPatrick Venture 
632262276f4SPatrick Venture         const std::string* configurationInterface =
633262276f4SPatrick Venture             getSensorConfigurationInterface(sensorInterfacesResponse);
634262276f4SPatrick Venture 
635262276f4SPatrick Venture         // We didnt' find a configuration interface for this sensor, but we
636262276f4SPatrick Venture         // followed the Association property to get here, so we're done
637262276f4SPatrick Venture         // searching.
638262276f4SPatrick Venture         if (!configurationInterface)
639262276f4SPatrick Venture         {
640262276f4SPatrick Venture             break;
641262276f4SPatrick Venture         }
642262276f4SPatrick Venture 
643262276f4SPatrick Venture         // We found a configuration interface.
644262276f4SPatrick Venture         std::map<std::string, DbusVariant> configurationProperties =
645262276f4SPatrick Venture             getEntityManagerProperties(sensorConfigPath.c_str(),
646262276f4SPatrick Venture                                        configurationInterface->c_str());
647262276f4SPatrick Venture 
648262276f4SPatrick Venture         entityIdProp = configurationProperties.find("EntityId");
649262276f4SPatrick Venture         entityInstanceProp = configurationProperties.find("EntityInstance");
650262276f4SPatrick Venture         if (entityIdProp != configurationProperties.end())
651262276f4SPatrick Venture         {
652262276f4SPatrick Venture             entityId =
653262276f4SPatrick Venture                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
654262276f4SPatrick Venture         }
655262276f4SPatrick Venture         if (entityInstanceProp != configurationProperties.end())
656262276f4SPatrick Venture         {
657262276f4SPatrick Venture             entityInstance = static_cast<uint8_t>(
658262276f4SPatrick Venture                 std::get<uint64_t>(entityInstanceProp->second));
659262276f4SPatrick Venture         }
660262276f4SPatrick Venture 
661262276f4SPatrick Venture         // stop searching Association records.
662262276f4SPatrick Venture         break;
663262276f4SPatrick Venture     } // end for Association vectors.
664262276f4SPatrick Venture 
665262276f4SPatrick Venture     if constexpr (debug)
666262276f4SPatrick Venture     {
667262276f4SPatrick Venture         std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
668262276f4SPatrick Venture                      path.c_str(), entityId, entityInstance);
669262276f4SPatrick Venture     }
670262276f4SPatrick Venture }
671262276f4SPatrick Venture 
672262276f4SPatrick Venture } // namespace ipmi
673