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