xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sensorcommands.cpp (revision e7ef94d350cd156c54a5789ce7d53eb1a55f7da9)
1  /*
2  // Copyright (c) 2017 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  
17  #include "config.h"
18  
19  #include "dbus-sdr/sensorcommands.hpp"
20  
21  #include "dbus-sdr/sdrutils.hpp"
22  #include "dbus-sdr/sensorutils.hpp"
23  #include "dbus-sdr/storagecommands.hpp"
24  
25  #include <boost/algorithm/string.hpp>
26  #include <boost/container/flat_map.hpp>
27  #include <ipmid/api.hpp>
28  #include <ipmid/entity_map_json.hpp>
29  #include <ipmid/types.hpp>
30  #include <ipmid/utils.hpp>
31  #include <phosphor-logging/lg2.hpp>
32  #include <sdbusplus/bus.hpp>
33  #include <user_channel/channel_layer.hpp>
34  
35  #include <algorithm>
36  #include <array>
37  #include <chrono>
38  #include <cmath>
39  #include <cstring>
40  #include <format>
41  #include <iostream>
42  #include <map>
43  #include <optional>
44  #include <stdexcept>
45  #include <string>
46  #include <utility>
47  #include <variant>
48  
49  #ifdef FEATURE_HYBRID_SENSORS
50  
51  #include "sensordatahandler.hpp"
52  namespace ipmi
53  {
54  namespace sensor
55  {
56  extern const IdInfoMap sensors;
57  } // namespace sensor
58  } // namespace ipmi
59  #endif
60  namespace ipmi
61  {
62  namespace dcmi
63  {
64  // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
65  static const std::map<uint8_t, uint8_t> validEntityId{
66      {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
67      {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
68  constexpr uint8_t temperatureSensorType = 0x01;
69  constexpr uint8_t maxRecords = 8;
70  } // namespace dcmi
71  } // namespace ipmi
72  constexpr std::array<const char*, 7> suffixes = {
73      "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
74      "_Output_Power",   "_Input_Power",   "_Temperature"};
75  namespace ipmi
76  {
77  
78  using phosphor::logging::entry;
79  using phosphor::logging::level;
80  using phosphor::logging::log;
81  
82  static constexpr int sensorMapUpdatePeriod = 10;
83  static constexpr int sensorMapSdrUpdatePeriod = 60;
84  
85  // BMC I2C address is generally at 0x20
86  static constexpr uint8_t bmcI2CAddr = 0x20;
87  
88  constexpr size_t maxSDRTotalSize =
89      76; // Largest SDR Record Size (type 01) + SDR Overheader Size
90  constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
91  
92  static uint16_t sdrReservationID;
93  static uint32_t sdrLastAdd = noTimestamp;
94  static uint32_t sdrLastRemove = noTimestamp;
95  static constexpr size_t lastRecordIndex = 0xFFFF;
96  
97  // The IPMI spec defines four Logical Units (LUN), each capable of supporting
98  // 255 sensors. The 256 values assigned to LUN 2 are special and are not used
99  // for general purpose sensors. Each LUN reserves location 0xFF. The maximum
100  // number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
101  // location.
102  static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
103  
104  static constexpr uint8_t lun0 = 0x0;
105  static constexpr uint8_t lun1 = 0x1;
106  static constexpr uint8_t lun3 = 0x3;
107  
108  static constexpr size_t lun0MaxSensorNum = 0xfe;
109  static constexpr size_t lun1MaxSensorNum = 0x1fe;
110  static constexpr size_t lun3MaxSensorNum = 0x3fe;
111  static constexpr int GENERAL_ERROR = -1;
112  
113  static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
114  
115  // Specify the comparison required to sort and find char* map objects
116  struct CmpStr
117  {
operator ()ipmi::CmpStr118      bool operator()(const char* a, const char* b) const
119      {
120          return std::strcmp(a, b) < 0;
121      }
122  };
123  const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
124      sensorUnits{{{"temperature", SensorUnits::degreesC},
125                   {"voltage", SensorUnits::volts},
126                   {"current", SensorUnits::amps},
127                   {"fan_tach", SensorUnits::rpm},
128                   {"power", SensorUnits::watts},
129                   {"energy", SensorUnits::joules}}};
130  
131  void registerSensorFunctions() __attribute__((constructor));
132  
133  static sdbusplus::bus::match_t sensorAdded(
134      *getSdBus(),
135      "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
136      "sensors/'",
__anon2f2fc9af0102(sdbusplus::message_t&) 137      [](sdbusplus::message_t&) {
138          getSensorTree().clear();
139          getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
140          sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
141                           std::chrono::system_clock::now().time_since_epoch())
142                           .count();
143      });
144  
145  static sdbusplus::bus::match_t sensorRemoved(
146      *getSdBus(),
147      "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
148      "sensors/'",
__anon2f2fc9af0202(sdbusplus::message_t&) 149      [](sdbusplus::message_t&) {
150          getSensorTree().clear();
151          getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
152          sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
153                              std::chrono::system_clock::now().time_since_epoch())
154                              .count();
155      });
156  
getSensorConnection(ipmi::Context::ptr ctx,uint8_t sensnum,std::string & connection,std::string & path,std::vector<std::string> * interfaces)157  ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
158                                 std::string& connection, std::string& path,
159                                 std::vector<std::string>* interfaces)
160  {
161      auto& sensorTree = getSensorTree();
162      if (!getSensorSubtree(sensorTree) && sensorTree.empty())
163      {
164          return IPMI_CC_RESPONSE_ERROR;
165      }
166  
167      if (ctx == nullptr)
168      {
169          return IPMI_CC_RESPONSE_ERROR;
170      }
171  
172      path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
173      if (path.empty())
174      {
175          return IPMI_CC_INVALID_FIELD_REQUEST;
176      }
177  
178      for (const auto& sensor : sensorTree)
179      {
180          if (path == sensor.first)
181          {
182              connection = sensor.second.begin()->first;
183              if (interfaces)
184                  *interfaces = sensor.second.begin()->second;
185              break;
186          }
187      }
188  
189      return 0;
190  }
191  
getSensorTree()192  SensorSubTree& getSensorTree()
193  {
194      static SensorSubTree sensorTree;
195      return sensorTree;
196  }
197  
198  // this keeps track of deassertions for sensor event status command. A
199  // deasertion can only happen if an assertion was seen first.
200  static boost::container::flat_map<
201      std::string, boost::container::flat_map<std::string, std::optional<bool>>>
202      thresholdDeassertMap;
203  
204  static sdbusplus::bus::match_t thresholdChanged(
205      *getSdBus(),
206      "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
207      "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
__anon2f2fc9af0302(sdbusplus::message_t& m) 208      [](sdbusplus::message_t& m) {
209          boost::container::flat_map<std::string, std::variant<bool, double>>
210              values;
211          m.read(std::string(), values);
212  
213          auto findAssert =
214              std::find_if(values.begin(), values.end(), [](const auto& pair) {
215                  return pair.first.find("Alarm") != std::string::npos;
216              });
217          if (findAssert != values.end())
218          {
219              auto ptr = std::get_if<bool>(&(findAssert->second));
220              if (ptr == nullptr)
221              {
222                  lg2::error("thresholdChanged: Assert non bool");
223                  return;
224              }
225              if (*ptr)
226              {
227                  lg2::info(
228                      "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
229                      "SENSOR_PATH", m.get_path());
230                  thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
231              }
232              else
233              {
234                  auto& value =
235                      thresholdDeassertMap[m.get_path()][findAssert->first];
236                  if (value)
237                  {
238                      lg2::info(
239                          "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
240                          "SENSOR_PATH", m.get_path());
241                      value = *ptr;
242                  }
243              }
244          }
245      });
246  
247  namespace sensor
248  {
249  static constexpr const char* vrInterface =
250      "xyz.openbmc_project.Control.VoltageRegulatorMode";
251  static constexpr const char* sensorInterface =
252      "xyz.openbmc_project.Sensor.Value";
253  } // namespace sensor
254  
getSensorMaxMin(const DbusInterfaceMap & sensorMap,double & max,double & min)255  static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
256                              double& min)
257  {
258      max = 127;
259      min = -128;
260  
261      auto sensorObject = sensorMap.find(sensor::sensorInterface);
262      auto critical =
263          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
264      auto warning =
265          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
266  
267      if (sensorObject != sensorMap.end())
268      {
269          auto maxMap = sensorObject->second.find("MaxValue");
270          auto minMap = sensorObject->second.find("MinValue");
271  
272          if (maxMap != sensorObject->second.end())
273          {
274              max = std::visit(VariantToDoubleVisitor(), maxMap->second);
275          }
276          if (minMap != sensorObject->second.end())
277          {
278              min = std::visit(VariantToDoubleVisitor(), minMap->second);
279          }
280      }
281      if (critical != sensorMap.end())
282      {
283          auto lower = critical->second.find("CriticalLow");
284          auto upper = critical->second.find("CriticalHigh");
285          if (lower != critical->second.end())
286          {
287              double value = std::visit(VariantToDoubleVisitor(), lower->second);
288              if (std::isfinite(value))
289              {
290                  min = std::fmin(value, min);
291              }
292          }
293          if (upper != critical->second.end())
294          {
295              double value = std::visit(VariantToDoubleVisitor(), upper->second);
296              if (std::isfinite(value))
297              {
298                  max = std::fmax(value, max);
299              }
300          }
301      }
302      if (warning != sensorMap.end())
303      {
304          auto lower = warning->second.find("WarningLow");
305          auto upper = warning->second.find("WarningHigh");
306          if (lower != warning->second.end())
307          {
308              double value = std::visit(VariantToDoubleVisitor(), lower->second);
309              if (std::isfinite(value))
310              {
311                  min = std::fmin(value, min);
312              }
313          }
314          if (upper != warning->second.end())
315          {
316              double value = std::visit(VariantToDoubleVisitor(), upper->second);
317              if (std::isfinite(value))
318              {
319                  max = std::fmax(value, max);
320              }
321          }
322      }
323  }
324  
getSensorMap(ipmi::Context::ptr ctx,std::string sensorConnection,std::string sensorPath,DbusInterfaceMap & sensorMap,int updatePeriod=sensorMapUpdatePeriod)325  static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
326                           std::string sensorPath, DbusInterfaceMap& sensorMap,
327                           int updatePeriod = sensorMapUpdatePeriod)
328  {
329  #ifdef FEATURE_HYBRID_SENSORS
330      if (auto sensor = findStaticSensor(sensorPath);
331          sensor != ipmi::sensor::sensors.end() &&
332          getSensorEventTypeFromPath(sensorPath) !=
333              static_cast<uint8_t>(SensorEventTypeCodes::threshold))
334      {
335          // If the incoming sensor is a discrete sensor, it might fail in
336          // getManagedObjects(), return true, and use its own getFunc to get
337          // value.
338          return true;
339      }
340  #endif
341  
342      static boost::container::flat_map<
343          std::string, std::chrono::time_point<std::chrono::steady_clock>>
344          updateTimeMap;
345  
346      auto updateFind = updateTimeMap.find(sensorConnection);
347      auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
348      if (updateFind != updateTimeMap.end())
349      {
350          lastUpdate = updateFind->second;
351      }
352  
353      auto now = std::chrono::steady_clock::now();
354  
355      if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
356              .count() > updatePeriod)
357      {
358          bool found = false;
359  
360          // Object managers for different kinds of OpenBMC DBus interfaces.
361          // Documented in the phosphor-dbus-interfaces repository.
362          const char* paths[] = {
363              "/xyz/openbmc_project/sensors",
364              "/xyz/openbmc_project/vr",
365          };
366          constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
367          ObjectValueTree allManagedObjects;
368  
369          for (size_t i = 0; i < num_paths; i++)
370          {
371              ObjectValueTree managedObjects;
372              boost::system::error_code ec = getManagedObjects(
373                  ctx, sensorConnection.c_str(), paths[i], managedObjects);
374              if (ec)
375              {
376                  continue;
377              }
378              allManagedObjects.merge(managedObjects);
379              found = true;
380          }
381  
382          if (!found)
383          {
384              lg2::error("GetMangagedObjects for getSensorMap failed, "
385                         "service: {SERVICE}",
386                         "SERVICE", sensorConnection);
387  
388              return false;
389          }
390  
391          SensorCache[sensorConnection] = allManagedObjects;
392          // Update time after finish building the map which allow the
393          // data to be cached for updatePeriod plus the build time.
394          updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
395      }
396      auto connection = SensorCache.find(sensorConnection);
397      if (connection == SensorCache.end())
398      {
399          return false;
400      }
401      auto path = connection->second.find(sensorPath);
402      if (path == connection->second.end())
403      {
404          return false;
405      }
406      sensorMap = path->second;
407  
408      return true;
409  }
410  
411  namespace sensor
412  {
413  // Read VR profiles from sensor(daemon) interface
414  static std::optional<std::vector<std::string>>
getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type & object)415      getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
416  {
417      // get VR mode profiles from Supported Interface
418      auto supportedProperty = object.find("Supported");
419      if (supportedProperty == object.end() ||
420          object.find("Selected") == object.end())
421      {
422          lg2::error("Missing the required Supported and Selected properties");
423          return std::nullopt;
424      }
425  
426      const auto profilesPtr =
427          std::get_if<std::vector<std::string>>(&supportedProperty->second);
428  
429      if (profilesPtr == nullptr)
430      {
431          lg2::error("property is not array of string");
432          return std::nullopt;
433      }
434      return *profilesPtr;
435  }
436  
437  // Calculate VR Mode from input IPMI discrete event bytes
calculateVRMode(uint15_t assertOffset,const ipmi::DbusInterfaceMap::mapped_type & VRObject)438  static std::optional<std::string> calculateVRMode(
439      uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
440  {
441      // get VR mode profiles from Supported Interface
442      auto profiles = getSupportedVrProfiles(VRObject);
443      if (!profiles)
444      {
445          return std::nullopt;
446      }
447  
448      // interpret IPMI cmd bits into profiles' index
449      long unsigned int index = 0;
450      // only one bit should be set and the highest bit should not be used.
451      if (assertOffset == 0 || assertOffset == (1u << 15) ||
452          (assertOffset & (assertOffset - 1)))
453      {
454          lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
455                     lg2::hex, static_cast<uint16_t>(assertOffset));
456          return std::nullopt;
457      }
458  
459      while (assertOffset != 1)
460      {
461          assertOffset >>= 1;
462          index++;
463      }
464  
465      if (index >= profiles->size())
466      {
467          lg2::error("profile index out of boundary");
468          return std::nullopt;
469      }
470  
471      return profiles->at(index);
472  }
473  
474  // Calculate sensor value from IPMI reading byte
475  static std::optional<double>
calculateValue(uint8_t reading,const ipmi::DbusInterfaceMap & sensorMap,const ipmi::DbusInterfaceMap::mapped_type & valueObject)476      calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
477                     const ipmi::DbusInterfaceMap::mapped_type& valueObject)
478  {
479      if (valueObject.find("Value") == valueObject.end())
480      {
481          lg2::error("Missing the required Value property");
482          return std::nullopt;
483      }
484  
485      double max = 0;
486      double min = 0;
487      getSensorMaxMin(sensorMap, max, min);
488  
489      int16_t mValue = 0;
490      int16_t bValue = 0;
491      int8_t rExp = 0;
492      int8_t bExp = 0;
493      bool bSigned = false;
494  
495      if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
496      {
497          return std::nullopt;
498      }
499  
500      double value = bSigned ? ((int8_t)reading) : reading;
501  
502      value *= ((double)mValue);
503      value += ((double)bValue) * std::pow(10.0, bExp);
504      value *= std::pow(10.0, rExp);
505  
506      return value;
507  }
508  
509  // Extract file name from sensor path as the sensors SDR ID. Simplify the name
510  // if it is too long.
parseSdrIdFromPath(const std::string & path)511  std::string parseSdrIdFromPath(const std::string& path)
512  {
513      std::string name;
514      size_t nameStart = path.rfind("/");
515      if (nameStart != std::string::npos)
516      {
517          name = path.substr(nameStart + 1, std::string::npos - nameStart);
518      }
519  
520      if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
521      {
522  #ifdef SHORTNAME_REMOVE_SUFFIX
523          for (const auto& suffix : suffixes)
524          {
525              if (boost::ends_with(name, suffix))
526              {
527                  boost::replace_all(name, suffix, "");
528                  break;
529              }
530          }
531  #endif
532  #ifdef SHORTNAME_REPLACE_WORDS
533          constexpr std::array<std::pair<const char*, const char*>, 2>
534              replaceWords = {std::make_pair("Output", "Out"),
535                              std::make_pair("Input", "In")};
536          for (const auto& [find, replace] : replaceWords)
537          {
538              boost::replace_all(name, find, replace);
539          }
540  #endif
541  
542          // as a backup and if nothing else is configured
543          name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
544      }
545      return name;
546  }
547  
getVrEventStatus(ipmi::Context::ptr ctx,const std::string & connection,const std::string & path,const ipmi::DbusInterfaceMap::mapped_type & object,std::bitset<16> & assertions)548  bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
549                        const std::string& path,
550                        const ipmi::DbusInterfaceMap::mapped_type& object,
551                        std::bitset<16>& assertions)
552  {
553      auto profiles = sensor::getSupportedVrProfiles(object);
554      if (!profiles)
555      {
556          return false;
557      }
558      std::string mode;
559  
560      auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
561                                "Selected", mode);
562      if (ec)
563      {
564          lg2::error("Failed to get Selected, path: {PATH}, "
565                     "interface: {INTERFACE}, error: {ERROR}",
566                     "PATH", path, "INTERFACE", sensor::sensorInterface, "ERROR",
567                     ec.message());
568          return false;
569      }
570  
571      auto itr = std::find(profiles->begin(), profiles->end(), mode);
572      if (itr == profiles->end())
573      {
574          lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
575                     "PATH", path);
576          return false;
577      }
578      std::size_t index =
579          static_cast<std::size_t>(std::distance(profiles->begin(), itr));
580  
581      // map index to response event assertion bit.
582      if (index < 16)
583      {
584          assertions.set(index);
585      }
586      else
587      {
588          lg2::error("VR profile index reaches max assertion bit, "
589                     "path: {PATH}, index: {INDEX}",
590                     "PATH", path, "INDEX", index);
591          return false;
592      }
593      if constexpr (debug)
594      {
595          std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
596                    << " mode is: [" << index << "] " << mode << std::endl;
597      }
598      return true;
599  }
600  
601  /*
602   * Handle every Sensor Data Record besides Type 01
603   *
604   * The D-Bus sensors work well for generating Type 01 SDRs.
605   * After the Type 01 sensors are processed the remaining sensor types require
606   * special handling. Each BMC vendor is going to have their own requirements for
607   * insertion of non-Type 01 records.
608   * Manage non-Type 01 records:
609   *
610   * Create a new file: dbus-sdr/sensorcommands_oem.cpp
611   * Populate it with the two weakly linked functions below, without adding the
612   * 'weak' attribute definition prior to the function definition.
613   *    getOtherSensorsCount(...)
614   *    getOtherSensorsDataRecord(...)
615   *    Example contents are provided in the weak definitions below
616   *    Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
617   *      'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
618   * The contents of the sensorcommands_oem.cpp file will then override the code
619   * provided below.
620   */
621  
622  size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
getOtherSensorsCount(ipmi::Context::ptr ctx)623  size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
624  {
625      size_t fruCount = 0;
626  
627      ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
628      if (ret != ipmi::ccSuccess)
629      {
630          lg2::error("getOtherSensorsCount: getFruSdrCount error");
631          return std::numeric_limits<size_t>::max();
632      }
633  
634      const auto& entityRecords =
635          ipmi::sensor::EntityInfoMapContainer::getContainer()
636              ->getIpmiEntityRecords();
637      size_t entityCount = entityRecords.size();
638  
639      return fruCount + ipmi::storage::type12Count + entityCount;
640  }
641  
642  int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
643                                std::vector<uint8_t>& recordData)
644      __attribute__((weak));
getOtherSensorsDataRecord(ipmi::Context::ptr ctx,uint16_t recordID,std::vector<uint8_t> & recordData)645  int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
646                                std::vector<uint8_t>& recordData)
647  {
648      size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
649      if (otherCount == std::numeric_limits<size_t>::max())
650      {
651          return GENERAL_ERROR;
652      }
653      const auto& entityRecords =
654          ipmi::sensor::EntityInfoMapContainer::getContainer()
655              ->getIpmiEntityRecords();
656  
657      size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
658      size_t entityCount{entityRecords.size()};
659      size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
660  
661      if (sdrIndex > otherCount)
662      {
663          return std::numeric_limits<int>::min();
664      }
665      else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
666      {
667          // handle type 8 entity map records
668          ipmi::sensor::EntityInfoMap::const_iterator entity =
669              entityRecords.find(static_cast<uint8_t>(
670                  sdrIndex - fruCount - ipmi::storage::type12Count));
671  
672          if (entity == entityRecords.end())
673          {
674              return GENERAL_ERROR;
675          }
676          recordData = ipmi::storage::getType8SDRs(entity, recordID);
677      }
678      else if (sdrIndex >= fruCount)
679      {
680          // handle type 12 hardcoded records
681          size_t type12Index = sdrIndex - fruCount;
682          if (type12Index >= ipmi::storage::type12Count)
683          {
684              lg2::error("getSensorDataRecord: type12Index error");
685              return GENERAL_ERROR;
686          }
687          recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
688      }
689      else
690      {
691          // handle fru records
692          get_sdr::SensorDataFruRecord data;
693          if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
694              ret != IPMI_CC_OK)
695          {
696              return GENERAL_ERROR;
697          }
698          data.header.record_id_msb = recordID >> 8;
699          data.header.record_id_lsb = recordID & 0xFF;
700          recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
701                            reinterpret_cast<uint8_t*>(&data) + sizeof(data));
702      }
703  
704      return 0;
705  }
706  
707  } // namespace sensor
708  
ipmiSenPlatformEvent(ipmi::Context::ptr ctx,ipmi::message::Payload & p)709  ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
710                                       ipmi::message::Payload& p)
711  {
712      constexpr const uint8_t validEnvmRev = 0x04;
713      constexpr const uint8_t lastSensorType = 0x2C;
714      constexpr const uint8_t oemReserved = 0xC0;
715  
716      uint8_t sysgeneratorID = 0;
717      uint8_t evmRev = 0;
718      uint8_t sensorType = 0;
719      uint8_t sensorNum = 0;
720      uint8_t eventType = 0;
721      uint8_t eventData1 = 0;
722      std::optional<uint8_t> eventData2 = 0;
723      std::optional<uint8_t> eventData3 = 0;
724      [[maybe_unused]] uint16_t generatorID = 0;
725      ipmi::ChannelInfo chInfo;
726  
727      if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
728      {
729          lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
730                     ctx->channel);
731          return ipmi::responseUnspecifiedError();
732      }
733  
734      if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
735          ipmi::EChannelMediumType::systemInterface)
736      {
737          p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
738                   eventData1, eventData2, eventData3);
739          constexpr const uint8_t isSoftwareID = 0x01;
740          if (!(sysgeneratorID & isSoftwareID))
741          {
742              return ipmi::responseInvalidFieldRequest();
743          }
744          // Refer to IPMI Spec Table 32: SEL Event Records
745          generatorID = (ctx->channel << 12) // Channel
746                        | (0x0 << 10)        // Reserved
747                        | (0x0 << 8)         // 0x0 for sys-soft ID
748                        | sysgeneratorID;
749      }
750      else
751      {
752          p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
753                   eventData2, eventData3);
754          // Refer to IPMI Spec Table 32: SEL Event Records
755          generatorID = (ctx->channel << 12)      // Channel
756                        | (0x0 << 10)             // Reserved
757                        | ((ctx->lun & 0x3) << 8) // Lun
758                        | (ctx->rqSA << 1);
759      }
760  
761      if (!p.fullyUnpacked())
762      {
763          return ipmi::responseReqDataLenInvalid();
764      }
765  
766      // Check for valid evmRev and Sensor Type(per Table 42 of spec)
767      if (evmRev != validEnvmRev)
768      {
769          return ipmi::responseInvalidFieldRequest();
770      }
771      if ((sensorType > lastSensorType) && (sensorType < oemReserved))
772      {
773          return ipmi::responseInvalidFieldRequest();
774      }
775  
776      return ipmi::responseSuccess();
777  }
778  
ipmiSetSensorReading(ipmi::Context::ptr ctx,uint8_t sensorNumber,uint8_t,uint8_t reading,uint15_t assertOffset,bool,uint15_t,bool,uint8_t,uint8_t,uint8_t)779  ipmi::RspType<> ipmiSetSensorReading(
780      ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
781      uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
782  {
783      std::string connection;
784      std::string path;
785      std::vector<std::string> interfaces;
786  
787      ipmi::Cc status =
788          getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
789      if (status)
790      {
791          return ipmi::response(status);
792      }
793  
794      // we can tell the sensor type by its interface type
795      if (std::find(interfaces.begin(), interfaces.end(),
796                    sensor::sensorInterface) != interfaces.end())
797      {
798          DbusInterfaceMap sensorMap;
799          if (!getSensorMap(ctx, connection, path, sensorMap))
800          {
801              return ipmi::responseResponseError();
802          }
803          auto sensorObject = sensorMap.find(sensor::sensorInterface);
804          if (sensorObject == sensorMap.end())
805          {
806              return ipmi::responseResponseError();
807          }
808  
809          // Only allow external SetSensor if write permission granted
810          if (!details::sdrWriteTable.getWritePermission(
811                  (ctx->lun << 8) | sensorNumber))
812          {
813              return ipmi::responseResponseError();
814          }
815  
816          auto value =
817              sensor::calculateValue(reading, sensorMap, sensorObject->second);
818          if (!value)
819          {
820              return ipmi::responseResponseError();
821          }
822  
823          if constexpr (debug)
824          {
825              lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
826                        "byte: {BYTE}, value: {VALUE}",
827                        "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
828                        "VALUE", *value);
829          }
830  
831          boost::system::error_code ec =
832              setDbusProperty(ctx, connection, path, sensor::sensorInterface,
833                              "Value", ipmi::Value(*value));
834  
835          // setDbusProperty intended to resolve dbus exception/rc within the
836          // function but failed to achieve that. Catch exception in the ipmi
837          // callback functions for now (e.g. ipmiSetSensorReading).
838          if (ec)
839          {
840              lg2::error("Failed to set Value, path: {PATH}, "
841                         "interface: {INTERFACE}, ERROR: {ERROR}",
842                         "PATH", path, "INTERFACE", sensor::sensorInterface,
843                         "ERROR", ec.message());
844              return ipmi::responseResponseError();
845          }
846          return ipmi::responseSuccess();
847      }
848  
849      if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
850          interfaces.end())
851      {
852          DbusInterfaceMap sensorMap;
853          if (!getSensorMap(ctx, connection, path, sensorMap))
854          {
855              return ipmi::responseResponseError();
856          }
857          auto sensorObject = sensorMap.find(sensor::vrInterface);
858          if (sensorObject == sensorMap.end())
859          {
860              return ipmi::responseResponseError();
861          }
862  
863          // VR sensors are treated as a special case and we will not check the
864          // write permission for VR sensors, since they always deemed writable
865          // and permission table are not applied to VR sensors.
866          auto vrMode =
867              sensor::calculateVRMode(assertOffset, sensorObject->second);
868          if (!vrMode)
869          {
870              return ipmi::responseResponseError();
871          }
872          boost::system::error_code ec = setDbusProperty(
873              ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
874          // setDbusProperty intended to resolve dbus exception/rc within the
875          // function but failed to achieve that. Catch exception in the ipmi
876          // callback functions for now (e.g. ipmiSetSensorReading).
877          if (ec)
878          {
879              lg2::error("Failed to set Selected, path: {PATH}, "
880                         "interface: {INTERFACE}, ERROR: {ERROR}",
881                         "PATH", path, "INTERFACE", sensor::sensorInterface,
882                         "ERROR", ec.message());
883          }
884          return ipmi::responseSuccess();
885      }
886  
887      lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
888      return ipmi::responseResponseError();
889  }
890  
891  ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
ipmiSenGetSensorReading(ipmi::Context::ptr ctx,uint8_t sensnum)892      ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
893  {
894      std::string connection;
895      std::string path;
896  
897      if (sensnum == reservedSensorNumber)
898      {
899          return ipmi::responseInvalidFieldRequest();
900      }
901  
902      auto status = getSensorConnection(ctx, sensnum, connection, path);
903      if (status)
904      {
905          return ipmi::response(status);
906      }
907  
908  #ifdef FEATURE_HYBRID_SENSORS
909      if (auto sensor = findStaticSensor(path);
910          sensor != ipmi::sensor::sensors.end() &&
911          getSensorEventTypeFromPath(path) !=
912              static_cast<uint8_t>(SensorEventTypeCodes::threshold))
913      {
914          if (ipmi::sensor::Mutability::Read !=
915              (sensor->second.mutability & ipmi::sensor::Mutability::Read))
916          {
917              return ipmi::responseIllegalCommand();
918          }
919  
920          uint8_t operation;
921          try
922          {
923              ipmi::sensor::GetSensorResponse getResponse =
924                  sensor->second.getFunc(sensor->second);
925  
926              if (getResponse.readingOrStateUnavailable)
927              {
928                  operation |= static_cast<uint8_t>(
929                      IPMISensorReadingByte2::readingStateUnavailable);
930              }
931              if (getResponse.scanningEnabled)
932              {
933                  operation |= static_cast<uint8_t>(
934                      IPMISensorReadingByte2::sensorScanningEnable);
935              }
936              if (getResponse.allEventMessagesEnabled)
937              {
938                  operation |= static_cast<uint8_t>(
939                      IPMISensorReadingByte2::eventMessagesEnable);
940              }
941              return ipmi::responseSuccess(
942                  getResponse.reading, operation,
943                  getResponse.thresholdLevelsStates,
944                  getResponse.discreteReadingSensorStates);
945          }
946          catch (const std::exception& e)
947          {
948              operation |= static_cast<uint8_t>(
949                  IPMISensorReadingByte2::readingStateUnavailable);
950              return ipmi::responseSuccess(0, operation, 0, std::nullopt);
951          }
952      }
953  #endif
954  
955      DbusInterfaceMap sensorMap;
956      if (!getSensorMap(ctx, connection, path, sensorMap))
957      {
958          return ipmi::responseResponseError();
959      }
960      auto sensorObject = sensorMap.find(sensor::sensorInterface);
961  
962      if (sensorObject == sensorMap.end() ||
963          sensorObject->second.find("Value") == sensorObject->second.end())
964      {
965          return ipmi::responseResponseError();
966      }
967      auto& valueVariant = sensorObject->second["Value"];
968      double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
969  
970      double max = 0;
971      double min = 0;
972      getSensorMaxMin(sensorMap, max, min);
973  
974      int16_t mValue = 0;
975      int16_t bValue = 0;
976      int8_t rExp = 0;
977      int8_t bExp = 0;
978      bool bSigned = false;
979  
980      if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
981      {
982          return ipmi::responseResponseError();
983      }
984  
985      uint8_t value =
986          scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
987      uint8_t operation =
988          static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
989      operation |=
990          static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
991      bool notReading = std::isnan(reading);
992  
993      if (!notReading)
994      {
995          auto availableObject =
996              sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
997          if (availableObject != sensorMap.end())
998          {
999              auto findAvailable = availableObject->second.find("Available");
1000              if (findAvailable != availableObject->second.end())
1001              {
1002                  bool* available = std::get_if<bool>(&(findAvailable->second));
1003                  if (available && !(*available))
1004                  {
1005                      notReading = true;
1006                  }
1007              }
1008          }
1009      }
1010  
1011      if (notReading)
1012      {
1013          operation |= static_cast<uint8_t>(
1014              IPMISensorReadingByte2::readingStateUnavailable);
1015      }
1016  
1017      if constexpr (details::enableInstrumentation)
1018      {
1019          int byteValue;
1020          if (bSigned)
1021          {
1022              byteValue = static_cast<int>(static_cast<int8_t>(value));
1023          }
1024          else
1025          {
1026              byteValue = static_cast<int>(static_cast<uint8_t>(value));
1027          }
1028  
1029          // Keep stats on the reading just obtained, even if it is "NaN"
1030          if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1031                                                   reading, byteValue))
1032          {
1033              // This is the first reading, show the coefficients
1034              double step = (max - min) / 255.0;
1035              std::cerr
1036                  << "IPMI sensor "
1037                  << details::sdrStatsTable.getName((ctx->lun << 8) | sensnum)
1038                  << ": Range min=" << min << " max=" << max << ", step=" << step
1039                  << ", Coefficients mValue=" << static_cast<int>(mValue)
1040                  << " rExp=" << static_cast<int>(rExp)
1041                  << " bValue=" << static_cast<int>(bValue)
1042                  << " bExp=" << static_cast<int>(bExp)
1043                  << " bSigned=" << static_cast<int>(bSigned) << "\n";
1044          }
1045      }
1046  
1047      uint8_t thresholds = 0;
1048  
1049      auto warningObject =
1050          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1051      if (warningObject != sensorMap.end())
1052      {
1053          auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1054          auto alarmLow = warningObject->second.find("WarningAlarmLow");
1055          if (alarmHigh != warningObject->second.end())
1056          {
1057              if (std::get<bool>(alarmHigh->second))
1058              {
1059                  thresholds |= static_cast<uint8_t>(
1060                      IPMISensorReadingByte3::upperNonCritical);
1061              }
1062          }
1063          if (alarmLow != warningObject->second.end())
1064          {
1065              if (std::get<bool>(alarmLow->second))
1066              {
1067                  thresholds |= static_cast<uint8_t>(
1068                      IPMISensorReadingByte3::lowerNonCritical);
1069              }
1070          }
1071      }
1072  
1073      auto criticalObject =
1074          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1075      if (criticalObject != sensorMap.end())
1076      {
1077          auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1078          auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1079          if (alarmHigh != criticalObject->second.end())
1080          {
1081              if (std::get<bool>(alarmHigh->second))
1082              {
1083                  thresholds |=
1084                      static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1085              }
1086          }
1087          if (alarmLow != criticalObject->second.end())
1088          {
1089              if (std::get<bool>(alarmLow->second))
1090              {
1091                  thresholds |=
1092                      static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1093              }
1094          }
1095      }
1096  
1097      // no discrete as of today so optional byte is never returned
1098      return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1099  }
1100  
1101  /** @brief implements the Set Sensor threshold command
1102   *  @param sensorNumber        - sensor number
1103   *  @param lowerNonCriticalThreshMask
1104   *  @param lowerCriticalThreshMask
1105   *  @param lowerNonRecovThreshMask
1106   *  @param upperNonCriticalThreshMask
1107   *  @param upperCriticalThreshMask
1108   *  @param upperNonRecovThreshMask
1109   *  @param reserved
1110   *  @param lowerNonCritical    - lower non-critical threshold
1111   *  @param lowerCritical       - Lower critical threshold
1112   *  @param lowerNonRecoverable - Lower non recovarable threshold
1113   *  @param upperNonCritical    - Upper non-critical threshold
1114   *  @param upperCritical       - Upper critical
1115   *  @param upperNonRecoverable - Upper Non-recoverable
1116   *
1117   *  @returns IPMI completion code
1118   */
ipmiSenSetSensorThresholds(ipmi::Context::ptr ctx,uint8_t sensorNum,bool lowerNonCriticalThreshMask,bool lowerCriticalThreshMask,bool lowerNonRecovThreshMask,bool upperNonCriticalThreshMask,bool upperCriticalThreshMask,bool upperNonRecovThreshMask,uint2_t reserved,uint8_t lowerNonCritical,uint8_t lowerCritical,uint8_t lowerNonRecoverable,uint8_t upperNonCritical,uint8_t upperCritical,uint8_t upperNonRecoverable)1119  ipmi::RspType<> ipmiSenSetSensorThresholds(
1120      ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1121      bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1122      bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1123      bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
1124      uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
1125      uint8_t upperNonCritical, uint8_t upperCritical,
1126      [[maybe_unused]] uint8_t upperNonRecoverable)
1127  {
1128      if (sensorNum == reservedSensorNumber || reserved)
1129      {
1130          return ipmi::responseInvalidFieldRequest();
1131      }
1132  
1133      // lower nc and upper nc not suppported on any sensor
1134      if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1135      {
1136          return ipmi::responseInvalidFieldRequest();
1137      }
1138  
1139      // if none of the threshold mask are set, nothing to do
1140      if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1141            lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1142            upperCriticalThreshMask | upperNonRecovThreshMask))
1143      {
1144          return ipmi::responseSuccess();
1145      }
1146  
1147      std::string connection;
1148      std::string path;
1149  
1150      ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1151      if (status)
1152      {
1153          return ipmi::response(status);
1154      }
1155      DbusInterfaceMap sensorMap;
1156      if (!getSensorMap(ctx, connection, path, sensorMap))
1157      {
1158          return ipmi::responseResponseError();
1159      }
1160  
1161      double max = 0;
1162      double min = 0;
1163      getSensorMaxMin(sensorMap, max, min);
1164  
1165      int16_t mValue = 0;
1166      int16_t bValue = 0;
1167      int8_t rExp = 0;
1168      int8_t bExp = 0;
1169      bool bSigned = false;
1170  
1171      if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1172      {
1173          return ipmi::responseResponseError();
1174      }
1175  
1176      // store a vector of property name, value to set, and interface
1177      std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1178  
1179      // define the indexes of the tuple
1180      constexpr uint8_t propertyName = 0;
1181      constexpr uint8_t thresholdValue = 1;
1182      constexpr uint8_t interface = 2;
1183      // verifiy all needed fields are present
1184      if (lowerCriticalThreshMask || upperCriticalThreshMask)
1185      {
1186          auto findThreshold =
1187              sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1188          if (findThreshold == sensorMap.end())
1189          {
1190              return ipmi::responseInvalidFieldRequest();
1191          }
1192          if (lowerCriticalThreshMask)
1193          {
1194              auto findLower = findThreshold->second.find("CriticalLow");
1195              if (findLower == findThreshold->second.end())
1196              {
1197                  return ipmi::responseInvalidFieldRequest();
1198              }
1199              thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1200                                           findThreshold->first);
1201          }
1202          if (upperCriticalThreshMask)
1203          {
1204              auto findUpper = findThreshold->second.find("CriticalHigh");
1205              if (findUpper == findThreshold->second.end())
1206              {
1207                  return ipmi::responseInvalidFieldRequest();
1208              }
1209              thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1210                                           findThreshold->first);
1211          }
1212      }
1213      if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1214      {
1215          auto findThreshold =
1216              sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1217          if (findThreshold == sensorMap.end())
1218          {
1219              return ipmi::responseInvalidFieldRequest();
1220          }
1221          if (lowerNonCriticalThreshMask)
1222          {
1223              auto findLower = findThreshold->second.find("WarningLow");
1224              if (findLower == findThreshold->second.end())
1225              {
1226                  return ipmi::responseInvalidFieldRequest();
1227              }
1228              thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1229                                           findThreshold->first);
1230          }
1231          if (upperNonCriticalThreshMask)
1232          {
1233              auto findUpper = findThreshold->second.find("WarningHigh");
1234              if (findUpper == findThreshold->second.end())
1235              {
1236                  return ipmi::responseInvalidFieldRequest();
1237              }
1238              thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1239                                           findThreshold->first);
1240          }
1241      }
1242      for (const auto& property : thresholdsToSet)
1243      {
1244          // from section 36.3 in the IPMI Spec, assume all linear
1245          double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1246                               (bValue * std::pow(10.0, bExp))) *
1247                              std::pow(10.0, rExp);
1248          setDbusProperty(
1249              *getSdBus(), connection, path, std::get<interface>(property),
1250              std::get<propertyName>(property), ipmi::Value(valueToSet));
1251      }
1252      return ipmi::responseSuccess();
1253  }
1254  
getIPMIThresholds(const DbusInterfaceMap & sensorMap)1255  IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1256  {
1257      IPMIThresholds resp;
1258      auto warningInterface =
1259          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1260      auto criticalInterface =
1261          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1262  
1263      if ((warningInterface != sensorMap.end()) ||
1264          (criticalInterface != sensorMap.end()))
1265      {
1266          auto sensorPair = sensorMap.find(sensor::sensorInterface);
1267  
1268          if (sensorPair == sensorMap.end())
1269          {
1270              // should not have been able to find a sensor not implementing
1271              // the sensor object
1272              throw std::runtime_error("Invalid sensor map");
1273          }
1274  
1275          double max = 0;
1276          double min = 0;
1277          getSensorMaxMin(sensorMap, max, min);
1278  
1279          int16_t mValue = 0;
1280          int16_t bValue = 0;
1281          int8_t rExp = 0;
1282          int8_t bExp = 0;
1283          bool bSigned = false;
1284  
1285          if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1286          {
1287              throw std::runtime_error("Invalid sensor atrributes");
1288          }
1289          if (warningInterface != sensorMap.end())
1290          {
1291              auto& warningMap = warningInterface->second;
1292  
1293              auto warningHigh = warningMap.find("WarningHigh");
1294              auto warningLow = warningMap.find("WarningLow");
1295  
1296              if (warningHigh != warningMap.end())
1297              {
1298                  double value =
1299                      std::visit(VariantToDoubleVisitor(), warningHigh->second);
1300                  if (std::isfinite(value))
1301                  {
1302                      resp.warningHigh = scaleIPMIValueFromDouble(
1303                          value, mValue, rExp, bValue, bExp, bSigned);
1304                  }
1305              }
1306              if (warningLow != warningMap.end())
1307              {
1308                  double value =
1309                      std::visit(VariantToDoubleVisitor(), warningLow->second);
1310                  if (std::isfinite(value))
1311                  {
1312                      resp.warningLow = scaleIPMIValueFromDouble(
1313                          value, mValue, rExp, bValue, bExp, bSigned);
1314                  }
1315              }
1316          }
1317          if (criticalInterface != sensorMap.end())
1318          {
1319              auto& criticalMap = criticalInterface->second;
1320  
1321              auto criticalHigh = criticalMap.find("CriticalHigh");
1322              auto criticalLow = criticalMap.find("CriticalLow");
1323  
1324              if (criticalHigh != criticalMap.end())
1325              {
1326                  double value =
1327                      std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1328                  if (std::isfinite(value))
1329                  {
1330                      resp.criticalHigh = scaleIPMIValueFromDouble(
1331                          value, mValue, rExp, bValue, bExp, bSigned);
1332                  }
1333              }
1334              if (criticalLow != criticalMap.end())
1335              {
1336                  double value =
1337                      std::visit(VariantToDoubleVisitor(), criticalLow->second);
1338                  if (std::isfinite(value))
1339                  {
1340                      resp.criticalLow = scaleIPMIValueFromDouble(
1341                          value, mValue, rExp, bValue, bExp, bSigned);
1342                  }
1343              }
1344          }
1345      }
1346      return resp;
1347  }
1348  
1349  ipmi::RspType<uint8_t, // readable
1350                uint8_t, // lowerNCrit
1351                uint8_t, // lowerCrit
1352                uint8_t, // lowerNrecoverable
1353                uint8_t, // upperNC
1354                uint8_t, // upperCrit
1355                uint8_t> // upperNRecoverable
ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx,uint8_t sensorNumber)1356      ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1357  {
1358      std::string connection;
1359      std::string path;
1360  
1361      if (sensorNumber == reservedSensorNumber)
1362      {
1363          return ipmi::responseInvalidFieldRequest();
1364      }
1365  
1366      auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1367      if (status)
1368      {
1369          return ipmi::response(status);
1370      }
1371  
1372      DbusInterfaceMap sensorMap;
1373      if (!getSensorMap(ctx, connection, path, sensorMap))
1374      {
1375          return ipmi::responseResponseError();
1376      }
1377  
1378      IPMIThresholds thresholdData;
1379      try
1380      {
1381          thresholdData = getIPMIThresholds(sensorMap);
1382      }
1383      catch (const std::exception&)
1384      {
1385          return ipmi::responseResponseError();
1386      }
1387  
1388      uint8_t readable = 0;
1389      uint8_t lowerNC = 0;
1390      uint8_t lowerCritical = 0;
1391      uint8_t lowerNonRecoverable = 0;
1392      uint8_t upperNC = 0;
1393      uint8_t upperCritical = 0;
1394      uint8_t upperNonRecoverable = 0;
1395  
1396      if (thresholdData.warningHigh)
1397      {
1398          readable |=
1399              1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1400          upperNC = *thresholdData.warningHigh;
1401      }
1402      if (thresholdData.warningLow)
1403      {
1404          readable |=
1405              1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1406          lowerNC = *thresholdData.warningLow;
1407      }
1408  
1409      if (thresholdData.criticalHigh)
1410      {
1411          readable |=
1412              1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1413          upperCritical = *thresholdData.criticalHigh;
1414      }
1415      if (thresholdData.criticalLow)
1416      {
1417          readable |=
1418              1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1419          lowerCritical = *thresholdData.criticalLow;
1420      }
1421  
1422      return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1423                                   lowerNonRecoverable, upperNC, upperCritical,
1424                                   upperNonRecoverable);
1425  }
1426  
1427  /** @brief implements the get Sensor event enable command
1428   *  @param sensorNumber - sensor number
1429   *
1430   *  @returns IPMI completion code plus response data
1431   *   - enabled               - Sensor Event messages
1432   *   - assertionEnabledLsb   - Assertion event messages
1433   *   - assertionEnabledMsb   - Assertion event messages
1434   *   - deassertionEnabledLsb - Deassertion event messages
1435   *   - deassertionEnabledMsb - Deassertion event messages
1436   */
1437  
1438  ipmi::RspType<uint8_t, // enabled
1439                uint8_t, // assertionEnabledLsb
1440                uint8_t, // assertionEnabledMsb
1441                uint8_t, // deassertionEnabledLsb
1442                uint8_t> // deassertionEnabledMsb
ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx,uint8_t sensorNum)1443      ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1444  {
1445      std::string connection;
1446      std::string path;
1447  
1448      uint8_t enabled = 0;
1449      uint8_t assertionEnabledLsb = 0;
1450      uint8_t assertionEnabledMsb = 0;
1451      uint8_t deassertionEnabledLsb = 0;
1452      uint8_t deassertionEnabledMsb = 0;
1453  
1454      if (sensorNum == reservedSensorNumber)
1455      {
1456          return ipmi::responseInvalidFieldRequest();
1457      }
1458  
1459      auto status = getSensorConnection(ctx, sensorNum, connection, path);
1460      if (status)
1461      {
1462          return ipmi::response(status);
1463      }
1464  
1465  #ifdef FEATURE_HYBRID_SENSORS
1466      if (auto sensor = findStaticSensor(path);
1467          sensor != ipmi::sensor::sensors.end() &&
1468          getSensorEventTypeFromPath(path) !=
1469              static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1470      {
1471          enabled = static_cast<uint8_t>(
1472              IPMISensorEventEnableByte2::sensorScanningEnable);
1473          uint16_t assertionEnabled = 0;
1474          for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1475                                        ->second.begin()
1476                                        ->second.second)
1477          {
1478              assertionEnabled |= (1 << offsetValMap.first);
1479          }
1480          assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1481          assertionEnabledMsb =
1482              static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1483  
1484          return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1485                                       assertionEnabledMsb, deassertionEnabledLsb,
1486                                       deassertionEnabledMsb);
1487      }
1488  #endif
1489  
1490      DbusInterfaceMap sensorMap;
1491      if (!getSensorMap(ctx, connection, path, sensorMap))
1492      {
1493          return ipmi::responseResponseError();
1494      }
1495  
1496      auto warningInterface =
1497          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1498      auto criticalInterface =
1499          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1500      if ((warningInterface != sensorMap.end()) ||
1501          (criticalInterface != sensorMap.end()))
1502      {
1503          enabled = static_cast<uint8_t>(
1504              IPMISensorEventEnableByte2::sensorScanningEnable);
1505          if (warningInterface != sensorMap.end())
1506          {
1507              auto& warningMap = warningInterface->second;
1508  
1509              auto warningHigh = warningMap.find("WarningHigh");
1510              auto warningLow = warningMap.find("WarningLow");
1511              if (warningHigh != warningMap.end())
1512              {
1513                  double value =
1514                      std::visit(VariantToDoubleVisitor(), warningHigh->second);
1515                  if (std::isfinite(value))
1516                  {
1517                      assertionEnabledLsb |= static_cast<uint8_t>(
1518                          IPMISensorEventEnableThresholds::
1519                              upperNonCriticalGoingHigh);
1520                      deassertionEnabledLsb |= static_cast<uint8_t>(
1521                          IPMISensorEventEnableThresholds::
1522                              upperNonCriticalGoingLow);
1523                  }
1524              }
1525              if (warningLow != warningMap.end())
1526              {
1527                  double value =
1528                      std::visit(VariantToDoubleVisitor(), warningLow->second);
1529                  if (std::isfinite(value))
1530                  {
1531                      assertionEnabledLsb |= static_cast<uint8_t>(
1532                          IPMISensorEventEnableThresholds::
1533                              lowerNonCriticalGoingLow);
1534                      deassertionEnabledLsb |= static_cast<uint8_t>(
1535                          IPMISensorEventEnableThresholds::
1536                              lowerNonCriticalGoingHigh);
1537                  }
1538              }
1539          }
1540          if (criticalInterface != sensorMap.end())
1541          {
1542              auto& criticalMap = criticalInterface->second;
1543  
1544              auto criticalHigh = criticalMap.find("CriticalHigh");
1545              auto criticalLow = criticalMap.find("CriticalLow");
1546  
1547              if (criticalHigh != criticalMap.end())
1548              {
1549                  double value =
1550                      std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1551                  if (std::isfinite(value))
1552                  {
1553                      assertionEnabledMsb |= static_cast<uint8_t>(
1554                          IPMISensorEventEnableThresholds::
1555                              upperCriticalGoingHigh);
1556                      deassertionEnabledMsb |= static_cast<uint8_t>(
1557                          IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1558                  }
1559              }
1560              if (criticalLow != criticalMap.end())
1561              {
1562                  double value =
1563                      std::visit(VariantToDoubleVisitor(), criticalLow->second);
1564                  if (std::isfinite(value))
1565                  {
1566                      assertionEnabledLsb |= static_cast<uint8_t>(
1567                          IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1568                      deassertionEnabledLsb |= static_cast<uint8_t>(
1569                          IPMISensorEventEnableThresholds::
1570                              lowerCriticalGoingHigh);
1571                  }
1572              }
1573          }
1574      }
1575  
1576      return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1577                                   assertionEnabledMsb, deassertionEnabledLsb,
1578                                   deassertionEnabledMsb);
1579  }
1580  
1581  /** @brief implements the get Sensor event status command
1582   *  @param sensorNumber - sensor number, FFh = reserved
1583   *
1584   *  @returns IPMI completion code plus response data
1585   *   - sensorEventStatus - Sensor Event messages state
1586   *   - assertions        - Assertion event messages
1587   *   - deassertions      - Deassertion event messages
1588   */
1589  ipmi::RspType<uint8_t,         // sensorEventStatus
1590                std::bitset<16>, // assertions
1591                std::bitset<16>  // deassertion
1592                >
ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx,uint8_t sensorNum)1593      ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1594  {
1595      if (sensorNum == reservedSensorNumber)
1596      {
1597          return ipmi::responseInvalidFieldRequest();
1598      }
1599  
1600      std::string connection;
1601      std::string path;
1602      auto status = getSensorConnection(ctx, sensorNum, connection, path);
1603      if (status)
1604      {
1605          lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1606                     "sensor number: {SENSOR_NUM}",
1607                     "SENSOR_NUM", sensorNum);
1608          return ipmi::response(status);
1609      }
1610  
1611  #ifdef FEATURE_HYBRID_SENSORS
1612      if (auto sensor = findStaticSensor(path);
1613          sensor != ipmi::sensor::sensors.end() &&
1614          getSensorEventTypeFromPath(path) !=
1615              static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1616      {
1617          auto response = ipmi::sensor::get::mapDbusToAssertion(
1618              sensor->second, path, sensor->second.sensorInterface);
1619          std::bitset<16> assertions;
1620          // deassertions are not used.
1621          std::bitset<16> deassertions = 0;
1622          uint8_t sensorEventStatus;
1623          if (response.readingOrStateUnavailable)
1624          {
1625              sensorEventStatus |= static_cast<uint8_t>(
1626                  IPMISensorReadingByte2::readingStateUnavailable);
1627          }
1628          if (response.scanningEnabled)
1629          {
1630              sensorEventStatus |= static_cast<uint8_t>(
1631                  IPMISensorReadingByte2::sensorScanningEnable);
1632          }
1633          if (response.allEventMessagesEnabled)
1634          {
1635              sensorEventStatus |= static_cast<uint8_t>(
1636                  IPMISensorReadingByte2::eventMessagesEnable);
1637          }
1638          assertions |= response.discreteReadingSensorStates << 8;
1639          assertions |= response.thresholdLevelsStates;
1640          return ipmi::responseSuccess(sensorEventStatus, assertions,
1641                                       deassertions);
1642      }
1643  #endif
1644  
1645      DbusInterfaceMap sensorMap;
1646      if (!getSensorMap(ctx, connection, path, sensorMap))
1647      {
1648          lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1649                     "sensor path: {SENSOR_PATH}",
1650                     "SENSOR_PATH", path);
1651          return ipmi::responseResponseError();
1652      }
1653  
1654      uint8_t sensorEventStatus =
1655          static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1656      std::bitset<16> assertions = 0;
1657      std::bitset<16> deassertions = 0;
1658  
1659      // handle VR typed sensor
1660      auto vrInterface = sensorMap.find(sensor::vrInterface);
1661      if (vrInterface != sensorMap.end())
1662      {
1663          if (!sensor::getVrEventStatus(ctx, connection, path,
1664                                        vrInterface->second, assertions))
1665          {
1666              return ipmi::responseResponseError();
1667          }
1668  
1669          // both Event Message and Sensor Scanning are disable for VR.
1670          sensorEventStatus = 0;
1671          return ipmi::responseSuccess(sensorEventStatus, assertions,
1672                                       deassertions);
1673      }
1674  
1675      auto warningInterface =
1676          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1677      auto criticalInterface =
1678          sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1679  
1680      std::optional<bool> criticalDeassertHigh =
1681          thresholdDeassertMap[path]["CriticalAlarmHigh"];
1682      std::optional<bool> criticalDeassertLow =
1683          thresholdDeassertMap[path]["CriticalAlarmLow"];
1684      std::optional<bool> warningDeassertHigh =
1685          thresholdDeassertMap[path]["WarningAlarmHigh"];
1686      std::optional<bool> warningDeassertLow =
1687          thresholdDeassertMap[path]["WarningAlarmLow"];
1688  
1689      if (criticalDeassertHigh && !*criticalDeassertHigh)
1690      {
1691          deassertions.set(static_cast<size_t>(
1692              IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1693      }
1694      if (criticalDeassertLow && !*criticalDeassertLow)
1695      {
1696          deassertions.set(static_cast<size_t>(
1697              IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1698      }
1699      if (warningDeassertHigh && !*warningDeassertHigh)
1700      {
1701          deassertions.set(static_cast<size_t>(
1702              IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1703      }
1704      if (warningDeassertLow && !*warningDeassertLow)
1705      {
1706          deassertions.set(static_cast<size_t>(
1707              IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1708      }
1709      if ((warningInterface != sensorMap.end()) ||
1710          (criticalInterface != sensorMap.end()))
1711      {
1712          sensorEventStatus = static_cast<size_t>(
1713              IPMISensorEventEnableByte2::eventMessagesEnable);
1714          if (warningInterface != sensorMap.end())
1715          {
1716              auto& warningMap = warningInterface->second;
1717  
1718              auto warningHigh = warningMap.find("WarningAlarmHigh");
1719              auto warningLow = warningMap.find("WarningAlarmLow");
1720              auto warningHighAlarm = false;
1721              auto warningLowAlarm = false;
1722  
1723              if (warningHigh != warningMap.end())
1724              {
1725                  warningHighAlarm = std::get<bool>(warningHigh->second);
1726              }
1727              if (warningLow != warningMap.end())
1728              {
1729                  warningLowAlarm = std::get<bool>(warningLow->second);
1730              }
1731              if (warningHighAlarm)
1732              {
1733                  assertions.set(static_cast<size_t>(
1734                      IPMIGetSensorEventEnableThresholds::
1735                          upperNonCriticalGoingHigh));
1736              }
1737              if (warningLowAlarm)
1738              {
1739                  assertions.set(static_cast<size_t>(
1740                      IPMIGetSensorEventEnableThresholds::
1741                          lowerNonCriticalGoingLow));
1742              }
1743          }
1744          if (criticalInterface != sensorMap.end())
1745          {
1746              auto& criticalMap = criticalInterface->second;
1747  
1748              auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1749              auto criticalLow = criticalMap.find("CriticalAlarmLow");
1750              auto criticalHighAlarm = false;
1751              auto criticalLowAlarm = false;
1752  
1753              if (criticalHigh != criticalMap.end())
1754              {
1755                  criticalHighAlarm = std::get<bool>(criticalHigh->second);
1756              }
1757              if (criticalLow != criticalMap.end())
1758              {
1759                  criticalLowAlarm = std::get<bool>(criticalLow->second);
1760              }
1761              if (criticalHighAlarm)
1762              {
1763                  assertions.set(static_cast<size_t>(
1764                      IPMIGetSensorEventEnableThresholds::
1765                          upperCriticalGoingHigh));
1766              }
1767              if (criticalLowAlarm)
1768              {
1769                  assertions.set(static_cast<size_t>(
1770                      IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1771              }
1772          }
1773      }
1774  
1775      return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1776  }
1777  
1778  // Construct a type 1 SDR for threshold sensor.
constructSensorSdrHeaderKey(uint16_t sensorNum,uint16_t recordID,get_sdr::SensorDataFullRecord & record)1779  void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1780                                   get_sdr::SensorDataFullRecord& record)
1781  {
1782      get_sdr::header::set_record_id(
1783          recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1784  
1785      uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1786      uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1787  
1788      record.header.sdr_version = ipmiSdrVersion;
1789      record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1790      record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1791                                    sizeof(get_sdr::SensorDataRecordHeader);
1792      record.key.owner_id = bmcI2CAddr;
1793      record.key.owner_lun = lun;
1794      record.key.sensor_number = sensornumber;
1795  }
constructSensorSdr(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,uint16_t sensorNum,uint16_t recordID,const std::string & service,const std::string & path,get_sdr::SensorDataFullRecord & record)1796  bool constructSensorSdr(
1797      ipmi::Context::ptr ctx,
1798      const std::unordered_set<std::string>& ipmiDecoratorPaths,
1799      uint16_t sensorNum, uint16_t recordID, const std::string& service,
1800      const std::string& path, get_sdr::SensorDataFullRecord& record)
1801  {
1802      constructSensorSdrHeaderKey(sensorNum, recordID, record);
1803  
1804      DbusInterfaceMap sensorMap;
1805      if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1806      {
1807          lg2::error("Failed to update sensor map for threshold sensor, "
1808                     "service: {SERVICE}, path: {PATH}",
1809                     "SERVICE", service, "PATH", path);
1810          return false;
1811      }
1812  
1813      record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1814      record.body.sensor_type = getSensorTypeFromPath(path);
1815      std::string type = getSensorTypeStringFromPath(path);
1816      auto typeCstr = type.c_str();
1817      auto findUnits = sensorUnits.find(typeCstr);
1818      if (findUnits != sensorUnits.end())
1819      {
1820          record.body.sensor_units_2_base =
1821              static_cast<uint8_t>(findUnits->second);
1822      } // else default 0x0 unspecified
1823  
1824      record.body.event_reading_type = getSensorEventTypeFromPath(path);
1825  
1826      auto sensorObject = sensorMap.find(sensor::sensorInterface);
1827      if (sensorObject == sensorMap.end())
1828      {
1829          lg2::error("constructSensorSdr: sensorObject error");
1830          return false;
1831      }
1832  
1833      uint8_t entityId = 0;
1834      uint8_t entityInstance = 0x01;
1835  
1836      // follow the association chain to get the parent board's entityid and
1837      // entityInstance
1838      updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1839                                entityInstance);
1840  
1841      record.body.entity_id = entityId;
1842      record.body.entity_instance = entityInstance;
1843  
1844      double max = 0;
1845      double min = 0;
1846      getSensorMaxMin(sensorMap, max, min);
1847  
1848      int16_t mValue = 0;
1849      int8_t rExp = 0;
1850      int16_t bValue = 0;
1851      int8_t bExp = 0;
1852      bool bSigned = false;
1853  
1854      if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1855      {
1856          lg2::error("constructSensorSdr: getSensorAttributes error");
1857          return false;
1858      }
1859  
1860      // The record.body is a struct SensorDataFullRecordBody
1861      // from sensorhandler.hpp in phosphor-ipmi-host.
1862      // The meaning of these bits appears to come from
1863      // table 43.1 of the IPMI spec.
1864      // The above 5 sensor attributes are stuffed in as follows:
1865      // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1866      // Byte 22-24 are for other purposes
1867      // Byte 25 = MMMMMMMM = LSB of M
1868      // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1869      // Byte 27 = BBBBBBBB = LSB of B
1870      // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1871      // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1872      // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1873  
1874      // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1875      record.body.m_lsb = mValue & 0xFF;
1876  
1877      uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1878      uint8_t mBitNine = (mValue & 0x0100) >> 8;
1879  
1880      // move the smallest bit of the MSB into place (bit 9)
1881      // the MSbs are bits 7:8 in m_msb_and_tolerance
1882      record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1883  
1884      record.body.b_lsb = bValue & 0xFF;
1885  
1886      uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1887      uint8_t bBitNine = (bValue & 0x0100) >> 8;
1888  
1889      // move the smallest bit of the MSB into place (bit 9)
1890      // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1891      record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1892  
1893      uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1894      uint8_t rExpBits = rExp & 0x07;
1895  
1896      uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1897      uint8_t bExpBits = bExp & 0x07;
1898  
1899      // move rExp and bExp into place
1900      record.body.r_b_exponents =
1901          (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1902  
1903      // Set the analog reading byte interpretation accordingly
1904      record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1905  
1906      // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1907      // These seem redundant, but derivable from the above 5 attributes
1908      // Original comment said "todo fill out rest of units"
1909  
1910      // populate sensor name from path
1911      auto name = sensor::parseSdrIdFromPath(path);
1912      get_sdr::body::set_id_strlen(name.size(), &record.body);
1913      get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
1914      std::memcpy(record.body.id_string, name.c_str(),
1915                  std::min(name.length() + 1, sizeof(record.body.id_string)));
1916  
1917      // Remember the sensor name, as determined for this sensor number
1918      details::sdrStatsTable.updateName(sensorNum, name);
1919  
1920      bool sensorSettable = false;
1921      auto mutability =
1922          sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1923      if (mutability != sensorMap.end())
1924      {
1925          sensorSettable =
1926              mappedVariant<bool>(mutability->second, "Mutable", false);
1927      }
1928      get_sdr::body::init_settable_state(sensorSettable, &record.body);
1929  
1930      // Grant write permission to sensors deemed externally settable
1931      details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
1932  
1933      IPMIThresholds thresholdData;
1934      try
1935      {
1936          thresholdData = getIPMIThresholds(sensorMap);
1937      }
1938      catch (const std::exception&)
1939      {
1940          lg2::error("constructSensorSdr: getIPMIThresholds error");
1941          return false;
1942      }
1943  
1944      if (thresholdData.criticalHigh)
1945      {
1946          record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1947          record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1948              IPMISensorEventEnableThresholds::criticalThreshold);
1949          record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1950              IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1951          record.body.supported_assertions[1] |= static_cast<uint8_t>(
1952              IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1953          record.body.discrete_reading_setting_mask[0] |=
1954              static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1955      }
1956      if (thresholdData.warningHigh)
1957      {
1958          record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1959          record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1960              IPMISensorEventEnableThresholds::nonCriticalThreshold);
1961          record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1962              IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1963          record.body.supported_assertions[0] |= static_cast<uint8_t>(
1964              IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1965          record.body.discrete_reading_setting_mask[0] |=
1966              static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1967      }
1968      if (thresholdData.criticalLow)
1969      {
1970          record.body.lower_critical_threshold = *thresholdData.criticalLow;
1971          record.body.supported_assertions[1] |= static_cast<uint8_t>(
1972              IPMISensorEventEnableThresholds::criticalThreshold);
1973          record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1974              IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1975          record.body.supported_assertions[0] |= static_cast<uint8_t>(
1976              IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1977          record.body.discrete_reading_setting_mask[0] |=
1978              static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1979      }
1980      if (thresholdData.warningLow)
1981      {
1982          record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1983          record.body.supported_assertions[1] |= static_cast<uint8_t>(
1984              IPMISensorEventEnableThresholds::nonCriticalThreshold);
1985          record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1986              IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1987          record.body.supported_assertions[0] |= static_cast<uint8_t>(
1988              IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1989          record.body.discrete_reading_setting_mask[0] |=
1990              static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1991      }
1992  
1993      // everything that is readable is setable
1994      record.body.discrete_reading_setting_mask[1] =
1995          record.body.discrete_reading_setting_mask[0];
1996      return true;
1997  }
1998  
1999  #ifdef FEATURE_HYBRID_SENSORS
2000  // Construct a type 1 SDR for discrete Sensor typed sensor.
constructStaticSensorSdr(ipmi::Context::ptr,uint16_t sensorNum,uint16_t recordID,ipmi::sensor::IdInfoMap::const_iterator sensor,get_sdr::SensorDataFullRecord & record)2001  void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
2002                                uint16_t recordID,
2003                                ipmi::sensor::IdInfoMap::const_iterator sensor,
2004                                get_sdr::SensorDataFullRecord& record)
2005  {
2006      constructSensorSdrHeaderKey(sensorNum, recordID, record);
2007  
2008      record.body.entity_id = sensor->second.entityType;
2009      record.body.sensor_type = sensor->second.sensorType;
2010      record.body.event_reading_type = sensor->second.sensorReadingType;
2011      record.body.entity_instance = sensor->second.instance;
2012      if (ipmi::sensor::Mutability::Write ==
2013          (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2014      {
2015          get_sdr::body::init_settable_state(true, &(record.body));
2016      }
2017  
2018      auto id_string = sensor->second.sensorName;
2019  
2020      if (id_string.empty())
2021      {
2022          id_string = sensor->second.sensorNameFunc(sensor->second);
2023      }
2024  
2025      if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2026      {
2027          get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2028                                       &(record.body));
2029      }
2030      else
2031      {
2032          get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2033      }
2034      get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
2035      std::strncpy(record.body.id_string, id_string.c_str(),
2036                   get_sdr::body::get_id_strlen(&(record.body)));
2037  }
2038  #endif
2039  
2040  // Construct type 3 SDR header and key (for VR and other discrete sensors)
constructEventSdrHeaderKey(uint16_t sensorNum,uint16_t recordID,get_sdr::SensorDataEventRecord & record)2041  void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2042                                  get_sdr::SensorDataEventRecord& record)
2043  {
2044      uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2045      uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2046  
2047      get_sdr::header::set_record_id(
2048          recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2049  
2050      record.header.sdr_version = ipmiSdrVersion;
2051      record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2052      record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2053                                    sizeof(get_sdr::SensorDataRecordHeader);
2054      record.key.owner_id = bmcI2CAddr;
2055      record.key.owner_lun = lun;
2056      record.key.sensor_number = sensornumber;
2057  
2058      record.body.entity_id = 0x00;
2059      record.body.entity_instance = 0x01;
2060  }
2061  
2062  // Construct a type 3 SDR for VR typed sensor(daemon).
constructVrSdr(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,uint16_t sensorNum,uint16_t recordID,const std::string & service,const std::string & path,get_sdr::SensorDataEventRecord & record)2063  bool constructVrSdr(ipmi::Context::ptr ctx,
2064                      const std::unordered_set<std::string>& ipmiDecoratorPaths,
2065                      uint16_t sensorNum, uint16_t recordID,
2066                      const std::string& service, const std::string& path,
2067                      get_sdr::SensorDataEventRecord& record)
2068  {
2069      constructEventSdrHeaderKey(sensorNum, recordID, record);
2070  
2071      DbusInterfaceMap sensorMap;
2072      if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2073      {
2074          lg2::error("Failed to update sensor map for VR sensor, "
2075                     "service: {SERVICE}, path: {PATH}",
2076                     "SERVICE", service, "PATH", path);
2077          return false;
2078      }
2079      // follow the association chain to get the parent board's entityid and
2080      // entityInstance
2081      updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2082                                record.body.entity_id,
2083                                record.body.entity_instance);
2084  
2085      // Sensor type is hardcoded as a module/board type instead of parsing from
2086      // sensor path. This is because VR control is allocated in an independent
2087      // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2088      // types.
2089      static constexpr const uint8_t module_board_type = 0x15;
2090      record.body.sensor_type = module_board_type;
2091      record.body.event_reading_type = 0x00;
2092  
2093      record.body.sensor_record_sharing_1 = 0x00;
2094      record.body.sensor_record_sharing_2 = 0x00;
2095  
2096      // populate sensor name from path
2097      auto name = sensor::parseSdrIdFromPath(path);
2098      int nameSize = std::min(name.size(), sizeof(record.body.id_string));
2099      get_sdr::body::set_id_strlen(nameSize, &record.body);
2100      get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
2101      std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2102      std::memcpy(record.body.id_string, name.c_str(), nameSize);
2103  
2104      // Remember the sensor name, as determined for this sensor number
2105      details::sdrStatsTable.updateName(sensorNum, name);
2106  
2107      return true;
2108  }
2109  
getNumberOfSensors()2110  uint16_t getNumberOfSensors()
2111  {
2112      return std::min(getSensorTree().size(), maxIPMISensors);
2113  }
2114  
getSensorDataRecord(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,std::vector<uint8_t> & recordData,uint16_t recordID,uint8_t readBytes=std::numeric_limits<uint8_t>::max ())2115  static int getSensorDataRecord(
2116      ipmi::Context::ptr ctx,
2117      const std::unordered_set<std::string>& ipmiDecoratorPaths,
2118      std::vector<uint8_t>& recordData, uint16_t recordID,
2119      uint8_t readBytes = std::numeric_limits<uint8_t>::max())
2120  {
2121      recordData.clear();
2122      size_t lastRecord = ipmi::getNumberOfSensors() +
2123                          ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2124      uint16_t nextRecord(recordID + 1);
2125  
2126      if (recordID == lastRecordIndex)
2127      {
2128          recordID = lastRecord;
2129      }
2130      if (recordID == lastRecord)
2131      {
2132          nextRecord = lastRecordIndex;
2133      }
2134      if (recordID > lastRecord)
2135      {
2136          lg2::error("getSensorDataRecord: recordID > lastRecord error");
2137          return GENERAL_ERROR;
2138      }
2139      if (recordID >= ipmi::getNumberOfSensors())
2140      {
2141          if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2142                                                                 recordData);
2143              err < 0)
2144          {
2145              return lastRecordIndex;
2146          }
2147          return nextRecord;
2148      }
2149  
2150      // Perform a incremental scan of the SDR Record ID's and translate the
2151      // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2152      // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2153      // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2154      // which has special meaning.
2155      std::string connection;
2156      std::string path;
2157      std::vector<std::string> interfaces;
2158      uint16_t sensNumFromRecID{recordID};
2159      if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
2160      {
2161          // LUN 0 has one reserved sensor number. Compensate here by adding one
2162          // to the record ID
2163          sensNumFromRecID = recordID + 1;
2164          ctx->lun = lun1;
2165      }
2166      else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
2167      {
2168          // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2169          // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2170          // rules governing its use.
2171          sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
2172          ctx->lun = lun3;
2173      }
2174  
2175      auto status =
2176          getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2177                              connection, path, &interfaces);
2178      if (status)
2179      {
2180          lg2::error("getSensorDataRecord: getSensorConnection error");
2181          return GENERAL_ERROR;
2182      }
2183      uint16_t sensorNum = getSensorNumberFromPath(path);
2184      // Return an error on LUN 2 assingments, and any sensor number beyond the
2185      // range of LUN 3
2186      if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2187          (sensorNum > lun3MaxSensorNum))
2188      {
2189          lg2::error("getSensorDataRecord: invalidSensorNumber");
2190          return GENERAL_ERROR;
2191      }
2192      uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2193      uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2194  
2195      if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2196          (lun != ctx->lun))
2197      {
2198          lg2::error("getSensorDataRecord: sensor record mismatch");
2199          return GENERAL_ERROR;
2200      }
2201  
2202      // Construct full record (SDR type 1) for the threshold sensors
2203      if (std::find(interfaces.begin(), interfaces.end(),
2204                    sensor::sensorInterface) != interfaces.end())
2205      {
2206          get_sdr::SensorDataFullRecord record = {};
2207  
2208          // If the request doesn't read SDR body, construct only header and key
2209          // part to avoid additional DBus transaction.
2210          if (readBytes <= sizeof(record.header) + sizeof(record.key))
2211          {
2212              constructSensorSdrHeaderKey(sensorNum, recordID, record);
2213          }
2214          else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2215                                       recordID, connection, path, record))
2216          {
2217              return GENERAL_ERROR;
2218          }
2219  
2220          recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2221                            reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2222  
2223          return nextRecord;
2224      }
2225  
2226  #ifdef FEATURE_HYBRID_SENSORS
2227      if (auto sensor = findStaticSensor(path);
2228          sensor != ipmi::sensor::sensors.end() &&
2229          getSensorEventTypeFromPath(path) !=
2230              static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2231      {
2232          get_sdr::SensorDataFullRecord record = {};
2233  
2234          // If the request doesn't read SDR body, construct only header and key
2235          // part to avoid additional DBus transaction.
2236          if (readBytes <= sizeof(record.header) + sizeof(record.key))
2237          {
2238              constructSensorSdrHeaderKey(sensorNum, recordID, record);
2239          }
2240          else
2241          {
2242              constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2243          }
2244  
2245          recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2246                            reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2247  
2248          return nextRecord;
2249      }
2250  #endif
2251  
2252      // Contruct SDR type 3 record for VR sensor (daemon)
2253      if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2254          interfaces.end())
2255      {
2256          get_sdr::SensorDataEventRecord record = {};
2257  
2258          // If the request doesn't read SDR body, construct only header and key
2259          // part to avoid additional DBus transaction.
2260          if (readBytes <= sizeof(record.header) + sizeof(record.key))
2261          {
2262              constructEventSdrHeaderKey(sensorNum, recordID, record);
2263          }
2264          else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2265                                   connection, path, record))
2266          {
2267              return GENERAL_ERROR;
2268          }
2269          recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2270                            reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2271      }
2272  
2273      return nextRecord;
2274  }
2275  
2276  /** @brief implements the get SDR Info command
2277   *  @param operation : 0 or not supplied returns sensor count
2278   *                     1 return SDR count
2279   *
2280   *  @returns IPMI completion code plus response data
2281   *   - sdrCount - sensor/SDR count
2282   *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2283   */
2284  static ipmi::RspType<uint8_t, // respcount
2285                       uint8_t, // dynamic population flags
2286                       uint32_t // last time a sensor was added
2287                       >
ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,std::optional<uint8_t> operation)2288      ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2289                                 std::optional<uint8_t> operation)
2290  {
2291      auto& sensorTree{getSensorTree()};
2292      uint8_t sdrCount{};
2293      // Sensors are dynamically allocated
2294      uint8_t lunsAndDynamicPopulation{0x80};
2295      constexpr uint8_t getSdrCount{1};
2296      constexpr uint8_t getSensorCount{0};
2297  
2298      if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2299      {
2300          return ipmi::responseResponseError();
2301      }
2302      uint16_t numSensors{ipmi::getNumberOfSensors()};
2303      if (operation.value_or(0) == getSdrCount)
2304      {
2305          sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2306      }
2307      else if (operation.value_or(0) == getSensorCount)
2308      {
2309          // Return the number of sensors attached to the LUN
2310          if ((ctx->lun == lun0) && (numSensors > 0))
2311          {
2312              sdrCount =
2313                  (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2314          }
2315          else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
2316          {
2317              sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2318                             ? maxSensorsPerLUN
2319                             : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2320          }
2321          else if (ctx->lun == lun3)
2322          {
2323              if (numSensors <= maxIPMISensors)
2324              {
2325                  sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2326                             maxSensorsPerLUN;
2327              }
2328              else
2329              {
2330                  throw std::out_of_range(
2331                      "Maximum number of IPMI sensors exceeded.");
2332              }
2333          }
2334      }
2335      else
2336      {
2337          return ipmi::responseInvalidFieldRequest();
2338      }
2339  
2340      // Flag which LUNs have sensors associated
2341      if (numSensors > 0)
2342      {
2343          lunsAndDynamicPopulation |= 1;
2344      }
2345      if (numSensors > maxSensorsPerLUN)
2346      {
2347          lunsAndDynamicPopulation |= 2;
2348      }
2349      if (numSensors >= (maxSensorsPerLUN * 2))
2350      {
2351          lunsAndDynamicPopulation |= 8;
2352      }
2353      if (numSensors > maxIPMISensors)
2354      {
2355          throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2356      }
2357  
2358      return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2359                                   sdrLastAdd);
2360  }
2361  
2362  /* end sensor commands */
2363  
2364  /* storage commands */
2365  
2366  ipmi::RspType<uint8_t,  // sdr version
2367                uint16_t, // record count
2368                uint16_t, // free space
2369                uint32_t, // most recent addition
2370                uint32_t, // most recent erase
2371                uint8_t   // operationSupport
2372                >
ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)2373      ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2374  {
2375      constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2376      uint16_t recordCount =
2377          ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
2378  
2379      uint8_t operationSupport = static_cast<uint8_t>(
2380          SdrRepositoryInfoOps::overflow); // write not supported
2381  
2382      operationSupport |=
2383          static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2384      operationSupport |= static_cast<uint8_t>(
2385          SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2386      return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2387                                   unspecifiedFreeSpace, sdrLastAdd,
2388                                   sdrLastRemove, operationSupport);
2389  }
2390  
2391  /** @brief implements the get SDR allocation info command
2392   *
2393   *  @returns IPMI completion code plus response data
2394   *   - allocUnits    - Number of possible allocation units
2395   *   - allocUnitSize - Allocation unit size in bytes.
2396   *   - allocUnitFree - Number of free allocation units
2397   *   - allocUnitLargestFree - Largest free block in allocation units
2398   *   - maxRecordSize    - Maximum record size in allocation units.
2399   */
2400  ipmi::RspType<uint16_t, // allocUnits
2401                uint16_t, // allocUnitSize
2402                uint16_t, // allocUnitFree
2403                uint16_t, // allocUnitLargestFree
2404                uint8_t   // maxRecordSize
2405                >
ipmiStorageGetSDRAllocationInfo()2406      ipmiStorageGetSDRAllocationInfo()
2407  {
2408      // 0000h unspecified number of alloc units
2409      constexpr uint16_t allocUnits = 0;
2410  
2411      constexpr uint16_t allocUnitFree = 0;
2412      constexpr uint16_t allocUnitLargestFree = 0;
2413      // only allow one block at a time
2414      constexpr uint8_t maxRecordSize = 1;
2415  
2416      return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2417                                   allocUnitLargestFree, maxRecordSize);
2418  }
2419  
2420  /** @brief implements the reserve SDR command
2421   *  @returns IPMI completion code plus response data
2422   *   - sdrReservationID
2423   */
ipmiStorageReserveSDR()2424  ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2425  {
2426      sdrReservationID++;
2427      if (sdrReservationID == 0)
2428      {
2429          sdrReservationID++;
2430      }
2431  
2432      return ipmi::responseSuccess(sdrReservationID);
2433  }
2434  
2435  ipmi::RspType<uint16_t,            // next record ID
2436                std::vector<uint8_t> // payload
2437                >
ipmiStorageGetSDR(ipmi::Context::ptr ctx,uint16_t reservationID,uint16_t recordID,uint8_t offset,uint8_t bytesToRead)2438      ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2439                        uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2440  {
2441      // reservation required for partial reads with non zero offset into
2442      // record
2443      if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2444      {
2445          lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
2446          return ipmi::responseInvalidReservationId();
2447      }
2448  
2449      auto& sensorTree = getSensorTree();
2450      if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2451      {
2452          lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
2453          return ipmi::responseResponseError();
2454      }
2455  
2456      auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2457  
2458      std::vector<uint8_t> record;
2459      int nextRecordId = getSensorDataRecord(
2460          ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2461          record, recordID, offset + bytesToRead);
2462  
2463      if (nextRecordId < 0)
2464      {
2465          lg2::error("ipmiStorageGetSDR: fail to get SDR");
2466          return ipmi::responseInvalidFieldRequest();
2467      }
2468      get_sdr::SensorDataRecordHeader* hdr =
2469          reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2470      if (!hdr)
2471      {
2472          lg2::error("ipmiStorageGetSDR: record header is null");
2473          return ipmi::responseSuccess(nextRecordId, record);
2474      }
2475  
2476      size_t sdrLength =
2477          sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2478      if (offset >= sdrLength)
2479      {
2480          lg2::error("ipmiStorageGetSDR: offset is outside the record");
2481          return ipmi::responseParmOutOfRange();
2482      }
2483      if (sdrLength < (offset + bytesToRead))
2484      {
2485          bytesToRead = sdrLength - offset;
2486      }
2487  
2488      uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2489      if (!respStart)
2490      {
2491          lg2::error("ipmiStorageGetSDR: record is null");
2492          return ipmi::responseSuccess(nextRecordId, record);
2493      }
2494  
2495      std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2496  
2497      return ipmi::responseSuccess(nextRecordId, recordData);
2498  }
2499  namespace dcmi
2500  {
2501  
2502  std::tuple<uint8_t,                // Total of instance sensors
2503             std::vector<sensorInfo> // The list of sensors
2504             >
getSensorsByEntityId(ipmi::Context::ptr ctx,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2505      getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2506                           uint8_t entityInstance, uint8_t instanceStart)
2507  {
2508      std::vector<sensorInfo> sensorList;
2509      uint8_t totalInstSensor = 0;
2510      auto match = ipmi::dcmi::validEntityId.find(entityId);
2511  
2512      if (match == ipmi::dcmi::validEntityId.end())
2513      {
2514          return std::make_tuple(totalInstSensor, sensorList);
2515      }
2516  
2517      auto& sensorTree = getSensorTree();
2518      if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2519      {
2520          return std::make_tuple(totalInstSensor, sensorList);
2521      }
2522  
2523      auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2524  
2525      size_t invalidSensorNumberErrCount = 0;
2526      for (const auto& sensor : sensorTree)
2527      {
2528          const std::string& sensorObjPath = sensor.first;
2529          const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2530  
2531          /*
2532           * In the DCMI specification, it only supports the sensor type is 0x01
2533           * (temperature type) for both Get Sensor Info and Get Temperature
2534           * Readings commands.
2535           */
2536          if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2537          {
2538              continue;
2539          }
2540  
2541          const auto& connection = sensor.second.begin()->first;
2542          DbusInterfaceMap sensorMap;
2543  
2544          if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2545                            sensorMapSdrUpdatePeriod))
2546          {
2547              lg2::error("Failed to update sensor map for threshold sensor, "
2548                         "service: {SERVICE}, path: {PATH}",
2549                         "SERVICE", connection, "PATH", sensorObjPath);
2550              continue;
2551          }
2552  
2553          uint8_t entityIdValue = 0;
2554          uint8_t entityInstanceValue = 0;
2555  
2556          /*
2557           * Get the Entity ID, Entity Instance information which are configured
2558           * in the Entity-Manger.
2559           */
2560          updateIpmiFromAssociation(
2561              sensorObjPath,
2562              ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2563              sensorMap, entityIdValue, entityInstanceValue);
2564  
2565          if (entityIdValue == match->first || entityIdValue == match->second)
2566          {
2567              totalInstSensor++;
2568  
2569              /*
2570               * When Entity Instance parameter is not 0, we only get the first
2571               * sensor whose Entity Instance number is equal input Entity
2572               * Instance parameter.
2573               */
2574              if (entityInstance)
2575              {
2576                  if (!sensorList.empty())
2577                  {
2578                      continue;
2579                  }
2580  
2581                  if (entityInstanceValue == entityInstance)
2582                  {
2583                      auto recordId = getSensorNumberFromPath(sensorObjPath);
2584                      if (recordId == invalidSensorNumber)
2585                      {
2586                          ++invalidSensorNumberErrCount;
2587                          continue;
2588                      }
2589                      sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2590                                              recordId, entityIdValue,
2591                                              entityInstanceValue);
2592                  }
2593              }
2594              else if (entityInstanceValue >= instanceStart)
2595              {
2596                  auto recordId = getSensorNumberFromPath(sensorObjPath);
2597                  if (recordId == invalidSensorNumber)
2598                  {
2599                      ++invalidSensorNumberErrCount;
2600                      continue;
2601                  }
2602                  sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2603                                          recordId, entityIdValue,
2604                                          entityInstanceValue);
2605              }
2606          }
2607      }
2608      if (invalidSensorNumberErrCount != 0)
2609      {
2610          lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2611                     "{ERR_COUNT} times",
2612                     "ERR_COUNT", invalidSensorNumberErrCount);
2613      }
2614  
2615      auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2616          return first.entityInstance <= second.entityInstance;
2617      };
2618  
2619      sort(sensorList.begin(), sensorList.end(), cmpFunc);
2620  
2621      return std::make_tuple(totalInstSensor, sensorList);
2622  }
2623  
2624  std::tuple<bool,    // Reading result
2625             uint7_t, // Temp value
2626             bool>    // Sign bit
readTemp(ipmi::Context::ptr ctx,const std::string & objectPath)2627      readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2628  {
2629      std::string service{};
2630      boost::system::error_code ec =
2631          ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2632      if (ec.value())
2633      {
2634          return std::make_tuple(false, 0, false);
2635      }
2636  
2637      ipmi::PropertyMap properties{};
2638      ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2639                                      sensor::sensorInterface, properties);
2640      if (ec.value())
2641      {
2642          return std::make_tuple(false, 0, false);
2643      }
2644  
2645      auto scaleIt = properties.find("Scale");
2646      double scaleVal = 0.0;
2647      if (scaleIt != properties.end())
2648      {
2649          scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2650      }
2651  
2652      auto tempValIt = properties.find("Value");
2653      double tempVal = 0.0;
2654      if (tempValIt == properties.end())
2655      {
2656          return std::make_tuple(false, 0, false);
2657      }
2658  
2659      const double maxTemp = 127;
2660      double absTempVal = 0.0;
2661      bool signBit = false;
2662  
2663      tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2664      tempVal = std::pow(10, scaleVal) * tempVal;
2665      absTempVal = std::abs(tempVal);
2666      absTempVal = std::min(absTempVal, maxTemp);
2667      signBit = (tempVal < 0) ? true : false;
2668  
2669      return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2670  }
2671  
2672  ipmi::RspType<uint8_t,              // No of instances for requested id
2673                uint8_t,              // No of record ids in the response
2674                std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2675                                      // IDs
2676                >
getSensorInfo(ipmi::Context::ptr ctx,uint8_t sensorType,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2677      getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
2678                    uint8_t entityInstance, uint8_t instanceStart)
2679  {
2680      auto match = ipmi::dcmi::validEntityId.find(entityId);
2681      if (match == ipmi::dcmi::validEntityId.end())
2682      {
2683          lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
2684  
2685          return ipmi::responseInvalidFieldRequest();
2686      }
2687  
2688      if (sensorType != ipmi::dcmi::temperatureSensorType)
2689      {
2690          lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2691                     sensorType);
2692  
2693          return ipmi::responseInvalidFieldRequest();
2694      }
2695  
2696      std::vector<uint16_t> sensorRec{};
2697      const auto& [totalSensorInst, sensorList] =
2698          getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2699  
2700      if (sensorList.empty())
2701      {
2702          return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2703      }
2704  
2705      /*
2706       * As DCMI specification, the maximum number of Record Ids of response data
2707       * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2708       * Record Ids of response data is 8. Therefore, not all of sensors are shown
2709       * in response data.
2710       */
2711      uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2712  
2713      for (const auto& sensor : sensorList)
2714      {
2715          sensorRec.emplace_back(sensor.recordId);
2716          if (sensorRec.size() >= numOfRec)
2717          {
2718              break;
2719          }
2720      }
2721  
2722      return ipmi::responseSuccess(
2723          totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
2724  }
2725  
2726  ipmi::RspType<uint8_t,                // No of instances for requested id
2727                uint8_t,                // No of record ids in the response
2728                std::vector<            // Temperature Data
2729                    std::tuple<uint7_t, // Temperature value
2730                               bool,    // Sign bit
2731                               uint8_t  // Entity Instance of sensor
2732                               >>>
getTempReadings(ipmi::Context::ptr ctx,uint8_t sensorType,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2733      getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2734                      uint8_t entityId, uint8_t entityInstance,
2735                      uint8_t instanceStart)
2736  {
2737      auto match = ipmi::dcmi::validEntityId.find(entityId);
2738      if (match == ipmi::dcmi::validEntityId.end())
2739      {
2740          lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
2741  
2742          return ipmi::responseInvalidFieldRequest();
2743      }
2744  
2745      if (sensorType != ipmi::dcmi::temperatureSensorType)
2746      {
2747          lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2748                     sensorType);
2749  
2750          return ipmi::responseInvalidFieldRequest();
2751      }
2752  
2753      std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2754      const auto& [totalSensorInst, sensorList] =
2755          getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2756  
2757      if (sensorList.empty())
2758      {
2759          return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2760      }
2761  
2762      /*
2763       * As DCMI specification, the maximum number of Record Ids of response data
2764       * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2765       * Record Ids of response data is 8. Therefore, not all of sensors are shown
2766       * in response data.
2767       */
2768      uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2769  
2770      for (const auto& sensor : sensorList)
2771      {
2772          const auto& [readResult, tempVal, signBit] =
2773              readTemp(ctx, sensor.objectPath);
2774  
2775          if (readResult)
2776          {
2777              tempReadingVal.emplace_back(
2778                  std::make_tuple(tempVal, signBit, sensor.entityInstance));
2779  
2780              if (tempReadingVal.size() >= numOfRec)
2781              {
2782                  break;
2783              }
2784          }
2785      }
2786  
2787      return ipmi::responseSuccess(totalSensorInst,
2788                                   static_cast<uint8_t>(tempReadingVal.size()),
2789                                   tempReadingVal);
2790  }
2791  
2792  } // namespace dcmi
2793  
2794  /* end storage commands */
2795  
registerSensorFunctions()2796  void registerSensorFunctions()
2797  {
2798      // <Platform Event>
2799      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2800                            ipmi::sensor_event::cmdPlatformEvent,
2801                            ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2802  
2803      // <Set Sensor Reading and Event Status>
2804      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2805                            ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2806                            ipmi::Privilege::Operator, ipmiSetSensorReading);
2807  
2808      // <Get Sensor Reading>
2809      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2810                            ipmi::sensor_event::cmdGetSensorReading,
2811                            ipmi::Privilege::User, ipmiSenGetSensorReading);
2812  
2813      // <Get Sensor Threshold>
2814      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2815                            ipmi::sensor_event::cmdGetSensorThreshold,
2816                            ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2817  
2818      // <Set Sensor Threshold>
2819      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2820                            ipmi::sensor_event::cmdSetSensorThreshold,
2821                            ipmi::Privilege::Operator,
2822                            ipmiSenSetSensorThresholds);
2823  
2824      // <Get Sensor Event Enable>
2825      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2826                            ipmi::sensor_event::cmdGetSensorEventEnable,
2827                            ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2828  
2829      // <Get Sensor Event Status>
2830      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2831                            ipmi::sensor_event::cmdGetSensorEventStatus,
2832                            ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2833  
2834      // register all storage commands for both Sensor and Storage command
2835      // versions
2836  
2837      // <Get SDR Repository Info>
2838      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2839                            ipmi::storage::cmdGetSdrRepositoryInfo,
2840                            ipmi::Privilege::User,
2841                            ipmiStorageGetSDRRepositoryInfo);
2842  
2843      // <Get Device SDR Info>
2844      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2845                            ipmi::sensor_event::cmdGetDeviceSdrInfo,
2846                            ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2847  
2848      // <Get SDR Allocation Info>
2849      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2850                            ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2851                            ipmi::Privilege::User,
2852                            ipmiStorageGetSDRAllocationInfo);
2853  
2854      // <Reserve SDR Repo>
2855      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2856                            ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2857                            ipmi::Privilege::User, ipmiStorageReserveSDR);
2858  
2859      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2860                            ipmi::storage::cmdReserveSdrRepository,
2861                            ipmi::Privilege::User, ipmiStorageReserveSDR);
2862  
2863      // <Get Sdr>
2864      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2865                            ipmi::sensor_event::cmdGetDeviceSdr,
2866                            ipmi::Privilege::User, ipmiStorageGetSDR);
2867  
2868      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2869                            ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2870                            ipmiStorageGetSDR);
2871      // <Get DCMI Sensor Info>
2872      ipmi::registerGroupHandler(
2873          ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2874          ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2875          ipmi::dcmi::getSensorInfo);
2876      // <Get Temperature Readings>
2877      ipmi::registerGroupHandler(
2878          ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2879          ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2880          ipmi::dcmi::getTempReadings);
2881  }
2882  } // namespace ipmi
2883