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::Cc 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 */ 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 */ 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 */ 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> 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 try 180 { 181 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 182 sensorInfo.sensorPath); 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)), 189 response); 190 } 191 catch (const std::exception& e) 192 { 193 lg2::error( 194 "Failed to call readingAssertion, path: {PATH}, interface: {INTERFACE}: {ERROR}", 195 "PATH", sensorInfo.sensorPath, "INTERFACE", 196 sensorInfo.sensorInterface, "ERROR", e); 197 } 198 199 return response; 200 } 201 202 /** @brief Map the Dbus info to the reading field in the Get sensor reading 203 * command response 204 * 205 * @tparam T - type of the dbus property related to sensor. 206 * @param[in] sensorInfo - Dbus info related to sensor. 207 * 208 * @return Response for get sensor reading command. 209 */ 210 template <typename T> 211 GetSensorResponse readingData(const Info& sensorInfo) 212 { 213 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 214 215 GetSensorResponse response{}; 216 217 enableScanning(response); 218 219 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 220 sensorInfo.sensorPath); 221 222 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 223 // Check the OperationalStatus interface for functional property 224 if (sensorInfo.propertyInterfaces.begin()->first == 225 "xyz.openbmc_project.Sensor.Value") 226 { 227 bool functional = true; 228 try 229 { 230 auto funcValue = ipmi::getDbusProperty( 231 bus, service, sensorInfo.sensorPath, 232 "xyz.openbmc_project.State.Decorator.OperationalStatus", 233 "Functional"); 234 functional = std::get<bool>(funcValue); 235 } 236 catch (...) 237 { 238 // No-op if Functional property could not be found since this 239 // check is only valid for Sensor.Value read for hwmonio 240 } 241 if (!functional) 242 { 243 throw SensorFunctionalError(); 244 } 245 } 246 #endif 247 248 double value{}; 249 try 250 { 251 auto propValue = ipmi::getDbusProperty( 252 bus, service, sensorInfo.sensorPath, 253 sensorInfo.propertyInterfaces.begin()->first, 254 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 255 256 value = std::get<T>(propValue) * 257 std::pow(10, sensorInfo.scale - sensorInfo.exponentR); 258 } 259 catch (const std::exception& e) 260 { 261 lg2::error( 262 "Failed to call readingData, path: {PATH}, interface: {INTERFACE}: {ERROR}", 263 "PATH", sensorInfo.sensorPath, "INTERFACE", 264 sensorInfo.sensorInterface, "ERROR", e); 265 return response; 266 } 267 268 int32_t rawData = 269 (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM; 270 271 constexpr uint8_t sensorUnitsSignedBits = 2 << 6; 272 constexpr uint8_t signedDataFormat = 0x80; 273 // if sensorUnits1 [7:6] = 10b, sensor is signed 274 int32_t minClamp; 275 int32_t maxClamp; 276 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 277 { 278 minClamp = std::numeric_limits<int8_t>::lowest(); 279 maxClamp = std::numeric_limits<int8_t>::max(); 280 } 281 else 282 { 283 minClamp = std::numeric_limits<uint8_t>::lowest(); 284 maxClamp = std::numeric_limits<uint8_t>::max(); 285 } 286 setReading(static_cast<uint8_t>(std::clamp(rawData, minClamp, maxClamp)), 287 response); 288 289 if (!std::isfinite(value)) 290 { 291 response.readingOrStateUnavailable = 1; 292 } 293 294 bool critAlarmHigh; 295 try 296 { 297 critAlarmHigh = std::get<bool>(ipmi::getDbusProperty( 298 bus, service, sensorInfo.sensorPath, 299 "xyz.openbmc_project.Sensor.Threshold.Critical", 300 "CriticalAlarmHigh")); 301 } 302 catch (const std::exception& e) 303 { 304 critAlarmHigh = false; 305 } 306 bool critAlarmLow; 307 try 308 { 309 critAlarmLow = std::get<bool>(ipmi::getDbusProperty( 310 bus, service, sensorInfo.sensorPath, 311 "xyz.openbmc_project.Sensor.Threshold.Critical", 312 "CriticalAlarmLow")); 313 } 314 catch (const std::exception& e) 315 { 316 critAlarmLow = false; 317 } 318 bool warningAlarmHigh; 319 try 320 { 321 warningAlarmHigh = std::get<bool>(ipmi::getDbusProperty( 322 bus, service, sensorInfo.sensorPath, 323 "xyz.openbmc_project.Sensor.Threshold.Warning", 324 "WarningAlarmHigh")); 325 } 326 catch (const std::exception& e) 327 { 328 warningAlarmHigh = false; 329 } 330 bool warningAlarmLow; 331 try 332 { 333 warningAlarmLow = std::get<bool>(ipmi::getDbusProperty( 334 bus, service, sensorInfo.sensorPath, 335 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningAlarmLow")); 336 } 337 catch (const std::exception& e) 338 { 339 warningAlarmLow = false; 340 } 341 response.thresholdLevelsStates = 342 (static_cast<uint8_t>(critAlarmHigh) << 3) | 343 (static_cast<uint8_t>(critAlarmLow) << 2) | 344 (static_cast<uint8_t>(warningAlarmHigh) << 1) | 345 (static_cast<uint8_t>(warningAlarmLow)); 346 347 return response; 348 } 349 350 #else 351 352 /** 353 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 354 * reading command response. 355 * 356 * @param[in] id - The sensor id 357 * @param[in] sensorInfo - Dbus info related to sensor. 358 * @param[in] msg - Dbus message from match callback. 359 * 360 * @return Response for get sensor reading command. 361 */ 362 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 363 const PropertyMap& properties); 364 365 /** 366 * @brief Maps the Dbus info to the reading field in the Get sensor reading 367 * command response. 368 * 369 * @param[in] id - The sensor id 370 * @param[in] sensorInfo - Dbus info related to sensor. 371 * @param[in] msg - Dbus message from match callback. 372 * 373 * @return Response for get sensor reading command. 374 */ 375 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo, 376 const PropertyMap& properties); 377 378 /** 379 * @brief readingAssertion is a case where the entire assertion state field 380 * serves as the sensor value. 381 * 382 * @tparam T - type of the dbus property related to sensor. 383 * @param[in] id - The sensor id 384 * @param[in] sensorInfo - Dbus info related to sensor. 385 * @param[in] msg - Dbus message from match callback. 386 * 387 * @return Response for get sensor reading command. 388 */ 389 template <typename T> 390 std::optional<GetSensorResponse> readingAssertion( 391 uint8_t id, const Info& sensorInfo, const PropertyMap& properties) 392 { 393 GetSensorResponse response{}; 394 enableScanning(response); 395 396 auto iter = properties.find( 397 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 398 if (iter == properties.end()) 399 { 400 return {}; 401 } 402 403 setAssertionBytes(static_cast<uint16_t>(std::get<T>(iter->second)), 404 response); 405 406 if (!sensorCacheMap[id].has_value()) 407 { 408 sensorCacheMap[id] = SensorData{}; 409 } 410 sensorCacheMap[id]->response = response; 411 return response; 412 } 413 414 /** @brief Get sensor reading from the dbus message from match 415 * 416 * @tparam T - type of the dbus property related to sensor. 417 * @param[in] id - The sensor id 418 * @param[in] sensorInfo - Dbus info related to sensor. 419 * @param[in] msg - Dbus message from match callback. 420 * 421 * @return Response for get sensor reading command. 422 */ 423 template <typename T> 424 std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo, 425 const PropertyMap& properties) 426 { 427 auto iter = properties.find("Functional"); 428 if (iter != properties.end()) 429 { 430 sensorCacheMap[id]->functional = std::get<bool>(iter->second); 431 } 432 iter = properties.find("Available"); 433 if (iter != properties.end()) 434 { 435 sensorCacheMap[id]->available = std::get<bool>(iter->second); 436 } 437 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 438 if (sensorCacheMap[id]) 439 { 440 if (!sensorCacheMap[id]->functional) 441 { 442 throw SensorFunctionalError(); 443 } 444 } 445 #endif 446 447 GetSensorResponse response{}; 448 449 enableScanning(response); 450 451 iter = properties.find( 452 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 453 if (iter == properties.end()) 454 { 455 return {}; 456 } 457 458 double value = std::get<T>(iter->second) * 459 std::pow(10, sensorInfo.scale - sensorInfo.exponentR); 460 int32_t rawData = 461 (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM; 462 463 constexpr uint8_t sensorUnitsSignedBits = 2 << 6; 464 constexpr uint8_t signedDataFormat = 0x80; 465 // if sensorUnits1 [7:6] = 10b, sensor is signed 466 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 467 { 468 if (rawData > std::numeric_limits<int8_t>::max() || 469 rawData < std::numeric_limits<int8_t>::lowest()) 470 { 471 lg2::error("Value out of range"); 472 throw std::out_of_range("Value out of range"); 473 } 474 setReading(static_cast<int8_t>(rawData), response); 475 } 476 else 477 { 478 if (rawData > std::numeric_limits<uint8_t>::max() || 479 rawData < std::numeric_limits<uint8_t>::lowest()) 480 { 481 lg2::error("Value out of range"); 482 throw std::out_of_range("Value out of range"); 483 } 484 setReading(static_cast<uint8_t>(rawData), response); 485 } 486 487 if (!std::isfinite(value)) 488 { 489 response.readingOrStateUnavailable = 1; 490 } 491 492 if (!sensorCacheMap[id].has_value()) 493 { 494 sensorCacheMap[id] = SensorData{}; 495 } 496 sensorCacheMap[id]->response = response; 497 498 return response; 499 } 500 501 #endif // FEATURE_SENSORS_CACHE 502 503 } // namespace get 504 505 namespace set 506 { 507 508 /** @brief Make a DBus message for a Dbus call 509 * @param[in] updateInterface - Interface name 510 * @param[in] sensorPath - Path of the sensor 511 * @param[in] command - command to be executed 512 * @param[in] sensorInterface - DBus interface of sensor 513 * @return a dbus message 514 */ 515 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 516 const std::string& sensorPath, 517 const std::string& command, 518 const std::string& sensorInterface); 519 520 /** @brief Update d-bus based on assertion type sensor data 521 * @param[in] cmdData - input sensor data 522 * @param[in] sensorInfo - sensor d-bus info 523 * @return a IPMI error code 524 */ 525 ipmi::Cc assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo); 526 527 /** @brief Update d-bus based on a reading assertion 528 * @tparam T - type of d-bus property mapping this sensor 529 * @param[in] cmdData - input sensor data 530 * @param[in] sensorInfo - sensor d-bus info 531 * @return a IPMI error code 532 */ 533 template <typename T> 534 ipmi::Cc readingAssertion(const SetSensorReadingReq& cmdData, 535 const Info& sensorInfo) 536 { 537 auto msg = 538 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 539 "Set", sensorInfo.sensorInterface); 540 541 const auto& interface = sensorInfo.propertyInterfaces.begin(); 542 msg.append(interface->first); 543 for (const auto& property : interface->second) 544 { 545 msg.append(property.first); 546 std::variant<T> value = static_cast<T>( 547 (cmdData.assertOffset8_14 << 8) | cmdData.assertOffset0_7); 548 msg.append(value); 549 } 550 return updateToDbus(msg); 551 } 552 553 /** @brief Update d-bus based on a discrete reading 554 * @param[in] cmdData - input sensor data 555 * @param[in] sensorInfo - sensor d-bus info 556 * @return an IPMI error code 557 */ 558 template <typename T> 559 ipmi::Cc readingData(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 560 { 561 T raw_value = (sensorInfo.coefficientM * cmdData.reading) + 562 sensorInfo.scaledOffset; 563 564 raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale); 565 566 auto msg = 567 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 568 "Set", sensorInfo.sensorInterface); 569 570 const auto& interface = sensorInfo.propertyInterfaces.begin(); 571 msg.append(interface->first); 572 573 for (const auto& property : interface->second) 574 { 575 msg.append(property.first); 576 std::variant<T> value = raw_value; 577 msg.append(value); 578 } 579 return updateToDbus(msg); 580 } 581 582 /** @brief Update d-bus based on eventdata 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 */ 587 ipmi::Cc eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 588 uint8_t data); 589 590 /** @brief Update d-bus based on eventdata1 type sensor data 591 * @param[in] cmdData - input sensor data 592 * @param[in] sensorInfo - sensor d-bus info 593 * @return a IPMI error code 594 */ 595 inline ipmi::Cc eventdata1(const SetSensorReadingReq& cmdData, 596 const Info& sensorInfo) 597 { 598 return eventdata(cmdData, sensorInfo, cmdData.eventData1); 599 } 600 601 /** @brief Update d-bus based on eventdata2 type sensor data 602 * @param[in] cmdData - input sensor data 603 * @param[in] sensorInfo - sensor d-bus info 604 * @return a IPMI error code 605 */ 606 inline ipmi::Cc eventdata2(const SetSensorReadingReq& cmdData, 607 const Info& sensorInfo) 608 { 609 return eventdata(cmdData, sensorInfo, cmdData.eventData2); 610 } 611 612 /** @brief Update d-bus based on eventdata3 type sensor data 613 * @param[in] cmdData - input sensor data 614 * @param[in] sensorInfo - sensor d-bus info 615 * @return a IPMI error code 616 */ 617 inline ipmi::Cc eventdata3(const SetSensorReadingReq& cmdData, 618 const Info& sensorInfo) 619 { 620 return eventdata(cmdData, sensorInfo, cmdData.eventData3); 621 } 622 623 } // namespace set 624 625 namespace notify 626 { 627 628 /** @brief Make a DBus message for a Dbus call 629 * @param[in] updateInterface - Interface name 630 * @param[in] sensorPath - Path of the sensor 631 * @param[in] command - command to be executed 632 * @param[in] sensorInterface - DBus interface of sensor 633 * @return a dbus message 634 */ 635 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 636 const std::string& sensorPath, 637 const std::string& command, 638 const std::string& sensorInterface); 639 640 /** @brief Update d-bus based on assertion type sensor data 641 * @param[in] interfaceMap - sensor interface 642 * @param[in] cmdData - input sensor data 643 * @param[in] sensorInfo - sensor d-bus info 644 * @return a IPMI error code 645 */ 646 ipmi::Cc assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo); 647 648 } // namespace notify 649 650 namespace inventory 651 { 652 653 namespace get 654 { 655 656 #ifndef FEATURE_SENSORS_CACHE 657 658 /** 659 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 660 * reading command response. 661 * 662 * @param[in] sensorInfo - Dbus info related to sensor. 663 * 664 * @return Response for get sensor reading command. 665 */ 666 GetSensorResponse assertion(const Info& sensorInfo); 667 668 #else 669 670 /** 671 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 672 * reading command response. 673 * 674 * @param[in] id - The sensor id 675 * @param[in] sensorInfo - Dbus info related to sensor. 676 * @param[in] msg - Dbus message from match callback. 677 * 678 * @return Response for get sensor reading command. 679 */ 680 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 681 const PropertyMap& properties); 682 683 #endif 684 685 } // namespace get 686 687 } // namespace inventory 688 } // namespace sensor 689 } // namespace ipmi 690