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 enableScanning(&response); 137 138 auto service = ipmi::getService(bus, interface, path); 139 140 const auto& interfaceList = sensorInfo.propertyInterfaces; 141 142 for (const auto& interface : interfaceList) 143 { 144 for (const auto& property : interface.second) 145 { 146 auto propValue = ipmi::getDbusProperty( 147 bus, service, path, interface.first, 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, &response); 154 break; 155 } 156 } 157 } 158 } 159 160 return response; 161 } 162 163 GetSensorResponse assertion(const Info& sensorInfo) 164 { 165 return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 166 sensorInfo.sensorInterface); 167 } 168 169 GetSensorResponse eventdata2(const Info& sensorInfo) 170 { 171 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 172 GetSensorResponse response{}; 173 174 enableScanning(&response); 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, &response); 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 // An interface with no properties - It is possible that the sensor 328 // object on DBUS implements a DBUS interface with no properties. 329 // Make sure we add the interface to the list if interfaces on the 330 // object with an empty property map. 331 if (interface.second.empty()) 332 { 333 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{}); 334 continue; 335 } 336 // For a property like functional state the result will be 337 // calculated based on the true value of all conditions. 338 for (const auto& property : interface.second) 339 { 340 ipmi::sensor::PropertyMap props; 341 bool valid = false; 342 auto result = true; 343 for (const auto& value : std::get<OffsetValueMap>(property.second)) 344 { 345 if (assertionSet.test(value.first)) 346 { 347 // Skip update if skipOn is ASSERT 348 if (SkipAssertion::ASSERT == value.second.skip) 349 { 350 return IPMI_CC_OK; 351 } 352 result = result && std::get<bool>(value.second.assert); 353 valid = true; 354 } 355 else if (deassertionSet.test(value.first)) 356 { 357 // Skip update if skipOn is DEASSERT 358 if (SkipAssertion::DEASSERT == value.second.skip) 359 { 360 return IPMI_CC_OK; 361 } 362 result = result && std::get<bool>(value.second.deassert); 363 valid = true; 364 } 365 } 366 for (const auto& value : 367 std::get<PreReqOffsetValueMap>(property.second)) 368 { 369 if (assertionSet.test(value.first)) 370 { 371 result = result && std::get<bool>(value.second.assert); 372 } 373 else if (deassertionSet.test(value.first)) 374 { 375 result = result && std::get<bool>(value.second.deassert); 376 } 377 } 378 if (valid) 379 { 380 props.emplace(property.first, result); 381 interfaces.emplace(interface.first, std::move(props)); 382 } 383 } 384 } 385 386 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 387 msg.append(std::move(objects)); 388 return updateToDbus(msg); 389 } 390 391 } // namespace notify 392 393 namespace inventory 394 { 395 396 namespace get 397 { 398 399 GetSensorResponse assertion(const Info& sensorInfo) 400 { 401 namespace fs = std::filesystem; 402 403 fs::path path{ipmi::sensor::inventoryRoot}; 404 path += sensorInfo.sensorPath; 405 406 return ipmi::sensor::get::mapDbusToAssertion( 407 sensorInfo, path.string(), 408 sensorInfo.propertyInterfaces.begin()->first); 409 } 410 411 } // namespace get 412 413 } // namespace inventory 414 } // namespace sensor 415 } // namespace ipmi 416