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_ret_t 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 log<level::ERR>("Error in D-Bus call", entry("ERROR=%s", e.what())); 44 commit<InternalFailure>(); 45 return IPMI_CC_UNSPECIFIED_ERROR; 46 } 47 return IPMI_CC_OK; 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& interface : interfaceList) 81 { 82 for (const auto& property : interface.second) 83 { 84 auto propValue = ipmi::getDbusProperty( 85 bus, service, path, interface.first, property.first); 86 87 for (const auto& value : std::get<OffsetValueMap>(property.second)) 88 { 89 if (propValue == value.second.assert) 90 { 91 setOffset(value.first, &response); 92 break; 93 } 94 } 95 } 96 } 97 98 return response; 99 } 100 101 GetSensorResponse mapDbusToEventdata2(const Info& sensorInfo) 102 { 103 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 104 GetSensorResponse response{}; 105 106 enableScanning(&response); 107 108 auto service = ipmi::getService(bus, sensorInfo.sensorInterface, 109 sensorInfo.sensorPath); 110 111 const auto& interfaceList = sensorInfo.propertyInterfaces; 112 113 for (const auto& interface : interfaceList) 114 { 115 for (const auto& property : interface.second) 116 { 117 auto propValue = 118 ipmi::getDbusProperty(bus, service, sensorInfo.sensorPath, 119 interface.first, property.first); 120 121 for (const auto& value : std::get<OffsetValueMap>(property.second)) 122 { 123 if (propValue == value.second.assert) 124 { 125 setReading(value.first, &response); 126 break; 127 } 128 } 129 } 130 } 131 132 return response; 133 } 134 135 #ifndef FEATURE_SENSORS_CACHE 136 GetSensorResponse assertion(const Info& sensorInfo) 137 { 138 return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 139 sensorInfo.sensorInterface); 140 } 141 142 GetSensorResponse eventdata2(const Info& sensorInfo) 143 { 144 return mapDbusToEventdata2(sensorInfo); 145 } 146 #else 147 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 148 const PropertyMap& /*properties*/) 149 { 150 // The assertion may contain multiple properties 151 // So we have to get the properties from DBus anyway 152 auto response = mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath, 153 sensorInfo.sensorInterface); 154 155 if (!sensorCacheMap[id].has_value()) 156 { 157 sensorCacheMap[id] = SensorData{}; 158 } 159 sensorCacheMap[id]->response = response; 160 return response; 161 } 162 163 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo, 164 const PropertyMap& /*properties*/) 165 { 166 // The eventdata2 may contain multiple properties 167 // So we have to get the properties from DBus anyway 168 auto response = mapDbusToEventdata2(sensorInfo); 169 170 if (!sensorCacheMap[id].has_value()) 171 { 172 sensorCacheMap[id] = SensorData{}; 173 } 174 sensorCacheMap[id]->response = response; 175 return response; 176 } 177 178 #endif // FEATURE_SENSORS_CACHE 179 180 } // namespace get 181 182 namespace set 183 { 184 185 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 186 const std::string& sensorPath, 187 const std::string& command, 188 const std::string& sensorInterface) 189 { 190 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 191 using namespace std::string_literals; 192 193 auto dbusService = getService(bus, sensorInterface, sensorPath); 194 195 return bus.new_method_call(dbusService.c_str(), sensorPath.c_str(), 196 updateInterface.c_str(), command.c_str()); 197 } 198 199 ipmi_ret_t eventdata(const SetSensorReadingReq&, const Info& sensorInfo, 200 uint8_t data) 201 { 202 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties", 203 sensorInfo.sensorPath, "Set", 204 sensorInfo.sensorInterface); 205 206 const auto& interface = sensorInfo.propertyInterfaces.begin(); 207 msg.append(interface->first); 208 for (const auto& property : interface->second) 209 { 210 msg.append(property.first); 211 const auto& iter = std::get<OffsetValueMap>(property.second).find(data); 212 if (iter == std::get<OffsetValueMap>(property.second).end()) 213 { 214 log<level::ERR>("Invalid event data"); 215 return IPMI_CC_PARM_OUT_OF_RANGE; 216 } 217 msg.append(iter->second.assert); 218 } 219 return updateToDbus(msg); 220 } 221 222 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 223 { 224 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 225 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 226 auto bothSet = assertionSet ^ deassertionSet; 227 228 const auto& interface = sensorInfo.propertyInterfaces.begin(); 229 230 for (const auto& property : interface->second) 231 { 232 std::optional<Value> tmp; 233 for (const auto& value : std::get<OffsetValueMap>(property.second)) 234 { 235 if (bothSet.size() <= value.first || !bothSet.test(value.first)) 236 { 237 // A BIOS shouldn't do this but ignore if they do. 238 continue; 239 } 240 241 if (assertionSet.test(value.first)) 242 { 243 tmp = value.second.assert; 244 break; 245 } 246 if (deassertionSet.test(value.first)) 247 { 248 tmp = value.second.deassert; 249 break; 250 } 251 } 252 253 if (tmp) 254 { 255 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties", 256 sensorInfo.sensorPath, "Set", 257 sensorInfo.sensorInterface); 258 msg.append(interface->first); 259 msg.append(property.first); 260 msg.append(*tmp); 261 262 auto rc = updateToDbus(msg); 263 if (rc) 264 { 265 return rc; 266 } 267 } 268 } 269 270 return IPMI_CC_OK; 271 } 272 273 } // namespace set 274 275 namespace notify 276 { 277 278 IpmiUpdateData makeDbusMsg(const std::string& updateInterface, 279 const std::string&, const std::string& command, 280 const std::string&) 281 { 282 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 283 using namespace std::string_literals; 284 285 static const auto dbusPath = "/xyz/openbmc_project/inventory"s; 286 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath); 287 288 return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(), 289 updateInterface.c_str(), command.c_str()); 290 } 291 292 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo) 293 { 294 auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath, 295 "Notify", sensorInfo.sensorInterface); 296 297 std::bitset<16> assertionSet(getAssertionSet(cmdData).first); 298 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second); 299 ipmi::sensor::ObjectMap objects; 300 ipmi::sensor::InterfaceMap interfaces; 301 for (const auto& interface : sensorInfo.propertyInterfaces) 302 { 303 // An interface with no properties - It is possible that the sensor 304 // object on DBUS implements a DBUS interface with no properties. 305 // Make sure we add the interface to the list if interfaces on the 306 // object with an empty property map. 307 if (interface.second.empty()) 308 { 309 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{}); 310 continue; 311 } 312 // For a property like functional state the result will be 313 // calculated based on the true value of all conditions. 314 for (const auto& property : interface.second) 315 { 316 ipmi::sensor::PropertyMap props; 317 bool valid = false; 318 auto result = true; 319 for (const auto& value : std::get<OffsetValueMap>(property.second)) 320 { 321 if (assertionSet.test(value.first)) 322 { 323 // Skip update if skipOn is ASSERT 324 if (SkipAssertion::ASSERT == value.second.skip) 325 { 326 return IPMI_CC_OK; 327 } 328 result = result && std::get<bool>(value.second.assert); 329 valid = true; 330 } 331 else if (deassertionSet.test(value.first)) 332 { 333 // Skip update if skipOn is DEASSERT 334 if (SkipAssertion::DEASSERT == value.second.skip) 335 { 336 return IPMI_CC_OK; 337 } 338 result = result && std::get<bool>(value.second.deassert); 339 valid = true; 340 } 341 } 342 for (const auto& value : 343 std::get<PreReqOffsetValueMap>(property.second)) 344 { 345 if (assertionSet.test(value.first)) 346 { 347 result = result && std::get<bool>(value.second.assert); 348 } 349 else if (deassertionSet.test(value.first)) 350 { 351 result = result && std::get<bool>(value.second.deassert); 352 } 353 } 354 if (valid) 355 { 356 props.emplace(property.first, result); 357 interfaces.emplace(interface.first, std::move(props)); 358 } 359 } 360 } 361 362 objects.emplace(sensorInfo.sensorPath, std::move(interfaces)); 363 msg.append(std::move(objects)); 364 return updateToDbus(msg); 365 } 366 367 } // namespace notify 368 369 namespace inventory 370 { 371 372 namespace get 373 { 374 375 #ifndef FEATURE_SENSORS_CACHE 376 377 GetSensorResponse assertion(const Info& sensorInfo) 378 { 379 namespace fs = std::filesystem; 380 381 fs::path path{ipmi::sensor::inventoryRoot}; 382 path += sensorInfo.sensorPath; 383 384 return ipmi::sensor::get::mapDbusToAssertion( 385 sensorInfo, path.string(), 386 sensorInfo.propertyInterfaces.begin()->first); 387 } 388 389 #else 390 391 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo, 392 const PropertyMap& /*properties*/) 393 { 394 // The assertion may contain multiple properties 395 // So we have to get the properties from DBus anyway 396 namespace fs = std::filesystem; 397 398 fs::path path{ipmi::sensor::inventoryRoot}; 399 path += sensorInfo.sensorPath; 400 401 auto response = ipmi::sensor::get::mapDbusToAssertion( 402 sensorInfo, path.string(), 403 sensorInfo.propertyInterfaces.begin()->first); 404 405 if (!sensorCacheMap[id].has_value()) 406 { 407 sensorCacheMap[id] = SensorData{}; 408 } 409 sensorCacheMap[id]->response = response; 410 return response; 411 } 412 413 #endif 414 415 } // namespace get 416 417 } // namespace inventory 418 } // namespace sensor 419 } // namespace ipmi 420