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. If the object path is /system/chassis/motherboard/cpu0/core0
105  *         then the sensor name is cpu0_core0. The leaf and the parent is put
106  *         together to get 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  */
112 SensorName nameParentLeaf(const Info& sensorInfo);
113 
114 /**
115  *  @brief Helper function to map the dbus info to sensor's assertion status
116  *         for the get sensor reading command.
117  *
118  *  @param[in] sensorInfo - Dbus info related to sensor.
119  *  @param[in] path - Dbus object path.
120  *  @param[in] interface - Dbus interface.
121  *
122  *  @return Response for get sensor reading command.
123  */
124 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
125                                      const InstancePath& path,
126                                      const DbusInterface& interface);
127 
128 /**
129  *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
130  *         reading command response.
131  *
132  *  @param[in] sensorInfo - Dbus info related to sensor.
133  *
134  *  @return Response for get sensor reading command.
135  */
136 GetSensorResponse assertion(const Info& sensorInfo);
137 
138 /**
139  *  @brief Maps the Dbus info to the reading field in the Get sensor reading
140  *         command response.
141  *
142  *  @param[in] sensorInfo - Dbus info related to sensor.
143  *
144  *  @return Response for get sensor reading command.
145  */
146 GetSensorResponse eventdata2(const Info& sensorInfo);
147 
148 /**
149  *  @brief readingAssertion is a case where the entire assertion state field
150  *         serves as the sensor value.
151  *
152  *  @tparam T - type of the dbus property related to sensor.
153  *  @param[in] sensorInfo - Dbus info related to sensor.
154  *
155  *  @return Response for get sensor reading command.
156  */
157 template <typename T>
158 GetSensorResponse readingAssertion(const Info& sensorInfo)
159 {
160     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
161     GetSensorResponse response{};
162 
163     enableScanning(&response);
164 
165     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
166                                     sensorInfo.sensorPath);
167 
168     auto propValue = ipmi::getDbusProperty(
169         bus, service, sensorInfo.sensorPath,
170         sensorInfo.propertyInterfaces.begin()->first,
171         sensorInfo.propertyInterfaces.begin()->second.begin()->first);
172 
173     setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response);
174 
175     return response;
176 }
177 
178 /** @brief Map the Dbus info to the reading field in the Get sensor reading
179  *         command response
180  *
181  *  @tparam T - type of the dbus property related to sensor.
182  *  @param[in] sensorInfo - Dbus info related to sensor.
183  *
184  *  @return Response for get sensor reading command.
185  */
186 template <typename T>
187 GetSensorResponse readingData(const Info& sensorInfo)
188 {
189     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
190 
191     GetSensorResponse response{};
192 
193     enableScanning(&response);
194 
195     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
196                                     sensorInfo.sensorPath);
197 
198 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
199     // Check the OperationalStatus interface for functional property
200     if (sensorInfo.propertyInterfaces.begin()->first ==
201         "xyz.openbmc_project.Sensor.Value")
202     {
203         bool functional = true;
204         try
205         {
206             auto funcValue = ipmi::getDbusProperty(
207                 bus, service, sensorInfo.sensorPath,
208                 "xyz.openbmc_project.State.Decorator.OperationalStatus",
209                 "Functional");
210             functional = std::get<bool>(funcValue);
211         }
212         catch (...)
213         {
214             // No-op if Functional property could not be found since this
215             // check is only valid for Sensor.Value read for hwmonio
216         }
217         if (!functional)
218         {
219             throw SensorFunctionalError();
220         }
221     }
222 #endif
223 
224     auto propValue = ipmi::getDbusProperty(
225         bus, service, sensorInfo.sensorPath,
226         sensorInfo.propertyInterfaces.begin()->first,
227         sensorInfo.propertyInterfaces.begin()->second.begin()->first);
228 
229     double value = std::get<T>(propValue) *
230                    std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
231     int32_t rawData =
232         (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM;
233 
234     constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
235     constexpr uint8_t signedDataFormat = 0x80;
236     // if sensorUnits1 [7:6] = 10b, sensor is signed
237     if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
238     {
239         if (rawData > std::numeric_limits<int8_t>::max() ||
240             rawData < std::numeric_limits<int8_t>::lowest())
241         {
242             log<level::ERR>("Value out of range");
243             throw std::out_of_range("Value out of range");
244         }
245         setReading(static_cast<int8_t>(rawData), &response);
246     }
247     else
248     {
249         if (rawData > std::numeric_limits<uint8_t>::max() ||
250             rawData < std::numeric_limits<uint8_t>::lowest())
251         {
252             log<level::ERR>("Value out of range");
253             throw std::out_of_range("Value out of range");
254         }
255         setReading(static_cast<uint8_t>(rawData), &response);
256     }
257 
258     if (!std::isfinite(value))
259     {
260         response.readingOrStateUnavailable = 1;
261     }
262 
263     return response;
264 }
265 
266 } // namespace get
267 
268 namespace set
269 {
270 
271 /** @brief Make a DBus message for a Dbus call
272  *  @param[in] updateInterface - Interface name
273  *  @param[in] sensorPath - Path of the sensor
274  *  @param[in] command - command to be executed
275  *  @param[in] sensorInterface - DBus interface of sensor
276  *  @return a dbus message
277  */
278 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
279                            const std::string& sensorPath,
280                            const std::string& command,
281                            const std::string& sensorInterface);
282 
283 /** @brief Update d-bus based on assertion type sensor data
284  *  @param[in] cmdData - input sensor data
285  *  @param[in] sensorInfo - sensor d-bus info
286  *  @return a IPMI error code
287  */
288 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
289                      const Info& sensorInfo);
290 
291 /** @brief Update d-bus based on a reading assertion
292  *  @tparam T - type of d-bus property mapping this sensor
293  *  @param[in] cmdData - input sensor data
294  *  @param[in] sensorInfo - sensor d-bus info
295  *  @return a IPMI error code
296  */
297 template <typename T>
298 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
299                             const Info& sensorInfo)
300 {
301     auto msg =
302         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
303                     "Set", sensorInfo.sensorInterface);
304 
305     const auto& interface = sensorInfo.propertyInterfaces.begin();
306     msg.append(interface->first);
307     for (const auto& property : interface->second)
308     {
309         msg.append(property.first);
310         std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) |
311                                                cmdData.assertOffset0_7);
312         msg.append(value);
313     }
314     return updateToDbus(msg);
315 }
316 
317 /** @brief Update d-bus based on a discrete reading
318  *  @param[in] cmdData - input sensor data
319  *  @param[in] sensorInfo - sensor d-bus info
320  *  @return an IPMI error code
321  */
322 template <typename T>
323 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
324                        const Info& sensorInfo)
325 {
326     T raw_value =
327         (sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset;
328 
329     raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
330 
331     auto msg =
332         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
333                     "Set", sensorInfo.sensorInterface);
334 
335     const auto& interface = sensorInfo.propertyInterfaces.begin();
336     msg.append(interface->first);
337 
338     for (const auto& property : interface->second)
339     {
340         msg.append(property.first);
341         std::variant<T> value = raw_value;
342         msg.append(value);
343     }
344     return updateToDbus(msg);
345 }
346 
347 /** @brief Update d-bus based on eventdata type sensor data
348  *  @param[in] cmdData - input sensor data
349  *  @param[in] sensorInfo - sensor d-bus info
350  *  @return a IPMI error code
351  */
352 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
353                      uint8_t data);
354 
355 /** @brief Update d-bus based on eventdata1 type sensor data
356  *  @param[in] cmdData - input sensor data
357  *  @param[in] sensorInfo - sensor d-bus info
358  *  @return a IPMI error code
359  */
360 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
361                              const Info& sensorInfo)
362 {
363     return eventdata(cmdData, sensorInfo, cmdData.eventData1);
364 }
365 
366 /** @brief Update d-bus based on eventdata2 type sensor data
367  *  @param[in] cmdData - input sensor data
368  *  @param[in] sensorInfo - sensor d-bus info
369  *  @return a IPMI error code
370  */
371 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
372                              const Info& sensorInfo)
373 {
374     return eventdata(cmdData, sensorInfo, cmdData.eventData2);
375 }
376 
377 /** @brief Update d-bus based on eventdata3 type sensor data
378  *  @param[in] cmdData - input sensor data
379  *  @param[in] sensorInfo - sensor d-bus info
380  *  @return a IPMI error code
381  */
382 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
383                              const Info& sensorInfo)
384 {
385     return eventdata(cmdData, sensorInfo, cmdData.eventData3);
386 }
387 
388 } // namespace set
389 
390 namespace notify
391 {
392 
393 /** @brief Make a DBus message for a Dbus call
394  *  @param[in] updateInterface - Interface name
395  *  @param[in] sensorPath - Path of the sensor
396  *  @param[in] command - command to be executed
397  *  @param[in] sensorInterface - DBus interface of sensor
398  *  @return a dbus message
399  */
400 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
401                            const std::string& sensorPath,
402                            const std::string& command,
403                            const std::string& sensorInterface);
404 
405 /** @brief Update d-bus based on assertion type sensor data
406  *  @param[in] interfaceMap - sensor interface
407  *  @param[in] cmdData - input sensor data
408  *  @param[in] sensorInfo - sensor d-bus info
409  *  @return a IPMI error code
410  */
411 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
412                      const Info& sensorInfo);
413 
414 } // namespace notify
415 
416 namespace inventory
417 {
418 
419 namespace get
420 {
421 
422 /**
423  *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
424  *         reading command response.
425  *
426  *  @param[in] sensorInfo - Dbus info related to sensor.
427  *
428  *  @return Response for get sensor reading command.
429  */
430 GetSensorResponse assertion(const Info& sensorInfo);
431 
432 } // namespace get
433 
434 } // namespace inventory
435 } // namespace sensor
436 } // namespace ipmi
437