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 */ 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 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> 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> 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> 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> 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> 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 */ 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 */ 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 */ 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