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