1 #include "sensordatahandler.hpp" 2 3 #include "sensorhandler.hpp" 4 5 #include <ipmid/types.hpp> 6 #include <ipmid/utils.hpp> 7 #include <sdbusplus/message/types.hpp> 8 #include <xyz/openbmc_project/Common/error.hpp> 9 10 #include <bitset> 11 #include <filesystem> 12 #include <optional> 13 14 namespace ipmi 15 { 16 namespace sensor 17 { 18 19 using namespace phosphor::logging; 20 using InternalFailure = 21 sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 22 23 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData) 24 { 25 Assertion assertionStates = 26 (static_cast<Assertion>(cmdData.assertOffset8_14)) << 8 | 27 cmdData.assertOffset0_7; 28 Deassertion deassertionStates = 29 (static_cast<Deassertion>(cmdData.deassertOffset8_14)) << 8 | 30 cmdData.deassertOffset0_7; 31 return std::make_pair(assertionStates, deassertionStates); 32 } 33 34 ipmi::Cc updateToDbus(IpmiUpdateData& msg) 35 { 36 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 37 try 38 { 39 auto serviceResponseMsg = bus.call(msg); 40 } 41 catch (const InternalFailure& e) 42 { 43 lg2::error("Error in D-Bus call: {ERROR}", "ERROR", e); 44 commit<InternalFailure>(); 45 return ipmi::ccUnspecifiedError; 46 } 47 return ipmi::ccSuccess; 48 } 49 50 namespace get 51 { 52 53 SensorName nameParentLeaf(const Info& sensorInfo) 54 { 55 const auto pos = sensorInfo.sensorPath.find_last_of('/'); 56 const auto leaf = sensorInfo.sensorPath.substr(pos + 1); 57 58 const auto remaining = sensorInfo.sensorPath.substr(0, pos); 59 60 const auto parentPos = remaining.find_last_of('/'); 61 auto parent = remaining.substr(parentPos + 1); 62 63 parent += "_" + leaf; 64 return parent; 65 } 66 67 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo, 68 const InstancePath& path, 69 const DbusInterface& interface) 70 { 71 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 72 GetSensorResponse response{}; 73 74 enableScanning(response); 75 76 auto service = ipmi::getService(bus, interface, path); 77 78 const auto& interfaceList = sensorInfo.propertyInterfaces; 79 80 for (const auto& [intf, propertyMap] : interfaceList) 81 { 82 for (const auto& [property, values] : propertyMap) 83 { 84 try 85 { 86 auto propValue = 87 ipmi::getDbusProperty(bus, service, path, intf, property); 88 89 for (const auto& value : std::get<OffsetValueMap>(values)) 90 { 91 if (propValue == value.second.assert) 92 { 93 setOffset(value.first, response); 94 break; 95 } 96 } 97 } 98 catch (const std::exception& e) 99 { 100 lg2::error( 101 "mapDbusToAssertion: Failed to get property, service: {SERVICE}," 102 " path: {PATH}, interface: {INTERFACE}, property name: {PRONAME}: {ERROR}", 103 "SERVICE", service, "PATH", path, "INTERFACE", intf, 104 "PRONAME", property, "ERROR", e); 105 } 106 } 107 } 108 109 return response; 110 } 111 112 GetSensorResponse mapDbusToEventdata2(const Info& sensorInfo) 113 { 114 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 115 GetSensorResponse response{}; 116 117 enableScanning(response); 118 119 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 120 sensorInfo.sensorPath); 121 122 const auto& interfaceList = sensorInfo.propertyInterfaces; 123 124 for (const auto& [intf, propertyMap] : interfaceList) 125 { 126 for (const auto& [property, values] : propertyMap) 127 { 128 try 129 { 130 auto propValue = ipmi::getDbusProperty( 131 bus, service, sensorInfo.sensorPath, intf, property); 132 133 for (const auto& value : std::get<OffsetValueMap>(values)) 134 { 135 if (propValue == value.second.assert) 136 { 137 setReading(value.first, response); 138 break; 139 } 140 } 141 } 142 catch (const std::exception& e) 143 { 144 lg2::error( 145 "mapDbusToEventdata2: Failed to get property, service: {SERVICE}," 146 " path: {PATH}, interface: {INTERFACE}, property name: {PRONAME}: {ERROR}", 147 "SERVICE", service, "PATH", sensorInfo.sensorPath, 148 "INTERFACE", intf, "PRONAME", property, "ERROR", e); 149 } 150 } 151 } 152 153 return response; 154 } 155 156 #ifndef FEATURE_SENSORS_CACHE 157 GetSensorResponse assertion(const Info& sensorInfo) 158 { 159 return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 160 sensorInfo.sensorInterface); 161 } 162 163 GetSensorResponse eventdata2(const Info& sensorInfo) 164 { 165 return mapDbusToEventdata2(sensorInfo); 166 } 167 #else 168 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 169 const PropertyMap& /*properties*/) 170 { 171 // The assertion may contain multiple properties 172 // So we have to get the properties from DBus anyway 173 auto response = mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 174 sensorInfo.sensorInterface); 175 176 if (!sensorCacheMap[id].has_value()) 177 { 178 sensorCacheMap[id] = SensorData{}; 179 } 180 sensorCacheMap[id]->response = response; 181 return response; 182 } 183 184 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo, 185 const PropertyMap& /*properties*/) 186 { 187 // The eventdata2 may contain multiple properties 188 // So we have to get the properties from DBus anyway 189 auto response = mapDbusToEventdata2(sensorInfo); 190 191 if (!sensorCacheMap[id].has_value()) 192 { 193 sensorCacheMap[id] = SensorData{}; 194 } 195 sensorCacheMap[id]->response = response; 196 return response; 197 } 198 199 #endif // FEATURE_SENSORS_CACHE 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_t 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::Cc eventdata(const SetSensorReadingReq&, 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 lg2::error("Invalid event data"); 236 return ipmi::ccParmOutOfRange; 237 } 238 msg.append(iter->second.assert); 239 } 240 return updateToDbus(msg); 241 } 242 243 ipmi::Cc 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::ccSuccess; 292 } 293 294 } // namespace set 295 296 namespace notify 297 { 298 299 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 300 const std::string&, const std::string& command, 301 const std::string&) 302 { 303 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 304 using namespace std::string_literals; 305 306 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 307 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 308 309 return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(), 310 updateInterface.c_str(), command.c_str()); 311 } 312 313 ipmi::Cc assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 314 { 315 auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath, 316 "Notify", sensorInfo.sensorInterface); 317 318 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 319 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 320 ipmi::sensor::ObjectMap objects; 321 ipmi::sensor::InterfaceMap interfaces; 322 for (const auto& interface : sensorInfo.propertyInterfaces) 323 { 324 // An interface with no properties - It is possible that the sensor 325 // object on DBUS implements a DBUS interface with no properties. 326 // Make sure we add the interface to the list if interfaces on the 327 // object with an empty property map. 328 if (interface.second.empty()) 329 { 330 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{}); 331 continue; 332 } 333 // For a property like functional state the result will be 334 // calculated based on the true value of all conditions. 335 for (const auto& property : interface.second) 336 { 337 ipmi::sensor::PropertyMap props; 338 bool valid = false; 339 auto result = true; 340 for (const auto& value : std::get<OffsetValueMap>(property.second)) 341 { 342 if (assertionSet.test(value.first)) 343 { 344 // Skip update if skipOn is ASSERT 345 if (SkipAssertion::ASSERT == value.second.skip) 346 { 347 return ipmi::ccSuccess; 348 } 349 result = result && std::get<bool>(value.second.assert); 350 valid = true; 351 } 352 else if (deassertionSet.test(value.first)) 353 { 354 // Skip update if skipOn is DEASSERT 355 if (SkipAssertion::DEASSERT == value.second.skip) 356 { 357 return ipmi::ccSuccess; 358 } 359 result = result && std::get<bool>(value.second.deassert); 360 valid = true; 361 } 362 } 363 for (const auto& value : 364 std::get<PreReqOffsetValueMap>(property.second)) 365 { 366 if (assertionSet.test(value.first)) 367 { 368 result = result && std::get<bool>(value.second.assert); 369 } 370 else if (deassertionSet.test(value.first)) 371 { 372 result = result && std::get<bool>(value.second.deassert); 373 } 374 } 375 if (valid) 376 { 377 props.emplace(property.first, result); 378 interfaces.emplace(interface.first, std::move(props)); 379 } 380 } 381 } 382 383 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 384 msg.append(std::move(objects)); 385 return updateToDbus(msg); 386 } 387 388 } // namespace notify 389 390 namespace inventory 391 { 392 393 namespace get 394 { 395 396 #ifndef FEATURE_SENSORS_CACHE 397 398 GetSensorResponse assertion(const Info& sensorInfo) 399 { 400 namespace fs = std::filesystem; 401 402 fs::path path{ipmi::sensor::inventoryRoot}; 403 path += sensorInfo.sensorPath; 404 405 return ipmi::sensor::get::mapDbusToAssertion( 406 sensorInfo, path.string(), 407 sensorInfo.propertyInterfaces.begin()->first); 408 } 409 410 #else 411 412 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 413 const PropertyMap& /*properties*/) 414 { 415 // The assertion may contain multiple properties 416 // So we have to get the properties from DBus anyway 417 namespace fs = std::filesystem; 418 419 fs::path path{ipmi::sensor::inventoryRoot}; 420 path += sensorInfo.sensorPath; 421 422 auto response = ipmi::sensor::get::mapDbusToAssertion( 423 sensorInfo, path.string(), 424 sensorInfo.propertyInterfaces.begin()->first); 425 426 if (!sensorCacheMap[id].has_value()) 427 { 428 sensorCacheMap[id] = SensorData{}; 429 } 430 sensorCacheMap[id]->response = response; 431 return response; 432 } 433 434 #endif 435 436 } // namespace get 437 438 } // namespace inventory 439 } // namespace sensor 440 } // namespace ipmi 441