xref: /openbmc/phosphor-host-ipmid/sensordatahandler.hpp (revision a2ad2da85fe34a7e1c95d52ec55032bbc71fa0f0)
1 #pragma once
2 
3 #include "config.h"
4 
5 #include "sensorhandler.hpp"
6 
7 #include <cmath>
8 #include <ipmid/api.hpp>
9 #include <ipmid/types.hpp>
10 #include <ipmid/utils.hpp>
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/log.hpp>
13 #include <sdbusplus/message/types.hpp>
14 
15 namespace ipmi
16 {
17 namespace sensor
18 {
19 
20 using Assertion = uint16_t;
21 using Deassertion = uint16_t;
22 using AssertionSet = std::pair<Assertion, Deassertion>;
23 
24 using Service = std::string;
25 using Path = std::string;
26 using Interface = std::string;
27 
28 using ServicePath = std::pair<Path, Service>;
29 
30 using Interfaces = std::vector<Interface>;
31 
32 using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>;
33 using namespace phosphor::logging;
34 
35 /** @brief get the D-Bus service and service path
36  *  @param[in] bus - The Dbus bus object
37  *  @param[in] interface - interface to the service
38  *  @param[in] path - interested path in the list of objects
39  *  @return pair of service path and service
40  */
41 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus,
42                               const std::string& interface,
43                               const std::string& path = std::string());
44 
45 /** @brief Make assertion set from input data
46  *  @param[in] cmdData - Input sensor data
47  *  @return pair of assertion and deassertion set
48  */
49 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData);
50 
51 /** @brief send the message to DBus
52  *  @param[in] msg - message to send
53  *  @return failure status in IPMI error code
54  */
55 ipmi_ret_t updateToDbus(IpmiUpdateData& msg);
56 
57 namespace get
58 {
59 
60 /** @brief Populate sensor name from the D-Bus property associated with the
61  *         sensor. In the example entry from the yaml, the name of the D-bus
62  *         property "AttemptsLeft" is the sensor name.
63  *
64  *         0x07:
65  *            sensorType: 195
66  *            path: /xyz/openbmc_project/state/host0
67  *            sensorReadingType: 0x6F
68  *            serviceInterface: org.freedesktop.DBus.Properties
69  *            readingType: readingAssertion
70  *            sensorNamePattern: nameProperty
71  *            interfaces:
72  *              xyz.openbmc_project.Control.Boot.RebootAttempts:
73  *                AttemptsLeft:
74  *                    Offsets:
75  *                        0xFF:
76  *                          type: uint32_t
77  *
78  *
79  *  @param[in] sensorInfo - Dbus info related to sensor.
80  *
81  *  @return On success return the sensor name for the sensor.
82  */
83 inline SensorName nameProperty(const Info& sensorInfo)
84 {
85     return sensorInfo.propertyInterfaces.begin()->second.begin()->first;
86 }
87 
88 /** @brief Populate sensor name from the D-Bus object associated with the
89  *         sensor. If the object path is /system/chassis/motherboard/dimm0 then
90  *         the leaf dimm0 is considered as the sensor name.
91  *
92  *  @param[in] sensorInfo - Dbus info related to sensor.
93  *
94  *  @return On success return the sensor name for the sensor.
95  */
96 inline SensorName nameLeaf(const Info& sensorInfo)
97 {
98     return sensorInfo.sensorPath.substr(
99         sensorInfo.sensorPath.find_last_of('/') + 1,
100         sensorInfo.sensorPath.length());
101 }
102 
103 /** @brief Populate sensor name from the D-Bus object associated with the
104  *         sensor and the property.
105  *         If the object path is /xyz/openbmc_project/inventory/Fan0 and
106  *         the property is Present, the leaf Fan0 and the Property is
107  *         joined to Fan0_Present as the sensor name.
108  *
109  *  @param[in] sensorInfo - Dbus info related to sensor.
110  *
111  *  @return On success return the sensor name for the sensor.
112  */
113 inline SensorName nameLeafProperty(const Info& sensorInfo)
114 {
115     return nameLeaf(sensorInfo) + "_" + nameProperty(sensorInfo);
116 }
117 
118 /** @brief Populate sensor name from the D-Bus object associated with the
119  *         sensor. If the object path is /system/chassis/motherboard/cpu0/core0
120  *         then the sensor name is cpu0_core0. The leaf and the parent is put
121  *         together to get the sensor name.
122  *
123  *  @param[in] sensorInfo - Dbus info related to sensor.
124  *
125  *  @return On success return the sensor name for the sensor.
126  */
127 SensorName nameParentLeaf(const Info& sensorInfo);
128 
129 /**
130  *  @brief Helper function to map the dbus info to sensor's assertion status
131  *         for the get sensor reading command.
132  *
133  *  @param[in] sensorInfo - Dbus info related to sensor.
134  *  @param[in] path - Dbus object path.
135  *  @param[in] interface - Dbus interface.
136  *
137  *  @return Response for get sensor reading command.
138  */
139 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
140                                      const InstancePath& path,
141                                      const DbusInterface& interface);
142 
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>
173 GetSensorResponse readingAssertion(const Info& sensorInfo)
174 {
175     sdbusplus::bus::bus 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>
202 GetSensorResponse readingData(const Info& sensorInfo)
203 {
204     sdbusplus::bus::bus 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     if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
253     {
254         if (rawData > std::numeric_limits<int8_t>::max() ||
255             rawData < std::numeric_limits<int8_t>::lowest())
256         {
257             log<level::ERR>("Value out of range");
258             throw std::out_of_range("Value out of range");
259         }
260         setReading(static_cast<int8_t>(rawData), &response);
261     }
262     else
263     {
264         if (rawData > std::numeric_limits<uint8_t>::max() ||
265             rawData < std::numeric_limits<uint8_t>::lowest())
266         {
267             log<level::ERR>("Value out of range");
268             throw std::out_of_range("Value out of range");
269         }
270         setReading(static_cast<uint8_t>(rawData), &response);
271     }
272 
273     if (!std::isfinite(value))
274     {
275         response.readingOrStateUnavailable = 1;
276     }
277 
278     return response;
279 }
280 
281 } // namespace get
282 
283 namespace set
284 {
285 
286 /** @brief Make a DBus message for a Dbus call
287  *  @param[in] updateInterface - Interface name
288  *  @param[in] sensorPath - Path of the sensor
289  *  @param[in] command - command to be executed
290  *  @param[in] sensorInterface - DBus interface of sensor
291  *  @return a dbus message
292  */
293 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
294                            const std::string& sensorPath,
295                            const std::string& command,
296                            const std::string& sensorInterface);
297 
298 /** @brief Update d-bus based on assertion type sensor data
299  *  @param[in] cmdData - input sensor data
300  *  @param[in] sensorInfo - sensor d-bus info
301  *  @return a IPMI error code
302  */
303 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
304                      const Info& sensorInfo);
305 
306 /** @brief Update d-bus based on a reading assertion
307  *  @tparam T - type of d-bus property mapping this sensor
308  *  @param[in] cmdData - input sensor data
309  *  @param[in] sensorInfo - sensor d-bus info
310  *  @return a IPMI error code
311  */
312 template <typename T>
313 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
314                             const Info& sensorInfo)
315 {
316     auto msg =
317         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
318                     "Set", sensorInfo.sensorInterface);
319 
320     const auto& interface = sensorInfo.propertyInterfaces.begin();
321     msg.append(interface->first);
322     for (const auto& property : interface->second)
323     {
324         msg.append(property.first);
325         std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) |
326                                                cmdData.assertOffset0_7);
327         msg.append(value);
328     }
329     return updateToDbus(msg);
330 }
331 
332 /** @brief Update d-bus based on a discrete reading
333  *  @param[in] cmdData - input sensor data
334  *  @param[in] sensorInfo - sensor d-bus info
335  *  @return an IPMI error code
336  */
337 template <typename T>
338 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
339                        const Info& sensorInfo)
340 {
341     T raw_value =
342         (sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset;
343 
344     raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
345 
346     auto msg =
347         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
348                     "Set", sensorInfo.sensorInterface);
349 
350     const auto& interface = sensorInfo.propertyInterfaces.begin();
351     msg.append(interface->first);
352 
353     for (const auto& property : interface->second)
354     {
355         msg.append(property.first);
356         std::variant<T> value = raw_value;
357         msg.append(value);
358     }
359     return updateToDbus(msg);
360 }
361 
362 /** @brief Update d-bus based on eventdata type sensor data
363  *  @param[in] cmdData - input sensor data
364  *  @param[in] sensorInfo - sensor d-bus info
365  *  @return a IPMI error code
366  */
367 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
368                      uint8_t data);
369 
370 /** @brief Update d-bus based on eventdata1 type sensor data
371  *  @param[in] cmdData - input sensor data
372  *  @param[in] sensorInfo - sensor d-bus info
373  *  @return a IPMI error code
374  */
375 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
376                              const Info& sensorInfo)
377 {
378     return eventdata(cmdData, sensorInfo, cmdData.eventData1);
379 }
380 
381 /** @brief Update d-bus based on eventdata2 type sensor data
382  *  @param[in] cmdData - input sensor data
383  *  @param[in] sensorInfo - sensor d-bus info
384  *  @return a IPMI error code
385  */
386 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
387                              const Info& sensorInfo)
388 {
389     return eventdata(cmdData, sensorInfo, cmdData.eventData2);
390 }
391 
392 /** @brief Update d-bus based on eventdata3 type sensor data
393  *  @param[in] cmdData - input sensor data
394  *  @param[in] sensorInfo - sensor d-bus info
395  *  @return a IPMI error code
396  */
397 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
398                              const Info& sensorInfo)
399 {
400     return eventdata(cmdData, sensorInfo, cmdData.eventData3);
401 }
402 
403 } // namespace set
404 
405 namespace notify
406 {
407 
408 /** @brief Make a DBus message for a Dbus call
409  *  @param[in] updateInterface - Interface name
410  *  @param[in] sensorPath - Path of the sensor
411  *  @param[in] command - command to be executed
412  *  @param[in] sensorInterface - DBus interface of sensor
413  *  @return a dbus message
414  */
415 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
416                            const std::string& sensorPath,
417                            const std::string& command,
418                            const std::string& sensorInterface);
419 
420 /** @brief Update d-bus based on assertion type sensor data
421  *  @param[in] interfaceMap - sensor interface
422  *  @param[in] cmdData - input sensor data
423  *  @param[in] sensorInfo - sensor d-bus info
424  *  @return a IPMI error code
425  */
426 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
427                      const Info& sensorInfo);
428 
429 } // namespace notify
430 
431 namespace inventory
432 {
433 
434 namespace get
435 {
436 
437 /**
438  *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
439  *         reading command response.
440  *
441  *  @param[in] sensorInfo - Dbus info related to sensor.
442  *
443  *  @return Response for get sensor reading command.
444  */
445 GetSensorResponse assertion(const Info& sensorInfo);
446 
447 } // namespace get
448 
449 } // namespace inventory
450 } // namespace sensor
451 } // namespace ipmi
452