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