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