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 <sdbusplus/message/types.hpp>
12 
13 namespace ipmi
14 {
15 namespace sensor
16 {
17 
18 using Assertion = uint16_t;
19 using Deassertion = uint16_t;
20 using AssertionSet = std::pair<Assertion, Deassertion>;
21 
22 using Service = std::string;
23 using Path = std::string;
24 using Interface = std::string;
25 
26 using ServicePath = std::pair<Path, Service>;
27 
28 using Interfaces = std::vector<Interface>;
29 
30 using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>;
31 
32 /** @brief get the D-Bus service and service path
33  *  @param[in] bus - The Dbus bus object
34  *  @param[in] interface - interface to the service
35  *  @param[in] path - interested path in the list of objects
36  *  @return pair of service path and service
37  */
38 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus,
39                               const std::string& interface,
40                               const std::string& path = std::string());
41 
42 /** @brief Make assertion set from input data
43  *  @param[in] cmdData - Input sensor data
44  *  @return pair of assertion and deassertion set
45  */
46 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData);
47 
48 /** @brief send the message to DBus
49  *  @param[in] msg - message to send
50  *  @return failure status in IPMI error code
51  */
52 ipmi_ret_t updateToDbus(IpmiUpdateData& msg);
53 
54 namespace get
55 {
56 
57 /** @brief Populate sensor name from the D-Bus property associated with the
58  *         sensor. In the example entry from the yaml, the name of the D-bus
59  *         property "AttemptsLeft" is the sensor name.
60  *
61  *         0x07:
62  *            sensorType: 195
63  *            path: /xyz/openbmc_project/state/host0
64  *            sensorReadingType: 0x6F
65  *            serviceInterface: org.freedesktop.DBus.Properties
66  *            readingType: readingAssertion
67  *            sensorNamePattern: nameProperty
68  *            interfaces:
69  *              xyz.openbmc_project.Control.Boot.RebootAttempts:
70  *                AttemptsLeft:
71  *                    Offsets:
72  *                        0xFF:
73  *                          type: uint32_t
74  *
75  *
76  *  @param[in] sensorInfo - Dbus info related to sensor.
77  *
78  *  @return On success return the sensor name for the sensor.
79  */
80 inline SensorName nameProperty(const Info& sensorInfo)
81 {
82     return sensorInfo.propertyInterfaces.begin()->second.begin()->first;
83 }
84 
85 /** @brief Populate sensor name from the D-Bus object associated with the
86  *         sensor. If the object path is /system/chassis/motherboard/dimm0 then
87  *         the leaf dimm0 is considered as the sensor name.
88  *
89  *  @param[in] sensorInfo - Dbus info related to sensor.
90  *
91  *  @return On success return the sensor name for the sensor.
92  */
93 inline SensorName nameLeaf(const Info& sensorInfo)
94 {
95     return sensorInfo.sensorPath.substr(
96         sensorInfo.sensorPath.find_last_of('/') + 1,
97         sensorInfo.sensorPath.length());
98 }
99 
100 /** @brief Populate sensor name from the D-Bus object associated with the
101  *         sensor. If the object path is /system/chassis/motherboard/cpu0/core0
102  *         then the sensor name is cpu0_core0. The leaf and the parent is put
103  *         together to get the sensor name.
104  *
105  *  @param[in] sensorInfo - Dbus info related to sensor.
106  *
107  *  @return On success return the sensor name for the sensor.
108  */
109 SensorName nameParentLeaf(const Info& sensorInfo);
110 
111 /**
112  *  @brief Helper function to map the dbus info to sensor's assertion status
113  *         for the get sensor reading command.
114  *
115  *  @param[in] sensorInfo - Dbus info related to sensor.
116  *  @param[in] path - Dbus object path.
117  *  @param[in] interface - Dbus interface.
118  *
119  *  @return Response for get sensor reading command.
120  */
121 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
122                                      const InstancePath& path,
123                                      const DbusInterface& interface);
124 
125 /**
126  *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
127  *         reading command response.
128  *
129  *  @param[in] sensorInfo - Dbus info related to sensor.
130  *
131  *  @return Response for get sensor reading command.
132  */
133 GetSensorResponse assertion(const Info& sensorInfo);
134 
135 /**
136  *  @brief Maps the Dbus info to the reading field in the Get sensor reading
137  *         command response.
138  *
139  *  @param[in] sensorInfo - Dbus info related to sensor.
140  *
141  *  @return Response for get sensor reading command.
142  */
143 GetSensorResponse eventdata2(const Info& sensorInfo);
144 
145 /**
146  *  @brief readingAssertion is a case where the entire assertion state field
147  *         serves as the sensor value.
148  *
149  *  @tparam T - type of the dbus property related to sensor.
150  *  @param[in] sensorInfo - Dbus info related to sensor.
151  *
152  *  @return Response for get sensor reading command.
153  */
154 template <typename T>
155 GetSensorResponse readingAssertion(const Info& sensorInfo)
156 {
157     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
158     GetSensorResponse response{};
159     auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
160 
161     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
162                                     sensorInfo.sensorPath);
163 
164     auto propValue = ipmi::getDbusProperty(
165         bus, service, sensorInfo.sensorPath,
166         sensorInfo.propertyInterfaces.begin()->first,
167         sensorInfo.propertyInterfaces.begin()->second.begin()->first);
168 
169     setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)),
170                       responseData);
171 
172     return response;
173 }
174 
175 /** @brief Map the Dbus info to the reading field in the Get sensor reading
176  *         command response
177  *
178  *  @tparam T - type of the dbus property related to sensor.
179  *  @param[in] sensorInfo - Dbus info related to sensor.
180  *
181  *  @return Response for get sensor reading command.
182  */
183 template <typename T>
184 GetSensorResponse readingData(const Info& sensorInfo)
185 {
186     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
187     GetSensorResponse response{};
188     auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
189 
190     enableScanning(responseData);
191 
192     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
193                                     sensorInfo.sensorPath);
194 
195 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
196     // Check the OperationalStatus interface for functional property
197     if (sensorInfo.propertyInterfaces.begin()->first ==
198         "xyz.openbmc_project.Sensor.Value")
199     {
200         bool functional = true;
201         try
202         {
203             auto funcValue = ipmi::getDbusProperty(
204                 bus, service, sensorInfo.sensorPath,
205                 "xyz.openbmc_project.State.Decorator.OperationalStatus",
206                 "Functional");
207             functional = std::get<bool>(funcValue);
208         }
209         catch (...)
210         {
211             // No-op if Functional property could not be found since this
212             // check is only valid for Sensor.Value read for hwmonio
213         }
214         if (!functional)
215         {
216             throw SensorFunctionalError();
217         }
218     }
219 #endif
220 
221     auto propValue = ipmi::getDbusProperty(
222         bus, service, sensorInfo.sensorPath,
223         sensorInfo.propertyInterfaces.begin()->first,
224         sensorInfo.propertyInterfaces.begin()->second.begin()->first);
225 
226     double value = std::get<T>(propValue) *
227                    std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
228 
229     auto rawData = static_cast<uint8_t>((value - sensorInfo.scaledOffset) /
230                                         sensorInfo.coefficientM);
231 
232     setReading(rawData, responseData);
233 
234     return response;
235 }
236 
237 } // namespace get
238 
239 namespace set
240 {
241 
242 /** @brief Make a DBus message for a Dbus call
243  *  @param[in] updateInterface - Interface name
244  *  @param[in] sensorPath - Path of the sensor
245  *  @param[in] command - command to be executed
246  *  @param[in] sensorInterface - DBus interface of sensor
247  *  @return a dbus message
248  */
249 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
250                            const std::string& sensorPath,
251                            const std::string& command,
252                            const std::string& sensorInterface);
253 
254 /** @brief Update d-bus based on assertion type sensor data
255  *  @param[in] cmdData - input sensor data
256  *  @param[in] sensorInfo - sensor d-bus info
257  *  @return a IPMI error code
258  */
259 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
260                      const Info& sensorInfo);
261 
262 /** @brief Update d-bus based on a reading assertion
263  *  @tparam T - type of d-bus property mapping this sensor
264  *  @param[in] cmdData - input sensor data
265  *  @param[in] sensorInfo - sensor d-bus info
266  *  @return a IPMI error code
267  */
268 template <typename T>
269 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
270                             const Info& sensorInfo)
271 {
272     auto msg =
273         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
274                     "Set", sensorInfo.sensorInterface);
275 
276     const auto& interface = sensorInfo.propertyInterfaces.begin();
277     msg.append(interface->first);
278     for (const auto& property : interface->second)
279     {
280         msg.append(property.first);
281         std::variant<T> value =
282             (cmdData.assertOffset8_14 << 8) | cmdData.assertOffset0_7;
283         msg.append(value);
284     }
285     return updateToDbus(msg);
286 }
287 
288 /** @brief Update d-bus based on a discrete reading
289  *  @param[in] cmdData - input sensor data
290  *  @param[in] sensorInfo - sensor d-bus info
291  *  @return an IPMI error code
292  */
293 template <typename T>
294 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
295                        const Info& sensorInfo)
296 {
297     T raw_value =
298         (sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset;
299 
300     raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
301 
302     auto msg =
303         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
304                     "Set", sensorInfo.sensorInterface);
305 
306     const auto& interface = sensorInfo.propertyInterfaces.begin();
307     msg.append(interface->first);
308 
309     for (const auto& property : interface->second)
310     {
311         msg.append(property.first);
312         std::variant<T> value = raw_value;
313         msg.append(value);
314     }
315     return updateToDbus(msg);
316 }
317 
318 /** @brief Update d-bus based on eventdata type sensor data
319  *  @param[in] cmdData - input sensor data
320  *  @param[in] sensorInfo - sensor d-bus info
321  *  @return a IPMI error code
322  */
323 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
324                      uint8_t data);
325 
326 /** @brief Update d-bus based on eventdata1 type sensor data
327  *  @param[in] cmdData - input sensor data
328  *  @param[in] sensorInfo - sensor d-bus info
329  *  @return a IPMI error code
330  */
331 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
332                              const Info& sensorInfo)
333 {
334     return eventdata(cmdData, sensorInfo, cmdData.eventData1);
335 }
336 
337 /** @brief Update d-bus based on eventdata2 type sensor data
338  *  @param[in] cmdData - input sensor data
339  *  @param[in] sensorInfo - sensor d-bus info
340  *  @return a IPMI error code
341  */
342 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
343                              const Info& sensorInfo)
344 {
345     return eventdata(cmdData, sensorInfo, cmdData.eventData2);
346 }
347 
348 /** @brief Update d-bus based on eventdata3 type sensor data
349  *  @param[in] cmdData - input sensor data
350  *  @param[in] sensorInfo - sensor d-bus info
351  *  @return a IPMI error code
352  */
353 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
354                              const Info& sensorInfo)
355 {
356     return eventdata(cmdData, sensorInfo, cmdData.eventData3);
357 }
358 
359 } // namespace set
360 
361 namespace notify
362 {
363 
364 /** @brief Make a DBus message for a Dbus call
365  *  @param[in] updateInterface - Interface name
366  *  @param[in] sensorPath - Path of the sensor
367  *  @param[in] command - command to be executed
368  *  @param[in] sensorInterface - DBus interface of sensor
369  *  @return a dbus message
370  */
371 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
372                            const std::string& sensorPath,
373                            const std::string& command,
374                            const std::string& sensorInterface);
375 
376 /** @brief Update d-bus based on assertion type sensor data
377  *  @param[in] interfaceMap - sensor interface
378  *  @param[in] cmdData - input sensor data
379  *  @param[in] sensorInfo - sensor d-bus info
380  *  @return a IPMI error code
381  */
382 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
383                      const Info& sensorInfo);
384 
385 } // namespace notify
386 
387 namespace inventory
388 {
389 
390 namespace get
391 {
392 
393 /**
394  *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
395  *         reading command response.
396  *
397  *  @param[in] sensorInfo - Dbus info related to sensor.
398  *
399  *  @return Response for get sensor reading command.
400  */
401 GetSensorResponse assertion(const Info& sensorInfo);
402 
403 } // namespace get
404 
405 } // namespace inventory
406 } // namespace sensor
407 } // namespace ipmi
408