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/log.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 get the D-Bus service and service path 45 * @param[in] bus - The Dbus bus object 46 * @param[in] interface - interface to the service 47 * @param[in] path - interested path in the list of objects 48 * @return pair of service path and service 49 */ 50 ServicePath getServiceAndPath(sdbusplus::bus_t& bus, 51 const std::string& interface, 52 const std::string& path = std::string()); 53 54 /** @brief Make assertion set from input data 55 * @param[in] cmdData - Input sensor data 56 * @return pair of assertion and deassertion set 57 */ 58 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData); 59 60 /** @brief send the message to DBus 61 * @param[in] msg - message to send 62 * @return failure status in IPMI error code 63 */ 64 ipmi_ret_t updateToDbus(IpmiUpdateData& msg); 65 66 namespace get 67 { 68 69 /** @brief Populate sensor name from the D-Bus property associated with the 70 * sensor. In the example entry from the yaml, the name of the D-bus 71 * property "AttemptsLeft" is the sensor name. 72 * 73 * 0x07: 74 * sensorType: 195 75 * path: /xyz/openbmc_project/state/host0 76 * sensorReadingType: 0x6F 77 * serviceInterface: org.freedesktop.DBus.Properties 78 * readingType: readingAssertion 79 * sensorNamePattern: nameProperty 80 * interfaces: 81 * xyz.openbmc_project.Control.Boot.RebootAttempts: 82 * AttemptsLeft: 83 * Offsets: 84 * 0xFF: 85 * type: uint32_t 86 * 87 * 88 * @param[in] sensorInfo - Dbus info related to sensor. 89 * 90 * @return On success return the sensor name for the sensor. 91 */ 92 inline SensorName nameProperty(const Info& sensorInfo) 93 { 94 return sensorInfo.propertyInterfaces.begin()->second.begin()->first; 95 } 96 97 /** @brief Populate sensor name from the D-Bus object associated with the 98 * sensor. If the object path is /system/chassis/motherboard/dimm0 then 99 * the leaf dimm0 is considered as the sensor name. 100 * 101 * @param[in] sensorInfo - Dbus info related to sensor. 102 * 103 * @return On success return the sensor name for the sensor. 104 */ 105 inline SensorName nameLeaf(const Info& sensorInfo) 106 { 107 return sensorInfo.sensorPath.substr( 108 sensorInfo.sensorPath.find_last_of('/') + 1, 109 sensorInfo.sensorPath.length()); 110 } 111 112 /** @brief Populate sensor name from the D-Bus object associated with the 113 * sensor and the property. 114 * If the object path is /xyz/openbmc_project/inventory/Fan0 and 115 * the property is Present, the leaf Fan0 and the Property is 116 * joined to Fan0_Present as the sensor name. 117 * 118 * @param[in] sensorInfo - Dbus info related to sensor. 119 * 120 * @return On success return the sensor name for the sensor. 121 */ 122 inline SensorName nameLeafProperty(const Info& sensorInfo) 123 { 124 return nameLeaf(sensorInfo) + "_" + nameProperty(sensorInfo); 125 } 126 127 /** @brief Populate sensor name from the D-Bus object associated with the 128 * sensor. If the object path is /system/chassis/motherboard/cpu0/core0 129 * then the sensor name is cpu0_core0. The leaf and the parent is put 130 * together to get the sensor name. 131 * 132 * @param[in] sensorInfo - Dbus info related to sensor. 133 * 134 * @return On success return the sensor name for the sensor. 135 */ 136 SensorName nameParentLeaf(const Info& sensorInfo); 137 138 /** 139 * @brief Helper function to map the dbus info to sensor's assertion status 140 * for the get sensor reading command. 141 * 142 * @param[in] sensorInfo - Dbus info related to sensor. 143 * @param[in] path - Dbus object path. 144 * @param[in] interface - Dbus interface. 145 * 146 * @return Response for get sensor reading command. 147 */ 148 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo, 149 const InstancePath& path, 150 const DbusInterface& interface); 151 152 #ifndef FEATURE_SENSORS_CACHE 153 /** 154 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 155 * reading command response. 156 * 157 * @param[in] sensorInfo - Dbus info related to sensor. 158 * 159 * @return Response for get sensor reading command. 160 */ 161 GetSensorResponse assertion(const Info& sensorInfo); 162 163 /** 164 * @brief Maps the Dbus info to the reading field in the Get sensor reading 165 * command response. 166 * 167 * @param[in] sensorInfo - Dbus info related to sensor. 168 * 169 * @return Response for get sensor reading command. 170 */ 171 GetSensorResponse eventdata2(const Info& sensorInfo); 172 173 /** 174 * @brief readingAssertion is a case where the entire assertion state field 175 * serves as the sensor value. 176 * 177 * @tparam T - type of the dbus property related to sensor. 178 * @param[in] sensorInfo - Dbus info related to sensor. 179 * 180 * @return Response for get sensor reading command. 181 */ 182 template <typename T> 183 GetSensorResponse readingAssertion(const Info& sensorInfo) 184 { 185 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 186 GetSensorResponse response{}; 187 188 enableScanning(&response); 189 190 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 191 sensorInfo.sensorPath); 192 193 auto propValue = ipmi::getDbusProperty( 194 bus, service, sensorInfo.sensorPath, 195 sensorInfo.propertyInterfaces.begin()->first, 196 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 197 198 setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response); 199 200 return response; 201 } 202 203 /** @brief Map the Dbus info to the reading field in the Get sensor reading 204 * command response 205 * 206 * @tparam T - type of the dbus property related to sensor. 207 * @param[in] sensorInfo - Dbus info related to sensor. 208 * 209 * @return Response for get sensor reading command. 210 */ 211 template <typename T> 212 GetSensorResponse readingData(const Info& sensorInfo) 213 { 214 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 215 216 GetSensorResponse response{}; 217 218 enableScanning(&response); 219 220 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 221 sensorInfo.sensorPath); 222 223 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 224 // Check the OperationalStatus interface for functional property 225 if (sensorInfo.propertyInterfaces.begin()->first == 226 "xyz.openbmc_project.Sensor.Value") 227 { 228 bool functional = true; 229 try 230 { 231 auto funcValue = ipmi::getDbusProperty( 232 bus, service, sensorInfo.sensorPath, 233 "xyz.openbmc_project.State.Decorator.OperationalStatus", 234 "Functional"); 235 functional = std::get<bool>(funcValue); 236 } 237 catch (...) 238 { 239 // No-op if Functional property could not be found since this 240 // check is only valid for Sensor.Value read for hwmonio 241 } 242 if (!functional) 243 { 244 throw SensorFunctionalError(); 245 } 246 } 247 #endif 248 249 auto propValue = ipmi::getDbusProperty( 250 bus, service, sensorInfo.sensorPath, 251 sensorInfo.propertyInterfaces.begin()->first, 252 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 253 254 double value = std::get<T>(propValue) * 255 std::pow(10, sensorInfo.scale - sensorInfo.exponentR); 256 int32_t rawData = (value - sensorInfo.scaledOffset) / 257 sensorInfo.coefficientM; 258 259 constexpr uint8_t sensorUnitsSignedBits = 2 << 6; 260 constexpr uint8_t signedDataFormat = 0x80; 261 // if sensorUnits1 [7:6] = 10b, sensor is signed 262 int32_t minClamp; 263 int32_t maxClamp; 264 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 265 { 266 minClamp = std::numeric_limits<int8_t>::lowest(); 267 maxClamp = std::numeric_limits<int8_t>::max(); 268 } 269 else 270 { 271 minClamp = std::numeric_limits<uint8_t>::lowest(); 272 maxClamp = std::numeric_limits<uint8_t>::max(); 273 } 274 setReading(static_cast<uint8_t>(std::clamp(rawData, minClamp, maxClamp)), 275 &response); 276 277 if (!std::isfinite(value)) 278 { 279 response.readingOrStateUnavailable = 1; 280 } 281 282 bool critAlarmHigh; 283 try 284 { 285 critAlarmHigh = std::get<bool>(ipmi::getDbusProperty( 286 bus, service, sensorInfo.sensorPath, 287 "xyz.openbmc_project.Sensor.Threshold.Critical", 288 "CriticalAlarmHigh")); 289 } 290 catch (const std::exception& e) 291 { 292 critAlarmHigh = false; 293 } 294 bool critAlarmLow; 295 try 296 { 297 critAlarmLow = std::get<bool>(ipmi::getDbusProperty( 298 bus, service, sensorInfo.sensorPath, 299 "xyz.openbmc_project.Sensor.Threshold.Critical", 300 "CriticalAlarmLow")); 301 } 302 catch (const std::exception& e) 303 { 304 critAlarmLow = false; 305 } 306 bool warningAlarmHigh; 307 try 308 { 309 warningAlarmHigh = std::get<bool>(ipmi::getDbusProperty( 310 bus, service, sensorInfo.sensorPath, 311 "xyz.openbmc_project.Sensor.Threshold.Warning", 312 "WarningAlarmHigh")); 313 } 314 catch (const std::exception& e) 315 { 316 warningAlarmHigh = false; 317 } 318 bool warningAlarmLow; 319 try 320 { 321 warningAlarmLow = std::get<bool>(ipmi::getDbusProperty( 322 bus, service, sensorInfo.sensorPath, 323 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningAlarmLow")); 324 } 325 catch (const std::exception& e) 326 { 327 warningAlarmLow = false; 328 } 329 response.thresholdLevelsStates = 330 (static_cast<uint8_t>(critAlarmHigh) << 3) | 331 (static_cast<uint8_t>(critAlarmLow) << 2) | 332 (static_cast<uint8_t>(warningAlarmHigh) << 1) | 333 (static_cast<uint8_t>(warningAlarmLow)); 334 335 return response; 336 } 337 338 #else 339 340 /** 341 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 342 * reading command response. 343 * 344 * @param[in] id - The sensor id 345 * @param[in] sensorInfo - Dbus info related to sensor. 346 * @param[in] msg - Dbus message from match callback. 347 * 348 * @return Response for get sensor reading command. 349 */ 350 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 351 const PropertyMap& properties); 352 353 /** 354 * @brief Maps the Dbus info to the reading field in the Get sensor reading 355 * command response. 356 * 357 * @param[in] id - The sensor id 358 * @param[in] sensorInfo - Dbus info related to sensor. 359 * @param[in] msg - Dbus message from match callback. 360 * 361 * @return Response for get sensor reading command. 362 */ 363 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo, 364 const PropertyMap& properties); 365 366 /** 367 * @brief readingAssertion is a case where the entire assertion state field 368 * serves as the sensor value. 369 * 370 * @tparam T - type of the dbus property related to sensor. 371 * @param[in] id - The sensor id 372 * @param[in] sensorInfo - Dbus info related to sensor. 373 * @param[in] msg - Dbus message from match callback. 374 * 375 * @return Response for get sensor reading command. 376 */ 377 template <typename T> 378 std::optional<GetSensorResponse> readingAssertion(uint8_t id, 379 const Info& sensorInfo, 380 const PropertyMap& properties) 381 { 382 GetSensorResponse response{}; 383 enableScanning(&response); 384 385 auto iter = properties.find( 386 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 387 if (iter == properties.end()) 388 { 389 return {}; 390 } 391 392 setAssertionBytes(static_cast<uint16_t>(std::get<T>(iter->second)), 393 &response); 394 395 if (!sensorCacheMap[id].has_value()) 396 { 397 sensorCacheMap[id] = SensorData{}; 398 } 399 sensorCacheMap[id]->response = response; 400 return response; 401 } 402 403 /** @brief Get sensor reading from the dbus message from match 404 * 405 * @tparam T - type of the dbus property related to sensor. 406 * @param[in] id - The sensor id 407 * @param[in] sensorInfo - Dbus info related to sensor. 408 * @param[in] msg - Dbus message from match callback. 409 * 410 * @return Response for get sensor reading command. 411 */ 412 template <typename T> 413 std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo, 414 const PropertyMap& properties) 415 { 416 auto iter = properties.find("Functional"); 417 if (iter != properties.end()) 418 { 419 sensorCacheMap[id]->functional = std::get<bool>(iter->second); 420 } 421 iter = properties.find("Available"); 422 if (iter != properties.end()) 423 { 424 sensorCacheMap[id]->available = std::get<bool>(iter->second); 425 } 426 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 427 if (sensorCacheMap[id]) 428 { 429 if (!sensorCacheMap[id]->functional) 430 { 431 throw SensorFunctionalError(); 432 } 433 } 434 #endif 435 436 GetSensorResponse response{}; 437 438 enableScanning(&response); 439 440 iter = properties.find( 441 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 442 if (iter == properties.end()) 443 { 444 return {}; 445 } 446 447 double value = std::get<T>(iter->second) * 448 std::pow(10, sensorInfo.scale - sensorInfo.exponentR); 449 int32_t rawData = (value - sensorInfo.scaledOffset) / 450 sensorInfo.coefficientM; 451 452 constexpr uint8_t sensorUnitsSignedBits = 2 << 6; 453 constexpr uint8_t signedDataFormat = 0x80; 454 // if sensorUnits1 [7:6] = 10b, sensor is signed 455 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 456 { 457 if (rawData > std::numeric_limits<int8_t>::max() || 458 rawData < std::numeric_limits<int8_t>::lowest()) 459 { 460 log<level::ERR>("Value out of range"); 461 throw std::out_of_range("Value out of range"); 462 } 463 setReading(static_cast<int8_t>(rawData), &response); 464 } 465 else 466 { 467 if (rawData > std::numeric_limits<uint8_t>::max() || 468 rawData < std::numeric_limits<uint8_t>::lowest()) 469 { 470 log<level::ERR>("Value out of range"); 471 throw std::out_of_range("Value out of range"); 472 } 473 setReading(static_cast<uint8_t>(rawData), &response); 474 } 475 476 if (!std::isfinite(value)) 477 { 478 response.readingOrStateUnavailable = 1; 479 } 480 481 if (!sensorCacheMap[id].has_value()) 482 { 483 sensorCacheMap[id] = SensorData{}; 484 } 485 sensorCacheMap[id]->response = response; 486 487 return response; 488 } 489 490 #endif // FEATURE_SENSORS_CACHE 491 492 } // namespace get 493 494 namespace set 495 { 496 497 /** @brief Make a DBus message for a Dbus call 498 * @param[in] updateInterface - Interface name 499 * @param[in] sensorPath - Path of the sensor 500 * @param[in] command - command to be executed 501 * @param[in] sensorInterface - DBus interface of sensor 502 * @return a dbus message 503 */ 504 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 505 const std::string& sensorPath, 506 const std::string& command, 507 const std::string& sensorInterface); 508 509 /** @brief Update d-bus based on assertion type sensor data 510 * @param[in] cmdData - input sensor data 511 * @param[in] sensorInfo - sensor d-bus info 512 * @return a IPMI error code 513 */ 514 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 515 const Info& sensorInfo); 516 517 /** @brief Update d-bus based on a reading assertion 518 * @tparam T - type of d-bus property mapping this sensor 519 * @param[in] cmdData - input sensor data 520 * @param[in] sensorInfo - sensor d-bus info 521 * @return a IPMI error code 522 */ 523 template <typename T> 524 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData, 525 const Info& sensorInfo) 526 { 527 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties", 528 sensorInfo.sensorPath, "Set", 529 sensorInfo.sensorInterface); 530 531 const auto& interface = sensorInfo.propertyInterfaces.begin(); 532 msg.append(interface->first); 533 for (const auto& property : interface->second) 534 { 535 msg.append(property.first); 536 std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) | 537 cmdData.assertOffset0_7); 538 msg.append(value); 539 } 540 return updateToDbus(msg); 541 } 542 543 /** @brief Update d-bus based on a discrete reading 544 * @param[in] cmdData - input sensor data 545 * @param[in] sensorInfo - sensor d-bus info 546 * @return an IPMI error code 547 */ 548 template <typename T> 549 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData, 550 const Info& sensorInfo) 551 { 552 T raw_value = (sensorInfo.coefficientM * cmdData.reading) + 553 sensorInfo.scaledOffset; 554 555 raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale); 556 557 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties", 558 sensorInfo.sensorPath, "Set", 559 sensorInfo.sensorInterface); 560 561 const auto& interface = sensorInfo.propertyInterfaces.begin(); 562 msg.append(interface->first); 563 564 for (const auto& property : interface->second) 565 { 566 msg.append(property.first); 567 std::variant<T> value = raw_value; 568 msg.append(value); 569 } 570 return updateToDbus(msg); 571 } 572 573 /** @brief Update d-bus based on eventdata type sensor data 574 * @param[in] cmdData - input sensor data 575 * @param[in] sensorInfo - sensor d-bus info 576 * @return a IPMI error code 577 */ 578 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 579 uint8_t data); 580 581 /** @brief Update d-bus based on eventdata1 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 */ 586 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData, 587 const Info& sensorInfo) 588 { 589 return eventdata(cmdData, sensorInfo, cmdData.eventData1); 590 } 591 592 /** @brief Update d-bus based on eventdata2 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 */ 597 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData, 598 const Info& sensorInfo) 599 { 600 return eventdata(cmdData, sensorInfo, cmdData.eventData2); 601 } 602 603 /** @brief Update d-bus based on eventdata3 type sensor data 604 * @param[in] cmdData - input sensor data 605 * @param[in] sensorInfo - sensor d-bus info 606 * @return a IPMI error code 607 */ 608 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData, 609 const Info& sensorInfo) 610 { 611 return eventdata(cmdData, sensorInfo, cmdData.eventData3); 612 } 613 614 } // namespace set 615 616 namespace notify 617 { 618 619 /** @brief Make a DBus message for a Dbus call 620 * @param[in] updateInterface - Interface name 621 * @param[in] sensorPath - Path of the sensor 622 * @param[in] command - command to be executed 623 * @param[in] sensorInterface - DBus interface of sensor 624 * @return a dbus message 625 */ 626 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 627 const std::string& sensorPath, 628 const std::string& command, 629 const std::string& sensorInterface); 630 631 /** @brief Update d-bus based on assertion type sensor data 632 * @param[in] interfaceMap - sensor interface 633 * @param[in] cmdData - input sensor data 634 * @param[in] sensorInfo - sensor d-bus info 635 * @return a IPMI error code 636 */ 637 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 638 const Info& sensorInfo); 639 640 } // namespace notify 641 642 namespace inventory 643 { 644 645 namespace get 646 { 647 648 #ifndef FEATURE_SENSORS_CACHE 649 650 /** 651 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 652 * reading command response. 653 * 654 * @param[in] sensorInfo - Dbus info related to sensor. 655 * 656 * @return Response for get sensor reading command. 657 */ 658 GetSensorResponse assertion(const Info& sensorInfo); 659 660 #else 661 662 /** 663 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 664 * reading command response. 665 * 666 * @param[in] id - The sensor id 667 * @param[in] sensorInfo - Dbus info related to sensor. 668 * @param[in] msg - Dbus message from match callback. 669 * 670 * @return Response for get sensor reading command. 671 */ 672 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 673 const PropertyMap& properties); 674 675 #endif 676 677 } // namespace get 678 679 } // namespace inventory 680 } // namespace sensor 681 } // namespace ipmi 682