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