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 namespace ipmi 16 { 17 namespace sensor 18 { 19 20 using Assertion = uint16_t; 21 using Deassertion = uint16_t; 22 using AssertionSet = std::pair<Assertion, Deassertion>; 23 24 using Service = std::string; 25 using Path = std::string; 26 using Interface = std::string; 27 28 using ServicePath = std::pair<Path, Service>; 29 30 using Interfaces = std::vector<Interface>; 31 32 using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>; 33 using namespace phosphor::logging; 34 35 /** @brief get the D-Bus service and service path 36 * @param[in] bus - The Dbus bus object 37 * @param[in] interface - interface to the service 38 * @param[in] path - interested path in the list of objects 39 * @return pair of service path and service 40 */ 41 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus, 42 const std::string& interface, 43 const std::string& path = std::string()); 44 45 /** @brief Make assertion set from input data 46 * @param[in] cmdData - Input sensor data 47 * @return pair of assertion and deassertion set 48 */ 49 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData); 50 51 /** @brief send the message to DBus 52 * @param[in] msg - message to send 53 * @return failure status in IPMI error code 54 */ 55 ipmi_ret_t updateToDbus(IpmiUpdateData& msg); 56 57 namespace get 58 { 59 60 /** @brief Populate sensor name from the D-Bus property associated with the 61 * sensor. In the example entry from the yaml, the name of the D-bus 62 * property "AttemptsLeft" is the sensor name. 63 * 64 * 0x07: 65 * sensorType: 195 66 * path: /xyz/openbmc_project/state/host0 67 * sensorReadingType: 0x6F 68 * serviceInterface: org.freedesktop.DBus.Properties 69 * readingType: readingAssertion 70 * sensorNamePattern: nameProperty 71 * interfaces: 72 * xyz.openbmc_project.Control.Boot.RebootAttempts: 73 * AttemptsLeft: 74 * Offsets: 75 * 0xFF: 76 * type: uint32_t 77 * 78 * 79 * @param[in] sensorInfo - Dbus info related to sensor. 80 * 81 * @return On success return the sensor name for the sensor. 82 */ 83 inline SensorName nameProperty(const Info& sensorInfo) 84 { 85 return sensorInfo.propertyInterfaces.begin()->second.begin()->first; 86 } 87 88 /** @brief Populate sensor name from the D-Bus object associated with the 89 * sensor. If the object path is /system/chassis/motherboard/dimm0 then 90 * the leaf dimm0 is considered as the sensor name. 91 * 92 * @param[in] sensorInfo - Dbus info related to sensor. 93 * 94 * @return On success return the sensor name for the sensor. 95 */ 96 inline SensorName nameLeaf(const Info& sensorInfo) 97 { 98 return sensorInfo.sensorPath.substr( 99 sensorInfo.sensorPath.find_last_of('/') + 1, 100 sensorInfo.sensorPath.length()); 101 } 102 103 /** @brief Populate sensor name from the D-Bus object associated with the 104 * sensor and the property. 105 * If the object path is /xyz/openbmc_project/inventory/Fan0 and 106 * the property is Present, the leaf Fan0 and the Property is 107 * joined to Fan0_Present as the sensor name. 108 * 109 * @param[in] sensorInfo - Dbus info related to sensor. 110 * 111 * @return On success return the sensor name for the sensor. 112 */ 113 inline SensorName nameLeafProperty(const Info& sensorInfo) 114 { 115 return nameLeaf(sensorInfo) + "_" + nameProperty(sensorInfo); 116 } 117 118 /** @brief Populate sensor name from the D-Bus object associated with the 119 * sensor. If the object path is /system/chassis/motherboard/cpu0/core0 120 * then the sensor name is cpu0_core0. The leaf and the parent is put 121 * together to get the sensor name. 122 * 123 * @param[in] sensorInfo - Dbus info related to sensor. 124 * 125 * @return On success return the sensor name for the sensor. 126 */ 127 SensorName nameParentLeaf(const Info& sensorInfo); 128 129 /** 130 * @brief Helper function to map the dbus info to sensor's assertion status 131 * for the get sensor reading command. 132 * 133 * @param[in] sensorInfo - Dbus info related to sensor. 134 * @param[in] path - Dbus object path. 135 * @param[in] interface - Dbus interface. 136 * 137 * @return Response for get sensor reading command. 138 */ 139 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo, 140 const InstancePath& path, 141 const DbusInterface& interface); 142 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::bus 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::bus 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 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 253 { 254 if (rawData > std::numeric_limits<int8_t>::max() || 255 rawData < std::numeric_limits<int8_t>::lowest()) 256 { 257 log<level::ERR>("Value out of range"); 258 throw std::out_of_range("Value out of range"); 259 } 260 setReading(static_cast<int8_t>(rawData), &response); 261 } 262 else 263 { 264 if (rawData > std::numeric_limits<uint8_t>::max() || 265 rawData < std::numeric_limits<uint8_t>::lowest()) 266 { 267 log<level::ERR>("Value out of range"); 268 throw std::out_of_range("Value out of range"); 269 } 270 setReading(static_cast<uint8_t>(rawData), &response); 271 } 272 273 if (!std::isfinite(value)) 274 { 275 response.readingOrStateUnavailable = 1; 276 } 277 278 bool critAlarmHigh; 279 try 280 { 281 critAlarmHigh = std::get<bool>(ipmi::getDbusProperty( 282 bus, service, sensorInfo.sensorPath, 283 "xyz.openbmc_project.Sensor.Threshold.Critical", 284 "CriticalAlarmHigh")); 285 } 286 catch (const std::exception& e) 287 { 288 critAlarmHigh = false; 289 } 290 bool critAlarmLow; 291 try 292 { 293 critAlarmLow = std::get<bool>(ipmi::getDbusProperty( 294 bus, service, sensorInfo.sensorPath, 295 "xyz.openbmc_project.Sensor.Threshold.Critical", 296 "CriticalAlarmLow")); 297 } 298 catch (const std::exception& e) 299 { 300 critAlarmLow = false; 301 } 302 bool warningAlarmHigh; 303 try 304 { 305 warningAlarmHigh = std::get<bool>(ipmi::getDbusProperty( 306 bus, service, sensorInfo.sensorPath, 307 "xyz.openbmc_project.Sensor.Threshold.Warning", 308 "WarningAlarmHigh")); 309 } 310 catch (const std::exception& e) 311 { 312 warningAlarmHigh = false; 313 } 314 bool warningAlarmLow; 315 try 316 { 317 warningAlarmLow = std::get<bool>(ipmi::getDbusProperty( 318 bus, service, sensorInfo.sensorPath, 319 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningAlarmlow")); 320 } 321 catch (const std::exception& e) 322 { 323 warningAlarmLow = false; 324 } 325 response.thresholdLevelsStates = 326 (static_cast<uint8_t>(critAlarmHigh) << 4) | 327 (static_cast<uint8_t>(warningAlarmHigh) << 3) | 328 (static_cast<uint8_t>(warningAlarmLow) << 2) | 329 (static_cast<uint8_t>(critAlarmLow) << 1); 330 331 return response; 332 } 333 334 } // namespace get 335 336 namespace set 337 { 338 339 /** @brief Make a DBus message for a Dbus call 340 * @param[in] updateInterface - Interface name 341 * @param[in] sensorPath - Path of the sensor 342 * @param[in] command - command to be executed 343 * @param[in] sensorInterface - DBus interface of sensor 344 * @return a dbus message 345 */ 346 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 347 const std::string& sensorPath, 348 const std::string& command, 349 const std::string& sensorInterface); 350 351 /** @brief Update d-bus based on assertion type sensor data 352 * @param[in] cmdData - input sensor data 353 * @param[in] sensorInfo - sensor d-bus info 354 * @return a IPMI error code 355 */ 356 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 357 const Info& sensorInfo); 358 359 /** @brief Update d-bus based on a reading assertion 360 * @tparam T - type of d-bus property mapping this sensor 361 * @param[in] cmdData - input sensor data 362 * @param[in] sensorInfo - sensor d-bus info 363 * @return a IPMI error code 364 */ 365 template <typename T> 366 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData, 367 const Info& sensorInfo) 368 { 369 auto msg = 370 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 371 "Set", sensorInfo.sensorInterface); 372 373 const auto& interface = sensorInfo.propertyInterfaces.begin(); 374 msg.append(interface->first); 375 for (const auto& property : interface->second) 376 { 377 msg.append(property.first); 378 std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) | 379 cmdData.assertOffset0_7); 380 msg.append(value); 381 } 382 return updateToDbus(msg); 383 } 384 385 /** @brief Update d-bus based on a discrete reading 386 * @param[in] cmdData - input sensor data 387 * @param[in] sensorInfo - sensor d-bus info 388 * @return an IPMI error code 389 */ 390 template <typename T> 391 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData, 392 const Info& sensorInfo) 393 { 394 T raw_value = 395 (sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset; 396 397 raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale); 398 399 auto msg = 400 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 401 "Set", sensorInfo.sensorInterface); 402 403 const auto& interface = sensorInfo.propertyInterfaces.begin(); 404 msg.append(interface->first); 405 406 for (const auto& property : interface->second) 407 { 408 msg.append(property.first); 409 std::variant<T> value = raw_value; 410 msg.append(value); 411 } 412 return updateToDbus(msg); 413 } 414 415 /** @brief Update d-bus based on eventdata type sensor data 416 * @param[in] cmdData - input sensor data 417 * @param[in] sensorInfo - sensor d-bus info 418 * @return a IPMI error code 419 */ 420 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 421 uint8_t data); 422 423 /** @brief Update d-bus based on eventdata1 type sensor data 424 * @param[in] cmdData - input sensor data 425 * @param[in] sensorInfo - sensor d-bus info 426 * @return a IPMI error code 427 */ 428 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData, 429 const Info& sensorInfo) 430 { 431 return eventdata(cmdData, sensorInfo, cmdData.eventData1); 432 } 433 434 /** @brief Update d-bus based on eventdata2 type sensor data 435 * @param[in] cmdData - input sensor data 436 * @param[in] sensorInfo - sensor d-bus info 437 * @return a IPMI error code 438 */ 439 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData, 440 const Info& sensorInfo) 441 { 442 return eventdata(cmdData, sensorInfo, cmdData.eventData2); 443 } 444 445 /** @brief Update d-bus based on eventdata3 type sensor data 446 * @param[in] cmdData - input sensor data 447 * @param[in] sensorInfo - sensor d-bus info 448 * @return a IPMI error code 449 */ 450 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData, 451 const Info& sensorInfo) 452 { 453 return eventdata(cmdData, sensorInfo, cmdData.eventData3); 454 } 455 456 } // namespace set 457 458 namespace notify 459 { 460 461 /** @brief Make a DBus message for a Dbus call 462 * @param[in] updateInterface - Interface name 463 * @param[in] sensorPath - Path of the sensor 464 * @param[in] command - command to be executed 465 * @param[in] sensorInterface - DBus interface of sensor 466 * @return a dbus message 467 */ 468 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 469 const std::string& sensorPath, 470 const std::string& command, 471 const std::string& sensorInterface); 472 473 /** @brief Update d-bus based on assertion type sensor data 474 * @param[in] interfaceMap - sensor interface 475 * @param[in] cmdData - input sensor data 476 * @param[in] sensorInfo - sensor d-bus info 477 * @return a IPMI error code 478 */ 479 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 480 const Info& sensorInfo); 481 482 } // namespace notify 483 484 namespace inventory 485 { 486 487 namespace get 488 { 489 490 /** 491 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 492 * reading command response. 493 * 494 * @param[in] sensorInfo - Dbus info related to sensor. 495 * 496 * @return Response for get sensor reading command. 497 */ 498 GetSensorResponse assertion(const Info& sensorInfo); 499 500 } // namespace get 501 502 } // namespace inventory 503 } // namespace sensor 504 } // namespace ipmi 505