xref: /openbmc/intel-ipmi-oem/include/sdrutils.hpp (revision 1bcced0820999e7e050257b9f78cf36b23eb762b)
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 {
operator ()CmpStrVersion393f7c5e40SJason 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:
getName(void) const7706aa21abSJosh Lehan     const std::string& getName(void) const
7806aa21abSJosh Lehan     {
7906aa21abSJosh Lehan         return sensorName;
8006aa21abSJosh Lehan     }
8106aa21abSJosh Lehan 
updateName(std::string_view name)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
updateReading(double reading,int raw)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
122*1bcced08SPatrick Williams                       << ": Recovered reading, value=" << reading << " byte="
123*1bcced08SPatrick Williams                       << raw << ", Reading counts good=" << numReadings
12406aa21abSJosh Lehan                       << " miss=" << numMissings
12506aa21abSJosh Lehan                       << ", Prior miss streak=" << numStreakMiss << "\n";
12606aa21abSJosh Lehan         }
12706aa21abSJosh Lehan 
12806aa21abSJosh Lehan         // Initialize min/max if the first successful reading
12906aa21abSJosh Lehan         if (numReadings == 0)
13006aa21abSJosh Lehan         {
13106aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
13206aa21abSJosh Lehan                       << ": First reading, value=" << reading << " byte=" << raw
13306aa21abSJosh Lehan                       << "\n";
13406aa21abSJosh Lehan 
13506aa21abSJosh Lehan             minValue = reading;
13606aa21abSJosh Lehan             maxValue = reading;
13706aa21abSJosh Lehan         }
13806aa21abSJosh Lehan 
13906aa21abSJosh Lehan         numStreakMiss = 0;
14006aa21abSJosh Lehan         ++numReadings;
14106aa21abSJosh Lehan         ++numStreakRead;
14206aa21abSJosh Lehan 
14306aa21abSJosh Lehan         // Only provide subsequent output if new min/max established
14406aa21abSJosh Lehan         if (reading < minValue)
14506aa21abSJosh Lehan         {
14606aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
14706aa21abSJosh Lehan                       << ": Lowest reading, value=" << reading
14806aa21abSJosh Lehan                       << " byte=" << raw << "\n";
14906aa21abSJosh Lehan 
15006aa21abSJosh Lehan             minValue = reading;
15106aa21abSJosh Lehan         }
15206aa21abSJosh Lehan 
15306aa21abSJosh Lehan         if (reading > maxValue)
15406aa21abSJosh Lehan         {
15506aa21abSJosh Lehan             std::cerr << "IPMI sensor " << sensorName
15606aa21abSJosh Lehan                       << ": Highest reading, value=" << reading
15706aa21abSJosh Lehan                       << " byte=" << raw << "\n";
15806aa21abSJosh Lehan 
15906aa21abSJosh Lehan             maxValue = reading;
16006aa21abSJosh Lehan         }
16106aa21abSJosh Lehan 
16206aa21abSJosh Lehan         return first;
16306aa21abSJosh Lehan     }
16406aa21abSJosh Lehan };
16506aa21abSJosh Lehan 
16606aa21abSJosh Lehan class IPMIStatsTable
16706aa21abSJosh Lehan {
16806aa21abSJosh Lehan   private:
16906aa21abSJosh Lehan     std::vector<IPMIStatsEntry> entries;
17006aa21abSJosh Lehan 
17106aa21abSJosh Lehan   private:
padEntries(size_t index)17206aa21abSJosh Lehan     void padEntries(size_t index)
17306aa21abSJosh Lehan     {
17406aa21abSJosh Lehan         char hexbuf[16];
17506aa21abSJosh Lehan 
17606aa21abSJosh Lehan         // Pad vector until entries[index] becomes a valid index
17706aa21abSJosh Lehan         while (entries.size() <= index)
17806aa21abSJosh Lehan         {
17906aa21abSJosh Lehan             // As name not known yet, use human-readable hex as name
18006aa21abSJosh Lehan             IPMIStatsEntry newEntry;
18106aa21abSJosh Lehan             sprintf(hexbuf, "0x%02zX", entries.size());
18206aa21abSJosh Lehan             newEntry.updateName(hexbuf);
18306aa21abSJosh Lehan 
18406aa21abSJosh Lehan             entries.push_back(std::move(newEntry));
18506aa21abSJosh Lehan         }
18606aa21abSJosh Lehan     }
18706aa21abSJosh Lehan 
18806aa21abSJosh Lehan   public:
wipeTable(void)18906aa21abSJosh Lehan     void wipeTable(void)
19006aa21abSJosh Lehan     {
19106aa21abSJosh Lehan         entries.clear();
19206aa21abSJosh Lehan     }
19306aa21abSJosh Lehan 
getName(size_t index)19406aa21abSJosh Lehan     const std::string& getName(size_t index)
19506aa21abSJosh Lehan     {
19606aa21abSJosh Lehan         padEntries(index);
19706aa21abSJosh Lehan         return entries[index].getName();
19806aa21abSJosh Lehan     }
19906aa21abSJosh Lehan 
updateName(size_t index,std::string_view name)20006aa21abSJosh Lehan     void updateName(size_t index, std::string_view name)
20106aa21abSJosh Lehan     {
20206aa21abSJosh Lehan         padEntries(index);
20306aa21abSJosh Lehan         entries[index].updateName(name);
20406aa21abSJosh Lehan     }
20506aa21abSJosh Lehan 
updateReading(size_t index,double reading,int raw)20606aa21abSJosh Lehan     bool updateReading(size_t index, double reading, int raw)
20706aa21abSJosh Lehan     {
20806aa21abSJosh Lehan         padEntries(index);
20906aa21abSJosh Lehan         return entries[index].updateReading(reading, raw);
21006aa21abSJosh Lehan     }
21106aa21abSJosh Lehan };
21206aa21abSJosh Lehan 
21306aa21abSJosh Lehan // This object is global singleton, used from a variety of places
21406aa21abSJosh Lehan inline IPMIStatsTable sdrStatsTable;
21506aa21abSJosh Lehan 
getSensorSubtree(std::shared_ptr<SensorSubTree> & subtree)21617eadbfbSKuiying Wang inline static uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
217a9423b6eSJason M. Bills {
218a9423b6eSJason M. Bills     static std::shared_ptr<SensorSubTree> sensorTreePtr;
21917eadbfbSKuiying Wang     static uint16_t sensorUpdatedIndex = 0;
2203f7c5e40SJason M. Bills     sd_bus* bus = NULL;
2213f7c5e40SJason M. Bills     int ret = sd_bus_default_system(&bus);
2223f7c5e40SJason M. Bills     if (ret < 0)
2233f7c5e40SJason M. Bills     {
2243f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
2253f7c5e40SJason M. Bills             "Failed to connect to system bus",
2263f7c5e40SJason M. Bills             phosphor::logging::entry("ERRNO=0x%X", -ret));
2273f7c5e40SJason M. Bills         sd_bus_unref(bus);
22817eadbfbSKuiying Wang         return sensorUpdatedIndex;
2293f7c5e40SJason M. Bills     }
230f944d2e5SPatrick Williams     sdbusplus::bus_t dbus(bus);
231f944d2e5SPatrick Williams     static sdbusplus::bus::match_t sensorAdded(
232a9423b6eSJason M. Bills         dbus,
233a9423b6eSJason M. Bills         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
234a9423b6eSJason M. Bills         "sensors/'",
235dcff1506SVernon Mauery         [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
236a9423b6eSJason M. Bills 
237f944d2e5SPatrick Williams     static sdbusplus::bus::match_t sensorRemoved(
238a9423b6eSJason M. Bills         dbus,
239a9423b6eSJason M. Bills         "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
240a9423b6eSJason M. Bills         "openbmc_project/sensors/'",
241dcff1506SVernon Mauery         [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
242a9423b6eSJason M. Bills 
243a9423b6eSJason M. Bills     if (sensorTreePtr)
244a9423b6eSJason M. Bills     {
245a9423b6eSJason M. Bills         subtree = sensorTreePtr;
24617eadbfbSKuiying Wang         return sensorUpdatedIndex;
247a9423b6eSJason M. Bills     }
248a9423b6eSJason M. Bills 
249a9423b6eSJason M. Bills     sensorTreePtr = std::make_shared<SensorSubTree>();
250a9423b6eSJason M. Bills 
251*1bcced08SPatrick Williams     auto mapperCall =
252*1bcced08SPatrick Williams         dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
2533f7c5e40SJason M. Bills                              "/xyz/openbmc_project/object_mapper",
254*1bcced08SPatrick Williams                              "xyz.openbmc_project.ObjectMapper", "GetSubTree");
25552341e85SJason M. Bills     static constexpr const auto depth = 2;
2563f7c5e40SJason M. Bills     static constexpr std::array<const char*, 3> interfaces = {
2573f7c5e40SJason M. Bills         "xyz.openbmc_project.Sensor.Value",
2583f7c5e40SJason M. Bills         "xyz.openbmc_project.Sensor.Threshold.Warning",
2593f7c5e40SJason M. Bills         "xyz.openbmc_project.Sensor.Threshold.Critical"};
2603f7c5e40SJason M. Bills     mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
2613f7c5e40SJason M. Bills 
2623f7c5e40SJason M. Bills     try
2633f7c5e40SJason M. Bills     {
2643f7c5e40SJason M. Bills         auto mapperReply = dbus.call(mapperCall);
265a9423b6eSJason M. Bills         mapperReply.read(*sensorTreePtr);
2663f7c5e40SJason M. Bills     }
267bd51e6a9SPatrick Williams     catch (const sdbusplus::exception_t& e)
2683f7c5e40SJason M. Bills     {
26952341e85SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
27017eadbfbSKuiying Wang         return sensorUpdatedIndex;
271a9423b6eSJason M. Bills     }
272a9423b6eSJason M. Bills     subtree = sensorTreePtr;
27317eadbfbSKuiying Wang     sensorUpdatedIndex++;
27406aa21abSJosh Lehan     // The SDR is being regenerated, wipe the old stats
27506aa21abSJosh Lehan     sdrStatsTable.wipeTable();
27617eadbfbSKuiying Wang     return sensorUpdatedIndex;
277a9423b6eSJason M. Bills }
278a9423b6eSJason M. Bills 
getSensorNumMap(std::shared_ptr<SensorNumMap> & sensorNumMap)279a9423b6eSJason M. Bills inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
280a9423b6eSJason M. Bills {
281a9423b6eSJason M. Bills     static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
282a9423b6eSJason M. Bills     bool sensorNumMapUpated = false;
28317eadbfbSKuiying Wang     static uint16_t prevSensorUpdatedIndex = 0;
284a9423b6eSJason M. Bills     std::shared_ptr<SensorSubTree> sensorTree;
28517eadbfbSKuiying Wang     uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
286a9423b6eSJason M. Bills     if (!sensorTree)
287a9423b6eSJason M. Bills     {
288a9423b6eSJason M. Bills         return sensorNumMapUpated;
289a9423b6eSJason M. Bills     }
290a9423b6eSJason M. Bills 
29117eadbfbSKuiying Wang     if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
292a9423b6eSJason M. Bills     {
293a9423b6eSJason M. Bills         sensorNumMap = sensorNumMapPtr;
294a9423b6eSJason M. Bills         return sensorNumMapUpated;
295a9423b6eSJason M. Bills     }
29617eadbfbSKuiying Wang     prevSensorUpdatedIndex = curSensorUpdatedIndex;
297a9423b6eSJason M. Bills 
298a9423b6eSJason M. Bills     sensorNumMapPtr = std::make_shared<SensorNumMap>();
299a9423b6eSJason M. Bills 
300308c3a8bSJohnathan Mantey     uint16_t sensorNum = 0;
301308c3a8bSJohnathan Mantey     uint16_t sensorIndex = 0;
302a9423b6eSJason M. Bills     for (const auto& sensor : *sensorTree)
303a9423b6eSJason M. Bills     {
304a9423b6eSJason M. Bills         sensorNumMapPtr->insert(
305308c3a8bSJohnathan Mantey             SensorNumMap::value_type(sensorNum, sensor.first));
306308c3a8bSJohnathan Mantey         sensorIndex++;
307308c3a8bSJohnathan Mantey         if (sensorIndex == maxSensorsPerLUN)
308308c3a8bSJohnathan Mantey         {
309308c3a8bSJohnathan Mantey             sensorIndex = lun1Sensor0;
310308c3a8bSJohnathan Mantey         }
311308c3a8bSJohnathan Mantey         else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
312308c3a8bSJohnathan Mantey         {
313308c3a8bSJohnathan Mantey             // Skip assigning LUN 0x2 any sensors
314308c3a8bSJohnathan Mantey             sensorIndex = lun3Sensor0;
315308c3a8bSJohnathan Mantey         }
316308c3a8bSJohnathan Mantey         else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
317308c3a8bSJohnathan Mantey         {
318308c3a8bSJohnathan Mantey             // this is an error, too many IPMI sensors
319308c3a8bSJohnathan Mantey             throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
320308c3a8bSJohnathan Mantey         }
321308c3a8bSJohnathan Mantey         sensorNum = sensorIndex;
322a9423b6eSJason M. Bills     }
323a9423b6eSJason M. Bills     sensorNumMap = sensorNumMapPtr;
324a9423b6eSJason M. Bills     sensorNumMapUpated = true;
325a9423b6eSJason M. Bills     return sensorNumMapUpated;
326a9423b6eSJason M. Bills }
327a9423b6eSJason M. Bills } // namespace details
328a9423b6eSJason M. Bills 
getSensorSubtree(SensorSubTree & subtree)329a9423b6eSJason M. Bills inline static bool getSensorSubtree(SensorSubTree& subtree)
330a9423b6eSJason M. Bills {
331a9423b6eSJason M. Bills     std::shared_ptr<SensorSubTree> sensorTree;
332a9423b6eSJason M. Bills     details::getSensorSubtree(sensorTree);
333a9423b6eSJason M. Bills     if (!sensorTree)
334a9423b6eSJason M. Bills     {
3353f7c5e40SJason M. Bills         return false;
3363f7c5e40SJason M. Bills     }
337a9423b6eSJason M. Bills 
338a9423b6eSJason M. Bills     subtree = *sensorTree;
3393f7c5e40SJason M. Bills     return true;
3403f7c5e40SJason M. Bills }
3413f7c5e40SJason M. Bills 
3423f7c5e40SJason M. Bills struct CmpStr
3433f7c5e40SJason M. Bills {
operator ()CmpStr3443f7c5e40SJason M. Bills     bool operator()(const char* a, const char* b) const
3453f7c5e40SJason M. Bills     {
3463f7c5e40SJason M. Bills         return std::strcmp(a, b) < 0;
3473f7c5e40SJason M. Bills     }
3483f7c5e40SJason M. Bills };
3493f7c5e40SJason M. Bills 
35052341e85SJason M. Bills enum class SensorTypeCodes : uint8_t
35152341e85SJason M. Bills {
35252341e85SJason M. Bills     reserved = 0x0,
35352341e85SJason M. Bills     temperature = 0x1,
35452341e85SJason M. Bills     voltage = 0x2,
35552341e85SJason M. Bills     current = 0x3,
35652341e85SJason M. Bills     fan = 0x4,
35752341e85SJason M. Bills     other = 0xB,
35852341e85SJason M. Bills };
35952341e85SJason M. Bills 
3603f7c5e40SJason M. Bills const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
3613f7c5e40SJason M. Bills     sensorTypes{{{"temperature", SensorTypeCodes::temperature},
3623f7c5e40SJason M. Bills                  {"voltage", SensorTypeCodes::voltage},
3633f7c5e40SJason M. Bills                  {"current", SensorTypeCodes::current},
3643f7c5e40SJason M. Bills                  {"fan_tach", SensorTypeCodes::fan},
365f426f334SJames Feist                  {"fan_pwm", SensorTypeCodes::fan},
3663f7c5e40SJason M. Bills                  {"power", SensorTypeCodes::other}}};
3673f7c5e40SJason M. Bills 
getSensorTypeStringFromPath(const std::string & path)3683f7c5e40SJason M. Bills inline static std::string getSensorTypeStringFromPath(const std::string& path)
3693f7c5e40SJason M. Bills {
3703f7c5e40SJason M. Bills     // get sensor type string from path, path is defined as
3713f7c5e40SJason M. Bills     // /xyz/openbmc_project/sensors/<type>/label
3723f7c5e40SJason M. Bills     size_t typeEnd = path.rfind("/");
373360f593bSJason M. Bills     if (typeEnd == std::string::npos)
3743f7c5e40SJason M. Bills     {
3753f7c5e40SJason M. Bills         return path;
3763f7c5e40SJason M. Bills     }
377360f593bSJason M. Bills     size_t typeStart = path.rfind("/", typeEnd - 1);
378360f593bSJason M. Bills     if (typeStart == std::string::npos)
379360f593bSJason M. Bills     {
380360f593bSJason M. Bills         return path;
381360f593bSJason M. Bills     }
382360f593bSJason M. Bills     // Start at the character after the '/'
383360f593bSJason M. Bills     typeStart++;
384360f593bSJason M. Bills     return path.substr(typeStart, typeEnd - typeStart);
385360f593bSJason M. Bills }
3863f7c5e40SJason M. Bills 
getSensorTypeFromPath(const std::string & path)3873f7c5e40SJason M. Bills inline static uint8_t getSensorTypeFromPath(const std::string& path)
3883f7c5e40SJason M. Bills {
3893f7c5e40SJason M. Bills     uint8_t sensorType = 0;
3903f7c5e40SJason M. Bills     std::string type = getSensorTypeStringFromPath(path);
3913f7c5e40SJason M. Bills     auto findSensor = sensorTypes.find(type.c_str());
3923f7c5e40SJason M. Bills     if (findSensor != sensorTypes.end())
3933f7c5e40SJason M. Bills     {
3943f7c5e40SJason M. Bills         sensorType = static_cast<uint8_t>(findSensor->second);
3953f7c5e40SJason M. Bills     } // else default 0x0 RESERVED
3963f7c5e40SJason M. Bills 
3973f7c5e40SJason M. Bills     return sensorType;
3983f7c5e40SJason M. Bills }
3993f7c5e40SJason M. Bills 
getSensorNumberFromPath(const std::string & path)400308c3a8bSJohnathan Mantey inline static uint16_t getSensorNumberFromPath(const std::string& path)
4013f7c5e40SJason M. Bills {
402a9423b6eSJason M. Bills     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
403a9423b6eSJason M. Bills     details::getSensorNumMap(sensorNumMapPtr);
404a9423b6eSJason M. Bills     if (!sensorNumMapPtr)
405a9423b6eSJason M. Bills     {
406308c3a8bSJohnathan Mantey         return invalidSensorNumber;
407a9423b6eSJason M. Bills     }
4083f7c5e40SJason M. Bills 
409a9423b6eSJason M. Bills     try
4103f7c5e40SJason M. Bills     {
411a9423b6eSJason M. Bills         return sensorNumMapPtr->right.at(path);
412a9423b6eSJason M. Bills     }
413bd51e6a9SPatrick Williams     catch (const std::out_of_range& e)
4143f7c5e40SJason M. Bills     {
415a9423b6eSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
416308c3a8bSJohnathan Mantey         return invalidSensorNumber;
4173f7c5e40SJason M. Bills     }
4183f7c5e40SJason M. Bills }
4193f7c5e40SJason M. Bills 
getSensorEventTypeFromPath(const std::string &)420dcff1506SVernon Mauery inline static uint8_t getSensorEventTypeFromPath(const std::string& /* path */)
4213f7c5e40SJason M. Bills {
4223f7c5e40SJason M. Bills     // TODO: Add support for additional reading types as needed
4233f7c5e40SJason M. Bills     return 0x1; // reading type = threshold
4243f7c5e40SJason M. Bills }
4253f7c5e40SJason M. Bills 
getPathFromSensorNumber(uint16_t sensorNum)426308c3a8bSJohnathan Mantey inline static std::string getPathFromSensorNumber(uint16_t sensorNum)
4273f7c5e40SJason M. Bills {
428a9423b6eSJason M. Bills     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
429a9423b6eSJason M. Bills     details::getSensorNumMap(sensorNumMapPtr);
430a9423b6eSJason M. Bills     if (!sensorNumMapPtr)
4313f7c5e40SJason M. Bills     {
432a9423b6eSJason M. Bills         return std::string();
4333f7c5e40SJason M. Bills     }
4343f7c5e40SJason M. Bills 
435a9423b6eSJason M. Bills     try
4363f7c5e40SJason M. Bills     {
437a9423b6eSJason M. Bills         return sensorNumMapPtr->left.at(sensorNum);
438a9423b6eSJason M. Bills     }
439bd51e6a9SPatrick Williams     catch (const std::out_of_range& e)
4403f7c5e40SJason M. Bills     {
441a9423b6eSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
442a9423b6eSJason M. Bills         return std::string();
4433f7c5e40SJason M. Bills     }
4443f7c5e40SJason M. Bills }
445262276f4SPatrick Venture 
446262276f4SPatrick Venture namespace ipmi
447262276f4SPatrick Venture {
448262276f4SPatrick Venture 
449262276f4SPatrick Venture static inline std::map<std::string, std::vector<std::string>>
getObjectInterfaces(const char * path)450262276f4SPatrick Venture     getObjectInterfaces(const char* path)
451262276f4SPatrick Venture {
452262276f4SPatrick Venture     std::map<std::string, std::vector<std::string>> interfacesResponse;
453262276f4SPatrick Venture     std::vector<std::string> interfaces;
454262276f4SPatrick Venture     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
455262276f4SPatrick Venture 
456f944d2e5SPatrick Williams     sdbusplus::message_t getObjectMessage =
457262276f4SPatrick Venture         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
458262276f4SPatrick Venture                               "/xyz/openbmc_project/object_mapper",
459262276f4SPatrick Venture                               "xyz.openbmc_project.ObjectMapper", "GetObject");
460262276f4SPatrick Venture     getObjectMessage.append(path, interfaces);
461262276f4SPatrick Venture 
462262276f4SPatrick Venture     try
463262276f4SPatrick Venture     {
464f944d2e5SPatrick Williams         sdbusplus::message_t response = dbus->call(getObjectMessage);
465262276f4SPatrick Venture         response.read(interfacesResponse);
466262276f4SPatrick Venture     }
467262276f4SPatrick Venture     catch (const std::exception& e)
468262276f4SPatrick Venture     {
469262276f4SPatrick Venture         phosphor::logging::log<phosphor::logging::level::ERR>(
470262276f4SPatrick Venture             "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
471262276f4SPatrick Venture             phosphor::logging::entry("WHAT=%s", e.what()));
472262276f4SPatrick Venture     }
473262276f4SPatrick Venture 
474262276f4SPatrick Venture     return interfacesResponse;
475262276f4SPatrick Venture }
476262276f4SPatrick Venture 
477262276f4SPatrick Venture static inline std::map<std::string, DbusVariant>
getEntityManagerProperties(const char * path,const char * interface)478262276f4SPatrick Venture     getEntityManagerProperties(const char* path, const char* interface)
479262276f4SPatrick Venture {
480262276f4SPatrick Venture     std::map<std::string, DbusVariant> properties;
481262276f4SPatrick Venture     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
482262276f4SPatrick Venture 
483f944d2e5SPatrick Williams     sdbusplus::message_t getProperties =
484262276f4SPatrick Venture         dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
485262276f4SPatrick Venture                               "org.freedesktop.DBus.Properties", "GetAll");
486262276f4SPatrick Venture     getProperties.append(interface);
487262276f4SPatrick Venture 
488262276f4SPatrick Venture     try
489262276f4SPatrick Venture     {
490f944d2e5SPatrick Williams         sdbusplus::message_t response = dbus->call(getProperties);
491262276f4SPatrick Venture         response.read(properties);
492262276f4SPatrick Venture     }
493262276f4SPatrick Venture     catch (const std::exception& e)
494262276f4SPatrick Venture     {
495262276f4SPatrick Venture         phosphor::logging::log<phosphor::logging::level::ERR>(
496262276f4SPatrick Venture             "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
497262276f4SPatrick Venture             phosphor::logging::entry("INTF=%s", interface),
498262276f4SPatrick Venture             phosphor::logging::entry("WHAT=%s", e.what()));
499262276f4SPatrick Venture     }
500262276f4SPatrick Venture 
501262276f4SPatrick Venture     return properties;
502262276f4SPatrick Venture }
503262276f4SPatrick Venture 
getSensorConfigurationInterface(const std::map<std::string,std::vector<std::string>> & sensorInterfacesResponse)504262276f4SPatrick Venture static inline const std::string* getSensorConfigurationInterface(
505262276f4SPatrick Venture     const std::map<std::string, std::vector<std::string>>&
506262276f4SPatrick Venture         sensorInterfacesResponse)
507262276f4SPatrick Venture {
508262276f4SPatrick Venture     auto entityManagerService =
509262276f4SPatrick Venture         sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
510262276f4SPatrick Venture     if (entityManagerService == sensorInterfacesResponse.end())
511262276f4SPatrick Venture     {
512262276f4SPatrick Venture         return nullptr;
513262276f4SPatrick Venture     }
514262276f4SPatrick Venture 
515262276f4SPatrick Venture     // Find the fan configuration first (fans can have multiple configuration
516262276f4SPatrick Venture     // interfaces).
517262276f4SPatrick Venture     for (const auto& entry : entityManagerService->second)
518262276f4SPatrick Venture     {
519262276f4SPatrick Venture         if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
520262276f4SPatrick Venture             entry == "xyz.openbmc_project.Configuration.I2CFan" ||
521262276f4SPatrick Venture             entry == "xyz.openbmc_project.Configuration.NuvotonFan")
522262276f4SPatrick Venture         {
523262276f4SPatrick Venture             return &entry;
524262276f4SPatrick Venture         }
525262276f4SPatrick Venture     }
526262276f4SPatrick Venture 
527262276f4SPatrick Venture     for (const auto& entry : entityManagerService->second)
528262276f4SPatrick Venture     {
529262276f4SPatrick Venture         if (boost::algorithm::starts_with(entry,
530262276f4SPatrick Venture                                           "xyz.openbmc_project.Configuration."))
531262276f4SPatrick Venture         {
532262276f4SPatrick Venture             return &entry;
533262276f4SPatrick Venture         }
534262276f4SPatrick Venture     }
535262276f4SPatrick Venture 
536262276f4SPatrick Venture     return nullptr;
537262276f4SPatrick Venture }
538262276f4SPatrick Venture 
539262276f4SPatrick Venture // Follow Association properties for Sensor back to the Board dbus object to
540262276f4SPatrick Venture // check for an EntityId and EntityInstance property.
updateIpmiFromAssociation(const std::string & path,const SensorMap & sensorMap,uint8_t & entityId,uint8_t & entityInstance)541*1bcced08SPatrick Williams static inline void updateIpmiFromAssociation(
542*1bcced08SPatrick Williams     const std::string& path, const SensorMap& sensorMap, uint8_t& entityId,
543262276f4SPatrick Venture     uint8_t& entityInstance)
544262276f4SPatrick Venture {
545262276f4SPatrick Venture     namespace fs = std::filesystem;
546262276f4SPatrick Venture 
547262276f4SPatrick Venture     auto sensorAssociationObject =
548262276f4SPatrick Venture         sensorMap.find("xyz.openbmc_project.Association.Definitions");
549262276f4SPatrick Venture     if (sensorAssociationObject == sensorMap.end())
550262276f4SPatrick Venture     {
551262276f4SPatrick Venture         if constexpr (debug)
552262276f4SPatrick Venture         {
553262276f4SPatrick Venture             std::fprintf(stderr, "path=%s, no association interface found\n",
554262276f4SPatrick Venture                          path.c_str());
555262276f4SPatrick Venture         }
556262276f4SPatrick Venture 
557262276f4SPatrick Venture         return;
558262276f4SPatrick Venture     }
559262276f4SPatrick Venture 
560262276f4SPatrick Venture     auto associationObject =
561262276f4SPatrick Venture         sensorAssociationObject->second.find("Associations");
562262276f4SPatrick Venture     if (associationObject == sensorAssociationObject->second.end())
563262276f4SPatrick Venture     {
564262276f4SPatrick Venture         if constexpr (debug)
565262276f4SPatrick Venture         {
566262276f4SPatrick Venture             std::fprintf(stderr, "path=%s, no association records found\n",
567262276f4SPatrick Venture                          path.c_str());
568262276f4SPatrick Venture         }
569262276f4SPatrick Venture 
570262276f4SPatrick Venture         return;
571262276f4SPatrick Venture     }
572262276f4SPatrick Venture 
573262276f4SPatrick Venture     std::vector<Association> associationValues =
574262276f4SPatrick Venture         std::get<std::vector<Association>>(associationObject->second);
575262276f4SPatrick Venture 
576262276f4SPatrick Venture     // loop through the Associations looking for the right one:
577262276f4SPatrick Venture     for (const auto& entry : associationValues)
578262276f4SPatrick Venture     {
579262276f4SPatrick Venture         // forward, reverse, endpoint
580262276f4SPatrick Venture         const std::string& forward = std::get<0>(entry);
581262276f4SPatrick Venture         const std::string& reverse = std::get<1>(entry);
582262276f4SPatrick Venture         const std::string& endpoint = std::get<2>(entry);
583262276f4SPatrick Venture 
584262276f4SPatrick Venture         // We only currently concern ourselves with chassis+all_sensors.
585262276f4SPatrick Venture         if (!(forward == "chassis" && reverse == "all_sensors"))
586262276f4SPatrick Venture         {
587262276f4SPatrick Venture             continue;
588262276f4SPatrick Venture         }
589262276f4SPatrick Venture 
590262276f4SPatrick Venture         // the endpoint is the board entry provided by
591262276f4SPatrick Venture         // Entity-Manager. so let's grab its properties if it has
592262276f4SPatrick Venture         // the right interface.
593262276f4SPatrick Venture 
594262276f4SPatrick Venture         // just try grabbing the properties first.
595262276f4SPatrick Venture         std::map<std::string, DbusVariant> ipmiProperties =
596262276f4SPatrick Venture             getEntityManagerProperties(
597262276f4SPatrick Venture                 endpoint.c_str(),
598262276f4SPatrick Venture                 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
599262276f4SPatrick Venture 
600262276f4SPatrick Venture         auto entityIdProp = ipmiProperties.find("EntityId");
601262276f4SPatrick Venture         auto entityInstanceProp = ipmiProperties.find("EntityInstance");
602262276f4SPatrick Venture         if (entityIdProp != ipmiProperties.end())
603262276f4SPatrick Venture         {
604262276f4SPatrick Venture             entityId =
605262276f4SPatrick Venture                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
606262276f4SPatrick Venture         }
607262276f4SPatrick Venture         if (entityInstanceProp != ipmiProperties.end())
608262276f4SPatrick Venture         {
609262276f4SPatrick Venture             entityInstance = static_cast<uint8_t>(
610262276f4SPatrick Venture                 std::get<uint64_t>(entityInstanceProp->second));
611262276f4SPatrick Venture         }
612262276f4SPatrick Venture 
613262276f4SPatrick Venture         // Now check the entity-manager entry for this sensor to see
614262276f4SPatrick Venture         // if it has its own value and use that instead.
615262276f4SPatrick Venture         //
616262276f4SPatrick Venture         // In theory, checking this first saves us from checking
617262276f4SPatrick Venture         // both, except in most use-cases identified, there won't be
618262276f4SPatrick Venture         // a per sensor override, so we need to always check both.
619262276f4SPatrick Venture         std::string sensorNameFromPath = fs::path(path).filename();
620262276f4SPatrick Venture 
621262276f4SPatrick Venture         std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
622262276f4SPatrick Venture 
623262276f4SPatrick Venture         // Download the interfaces for the sensor from
624262276f4SPatrick Venture         // Entity-Manager to find the name of the configuration
625262276f4SPatrick Venture         // interface.
626262276f4SPatrick Venture         std::map<std::string, std::vector<std::string>>
627262276f4SPatrick Venture             sensorInterfacesResponse =
628262276f4SPatrick Venture                 getObjectInterfaces(sensorConfigPath.c_str());
629262276f4SPatrick Venture 
630262276f4SPatrick Venture         const std::string* configurationInterface =
631262276f4SPatrick Venture             getSensorConfigurationInterface(sensorInterfacesResponse);
632262276f4SPatrick Venture 
633262276f4SPatrick Venture         // We didnt' find a configuration interface for this sensor, but we
634262276f4SPatrick Venture         // followed the Association property to get here, so we're done
635262276f4SPatrick Venture         // searching.
636262276f4SPatrick Venture         if (!configurationInterface)
637262276f4SPatrick Venture         {
638262276f4SPatrick Venture             break;
639262276f4SPatrick Venture         }
640262276f4SPatrick Venture 
641262276f4SPatrick Venture         // We found a configuration interface.
642262276f4SPatrick Venture         std::map<std::string, DbusVariant> configurationProperties =
643262276f4SPatrick Venture             getEntityManagerProperties(sensorConfigPath.c_str(),
644262276f4SPatrick Venture                                        configurationInterface->c_str());
645262276f4SPatrick Venture 
646262276f4SPatrick Venture         entityIdProp = configurationProperties.find("EntityId");
647262276f4SPatrick Venture         entityInstanceProp = configurationProperties.find("EntityInstance");
648262276f4SPatrick Venture         if (entityIdProp != configurationProperties.end())
649262276f4SPatrick Venture         {
650262276f4SPatrick Venture             entityId =
651262276f4SPatrick Venture                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
652262276f4SPatrick Venture         }
653262276f4SPatrick Venture         if (entityInstanceProp != configurationProperties.end())
654262276f4SPatrick Venture         {
655262276f4SPatrick Venture             entityInstance = static_cast<uint8_t>(
656262276f4SPatrick Venture                 std::get<uint64_t>(entityInstanceProp->second));
657262276f4SPatrick Venture         }
658262276f4SPatrick Venture 
659262276f4SPatrick Venture         // stop searching Association records.
660262276f4SPatrick Venture         break;
661262276f4SPatrick Venture     } // end for Association vectors.
662262276f4SPatrick Venture 
663262276f4SPatrick Venture     if constexpr (debug)
664262276f4SPatrick Venture     {
665262276f4SPatrick Venture         std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
666262276f4SPatrick Venture                      path.c_str(), entityId, entityInstance);
667262276f4SPatrick Venture     }
668262276f4SPatrick Venture }
669262276f4SPatrick Venture 
670262276f4SPatrick Venture } // namespace ipmi
671