xref: /openbmc/phosphor-host-ipmid/sensordatahandler.hpp (revision 1318a5ed36cfd41335e687b54db1c17c0dde8f45)
1  #pragma once
2  
3  #include "config.h"
4  
5  #include "sensorhandler.hpp"
6  
7  #include <ipmid/api.hpp>
8  #include <ipmid/types.hpp>
9  #include <ipmid/utils.hpp>
10  #include <phosphor-logging/elog-errors.hpp>
11  #include <phosphor-logging/lg2.hpp>
12  #include <sdbusplus/message/types.hpp>
13  
14  #include <cmath>
15  
16  #ifdef FEATURE_SENSORS_CACHE
17  
18  extern ipmi::sensor::SensorCacheMap sensorCacheMap;
19  
20  // The signal's message type is 0x04 from DBus spec:
21  // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
22  static constexpr auto msgTypeSignal = 0x04;
23  
24  #endif
25  
26  namespace ipmi
27  {
28  namespace sensor
29  {
30  
31  using Assertion = uint16_t;
32  using Deassertion = uint16_t;
33  using AssertionSet = std::pair<Assertion, Deassertion>;
34  using Service = std::string;
35  using Path = std::string;
36  using Interface = std::string;
37  using ServicePath = std::pair<Path, Service>;
38  using Interfaces = std::vector<Interface>;
39  using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>;
40  using PropertyMap = ipmi::PropertyMap;
41  
42  using namespace phosphor::logging;
43  
44  /** @brief Make assertion set from input data
45   *  @param[in] cmdData - Input sensor data
46   *  @return pair of assertion and deassertion set
47   */
48  AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData);
49  
50  /** @brief send the message to DBus
51   *  @param[in] msg - message to send
52   *  @return failure status in IPMI error code
53   */
54  ipmi_ret_t updateToDbus(IpmiUpdateData& msg);
55  
56  namespace get
57  {
58  
59  /** @brief Populate sensor name from the D-Bus property associated with the
60   *         sensor. In the example entry from the yaml, the name of the D-bus
61   *         property "AttemptsLeft" is the sensor name.
62   *
63   *         0x07:
64   *            sensorType: 195
65   *            path: /xyz/openbmc_project/state/host0
66   *            sensorReadingType: 0x6F
67   *            serviceInterface: org.freedesktop.DBus.Properties
68   *            readingType: readingAssertion
69   *            sensorNamePattern: nameProperty
70   *            interfaces:
71   *              xyz.openbmc_project.Control.Boot.RebootAttempts:
72   *                AttemptsLeft:
73   *                    Offsets:
74   *                        0xFF:
75   *                          type: uint32_t
76   *
77   *
78   *  @param[in] sensorInfo - Dbus info related to sensor.
79   *
80   *  @return On success return the sensor name for the sensor.
81   */
nameProperty(const Info & sensorInfo)82  inline SensorName nameProperty(const Info& sensorInfo)
83  {
84      return sensorInfo.propertyInterfaces.begin()->second.begin()->first;
85  }
86  
87  /** @brief Populate sensor name from the D-Bus object associated with the
88   *         sensor. If the object path is /system/chassis/motherboard/dimm0 then
89   *         the leaf dimm0 is considered as the sensor name.
90   *
91   *  @param[in] sensorInfo - Dbus info related to sensor.
92   *
93   *  @return On success return the sensor name for the sensor.
94   */
nameLeaf(const Info & sensorInfo)95  inline SensorName nameLeaf(const Info& sensorInfo)
96  {
97      return sensorInfo.sensorPath.substr(
98          sensorInfo.sensorPath.find_last_of('/') + 1,
99          sensorInfo.sensorPath.length());
100  }
101  
102  /** @brief Populate sensor name from the D-Bus object associated with the
103   *         sensor and the property.
104   *         If the object path is /xyz/openbmc_project/inventory/Fan0 and
105   *         the property is Present, the leaf Fan0 and the Property is
106   *         joined to Fan0_Present as the sensor name.
107   *
108   *  @param[in] sensorInfo - Dbus info related to sensor.
109   *
110   *  @return On success return the sensor name for the sensor.
111   */
nameLeafProperty(const Info & sensorInfo)112  inline SensorName nameLeafProperty(const Info& sensorInfo)
113  {
114      return nameLeaf(sensorInfo) + "_" + nameProperty(sensorInfo);
115  }
116  
117  /** @brief Populate sensor name from the D-Bus object associated with the
118   *         sensor. If the object path is /system/chassis/motherboard/cpu0/core0
119   *         then the sensor name is cpu0_core0. The leaf and the parent is put
120   *         together to get the sensor name.
121   *
122   *  @param[in] sensorInfo - Dbus info related to sensor.
123   *
124   *  @return On success return the sensor name for the sensor.
125   */
126  SensorName nameParentLeaf(const Info& sensorInfo);
127  
128  /**
129   *  @brief Helper function to map the dbus info to sensor's assertion status
130   *         for the get sensor reading command.
131   *
132   *  @param[in] sensorInfo - Dbus info related to sensor.
133   *  @param[in] path - Dbus object path.
134   *  @param[in] interface - Dbus interface.
135   *
136   *  @return Response for get sensor reading command.
137   */
138  GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
139                                       const InstancePath& path,
140                                       const DbusInterface& interface);
141  
142  #ifndef FEATURE_SENSORS_CACHE
143  /**
144   *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
145   *         reading command response.
146   *
147   *  @param[in] sensorInfo - Dbus info related to sensor.
148   *
149   *  @return Response for get sensor reading command.
150   */
151  GetSensorResponse assertion(const Info& sensorInfo);
152  
153  /**
154   *  @brief Maps the Dbus info to the reading field in the Get sensor reading
155   *         command response.
156   *
157   *  @param[in] sensorInfo - Dbus info related to sensor.
158   *
159   *  @return Response for get sensor reading command.
160   */
161  GetSensorResponse eventdata2(const Info& sensorInfo);
162  
163  /**
164   *  @brief readingAssertion is a case where the entire assertion state field
165   *         serves as the sensor value.
166   *
167   *  @tparam T - type of the dbus property related to sensor.
168   *  @param[in] sensorInfo - Dbus info related to sensor.
169   *
170   *  @return Response for get sensor reading command.
171   */
172  template <typename T>
readingAssertion(const Info & sensorInfo)173  GetSensorResponse readingAssertion(const Info& sensorInfo)
174  {
175      sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
176      GetSensorResponse response{};
177  
178      enableScanning(&response);
179  
180      auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
181                                      sensorInfo.sensorPath);
182  
183      auto propValue = ipmi::getDbusProperty(
184          bus, service, sensorInfo.sensorPath,
185          sensorInfo.propertyInterfaces.begin()->first,
186          sensorInfo.propertyInterfaces.begin()->second.begin()->first);
187  
188      setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response);
189  
190      return response;
191  }
192  
193  /** @brief Map the Dbus info to the reading field in the Get sensor reading
194   *         command response
195   *
196   *  @tparam T - type of the dbus property related to sensor.
197   *  @param[in] sensorInfo - Dbus info related to sensor.
198   *
199   *  @return Response for get sensor reading command.
200   */
201  template <typename T>
readingData(const Info & sensorInfo)202  GetSensorResponse readingData(const Info& sensorInfo)
203  {
204      sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
205  
206      GetSensorResponse response{};
207  
208      enableScanning(&response);
209  
210      auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
211                                      sensorInfo.sensorPath);
212  
213  #ifdef UPDATE_FUNCTIONAL_ON_FAIL
214      // Check the OperationalStatus interface for functional property
215      if (sensorInfo.propertyInterfaces.begin()->first ==
216          "xyz.openbmc_project.Sensor.Value")
217      {
218          bool functional = true;
219          try
220          {
221              auto funcValue = ipmi::getDbusProperty(
222                  bus, service, sensorInfo.sensorPath,
223                  "xyz.openbmc_project.State.Decorator.OperationalStatus",
224                  "Functional");
225              functional = std::get<bool>(funcValue);
226          }
227          catch (...)
228          {
229              // No-op if Functional property could not be found since this
230              // check is only valid for Sensor.Value read for hwmonio
231          }
232          if (!functional)
233          {
234              throw SensorFunctionalError();
235          }
236      }
237  #endif
238  
239      auto propValue = ipmi::getDbusProperty(
240          bus, service, sensorInfo.sensorPath,
241          sensorInfo.propertyInterfaces.begin()->first,
242          sensorInfo.propertyInterfaces.begin()->second.begin()->first);
243  
244      double value = std::get<T>(propValue) *
245                     std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
246      int32_t rawData =
247          (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM;
248  
249      constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
250      constexpr uint8_t signedDataFormat = 0x80;
251      // if sensorUnits1 [7:6] = 10b, sensor is signed
252      int32_t minClamp;
253      int32_t maxClamp;
254      if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
255      {
256          minClamp = std::numeric_limits<int8_t>::lowest();
257          maxClamp = std::numeric_limits<int8_t>::max();
258      }
259      else
260      {
261          minClamp = std::numeric_limits<uint8_t>::lowest();
262          maxClamp = std::numeric_limits<uint8_t>::max();
263      }
264      setReading(static_cast<uint8_t>(std::clamp(rawData, minClamp, maxClamp)),
265                 &response);
266  
267      if (!std::isfinite(value))
268      {
269          response.readingOrStateUnavailable = 1;
270      }
271  
272      bool critAlarmHigh;
273      try
274      {
275          critAlarmHigh = std::get<bool>(ipmi::getDbusProperty(
276              bus, service, sensorInfo.sensorPath,
277              "xyz.openbmc_project.Sensor.Threshold.Critical",
278              "CriticalAlarmHigh"));
279      }
280      catch (const std::exception& e)
281      {
282          critAlarmHigh = false;
283      }
284      bool critAlarmLow;
285      try
286      {
287          critAlarmLow = std::get<bool>(ipmi::getDbusProperty(
288              bus, service, sensorInfo.sensorPath,
289              "xyz.openbmc_project.Sensor.Threshold.Critical",
290              "CriticalAlarmLow"));
291      }
292      catch (const std::exception& e)
293      {
294          critAlarmLow = false;
295      }
296      bool warningAlarmHigh;
297      try
298      {
299          warningAlarmHigh = std::get<bool>(ipmi::getDbusProperty(
300              bus, service, sensorInfo.sensorPath,
301              "xyz.openbmc_project.Sensor.Threshold.Warning",
302              "WarningAlarmHigh"));
303      }
304      catch (const std::exception& e)
305      {
306          warningAlarmHigh = false;
307      }
308      bool warningAlarmLow;
309      try
310      {
311          warningAlarmLow = std::get<bool>(ipmi::getDbusProperty(
312              bus, service, sensorInfo.sensorPath,
313              "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningAlarmLow"));
314      }
315      catch (const std::exception& e)
316      {
317          warningAlarmLow = false;
318      }
319      response.thresholdLevelsStates =
320          (static_cast<uint8_t>(critAlarmHigh) << 3) |
321          (static_cast<uint8_t>(critAlarmLow) << 2) |
322          (static_cast<uint8_t>(warningAlarmHigh) << 1) |
323          (static_cast<uint8_t>(warningAlarmLow));
324  
325      return response;
326  }
327  
328  #else
329  
330  /**
331   *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
332   *         reading command response.
333   *
334   *  @param[in] id - The sensor id
335   *  @param[in] sensorInfo - Dbus info related to sensor.
336   *  @param[in] msg - Dbus message from match callback.
337   *
338   *  @return Response for get sensor reading command.
339   */
340  std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
341                                             const PropertyMap& properties);
342  
343  /**
344   *  @brief Maps the Dbus info to the reading field in the Get sensor reading
345   *         command response.
346   *
347   *  @param[in] id - The sensor id
348   *  @param[in] sensorInfo - Dbus info related to sensor.
349   *  @param[in] msg - Dbus message from match callback.
350   *
351   *  @return Response for get sensor reading command.
352   */
353  std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo,
354                                              const PropertyMap& properties);
355  
356  /**
357   *  @brief readingAssertion is a case where the entire assertion state field
358   *         serves as the sensor value.
359   *
360   *  @tparam T - type of the dbus property related to sensor.
361   *  @param[in] id - The sensor id
362   *  @param[in] sensorInfo - Dbus info related to sensor.
363   *  @param[in] msg - Dbus message from match callback.
364   *
365   *  @return Response for get sensor reading command.
366   */
367  template <typename T>
readingAssertion(uint8_t id,const Info & sensorInfo,const PropertyMap & properties)368  std::optional<GetSensorResponse> readingAssertion(
369      uint8_t id, const Info& sensorInfo, const PropertyMap& properties)
370  {
371      GetSensorResponse response{};
372      enableScanning(&response);
373  
374      auto iter = properties.find(
375          sensorInfo.propertyInterfaces.begin()->second.begin()->first);
376      if (iter == properties.end())
377      {
378          return {};
379      }
380  
381      setAssertionBytes(static_cast<uint16_t>(std::get<T>(iter->second)),
382                        &response);
383  
384      if (!sensorCacheMap[id].has_value())
385      {
386          sensorCacheMap[id] = SensorData{};
387      }
388      sensorCacheMap[id]->response = response;
389      return response;
390  }
391  
392  /** @brief Get sensor reading from the dbus message from match
393   *
394   *  @tparam T - type of the dbus property related to sensor.
395   *  @param[in] id - The sensor id
396   *  @param[in] sensorInfo - Dbus info related to sensor.
397   *  @param[in] msg - Dbus message from match callback.
398   *
399   *  @return Response for get sensor reading command.
400   */
401  template <typename T>
readingData(uint8_t id,const Info & sensorInfo,const PropertyMap & properties)402  std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo,
403                                               const PropertyMap& properties)
404  {
405      auto iter = properties.find("Functional");
406      if (iter != properties.end())
407      {
408          sensorCacheMap[id]->functional = std::get<bool>(iter->second);
409      }
410      iter = properties.find("Available");
411      if (iter != properties.end())
412      {
413          sensorCacheMap[id]->available = std::get<bool>(iter->second);
414      }
415  #ifdef UPDATE_FUNCTIONAL_ON_FAIL
416      if (sensorCacheMap[id])
417      {
418          if (!sensorCacheMap[id]->functional)
419          {
420              throw SensorFunctionalError();
421          }
422      }
423  #endif
424  
425      GetSensorResponse response{};
426  
427      enableScanning(&response);
428  
429      iter = properties.find(
430          sensorInfo.propertyInterfaces.begin()->second.begin()->first);
431      if (iter == properties.end())
432      {
433          return {};
434      }
435  
436      double value = std::get<T>(iter->second) *
437                     std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
438      int32_t rawData =
439          (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM;
440  
441      constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
442      constexpr uint8_t signedDataFormat = 0x80;
443      // if sensorUnits1 [7:6] = 10b, sensor is signed
444      if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
445      {
446          if (rawData > std::numeric_limits<int8_t>::max() ||
447              rawData < std::numeric_limits<int8_t>::lowest())
448          {
449              lg2::error("Value out of range");
450              throw std::out_of_range("Value out of range");
451          }
452          setReading(static_cast<int8_t>(rawData), &response);
453      }
454      else
455      {
456          if (rawData > std::numeric_limits<uint8_t>::max() ||
457              rawData < std::numeric_limits<uint8_t>::lowest())
458          {
459              lg2::error("Value out of range");
460              throw std::out_of_range("Value out of range");
461          }
462          setReading(static_cast<uint8_t>(rawData), &response);
463      }
464  
465      if (!std::isfinite(value))
466      {
467          response.readingOrStateUnavailable = 1;
468      }
469  
470      if (!sensorCacheMap[id].has_value())
471      {
472          sensorCacheMap[id] = SensorData{};
473      }
474      sensorCacheMap[id]->response = response;
475  
476      return response;
477  }
478  
479  #endif // FEATURE_SENSORS_CACHE
480  
481  } // namespace get
482  
483  namespace set
484  {
485  
486  /** @brief Make a DBus message for a Dbus call
487   *  @param[in] updateInterface - Interface name
488   *  @param[in] sensorPath - Path of the sensor
489   *  @param[in] command - command to be executed
490   *  @param[in] sensorInterface - DBus interface of sensor
491   *  @return a dbus message
492   */
493  IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
494                             const std::string& sensorPath,
495                             const std::string& command,
496                             const std::string& sensorInterface);
497  
498  /** @brief Update d-bus based on assertion type sensor data
499   *  @param[in] cmdData - input sensor data
500   *  @param[in] sensorInfo - sensor d-bus info
501   *  @return a IPMI error code
502   */
503  ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
504                       const Info& sensorInfo);
505  
506  /** @brief Update d-bus based on a reading assertion
507   *  @tparam T - type of d-bus property mapping this sensor
508   *  @param[in] cmdData - input sensor data
509   *  @param[in] sensorInfo - sensor d-bus info
510   *  @return a IPMI error code
511   */
512  template <typename T>
readingAssertion(const SetSensorReadingReq & cmdData,const Info & sensorInfo)513  ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
514                              const Info& sensorInfo)
515  {
516      auto msg =
517          makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
518                      "Set", sensorInfo.sensorInterface);
519  
520      const auto& interface = sensorInfo.propertyInterfaces.begin();
521      msg.append(interface->first);
522      for (const auto& property : interface->second)
523      {
524          msg.append(property.first);
525          std::variant<T> value = static_cast<T>(
526              (cmdData.assertOffset8_14 << 8) | cmdData.assertOffset0_7);
527          msg.append(value);
528      }
529      return updateToDbus(msg);
530  }
531  
532  /** @brief Update d-bus based on a discrete reading
533   *  @param[in] cmdData - input sensor data
534   *  @param[in] sensorInfo - sensor d-bus info
535   *  @return an IPMI error code
536   */
537  template <typename T>
readingData(const SetSensorReadingReq & cmdData,const Info & sensorInfo)538  ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
539                         const Info& sensorInfo)
540  {
541      T raw_value = (sensorInfo.coefficientM * cmdData.reading) +
542                    sensorInfo.scaledOffset;
543  
544      raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
545  
546      auto msg =
547          makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
548                      "Set", sensorInfo.sensorInterface);
549  
550      const auto& interface = sensorInfo.propertyInterfaces.begin();
551      msg.append(interface->first);
552  
553      for (const auto& property : interface->second)
554      {
555          msg.append(property.first);
556          std::variant<T> value = raw_value;
557          msg.append(value);
558      }
559      return updateToDbus(msg);
560  }
561  
562  /** @brief Update d-bus based on eventdata type sensor data
563   *  @param[in] cmdData - input sensor data
564   *  @param[in] sensorInfo - sensor d-bus info
565   *  @return a IPMI error code
566   */
567  ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
568                       uint8_t data);
569  
570  /** @brief Update d-bus based on eventdata1 type sensor data
571   *  @param[in] cmdData - input sensor data
572   *  @param[in] sensorInfo - sensor d-bus info
573   *  @return a IPMI error code
574   */
eventdata1(const SetSensorReadingReq & cmdData,const Info & sensorInfo)575  inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
576                               const Info& sensorInfo)
577  {
578      return eventdata(cmdData, sensorInfo, cmdData.eventData1);
579  }
580  
581  /** @brief Update d-bus based on eventdata2 type sensor data
582   *  @param[in] cmdData - input sensor data
583   *  @param[in] sensorInfo - sensor d-bus info
584   *  @return a IPMI error code
585   */
eventdata2(const SetSensorReadingReq & cmdData,const Info & sensorInfo)586  inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
587                               const Info& sensorInfo)
588  {
589      return eventdata(cmdData, sensorInfo, cmdData.eventData2);
590  }
591  
592  /** @brief Update d-bus based on eventdata3 type sensor data
593   *  @param[in] cmdData - input sensor data
594   *  @param[in] sensorInfo - sensor d-bus info
595   *  @return a IPMI error code
596   */
eventdata3(const SetSensorReadingReq & cmdData,const Info & sensorInfo)597  inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
598                               const Info& sensorInfo)
599  {
600      return eventdata(cmdData, sensorInfo, cmdData.eventData3);
601  }
602  
603  } // namespace set
604  
605  namespace notify
606  {
607  
608  /** @brief Make a DBus message for a Dbus call
609   *  @param[in] updateInterface - Interface name
610   *  @param[in] sensorPath - Path of the sensor
611   *  @param[in] command - command to be executed
612   *  @param[in] sensorInterface - DBus interface of sensor
613   *  @return a dbus message
614   */
615  IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
616                             const std::string& sensorPath,
617                             const std::string& command,
618                             const std::string& sensorInterface);
619  
620  /** @brief Update d-bus based on assertion type sensor data
621   *  @param[in] interfaceMap - sensor interface
622   *  @param[in] cmdData - input sensor data
623   *  @param[in] sensorInfo - sensor d-bus info
624   *  @return a IPMI error code
625   */
626  ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
627                       const Info& sensorInfo);
628  
629  } // namespace notify
630  
631  namespace inventory
632  {
633  
634  namespace get
635  {
636  
637  #ifndef FEATURE_SENSORS_CACHE
638  
639  /**
640   *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
641   *         reading command response.
642   *
643   *  @param[in] sensorInfo - Dbus info related to sensor.
644   *
645   *  @return Response for get sensor reading command.
646   */
647  GetSensorResponse assertion(const Info& sensorInfo);
648  
649  #else
650  
651  /**
652   *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
653   *         reading command response.
654   *
655   *  @param[in] id - The sensor id
656   *  @param[in] sensorInfo - Dbus info related to sensor.
657   *  @param[in] msg - Dbus message from match callback.
658   *
659   *  @return Response for get sensor reading command.
660   */
661  std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
662                                             const PropertyMap& properties);
663  
664  #endif
665  
666  } // namespace get
667  
668  } // namespace inventory
669  } // namespace sensor
670  } // namespace ipmi
671