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. If the object path is /system/chassis/motherboard/cpu0/core0 105 * then the sensor name is cpu0_core0. The leaf and the parent is put 106 * together to get 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 SensorName nameParentLeaf(const Info& sensorInfo); 113 114 /** 115 * @brief Helper function to map the dbus info to sensor's assertion status 116 * for the get sensor reading command. 117 * 118 * @param[in] sensorInfo - Dbus info related to sensor. 119 * @param[in] path - Dbus object path. 120 * @param[in] interface - Dbus interface. 121 * 122 * @return Response for get sensor reading command. 123 */ 124 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo, 125 const InstancePath& path, 126 const DbusInterface& interface); 127 128 /** 129 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 130 * reading command response. 131 * 132 * @param[in] sensorInfo - Dbus info related to sensor. 133 * 134 * @return Response for get sensor reading command. 135 */ 136 GetSensorResponse assertion(const Info& sensorInfo); 137 138 /** 139 * @brief Maps the Dbus info to the reading field in the Get sensor reading 140 * command response. 141 * 142 * @param[in] sensorInfo - Dbus info related to sensor. 143 * 144 * @return Response for get sensor reading command. 145 */ 146 GetSensorResponse eventdata2(const Info& sensorInfo); 147 148 /** 149 * @brief readingAssertion is a case where the entire assertion state field 150 * serves as the sensor value. 151 * 152 * @tparam T - type of the dbus property related to sensor. 153 * @param[in] sensorInfo - Dbus info related to sensor. 154 * 155 * @return Response for get sensor reading command. 156 */ 157 template <typename T> 158 GetSensorResponse readingAssertion(const Info& sensorInfo) 159 { 160 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 161 GetSensorResponse response{}; 162 163 enableScanning(&response); 164 165 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 166 sensorInfo.sensorPath); 167 168 auto propValue = ipmi::getDbusProperty( 169 bus, service, sensorInfo.sensorPath, 170 sensorInfo.propertyInterfaces.begin()->first, 171 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 172 173 setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response); 174 175 return response; 176 } 177 178 /** @brief Map the Dbus info to the reading field in the Get sensor reading 179 * command response 180 * 181 * @tparam T - type of the dbus property related to sensor. 182 * @param[in] sensorInfo - Dbus info related to sensor. 183 * 184 * @return Response for get sensor reading command. 185 */ 186 template <typename T> 187 GetSensorResponse readingData(const Info& sensorInfo) 188 { 189 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 190 191 GetSensorResponse response{}; 192 193 enableScanning(&response); 194 195 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 196 sensorInfo.sensorPath); 197 198 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 199 // Check the OperationalStatus interface for functional property 200 if (sensorInfo.propertyInterfaces.begin()->first == 201 "xyz.openbmc_project.Sensor.Value") 202 { 203 bool functional = true; 204 try 205 { 206 auto funcValue = ipmi::getDbusProperty( 207 bus, service, sensorInfo.sensorPath, 208 "xyz.openbmc_project.State.Decorator.OperationalStatus", 209 "Functional"); 210 functional = std::get<bool>(funcValue); 211 } 212 catch (...) 213 { 214 // No-op if Functional property could not be found since this 215 // check is only valid for Sensor.Value read for hwmonio 216 } 217 if (!functional) 218 { 219 throw SensorFunctionalError(); 220 } 221 } 222 #endif 223 224 auto propValue = ipmi::getDbusProperty( 225 bus, service, sensorInfo.sensorPath, 226 sensorInfo.propertyInterfaces.begin()->first, 227 sensorInfo.propertyInterfaces.begin()->second.begin()->first); 228 229 double value = std::get<T>(propValue) * 230 std::pow(10, sensorInfo.scale - sensorInfo.exponentR); 231 int32_t rawData = 232 (value - sensorInfo.scaledOffset) / sensorInfo.coefficientM; 233 234 constexpr uint8_t sensorUnitsSignedBits = 2 << 6; 235 constexpr uint8_t signedDataFormat = 0x80; 236 // if sensorUnits1 [7:6] = 10b, sensor is signed 237 if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat) 238 { 239 if (rawData > std::numeric_limits<int8_t>::max() || 240 rawData < std::numeric_limits<int8_t>::lowest()) 241 { 242 log<level::ERR>("Value out of range"); 243 throw std::out_of_range("Value out of range"); 244 } 245 setReading(static_cast<int8_t>(rawData), &response); 246 } 247 else 248 { 249 if (rawData > std::numeric_limits<uint8_t>::max() || 250 rawData < std::numeric_limits<uint8_t>::lowest()) 251 { 252 log<level::ERR>("Value out of range"); 253 throw std::out_of_range("Value out of range"); 254 } 255 setReading(static_cast<uint8_t>(rawData), &response); 256 } 257 258 if (!std::isfinite(value)) 259 { 260 response.readingOrStateUnavailable = 1; 261 } 262 263 return response; 264 } 265 266 } // namespace get 267 268 namespace set 269 { 270 271 /** @brief Make a DBus message for a Dbus call 272 * @param[in] updateInterface - Interface name 273 * @param[in] sensorPath - Path of the sensor 274 * @param[in] command - command to be executed 275 * @param[in] sensorInterface - DBus interface of sensor 276 * @return a dbus message 277 */ 278 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 279 const std::string& sensorPath, 280 const std::string& command, 281 const std::string& sensorInterface); 282 283 /** @brief Update d-bus based on assertion type sensor data 284 * @param[in] cmdData - input sensor data 285 * @param[in] sensorInfo - sensor d-bus info 286 * @return a IPMI error code 287 */ 288 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 289 const Info& sensorInfo); 290 291 /** @brief Update d-bus based on a reading assertion 292 * @tparam T - type of d-bus property mapping this sensor 293 * @param[in] cmdData - input sensor data 294 * @param[in] sensorInfo - sensor d-bus info 295 * @return a IPMI error code 296 */ 297 template <typename T> 298 ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData, 299 const Info& sensorInfo) 300 { 301 auto msg = 302 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 303 "Set", sensorInfo.sensorInterface); 304 305 const auto& interface = sensorInfo.propertyInterfaces.begin(); 306 msg.append(interface->first); 307 for (const auto& property : interface->second) 308 { 309 msg.append(property.first); 310 std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) | 311 cmdData.assertOffset0_7); 312 msg.append(value); 313 } 314 return updateToDbus(msg); 315 } 316 317 /** @brief Update d-bus based on a discrete reading 318 * @param[in] cmdData - input sensor data 319 * @param[in] sensorInfo - sensor d-bus info 320 * @return an IPMI error code 321 */ 322 template <typename T> 323 ipmi_ret_t readingData(const SetSensorReadingReq& cmdData, 324 const Info& sensorInfo) 325 { 326 T raw_value = 327 (sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset; 328 329 raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale); 330 331 auto msg = 332 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 333 "Set", sensorInfo.sensorInterface); 334 335 const auto& interface = sensorInfo.propertyInterfaces.begin(); 336 msg.append(interface->first); 337 338 for (const auto& property : interface->second) 339 { 340 msg.append(property.first); 341 std::variant<T> value = raw_value; 342 msg.append(value); 343 } 344 return updateToDbus(msg); 345 } 346 347 /** @brief Update d-bus based on eventdata type sensor data 348 * @param[in] cmdData - input sensor data 349 * @param[in] sensorInfo - sensor d-bus info 350 * @return a IPMI error code 351 */ 352 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 353 uint8_t data); 354 355 /** @brief Update d-bus based on eventdata1 type sensor data 356 * @param[in] cmdData - input sensor data 357 * @param[in] sensorInfo - sensor d-bus info 358 * @return a IPMI error code 359 */ 360 inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData, 361 const Info& sensorInfo) 362 { 363 return eventdata(cmdData, sensorInfo, cmdData.eventData1); 364 } 365 366 /** @brief Update d-bus based on eventdata2 type sensor data 367 * @param[in] cmdData - input sensor data 368 * @param[in] sensorInfo - sensor d-bus info 369 * @return a IPMI error code 370 */ 371 inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData, 372 const Info& sensorInfo) 373 { 374 return eventdata(cmdData, sensorInfo, cmdData.eventData2); 375 } 376 377 /** @brief Update d-bus based on eventdata3 type sensor data 378 * @param[in] cmdData - input sensor data 379 * @param[in] sensorInfo - sensor d-bus info 380 * @return a IPMI error code 381 */ 382 inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData, 383 const Info& sensorInfo) 384 { 385 return eventdata(cmdData, sensorInfo, cmdData.eventData3); 386 } 387 388 } // namespace set 389 390 namespace notify 391 { 392 393 /** @brief Make a DBus message for a Dbus call 394 * @param[in] updateInterface - Interface name 395 * @param[in] sensorPath - Path of the sensor 396 * @param[in] command - command to be executed 397 * @param[in] sensorInterface - DBus interface of sensor 398 * @return a dbus message 399 */ 400 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 401 const std::string& sensorPath, 402 const std::string& command, 403 const std::string& sensorInterface); 404 405 /** @brief Update d-bus based on assertion type sensor data 406 * @param[in] interfaceMap - sensor interface 407 * @param[in] cmdData - input sensor data 408 * @param[in] sensorInfo - sensor d-bus info 409 * @return a IPMI error code 410 */ 411 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 412 const Info& sensorInfo); 413 414 } // namespace notify 415 416 namespace inventory 417 { 418 419 namespace get 420 { 421 422 /** 423 * @brief Map the Dbus info to sensor's assertion status in the Get sensor 424 * reading command response. 425 * 426 * @param[in] sensorInfo - Dbus info related to sensor. 427 * 428 * @return Response for get sensor reading command. 429 */ 430 GetSensorResponse assertion(const Info& sensorInfo); 431 432 } // namespace get 433 434 } // namespace inventory 435 } // namespace sensor 436 } // namespace ipmi 437