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