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 
160     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
161                                     sensorInfo.sensorPath);
162 
163     auto propValue = ipmi::getDbusProperty(
164         bus, service, sensorInfo.sensorPath,
165         sensorInfo.propertyInterfaces.begin()->first,
166         sensorInfo.propertyInterfaces.begin()->second.begin()->first);
167 
168     setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response);
169 
170     return response;
171 }
172 
173 /** @brief Map the Dbus info to the reading field in the Get sensor reading
174  *         command response
175  *
176  *  @tparam T - type of the dbus property related to sensor.
177  *  @param[in] sensorInfo - Dbus info related to sensor.
178  *
179  *  @return Response for get sensor reading command.
180  */
181 template <typename T>
182 GetSensorResponse readingData(const Info& sensorInfo)
183 {
184     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
185 
186     GetSensorResponse response{};
187 
188     enableScanning(&response);
189 
190     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
191                                     sensorInfo.sensorPath);
192 
193 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
194     // Check the OperationalStatus interface for functional property
195     if (sensorInfo.propertyInterfaces.begin()->first ==
196         "xyz.openbmc_project.Sensor.Value")
197     {
198         bool functional = true;
199         try
200         {
201             auto funcValue = ipmi::getDbusProperty(
202                 bus, service, sensorInfo.sensorPath,
203                 "xyz.openbmc_project.State.Decorator.OperationalStatus",
204                 "Functional");
205             functional = std::get<bool>(funcValue);
206         }
207         catch (...)
208         {
209             // No-op if Functional property could not be found since this
210             // check is only valid for Sensor.Value read for hwmonio
211         }
212         if (!functional)
213         {
214             throw SensorFunctionalError();
215         }
216     }
217 #endif
218 
219     auto propValue = ipmi::getDbusProperty(
220         bus, service, sensorInfo.sensorPath,
221         sensorInfo.propertyInterfaces.begin()->first,
222         sensorInfo.propertyInterfaces.begin()->second.begin()->first);
223 
224     double value = std::get<T>(propValue) *
225                    std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
226 
227     auto rawData = static_cast<uint8_t>((value - sensorInfo.scaledOffset) /
228                                         sensorInfo.coefficientM);
229     setReading(rawData, &response);
230 
231     return response;
232 }
233 
234 } // namespace get
235 
236 namespace set
237 {
238 
239 /** @brief Make a DBus message for a Dbus call
240  *  @param[in] updateInterface - Interface name
241  *  @param[in] sensorPath - Path of the sensor
242  *  @param[in] command - command to be executed
243  *  @param[in] sensorInterface - DBus interface of sensor
244  *  @return a dbus message
245  */
246 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
247                            const std::string& sensorPath,
248                            const std::string& command,
249                            const std::string& sensorInterface);
250 
251 /** @brief Update d-bus based on assertion type sensor data
252  *  @param[in] cmdData - input sensor data
253  *  @param[in] sensorInfo - sensor d-bus info
254  *  @return a IPMI error code
255  */
256 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
257                      const Info& sensorInfo);
258 
259 /** @brief Update d-bus based on a reading assertion
260  *  @tparam T - type of d-bus property mapping this sensor
261  *  @param[in] cmdData - input sensor data
262  *  @param[in] sensorInfo - sensor d-bus info
263  *  @return a IPMI error code
264  */
265 template <typename T>
266 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
267                             const Info& sensorInfo)
268 {
269     auto msg =
270         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
271                     "Set", sensorInfo.sensorInterface);
272 
273     const auto& interface = sensorInfo.propertyInterfaces.begin();
274     msg.append(interface->first);
275     for (const auto& property : interface->second)
276     {
277         msg.append(property.first);
278         std::variant<T> value =
279             (cmdData.assertOffset8_14 << 8) | cmdData.assertOffset0_7;
280         msg.append(value);
281     }
282     return updateToDbus(msg);
283 }
284 
285 /** @brief Update d-bus based on a discrete reading
286  *  @param[in] cmdData - input sensor data
287  *  @param[in] sensorInfo - sensor d-bus info
288  *  @return an IPMI error code
289  */
290 template <typename T>
291 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
292                        const Info& sensorInfo)
293 {
294     T raw_value =
295         (sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset;
296 
297     raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
298 
299     auto msg =
300         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
301                     "Set", sensorInfo.sensorInterface);
302 
303     const auto& interface = sensorInfo.propertyInterfaces.begin();
304     msg.append(interface->first);
305 
306     for (const auto& property : interface->second)
307     {
308         msg.append(property.first);
309         std::variant<T> value = raw_value;
310         msg.append(value);
311     }
312     return updateToDbus(msg);
313 }
314 
315 /** @brief Update d-bus based on eventdata type sensor data
316  *  @param[in] cmdData - input sensor data
317  *  @param[in] sensorInfo - sensor d-bus info
318  *  @return a IPMI error code
319  */
320 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
321                      uint8_t data);
322 
323 /** @brief Update d-bus based on eventdata1 type sensor data
324  *  @param[in] cmdData - input sensor data
325  *  @param[in] sensorInfo - sensor d-bus info
326  *  @return a IPMI error code
327  */
328 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
329                              const Info& sensorInfo)
330 {
331     return eventdata(cmdData, sensorInfo, cmdData.eventData1);
332 }
333 
334 /** @brief Update d-bus based on eventdata2 type sensor data
335  *  @param[in] cmdData - input sensor data
336  *  @param[in] sensorInfo - sensor d-bus info
337  *  @return a IPMI error code
338  */
339 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
340                              const Info& sensorInfo)
341 {
342     return eventdata(cmdData, sensorInfo, cmdData.eventData2);
343 }
344 
345 /** @brief Update d-bus based on eventdata3 type sensor data
346  *  @param[in] cmdData - input sensor data
347  *  @param[in] sensorInfo - sensor d-bus info
348  *  @return a IPMI error code
349  */
350 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
351                              const Info& sensorInfo)
352 {
353     return eventdata(cmdData, sensorInfo, cmdData.eventData3);
354 }
355 
356 } // namespace set
357 
358 namespace notify
359 {
360 
361 /** @brief Make a DBus message for a Dbus call
362  *  @param[in] updateInterface - Interface name
363  *  @param[in] sensorPath - Path of the sensor
364  *  @param[in] command - command to be executed
365  *  @param[in] sensorInterface - DBus interface of sensor
366  *  @return a dbus message
367  */
368 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
369                            const std::string& sensorPath,
370                            const std::string& command,
371                            const std::string& sensorInterface);
372 
373 /** @brief Update d-bus based on assertion type sensor data
374  *  @param[in] interfaceMap - sensor interface
375  *  @param[in] cmdData - input sensor data
376  *  @param[in] sensorInfo - sensor d-bus info
377  *  @return a IPMI error code
378  */
379 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
380                      const Info& sensorInfo);
381 
382 } // namespace notify
383 
384 namespace inventory
385 {
386 
387 namespace get
388 {
389 
390 /**
391  *  @brief Map the Dbus info to sensor's assertion status in the Get sensor
392  *         reading command response.
393  *
394  *  @param[in] sensorInfo - Dbus info related to sensor.
395  *
396  *  @return Response for get sensor reading command.
397  */
398 GetSensorResponse assertion(const Info& sensorInfo);
399 
400 } // namespace get
401 
402 } // namespace inventory
403 } // namespace sensor
404 } // namespace ipmi
405