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 = (value - sensorInfo.scaledOffset) /
247 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(uint8_t id,
369 const Info& sensorInfo,
370 const PropertyMap& properties)
371 {
372 GetSensorResponse response{};
373 enableScanning(&response);
374
375 auto iter = properties.find(
376 sensorInfo.propertyInterfaces.begin()->second.begin()->first);
377 if (iter == properties.end())
378 {
379 return {};
380 }
381
382 setAssertionBytes(static_cast<uint16_t>(std::get<T>(iter->second)),
383 &response);
384
385 if (!sensorCacheMap[id].has_value())
386 {
387 sensorCacheMap[id] = SensorData{};
388 }
389 sensorCacheMap[id]->response = response;
390 return response;
391 }
392
393 /** @brief Get sensor reading from the dbus message from match
394 *
395 * @tparam T - type of the dbus property related to sensor.
396 * @param[in] id - The sensor id
397 * @param[in] sensorInfo - Dbus info related to sensor.
398 * @param[in] msg - Dbus message from match callback.
399 *
400 * @return Response for get sensor reading command.
401 */
402 template <typename T>
readingData(uint8_t id,const Info & sensorInfo,const PropertyMap & properties)403 std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo,
404 const PropertyMap& properties)
405 {
406 auto iter = properties.find("Functional");
407 if (iter != properties.end())
408 {
409 sensorCacheMap[id]->functional = std::get<bool>(iter->second);
410 }
411 iter = properties.find("Available");
412 if (iter != properties.end())
413 {
414 sensorCacheMap[id]->available = std::get<bool>(iter->second);
415 }
416 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
417 if (sensorCacheMap[id])
418 {
419 if (!sensorCacheMap[id]->functional)
420 {
421 throw SensorFunctionalError();
422 }
423 }
424 #endif
425
426 GetSensorResponse response{};
427
428 enableScanning(&response);
429
430 iter = properties.find(
431 sensorInfo.propertyInterfaces.begin()->second.begin()->first);
432 if (iter == properties.end())
433 {
434 return {};
435 }
436
437 double value = std::get<T>(iter->second) *
438 std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
439 int32_t rawData = (value - sensorInfo.scaledOffset) /
440 sensorInfo.coefficientM;
441
442 constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
443 constexpr uint8_t signedDataFormat = 0x80;
444 // if sensorUnits1 [7:6] = 10b, sensor is signed
445 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
446 {
447 if (rawData > std::numeric_limits<int8_t>::max() ||
448 rawData < std::numeric_limits<int8_t>::lowest())
449 {
450 lg2::error("Value out of range");
451 throw std::out_of_range("Value out of range");
452 }
453 setReading(static_cast<int8_t>(rawData), &response);
454 }
455 else
456 {
457 if (rawData > std::numeric_limits<uint8_t>::max() ||
458 rawData < std::numeric_limits<uint8_t>::lowest())
459 {
460 lg2::error("Value out of range");
461 throw std::out_of_range("Value out of range");
462 }
463 setReading(static_cast<uint8_t>(rawData), &response);
464 }
465
466 if (!std::isfinite(value))
467 {
468 response.readingOrStateUnavailable = 1;
469 }
470
471 if (!sensorCacheMap[id].has_value())
472 {
473 sensorCacheMap[id] = SensorData{};
474 }
475 sensorCacheMap[id]->response = response;
476
477 return response;
478 }
479
480 #endif // FEATURE_SENSORS_CACHE
481
482 } // namespace get
483
484 namespace set
485 {
486
487 /** @brief Make a DBus message for a Dbus call
488 * @param[in] updateInterface - Interface name
489 * @param[in] sensorPath - Path of the sensor
490 * @param[in] command - command to be executed
491 * @param[in] sensorInterface - DBus interface of sensor
492 * @return a dbus message
493 */
494 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
495 const std::string& sensorPath,
496 const std::string& command,
497 const std::string& sensorInterface);
498
499 /** @brief Update d-bus based on assertion type sensor data
500 * @param[in] cmdData - input sensor data
501 * @param[in] sensorInfo - sensor d-bus info
502 * @return a IPMI error code
503 */
504 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
505 const Info& sensorInfo);
506
507 /** @brief Update d-bus based on a reading assertion
508 * @tparam T - type of d-bus property mapping this sensor
509 * @param[in] cmdData - input sensor data
510 * @param[in] sensorInfo - sensor d-bus info
511 * @return a IPMI error code
512 */
513 template <typename T>
readingAssertion(const SetSensorReadingReq & cmdData,const Info & sensorInfo)514 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
515 const Info& sensorInfo)
516 {
517 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties",
518 sensorInfo.sensorPath, "Set",
519 sensorInfo.sensorInterface);
520
521 const auto& interface = sensorInfo.propertyInterfaces.begin();
522 msg.append(interface->first);
523 for (const auto& property : interface->second)
524 {
525 msg.append(property.first);
526 std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) |
527 cmdData.assertOffset0_7);
528 msg.append(value);
529 }
530 return updateToDbus(msg);
531 }
532
533 /** @brief Update d-bus based on a discrete reading
534 * @param[in] cmdData - input sensor data
535 * @param[in] sensorInfo - sensor d-bus info
536 * @return an IPMI error code
537 */
538 template <typename T>
readingData(const SetSensorReadingReq & cmdData,const Info & sensorInfo)539 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
540 const Info& sensorInfo)
541 {
542 T raw_value = (sensorInfo.coefficientM * cmdData.reading) +
543 sensorInfo.scaledOffset;
544
545 raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
546
547 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties",
548 sensorInfo.sensorPath, "Set",
549 sensorInfo.sensorInterface);
550
551 const auto& interface = sensorInfo.propertyInterfaces.begin();
552 msg.append(interface->first);
553
554 for (const auto& property : interface->second)
555 {
556 msg.append(property.first);
557 std::variant<T> value = raw_value;
558 msg.append(value);
559 }
560 return updateToDbus(msg);
561 }
562
563 /** @brief Update d-bus based on eventdata type sensor data
564 * @param[in] cmdData - input sensor data
565 * @param[in] sensorInfo - sensor d-bus info
566 * @return a IPMI error code
567 */
568 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
569 uint8_t data);
570
571 /** @brief Update d-bus based on eventdata1 type sensor data
572 * @param[in] cmdData - input sensor data
573 * @param[in] sensorInfo - sensor d-bus info
574 * @return a IPMI error code
575 */
eventdata1(const SetSensorReadingReq & cmdData,const Info & sensorInfo)576 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
577 const Info& sensorInfo)
578 {
579 return eventdata(cmdData, sensorInfo, cmdData.eventData1);
580 }
581
582 /** @brief Update d-bus based on eventdata2 type sensor data
583 * @param[in] cmdData - input sensor data
584 * @param[in] sensorInfo - sensor d-bus info
585 * @return a IPMI error code
586 */
eventdata2(const SetSensorReadingReq & cmdData,const Info & sensorInfo)587 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
588 const Info& sensorInfo)
589 {
590 return eventdata(cmdData, sensorInfo, cmdData.eventData2);
591 }
592
593 /** @brief Update d-bus based on eventdata3 type sensor data
594 * @param[in] cmdData - input sensor data
595 * @param[in] sensorInfo - sensor d-bus info
596 * @return a IPMI error code
597 */
eventdata3(const SetSensorReadingReq & cmdData,const Info & sensorInfo)598 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
599 const Info& sensorInfo)
600 {
601 return eventdata(cmdData, sensorInfo, cmdData.eventData3);
602 }
603
604 } // namespace set
605
606 namespace notify
607 {
608
609 /** @brief Make a DBus message for a Dbus call
610 * @param[in] updateInterface - Interface name
611 * @param[in] sensorPath - Path of the sensor
612 * @param[in] command - command to be executed
613 * @param[in] sensorInterface - DBus interface of sensor
614 * @return a dbus message
615 */
616 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
617 const std::string& sensorPath,
618 const std::string& command,
619 const std::string& sensorInterface);
620
621 /** @brief Update d-bus based on assertion type sensor data
622 * @param[in] interfaceMap - sensor interface
623 * @param[in] cmdData - input sensor data
624 * @param[in] sensorInfo - sensor d-bus info
625 * @return a IPMI error code
626 */
627 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
628 const Info& sensorInfo);
629
630 } // namespace notify
631
632 namespace inventory
633 {
634
635 namespace get
636 {
637
638 #ifndef FEATURE_SENSORS_CACHE
639
640 /**
641 * @brief Map the Dbus info to sensor's assertion status in the Get sensor
642 * reading command response.
643 *
644 * @param[in] sensorInfo - Dbus info related to sensor.
645 *
646 * @return Response for get sensor reading command.
647 */
648 GetSensorResponse assertion(const Info& sensorInfo);
649
650 #else
651
652 /**
653 * @brief Map the Dbus info to sensor's assertion status in the Get sensor
654 * reading command response.
655 *
656 * @param[in] id - The sensor id
657 * @param[in] sensorInfo - Dbus info related to sensor.
658 * @param[in] msg - Dbus message from match callback.
659 *
660 * @return Response for get sensor reading command.
661 */
662 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
663 const PropertyMap& properties);
664
665 #endif
666
667 } // namespace get
668
669 } // namespace inventory
670 } // namespace sensor
671 } // namespace ipmi
672