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