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 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo, 114 const InstancePath& path, 115 const DbusInterface& interface) 116 { 117 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 118 GetSensorResponse response {}; 119 auto responseData = reinterpret_cast<GetReadingResponse*>(response.data()); 120 121 auto service = ipmi::getService(bus, interface, path); 122 123 const auto& interfaceList = sensorInfo.propertyInterfaces; 124 125 for (const auto& interface : interfaceList) 126 { 127 for (const auto& property : interface.second) 128 { 129 auto propValue = ipmi::getDbusProperty(bus, 130 service, 131 path, 132 interface.first, 133 property.first); 134 135 for (const auto& value : std::get<OffsetValueMap>(property.second)) 136 { 137 if (propValue == value.second.assert) 138 { 139 setOffset(value.first, responseData); 140 break; 141 } 142 143 } 144 } 145 } 146 147 return response; 148 } 149 150 GetSensorResponse assertion(const Info& sensorInfo) 151 { 152 return mapDbusToAssertion(sensorInfo, 153 sensorInfo.sensorPath, 154 sensorInfo.sensorInterface); 155 } 156 157 GetSensorResponse eventdata2(const Info& sensorInfo) 158 { 159 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 160 GetSensorResponse response {}; 161 auto responseData = reinterpret_cast<GetReadingResponse*>(response.data()); 162 163 auto service = ipmi::getService(bus, 164 sensorInfo.sensorInterface, 165 sensorInfo.sensorPath); 166 167 const auto& interfaceList = sensorInfo.propertyInterfaces; 168 169 for (const auto& interface : interfaceList) 170 { 171 for (const auto& property : interface.second) 172 { 173 auto propValue = ipmi::getDbusProperty(bus, 174 service, 175 sensorInfo.sensorPath, 176 interface.first, 177 property.first); 178 179 for (const auto& value : std::get<OffsetValueMap>(property.second)) 180 { 181 if (propValue == value.second.assert) 182 { 183 setReading(value.first, responseData); 184 break; 185 } 186 } 187 } 188 } 189 190 return response; 191 } 192 193 } //namespace get 194 195 namespace set 196 { 197 198 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 199 const std::string& sensorPath, 200 const std::string& command, 201 const std::string& sensorInterface) 202 { 203 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 204 using namespace std::string_literals; 205 206 std::string dbusService; 207 std::string dbusPath; 208 209 std::tie(dbusPath, dbusService) = getServiceAndPath(bus, 210 sensorInterface, 211 sensorPath); 212 return bus.new_method_call(dbusService.c_str(), 213 dbusPath.c_str(), 214 updateInterface.c_str(), 215 command.c_str()); 216 } 217 218 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, 219 const Info& sensorInfo, 220 uint8_t data) 221 { 222 auto msg = makeDbusMsg( 223 "org.freedesktop.DBus.Properties", 224 sensorInfo.sensorPath, 225 "Set", 226 sensorInfo.sensorInterface); 227 228 const auto& interface = sensorInfo.propertyInterfaces.begin(); 229 msg.append(interface->first); 230 for (const auto& property : interface->second) 231 { 232 msg.append(property.first); 233 const auto& iter = std::get<OffsetValueMap>(property.second).find(data); 234 if (iter == std::get<OffsetValueMap>(property.second).end()) 235 { 236 log<level::ERR>("Invalid event data"); 237 return IPMI_CC_PARM_OUT_OF_RANGE; 238 } 239 msg.append(iter->second.assert); 240 } 241 return updateToDbus(msg); 242 } 243 244 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 245 const Info& sensorInfo) 246 { 247 auto msg = makeDbusMsg( 248 "org.freedesktop.DBus.Properties", 249 sensorInfo.sensorPath, 250 "Set", 251 sensorInfo.sensorInterface); 252 253 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 254 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 255 256 const auto& interface = sensorInfo.propertyInterfaces.begin(); 257 msg.append(interface->first); 258 for (const auto& property : interface->second) 259 { 260 msg.append(property.first); 261 for (const auto& value : std::get<OffsetValueMap>(property.second)) 262 { 263 if (assertionSet.test(value.first)) 264 { 265 msg.append(value.second.assert); 266 } 267 if (deassertionSet.test(value.first)) 268 { 269 msg.append(value.second.deassert); 270 } 271 } 272 } 273 return updateToDbus(msg); 274 } 275 276 }//namespace set 277 278 namespace notify 279 { 280 281 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 282 const std::string& sensorPath, 283 const std::string& command, 284 const std::string& sensorInterface) 285 { 286 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 287 using namespace std::string_literals; 288 289 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 290 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 291 292 return bus.new_method_call(dbusService.c_str(), 293 dbusPath.c_str(), 294 updateInterface.c_str(), 295 command.c_str()); 296 } 297 298 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, 299 const Info& sensorInfo) 300 { 301 auto msg = makeDbusMsg( 302 sensorInfo.sensorInterface, 303 sensorInfo.sensorPath, 304 "Notify", 305 sensorInfo.sensorInterface); 306 307 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 308 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 309 ipmi::sensor::ObjectMap objects; 310 ipmi::sensor::InterfaceMap interfaces; 311 for (const auto& interface : sensorInfo.propertyInterfaces) 312 { 313 //For a property like functional state the result will be 314 //calculated based on the true value of all conditions. 315 for (const auto& property : interface.second) 316 { 317 ipmi::sensor::PropertyMap props; 318 bool valid = false; 319 auto result = true; 320 for (const auto& value : std::get<OffsetValueMap>(property.second)) 321 { 322 if (assertionSet.test(value.first)) 323 { 324 //Skip update if skipOn is ASSERT 325 if (SkipAssertion::ASSERT == value.second.skip) 326 { 327 return IPMI_CC_OK; 328 } 329 result = result && value.second.assert.get<bool>(); 330 valid = true; 331 } 332 else if (deassertionSet.test(value.first)) 333 { 334 //Skip update if skipOn is DEASSERT 335 if (SkipAssertion::DEASSERT == value.second.skip) 336 { 337 return IPMI_CC_OK; 338 } 339 result = result && value.second.deassert.get<bool>(); 340 valid = true; 341 } 342 } 343 for (const auto& value : 344 std::get<PreReqOffsetValueMap>(property.second)) 345 { 346 if (assertionSet.test(value.first)) 347 { 348 result = result && value.second.assert.get<bool>(); 349 } 350 else if (deassertionSet.test(value.first)) 351 { 352 result = result && value.second.deassert.get<bool>(); 353 } 354 } 355 if (valid) 356 { 357 props.emplace(property.first, result); 358 interfaces.emplace(interface.first, std::move(props)); 359 } 360 } 361 } 362 363 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 364 msg.append(std::move(objects)); 365 return updateToDbus(msg); 366 } 367 368 }//namespace notify 369 370 namespace inventory 371 { 372 373 namespace get 374 { 375 376 GetSensorResponse assertion(const Info& sensorInfo) 377 { 378 namespace fs = std::experimental::filesystem; 379 380 fs::path path{ipmi::sensor::inventoryRoot}; 381 path += sensorInfo.sensorPath; 382 383 return ipmi::sensor::get::mapDbusToAssertion( 384 sensorInfo, 385 path.string(), 386 sensorInfo.propertyInterfaces.begin()->first); 387 } 388 389 } //namespace get 390 391 } // namespace inventory 392 }//namespace sensor 393 }//namespace ipmi 394