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_t& 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_t 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_t 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_t 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_t 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&, 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&, const std::string& command, 340 const std::string&) 341 { 342 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 343 using namespace std::string_literals; 344 345 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 346 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 347 348 return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(), 349 updateInterface.c_str(), command.c_str()); 350 } 351 352 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 353 { 354 auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath, 355 "Notify", sensorInfo.sensorInterface); 356 357 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 358 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 359 ipmi::sensor::ObjectMap objects; 360 ipmi::sensor::InterfaceMap interfaces; 361 for (const auto& interface : sensorInfo.propertyInterfaces) 362 { 363 // An interface with no properties - It is possible that the sensor 364 // object on DBUS implements a DBUS interface with no properties. 365 // Make sure we add the interface to the list if interfaces on the 366 // object with an empty property map. 367 if (interface.second.empty()) 368 { 369 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{}); 370 continue; 371 } 372 // For a property like functional state the result will be 373 // calculated based on the true value of all conditions. 374 for (const auto& property : interface.second) 375 { 376 ipmi::sensor::PropertyMap props; 377 bool valid = false; 378 auto result = true; 379 for (const auto& value : std::get<OffsetValueMap>(property.second)) 380 { 381 if (assertionSet.test(value.first)) 382 { 383 // Skip update if skipOn is ASSERT 384 if (SkipAssertion::ASSERT == value.second.skip) 385 { 386 return IPMI_CC_OK; 387 } 388 result = result && std::get<bool>(value.second.assert); 389 valid = true; 390 } 391 else if (deassertionSet.test(value.first)) 392 { 393 // Skip update if skipOn is DEASSERT 394 if (SkipAssertion::DEASSERT == value.second.skip) 395 { 396 return IPMI_CC_OK; 397 } 398 result = result && std::get<bool>(value.second.deassert); 399 valid = true; 400 } 401 } 402 for (const auto& value : 403 std::get<PreReqOffsetValueMap>(property.second)) 404 { 405 if (assertionSet.test(value.first)) 406 { 407 result = result && std::get<bool>(value.second.assert); 408 } 409 else if (deassertionSet.test(value.first)) 410 { 411 result = result && std::get<bool>(value.second.deassert); 412 } 413 } 414 if (valid) 415 { 416 props.emplace(property.first, result); 417 interfaces.emplace(interface.first, std::move(props)); 418 } 419 } 420 } 421 422 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 423 msg.append(std::move(objects)); 424 return updateToDbus(msg); 425 } 426 427 } // namespace notify 428 429 namespace inventory 430 { 431 432 namespace get 433 { 434 435 #ifndef FEATURE_SENSORS_CACHE 436 437 GetSensorResponse assertion(const Info& sensorInfo) 438 { 439 namespace fs = std::filesystem; 440 441 fs::path path{ipmi::sensor::inventoryRoot}; 442 path += sensorInfo.sensorPath; 443 444 return ipmi::sensor::get::mapDbusToAssertion( 445 sensorInfo, path.string(), 446 sensorInfo.propertyInterfaces.begin()->first); 447 } 448 449 #else 450 451 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 452 const PropertyMap& /*properties*/) 453 { 454 // The assertion may contain multiple properties 455 // So we have to get the properties from DBus anyway 456 namespace fs = std::filesystem; 457 458 fs::path path{ipmi::sensor::inventoryRoot}; 459 path += sensorInfo.sensorPath; 460 461 auto response = ipmi::sensor::get::mapDbusToAssertion( 462 sensorInfo, path.string(), 463 sensorInfo.propertyInterfaces.begin()->first); 464 465 if (!sensorCacheMap[id].has_value()) 466 { 467 sensorCacheMap[id] = SensorData{}; 468 } 469 sensorCacheMap[id]->response = response; 470 return response; 471 } 472 473 #endif 474 475 } // namespace get 476 477 } // namespace inventory 478 } // namespace sensor 479 } // namespace ipmi 480