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