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