1 #include <bitset> 2 #include <experimental/filesystem> 3 #include <phosphor-logging/elog-errors.hpp> 4 #include <phosphor-logging/log.hpp> 5 #include "xyz/openbmc_project/Common/error.hpp" 6 #include "types.hpp" 7 #include "sensorhandler.h" 8 #include "sensordatahandler.hpp" 9 #include "utils.hpp" 10 11 namespace ipmi 12 { 13 namespace sensor 14 { 15 16 using namespace phosphor::logging; 17 using InternalFailure = 18 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 19 20 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 21 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 22 static constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 23 24 /** @brief get the D-Bus service and service path 25 * @param[in] bus - The Dbus bus object 26 * @param[in] interface - interface to the service 27 * @param[in] path - interested path in the list of objects 28 * @return pair of service path and service 29 */ 30 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus, 31 const std::string& interface, 32 const std::string& path) 33 { 34 auto depth = 0; 35 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, 36 MAPPER_PATH, 37 MAPPER_INTERFACE, 38 "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), 48 entry("INTERFACE=%s", interface)); 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), 58 entry("INTERFACE=%s", interface)); 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>("Coudn't find d-bus path", 72 entry("PATH=%s", path), 73 entry("INTERFACE=%s", interface)); 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::bus 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 (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::bus bus{ipmid_get_sd_bus_connection()}; 132 GetSensorResponse response {}; 133 auto responseData = reinterpret_cast<GetReadingResponse*>(response.data()); 134 135 auto service = ipmi::getService(bus, interface, path); 136 137 const auto& interfaceList = sensorInfo.propertyInterfaces; 138 139 for (const auto& interface : interfaceList) 140 { 141 for (const auto& property : interface.second) 142 { 143 auto propValue = ipmi::getDbusProperty(bus, 144 service, 145 path, 146 interface.first, 147 property.first); 148 149 for (const auto& value : std::get<OffsetValueMap>(property.second)) 150 { 151 if (propValue == value.second.assert) 152 { 153 setOffset(value.first, responseData); 154 break; 155 } 156 157 } 158 } 159 } 160 161 return response; 162 } 163 164 GetSensorResponse assertion(const Info& sensorInfo) 165 { 166 return mapDbusToAssertion(sensorInfo, 167 sensorInfo.sensorPath, 168 sensorInfo.sensorInterface); 169 } 170 171 GetSensorResponse eventdata2(const Info& sensorInfo) 172 { 173 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 174 GetSensorResponse response {}; 175 auto responseData = reinterpret_cast<GetReadingResponse*>(response.data()); 176 177 auto service = ipmi::getService(bus, 178 sensorInfo.sensorInterface, 179 sensorInfo.sensorPath); 180 181 const auto& interfaceList = sensorInfo.propertyInterfaces; 182 183 for (const auto& interface : interfaceList) 184 { 185 for (const auto& property : interface.second) 186 { 187 auto propValue = ipmi::getDbusProperty(bus, 188 service, 189 sensorInfo.sensorPath, 190 interface.first, 191 property.first); 192 193 for (const auto& value : std::get<OffsetValueMap>(property.second)) 194 { 195 if (propValue == value.second.assert) 196 { 197 setReading(value.first, responseData); 198 break; 199 } 200 } 201 } 202 } 203 204 return response; 205 } 206 207 } //namespace get 208 209 namespace set 210 { 211 212 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 213 const std::string& sensorPath, 214 const std::string& command, 215 const std::string& sensorInterface) 216 { 217 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 218 using namespace std::string_literals; 219 220 auto dbusService = getService(bus, 221 sensorInterface, 222 sensorPath); 223 224 return bus.new_method_call(dbusService.c_str(), 225 sensorPath.c_str(), 226 updateInterface.c_str(), 227 command.c_str()); 228 } 229 230 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, 231 const Info& sensorInfo, 232 uint8_t data) 233 { 234 auto msg = makeDbusMsg( 235 "org.freedesktop.DBus.Properties", 236 sensorInfo.sensorPath, 237 "Set", 238 sensorInfo.sensorInterface); 239 240 const auto& interface = sensorInfo.propertyInterfaces.begin(); 241 msg.append(interface->first); 242 for (const auto& property : interface->second) 243 { 244 msg.append(property.first); 245 const auto& iter = std::get<OffsetValueMap>(property.second).find(data); 246 if (iter == std::get<OffsetValueMap>(property.second).end()) 247 { 248 log<level::ERR>("Invalid event data"); 249 return IPMI_CC_PARM_OUT_OF_RANGE; 250 } 251 msg.append(iter->second.assert); 252 } 253 return updateToDbus(msg); 254 } 255 256 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 257 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 Value tmp{mapbox::util::no_init()}; 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.valid()) 289 { 290 auto msg = makeDbusMsg( 291 "org.freedesktop.DBus.Properties", 292 sensorInfo.sensorPath, 293 "Set", 294 sensorInfo.sensorInterface); 295 msg.append(interface->first); 296 msg.append(property.first); 297 msg.append(tmp); 298 299 auto rc = updateToDbus(msg); 300 if (rc) 301 { 302 return rc; 303 } 304 } 305 } 306 307 return IPMI_CC_OK; 308 } 309 310 }//namespace set 311 312 namespace notify 313 { 314 315 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 316 const std::string& sensorPath, 317 const std::string& command, 318 const std::string& sensorInterface) 319 { 320 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 321 using namespace std::string_literals; 322 323 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 324 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 325 326 return bus.new_method_call(dbusService.c_str(), 327 dbusPath.c_str(), 328 updateInterface.c_str(), 329 command.c_str()); 330 } 331 332 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 333 const Info& sensorInfo) 334 { 335 auto msg = makeDbusMsg( 336 sensorInfo.sensorInterface, 337 sensorInfo.sensorPath, 338 "Notify", 339 sensorInfo.sensorInterface); 340 341 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 342 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 343 ipmi::sensor::ObjectMap objects; 344 ipmi::sensor::InterfaceMap interfaces; 345 for (const auto& interface : sensorInfo.propertyInterfaces) 346 { 347 //For a property like functional state the result will be 348 //calculated based on the true value of all conditions. 349 for (const auto& property : interface.second) 350 { 351 ipmi::sensor::PropertyMap props; 352 bool valid = false; 353 auto result = true; 354 for (const auto& value : std::get<OffsetValueMap>(property.second)) 355 { 356 if (assertionSet.test(value.first)) 357 { 358 //Skip update if skipOn is ASSERT 359 if (SkipAssertion::ASSERT == value.second.skip) 360 { 361 return IPMI_CC_OK; 362 } 363 result = result && value.second.assert.get<bool>(); 364 valid = true; 365 } 366 else if (deassertionSet.test(value.first)) 367 { 368 //Skip update if skipOn is DEASSERT 369 if (SkipAssertion::DEASSERT == value.second.skip) 370 { 371 return IPMI_CC_OK; 372 } 373 result = result && value.second.deassert.get<bool>(); 374 valid = true; 375 } 376 } 377 for (const auto& value : 378 std::get<PreReqOffsetValueMap>(property.second)) 379 { 380 if (assertionSet.test(value.first)) 381 { 382 result = result && value.second.assert.get<bool>(); 383 } 384 else if (deassertionSet.test(value.first)) 385 { 386 result = result && value.second.deassert.get<bool>(); 387 } 388 } 389 if (valid) 390 { 391 props.emplace(property.first, result); 392 interfaces.emplace(interface.first, std::move(props)); 393 } 394 } 395 } 396 397 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 398 msg.append(std::move(objects)); 399 return updateToDbus(msg); 400 } 401 402 }//namespace notify 403 404 namespace inventory 405 { 406 407 namespace get 408 { 409 410 GetSensorResponse assertion(const Info& sensorInfo) 411 { 412 namespace fs = std::experimental::filesystem; 413 414 fs::path path{ipmi::sensor::inventoryRoot}; 415 path += sensorInfo.sensorPath; 416 417 return ipmi::sensor::get::mapDbusToAssertion( 418 sensorInfo, 419 path.string(), 420 sensorInfo.propertyInterfaces.begin()->first); 421 } 422 423 } //namespace get 424 425 } // namespace inventory 426 }//namespace sensor 427 }//namespace ipmi 428