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 */ nameProperty(const Info & sensorInfo)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 */ nameLeaf(const Info & sensorInfo)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 */ nameLeafProperty(const Info & sensorInfo)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> readingAssertion(const Info & sensorInfo)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> readingData(const Info & sensorInfo)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 = 247 (value - sensorInfo.scaledOffset) / 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> readingAssertion(uint8_t id,const Info & sensorInfo,const PropertyMap & properties)368 std::optional<GetSensorResponse> readingAssertion( 369 uint8_t id, const Info& sensorInfo, const PropertyMap& properties) 370 { 371 GetSensorResponse response{}; 372 enableScanning(&response); 373 374 auto iter = properties.find( 375 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 376 if (iter == properties.end()) 377 { 378 return {}; 379 } 380 381 setAssertionBytes(static_cast<uint16_t>(std::get<T>(iter->second)), 382 &response); 383 384 if (!sensorCacheMap[id].has_value()) 385 { 386 sensorCacheMap[id] = SensorData{}; 387 } 388 sensorCacheMap[id]->response = response; 389 return response; 390 } 391 392 /** @brief Get sensor reading from the dbus message from match 393 * 394 * @tparam T - type of the dbus property related to sensor. 395 * @param[in] id - The sensor id 396 * @param[in] sensorInfo - Dbus info related to sensor. 397 * @param[in] msg - Dbus message from match callback. 398 * 399 * @return Response for get sensor reading command. 400 */ 401 template <typename T> readingData(uint8_t id,const Info & sensorInfo,const PropertyMap & properties)402 std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo, 403 const PropertyMap& properties) 404 { 405 auto iter = properties.find("Functional"); 406 if (iter != properties.end()) 407 { 408 sensorCacheMap[id]->functional = std::get<bool>(iter->second); 409 } 410 iter = properties.find("Available"); 411 if (iter != properties.end()) 412 { 413 sensorCacheMap[id]->available = std::get<bool>(iter->second); 414 } 415 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 416 if (sensorCacheMap[id]) 417 { 418 if (!sensorCacheMap[id]->functional) 419 { 420 throw SensorFunctionalError(); 421 } 422 } 423 #endif 424 425 GetSensorResponse response{}; 426 427 enableScanning(&response); 428 429 iter = properties.find( 430 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 431 if (iter == properties.end()) 432 { 433 return {}; 434 } 435 436 double value = std::get<T>(iter->second) * 437 std::pow(10, sensorInfo.scale - sensorInfo.exponentR); 438 int32_t rawData = 439 (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM; 440 441 constexpr uint8_t sensorUnitsSignedBits = 2 << 6; 442 constexpr uint8_t signedDataFormat = 0x80; 443 // if sensorUnits1 [7:6] = 10b, sensor is signed 444 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 445 { 446 if (rawData > std::numeric_limits<int8_t>::max() || 447 rawData < std::numeric_limits<int8_t>::lowest()) 448 { 449 lg2::error("Value out of range"); 450 throw std::out_of_range("Value out of range"); 451 } 452 setReading(static_cast<int8_t>(rawData), &response); 453 } 454 else 455 { 456 if (rawData > std::numeric_limits<uint8_t>::max() || 457 rawData < std::numeric_limits<uint8_t>::lowest()) 458 { 459 lg2::error("Value out of range"); 460 throw std::out_of_range("Value out of range"); 461 } 462 setReading(static_cast<uint8_t>(rawData), &response); 463 } 464 465 if (!std::isfinite(value)) 466 { 467 response.readingOrStateUnavailable = 1; 468 } 469 470 if (!sensorCacheMap[id].has_value()) 471 { 472 sensorCacheMap[id] = SensorData{}; 473 } 474 sensorCacheMap[id]->response = response; 475 476 return response; 477 } 478 479 #endif // FEATURE_SENSORS_CACHE 480 481 } // namespace get 482 483 namespace set 484 { 485 486 /** @brief Make a DBus message for a Dbus call 487 * @param[in] updateInterface - Interface name 488 * @param[in] sensorPath - Path of the sensor 489 * @param[in] command - command to be executed 490 * @param[in] sensorInterface - DBus interface of sensor 491 * @return a dbus message 492 */ 493 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 494 const std::string& sensorPath, 495 const std::string& command, 496 const std::string& sensorInterface); 497 498 /** @brief Update d-bus based on assertion type sensor data 499 * @param[in] cmdData - input sensor data 500 * @param[in] sensorInfo - sensor d-bus info 501 * @return a IPMI error code 502 */ 503 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 504 const Info& sensorInfo); 505 506 /** @brief Update d-bus based on a reading assertion 507 * @tparam T - type of d-bus property mapping this sensor 508 * @param[in] cmdData - input sensor data 509 * @param[in] sensorInfo - sensor d-bus info 510 * @return a IPMI error code 511 */ 512 template <typename T> readingAssertion(const SetSensorReadingReq & cmdData,const Info & sensorInfo)513 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData, 514 const Info& sensorInfo) 515 { 516 auto msg = 517 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 518 "Set", sensorInfo.sensorInterface); 519 520 const auto& interface = sensorInfo.propertyInterfaces.begin(); 521 msg.append(interface->first); 522 for (const auto& property : interface->second) 523 { 524 msg.append(property.first); 525 std::variant<T> value = static_cast<T>( 526 (cmdData.assertOffset8_14 << 8) | cmdData.assertOffset0_7); 527 msg.append(value); 528 } 529 return updateToDbus(msg); 530 } 531 532 /** @brief Update d-bus based on a discrete reading 533 * @param[in] cmdData - input sensor data 534 * @param[in] sensorInfo - sensor d-bus info 535 * @return an IPMI error code 536 */ 537 template <typename T> readingData(const SetSensorReadingReq & cmdData,const Info & sensorInfo)538 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData, 539 const Info& sensorInfo) 540 { 541 T raw_value = (sensorInfo.coefficientM * cmdData.reading) + 542 sensorInfo.scaledOffset; 543 544 raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale); 545 546 auto msg = 547 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 548 "Set", sensorInfo.sensorInterface); 549 550 const auto& interface = sensorInfo.propertyInterfaces.begin(); 551 msg.append(interface->first); 552 553 for (const auto& property : interface->second) 554 { 555 msg.append(property.first); 556 std::variant<T> value = raw_value; 557 msg.append(value); 558 } 559 return updateToDbus(msg); 560 } 561 562 /** @brief Update d-bus based on eventdata type sensor data 563 * @param[in] cmdData - input sensor data 564 * @param[in] sensorInfo - sensor d-bus info 565 * @return a IPMI error code 566 */ 567 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 568 uint8_t data); 569 570 /** @brief Update d-bus based on eventdata1 type sensor data 571 * @param[in] cmdData - input sensor data 572 * @param[in] sensorInfo - sensor d-bus info 573 * @return a IPMI error code 574 */ eventdata1(const SetSensorReadingReq & cmdData,const Info & sensorInfo)575 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData, 576 const Info& sensorInfo) 577 { 578 return eventdata(cmdData, sensorInfo, cmdData.eventData1); 579 } 580 581 /** @brief Update d-bus based on eventdata2 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 */ eventdata2(const SetSensorReadingReq & cmdData,const Info & sensorInfo)586 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData, 587 const Info& sensorInfo) 588 { 589 return eventdata(cmdData, sensorInfo, cmdData.eventData2); 590 } 591 592 /** @brief Update d-bus based on eventdata3 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 */ eventdata3(const SetSensorReadingReq & cmdData,const Info & sensorInfo)597 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData, 598 const Info& sensorInfo) 599 { 600 return eventdata(cmdData, sensorInfo, cmdData.eventData3); 601 } 602 603 } // namespace set 604 605 namespace notify 606 { 607 608 /** @brief Make a DBus message for a Dbus call 609 * @param[in] updateInterface - Interface name 610 * @param[in] sensorPath - Path of the sensor 611 * @param[in] command - command to be executed 612 * @param[in] sensorInterface - DBus interface of sensor 613 * @return a dbus message 614 */ 615 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 616 const std::string& sensorPath, 617 const std::string& command, 618 const std::string& sensorInterface); 619 620 /** @brief Update d-bus based on assertion type sensor data 621 * @param[in] interfaceMap - sensor interface 622 * @param[in] cmdData - input sensor data 623 * @param[in] sensorInfo - sensor d-bus info 624 * @return a IPMI error code 625 */ 626 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 627 const Info& sensorInfo); 628 629 } // namespace notify 630 631 namespace inventory 632 { 633 634 namespace get 635 { 636 637 #ifndef FEATURE_SENSORS_CACHE 638 639 /** 640 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 641 * reading command response. 642 * 643 * @param[in] sensorInfo - Dbus info related to sensor. 644 * 645 * @return Response for get sensor reading command. 646 */ 647 GetSensorResponse assertion(const Info& sensorInfo); 648 649 #else 650 651 /** 652 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 653 * reading command response. 654 * 655 * @param[in] id - The sensor id 656 * @param[in] sensorInfo - Dbus info related to sensor. 657 * @param[in] msg - Dbus message from match callback. 658 * 659 * @return Response for get sensor reading command. 660 */ 661 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 662 const PropertyMap& properties); 663 664 #endif 665 666 } // namespace get 667 668 } // namespace inventory 669 } // namespace sensor 670 } // namespace ipmi 671