1 #include "sensordatahandler.hpp" 2 3 #include "sensorhandler.hpp" 4 5 #include <bitset> 6 #include <filesystem> 7 #include <ipmid/types.hpp> 8 #include <ipmid/utils.hpp> 9 #include <optional> 10 #include <sdbusplus/message/types.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 namespace ipmi 14 { 15 namespace sensor 16 { 17 18 using namespace phosphor::logging; 19 using InternalFailure = 20 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 21 22 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 23 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 24 static constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 25 26 /** @brief get the D-Bus service and service path 27 * @param[in] bus - The Dbus bus object 28 * @param[in] interface - interface to the service 29 * @param[in] path - interested path in the list of objects 30 * @return pair of service path and service 31 */ 32 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus, 33 const std::string& interface, 34 const std::string& path) 35 { 36 auto depth = 0; 37 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 38 MAPPER_INTERFACE, "GetSubTree"); 39 mapperCall.append("/"); 40 mapperCall.append(depth); 41 mapperCall.append(std::vector<Interface>({interface})); 42 43 auto mapperResponseMsg = bus.call(mapperCall); 44 if (mapperResponseMsg.is_method_error()) 45 { 46 log<level::ERR>("Mapper GetSubTree failed", 47 entry("PATH=%s", path.c_str()), 48 entry("INTERFACE=%s", interface.c_str())); 49 elog<InternalFailure>(); 50 } 51 52 MapperResponseType mapperResponse; 53 mapperResponseMsg.read(mapperResponse); 54 if (mapperResponse.empty()) 55 { 56 log<level::ERR>("Invalid mapper response", 57 entry("PATH=%s", path.c_str()), 58 entry("INTERFACE=%s", interface.c_str())); 59 elog<InternalFailure>(); 60 } 61 62 if (path.empty()) 63 { 64 // Get the first one if the path is not in list. 65 return std::make_pair(mapperResponse.begin()->first, 66 mapperResponse.begin()->second.begin()->first); 67 } 68 const auto& iter = mapperResponse.find(path); 69 if (iter == mapperResponse.end()) 70 { 71 log<level::ERR>("Couldn't find D-Bus path", 72 entry("PATH=%s", path.c_str()), 73 entry("INTERFACE=%s", interface.c_str())); 74 elog<InternalFailure>(); 75 } 76 return std::make_pair(iter->first, iter->second.begin()->first); 77 } 78 79 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData) 80 { 81 Assertion assertionStates = 82 (static_cast<Assertion>(cmdData.assertOffset8_14)) << 8 | 83 cmdData.assertOffset0_7; 84 Deassertion deassertionStates = 85 (static_cast<Deassertion>(cmdData.deassertOffset8_14)) << 8 | 86 cmdData.deassertOffset0_7; 87 return std::make_pair(assertionStates, deassertionStates); 88 } 89 90 ipmi_ret_t updateToDbus(IpmiUpdateData& msg) 91 { 92 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 93 try 94 { 95 auto serviceResponseMsg = bus.call(msg); 96 if (serviceResponseMsg.is_method_error()) 97 { 98 log<level::ERR>("Error in D-Bus call"); 99 return IPMI_CC_UNSPECIFIED_ERROR; 100 } 101 } 102 catch (const InternalFailure& e) 103 { 104 commit<InternalFailure>(); 105 return IPMI_CC_UNSPECIFIED_ERROR; 106 } 107 return IPMI_CC_OK; 108 } 109 110 namespace get 111 { 112 113 SensorName nameParentLeaf(const Info& sensorInfo) 114 { 115 const auto pos = sensorInfo.sensorPath.find_last_of('/'); 116 const auto leaf = sensorInfo.sensorPath.substr(pos + 1); 117 118 const auto remaining = sensorInfo.sensorPath.substr(0, pos); 119 120 const auto parentPos = remaining.find_last_of('/'); 121 auto parent = remaining.substr(parentPos + 1); 122 123 parent += "_" + leaf; 124 return parent; 125 } 126 127 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo, 128 const InstancePath& path, 129 const DbusInterface& interface) 130 { 131 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 132 GetSensorResponse response{}; 133 134 enableScanning(&response); 135 136 auto service = ipmi::getService(bus, interface, path); 137 138 const auto& interfaceList = sensorInfo.propertyInterfaces; 139 140 for (const auto& interface : interfaceList) 141 { 142 for (const auto& property : interface.second) 143 { 144 auto propValue = ipmi::getDbusProperty( 145 bus, service, path, interface.first, property.first); 146 147 for (const auto& value : std::get<OffsetValueMap>(property.second)) 148 { 149 if (propValue == value.second.assert) 150 { 151 setOffset(value.first, &response); 152 break; 153 } 154 } 155 } 156 } 157 158 return response; 159 } 160 161 GetSensorResponse mapDbusToEventdata2(const Info& sensorInfo) 162 { 163 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 164 GetSensorResponse response{}; 165 166 enableScanning(&response); 167 168 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 169 sensorInfo.sensorPath); 170 171 const auto& interfaceList = sensorInfo.propertyInterfaces; 172 173 for (const auto& interface : interfaceList) 174 { 175 for (const auto& property : interface.second) 176 { 177 auto propValue = 178 ipmi::getDbusProperty(bus, service, sensorInfo.sensorPath, 179 interface.first, property.first); 180 181 for (const auto& value : std::get<OffsetValueMap>(property.second)) 182 { 183 if (propValue == value.second.assert) 184 { 185 setReading(value.first, &response); 186 break; 187 } 188 } 189 } 190 } 191 192 return response; 193 } 194 195 #ifndef FEATURE_SENSORS_CACHE 196 GetSensorResponse assertion(const Info& sensorInfo) 197 { 198 return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 199 sensorInfo.sensorInterface); 200 } 201 202 GetSensorResponse eventdata2(const Info& sensorInfo) 203 { 204 return mapDbusToEventdata2(sensorInfo); 205 } 206 #else 207 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 208 const PropertyMap& /*properties*/) 209 { 210 // The assertion may contain multiple properties 211 // So we have to get the properties from DBus anyway 212 auto response = mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 213 sensorInfo.sensorInterface); 214 215 if (!sensorCacheMap[id].has_value()) 216 { 217 sensorCacheMap[id] = SensorData{}; 218 } 219 sensorCacheMap[id]->response = response; 220 return response; 221 } 222 223 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo, 224 const PropertyMap& /*properties*/) 225 { 226 // The eventdata2 may contain multiple properties 227 // So we have to get the properties from DBus anyway 228 auto response = mapDbusToEventdata2(sensorInfo); 229 230 if (!sensorCacheMap[id].has_value()) 231 { 232 sensorCacheMap[id] = SensorData{}; 233 } 234 sensorCacheMap[id]->response = response; 235 return response; 236 } 237 238 #endif // FEATURE_SENSORS_CACHE 239 240 } // namespace get 241 242 namespace set 243 { 244 245 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 246 const std::string& sensorPath, 247 const std::string& command, 248 const std::string& sensorInterface) 249 { 250 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 251 using namespace std::string_literals; 252 253 auto dbusService = getService(bus, sensorInterface, sensorPath); 254 255 return bus.new_method_call(dbusService.c_str(), sensorPath.c_str(), 256 updateInterface.c_str(), command.c_str()); 257 } 258 259 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 260 uint8_t data) 261 { 262 auto msg = 263 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 264 "Set", sensorInfo.sensorInterface); 265 266 const auto& interface = sensorInfo.propertyInterfaces.begin(); 267 msg.append(interface->first); 268 for (const auto& property : interface->second) 269 { 270 msg.append(property.first); 271 const auto& iter = std::get<OffsetValueMap>(property.second).find(data); 272 if (iter == std::get<OffsetValueMap>(property.second).end()) 273 { 274 log<level::ERR>("Invalid event data"); 275 return IPMI_CC_PARM_OUT_OF_RANGE; 276 } 277 msg.append(iter->second.assert); 278 } 279 return updateToDbus(msg); 280 } 281 282 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 283 { 284 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 285 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 286 auto bothSet = assertionSet ^ deassertionSet; 287 288 const auto& interface = sensorInfo.propertyInterfaces.begin(); 289 290 for (const auto& property : interface->second) 291 { 292 std::optional<Value> tmp; 293 for (const auto& value : std::get<OffsetValueMap>(property.second)) 294 { 295 if (bothSet.size() <= value.first || !bothSet.test(value.first)) 296 { 297 // A BIOS shouldn't do this but ignore if they do. 298 continue; 299 } 300 301 if (assertionSet.test(value.first)) 302 { 303 tmp = value.second.assert; 304 break; 305 } 306 if (deassertionSet.test(value.first)) 307 { 308 tmp = value.second.deassert; 309 break; 310 } 311 } 312 313 if (tmp) 314 { 315 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties", 316 sensorInfo.sensorPath, "Set", 317 sensorInfo.sensorInterface); 318 msg.append(interface->first); 319 msg.append(property.first); 320 msg.append(*tmp); 321 322 auto rc = updateToDbus(msg); 323 if (rc) 324 { 325 return rc; 326 } 327 } 328 } 329 330 return IPMI_CC_OK; 331 } 332 333 } // namespace set 334 335 namespace notify 336 { 337 338 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 339 const std::string& sensorPath, 340 const std::string& command, 341 const std::string& sensorInterface) 342 { 343 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 344 using namespace std::string_literals; 345 346 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 347 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 348 349 return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(), 350 updateInterface.c_str(), command.c_str()); 351 } 352 353 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 354 { 355 auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath, 356 "Notify", sensorInfo.sensorInterface); 357 358 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 359 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 360 ipmi::sensor::ObjectMap objects; 361 ipmi::sensor::InterfaceMap interfaces; 362 for (const auto& interface : sensorInfo.propertyInterfaces) 363 { 364 // An interface with no properties - It is possible that the sensor 365 // object on DBUS implements a DBUS interface with no properties. 366 // Make sure we add the interface to the list if interfaces on the 367 // object with an empty property map. 368 if (interface.second.empty()) 369 { 370 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{}); 371 continue; 372 } 373 // For a property like functional state the result will be 374 // calculated based on the true value of all conditions. 375 for (const auto& property : interface.second) 376 { 377 ipmi::sensor::PropertyMap props; 378 bool valid = false; 379 auto result = true; 380 for (const auto& value : std::get<OffsetValueMap>(property.second)) 381 { 382 if (assertionSet.test(value.first)) 383 { 384 // Skip update if skipOn is ASSERT 385 if (SkipAssertion::ASSERT == value.second.skip) 386 { 387 return IPMI_CC_OK; 388 } 389 result = result && std::get<bool>(value.second.assert); 390 valid = true; 391 } 392 else if (deassertionSet.test(value.first)) 393 { 394 // Skip update if skipOn is DEASSERT 395 if (SkipAssertion::DEASSERT == value.second.skip) 396 { 397 return IPMI_CC_OK; 398 } 399 result = result && std::get<bool>(value.second.deassert); 400 valid = true; 401 } 402 } 403 for (const auto& value : 404 std::get<PreReqOffsetValueMap>(property.second)) 405 { 406 if (assertionSet.test(value.first)) 407 { 408 result = result && std::get<bool>(value.second.assert); 409 } 410 else if (deassertionSet.test(value.first)) 411 { 412 result = result && std::get<bool>(value.second.deassert); 413 } 414 } 415 if (valid) 416 { 417 props.emplace(property.first, result); 418 interfaces.emplace(interface.first, std::move(props)); 419 } 420 } 421 } 422 423 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 424 msg.append(std::move(objects)); 425 return updateToDbus(msg); 426 } 427 428 } // namespace notify 429 430 namespace inventory 431 { 432 433 namespace get 434 { 435 436 #ifndef FEATURE_SENSORS_CACHE 437 438 GetSensorResponse assertion(const Info& sensorInfo) 439 { 440 namespace fs = std::filesystem; 441 442 fs::path path{ipmi::sensor::inventoryRoot}; 443 path += sensorInfo.sensorPath; 444 445 return ipmi::sensor::get::mapDbusToAssertion( 446 sensorInfo, path.string(), 447 sensorInfo.propertyInterfaces.begin()->first); 448 } 449 450 #else 451 452 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 453 const PropertyMap& /*properties*/) 454 { 455 // The assertion may contain multiple properties 456 // So we have to get the properties from DBus anyway 457 namespace fs = std::filesystem; 458 459 fs::path path{ipmi::sensor::inventoryRoot}; 460 path += sensorInfo.sensorPath; 461 462 auto response = ipmi::sensor::get::mapDbusToAssertion( 463 sensorInfo, path.string(), 464 sensorInfo.propertyInterfaces.begin()->first); 465 466 if (!sensorCacheMap[id].has_value()) 467 { 468 sensorCacheMap[id] = SensorData{}; 469 } 470 sensorCacheMap[id]->response = response; 471 return response; 472 } 473 474 #endif 475 476 } // namespace get 477 478 } // namespace inventory 479 } // namespace sensor 480 } // namespace ipmi 481