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 <sdbusplus/message/types.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 namespace ipmi 14 { 15 namespace sensor 16 { 17 18 using namespace phosphor::logging; 19 using InternalFailure = 20 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 21 22 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 23 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 24 static constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 25 26 /** @brief get the D-Bus service and service path 27 * @param[in] bus - The Dbus bus object 28 * @param[in] interface - interface to the service 29 * @param[in] path - interested path in the list of objects 30 * @return pair of service path and service 31 */ 32 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus, 33 const std::string& interface, 34 const std::string& path) 35 { 36 auto depth = 0; 37 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 38 MAPPER_INTERFACE, "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.c_str()), 48 entry("INTERFACE=%s", interface.c_str())); 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.c_str()), 58 entry("INTERFACE=%s", interface.c_str())); 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>("Couldn't find D-Bus path", 72 entry("PATH=%s", path.c_str()), 73 entry("INTERFACE=%s", interface.c_str())); 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 134 enableScanning(&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 enableScanning(&response); 173 174 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 175 sensorInfo.sensorPath); 176 177 const auto& interfaceList = sensorInfo.propertyInterfaces; 178 179 for (const auto& interface : interfaceList) 180 { 181 for (const auto& property : interface.second) 182 { 183 auto propValue = 184 ipmi::getDbusProperty(bus, service, sensorInfo.sensorPath, 185 interface.first, property.first); 186 187 for (const auto& value : std::get<OffsetValueMap>(property.second)) 188 { 189 if (propValue == value.second.assert) 190 { 191 setReading(value.first, &response); 192 break; 193 } 194 } 195 } 196 } 197 198 return response; 199 } 200 201 } // namespace get 202 203 namespace set 204 { 205 206 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 207 const std::string& sensorPath, 208 const std::string& command, 209 const std::string& sensorInterface) 210 { 211 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 212 using namespace std::string_literals; 213 214 auto dbusService = getService(bus, sensorInterface, sensorPath); 215 216 return bus.new_method_call(dbusService.c_str(), sensorPath.c_str(), 217 updateInterface.c_str(), command.c_str()); 218 } 219 220 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo, 221 uint8_t data) 222 { 223 auto msg = 224 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath, 225 "Set", sensorInfo.sensorInterface); 226 227 const auto& interface = sensorInfo.propertyInterfaces.begin(); 228 msg.append(interface->first); 229 for (const auto& property : interface->second) 230 { 231 msg.append(property.first); 232 const auto& iter = std::get<OffsetValueMap>(property.second).find(data); 233 if (iter == std::get<OffsetValueMap>(property.second).end()) 234 { 235 log<level::ERR>("Invalid event data"); 236 return IPMI_CC_PARM_OUT_OF_RANGE; 237 } 238 msg.append(iter->second.assert); 239 } 240 return updateToDbus(msg); 241 } 242 243 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 244 { 245 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 246 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 247 auto bothSet = assertionSet ^ deassertionSet; 248 249 const auto& interface = sensorInfo.propertyInterfaces.begin(); 250 251 for (const auto& property : interface->second) 252 { 253 std::optional<Value> tmp; 254 for (const auto& value : std::get<OffsetValueMap>(property.second)) 255 { 256 if (bothSet.size() <= value.first || !bothSet.test(value.first)) 257 { 258 // A BIOS shouldn't do this but ignore if they do. 259 continue; 260 } 261 262 if (assertionSet.test(value.first)) 263 { 264 tmp = value.second.assert; 265 break; 266 } 267 if (deassertionSet.test(value.first)) 268 { 269 tmp = value.second.deassert; 270 break; 271 } 272 } 273 274 if (tmp) 275 { 276 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties", 277 sensorInfo.sensorPath, "Set", 278 sensorInfo.sensorInterface); 279 msg.append(interface->first); 280 msg.append(property.first); 281 msg.append(*tmp); 282 283 auto rc = updateToDbus(msg); 284 if (rc) 285 { 286 return rc; 287 } 288 } 289 } 290 291 return IPMI_CC_OK; 292 } 293 294 } // namespace set 295 296 namespace notify 297 { 298 299 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 300 const std::string& sensorPath, 301 const std::string& command, 302 const std::string& sensorInterface) 303 { 304 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 305 using namespace std::string_literals; 306 307 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 308 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 309 310 return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(), 311 updateInterface.c_str(), command.c_str()); 312 } 313 314 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 315 { 316 auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath, 317 "Notify", sensorInfo.sensorInterface); 318 319 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 320 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 321 ipmi::sensor::ObjectMap objects; 322 ipmi::sensor::InterfaceMap interfaces; 323 for (const auto& interface : sensorInfo.propertyInterfaces) 324 { 325 // An interface with no properties - It is possible that the sensor 326 // object on DBUS implements a DBUS interface with no properties. 327 // Make sure we add the interface to the list if interfaces on the 328 // object with an empty property map. 329 if (interface.second.empty()) 330 { 331 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{}); 332 continue; 333 } 334 // For a property like functional state the result will be 335 // calculated based on the true value of all conditions. 336 for (const auto& property : interface.second) 337 { 338 ipmi::sensor::PropertyMap props; 339 bool valid = false; 340 auto result = true; 341 for (const auto& value : std::get<OffsetValueMap>(property.second)) 342 { 343 if (assertionSet.test(value.first)) 344 { 345 // Skip update if skipOn is ASSERT 346 if (SkipAssertion::ASSERT == value.second.skip) 347 { 348 return IPMI_CC_OK; 349 } 350 result = result && std::get<bool>(value.second.assert); 351 valid = true; 352 } 353 else if (deassertionSet.test(value.first)) 354 { 355 // Skip update if skipOn is DEASSERT 356 if (SkipAssertion::DEASSERT == value.second.skip) 357 { 358 return IPMI_CC_OK; 359 } 360 result = result && std::get<bool>(value.second.deassert); 361 valid = true; 362 } 363 } 364 for (const auto& value : 365 std::get<PreReqOffsetValueMap>(property.second)) 366 { 367 if (assertionSet.test(value.first)) 368 { 369 result = result && std::get<bool>(value.second.assert); 370 } 371 else if (deassertionSet.test(value.first)) 372 { 373 result = result && std::get<bool>(value.second.deassert); 374 } 375 } 376 if (valid) 377 { 378 props.emplace(property.first, result); 379 interfaces.emplace(interface.first, std::move(props)); 380 } 381 } 382 } 383 384 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 385 msg.append(std::move(objects)); 386 return updateToDbus(msg); 387 } 388 389 } // namespace notify 390 391 namespace inventory 392 { 393 394 namespace get 395 { 396 397 GetSensorResponse assertion(const Info& sensorInfo) 398 { 399 namespace fs = std::filesystem; 400 401 fs::path path{ipmi::sensor::inventoryRoot}; 402 path += sensorInfo.sensorPath; 403 404 return ipmi::sensor::get::mapDbusToAssertion( 405 sensorInfo, path.string(), 406 sensorInfo.propertyInterfaces.begin()->first); 407 } 408 409 } // namespace get 410 411 } // namespace inventory 412 } // namespace sensor 413 } // namespace ipmi 414