1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include "dbus-sdr/sdrutils.hpp" 18 19 #include <optional> 20 #include <unordered_set> 21 22 #ifdef FEATURE_HYBRID_SENSORS 23 24 #include <ipmid/utils.hpp> 25 namespace ipmi 26 { 27 namespace sensor 28 { 29 extern const IdInfoMap sensors; 30 } // namespace sensor 31 } // namespace ipmi 32 33 #endif 34 35 namespace details 36 { 37 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree) 38 { 39 static std::shared_ptr<SensorSubTree> sensorTreePtr; 40 static uint16_t sensorUpdatedIndex = 0; 41 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 42 static sdbusplus::bus::match_t sensorAdded( 43 *dbus, 44 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 45 "sensors/'", 46 [](sdbusplus::message_t&) { sensorTreePtr.reset(); }); 47 48 static sdbusplus::bus::match_t sensorRemoved( 49 *dbus, 50 "type='signal',member='InterfacesRemoved',arg0path='/xyz/" 51 "openbmc_project/sensors/'", 52 [](sdbusplus::message_t&) { sensorTreePtr.reset(); }); 53 54 if (sensorTreePtr) 55 { 56 subtree = sensorTreePtr; 57 return sensorUpdatedIndex; 58 } 59 60 sensorTreePtr = std::make_shared<SensorSubTree>(); 61 62 static constexpr const int32_t depth = 2; 63 64 auto lbdUpdateSensorTree = [&dbus](const char* path, 65 const auto& interfaces) { 66 auto mapperCall = dbus->new_method_call( 67 "xyz.openbmc_project.ObjectMapper", 68 "/xyz/openbmc_project/object_mapper", 69 "xyz.openbmc_project.ObjectMapper", "GetSubTree"); 70 SensorSubTree sensorTreePartial; 71 72 mapperCall.append(path, depth, interfaces); 73 74 try 75 { 76 auto mapperReply = dbus->call(mapperCall); 77 mapperReply.read(sensorTreePartial); 78 } 79 catch (const sdbusplus::exception_t& e) 80 { 81 phosphor::logging::log<phosphor::logging::level::ERR>( 82 "fail to update subtree", 83 phosphor::logging::entry("PATH=%s", path), 84 phosphor::logging::entry("WHAT=%s", e.what())); 85 return false; 86 } 87 if constexpr (debug) 88 { 89 std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n", 90 sensorTreePartial.size(), path); 91 } 92 sensorTreePtr->merge(std::move(sensorTreePartial)); 93 return true; 94 }; 95 96 // Add sensors to SensorTree 97 static constexpr const std::array sensorInterfaces = { 98 "xyz.openbmc_project.Sensor.Value", 99 "xyz.openbmc_project.Sensor.ValueMutability", 100 "xyz.openbmc_project.Sensor.Threshold.Warning", 101 "xyz.openbmc_project.Sensor.Threshold.Critical"}; 102 static constexpr const std::array vrInterfaces = { 103 "xyz.openbmc_project.Control.VoltageRegulatorMode"}; 104 105 bool sensorRez = lbdUpdateSensorTree("/xyz/openbmc_project/sensors", 106 sensorInterfaces); 107 108 #ifdef FEATURE_HYBRID_SENSORS 109 110 if (!ipmi::sensor::sensors.empty()) 111 { 112 for (const auto& sensor : ipmi::sensor::sensors) 113 { 114 // Threshold sensors should not be emplaced in here. 115 if (boost::starts_with(sensor.second.sensorPath, 116 "/xyz/openbmc_project/sensors/")) 117 { 118 continue; 119 } 120 121 // The bus service name is not listed in ipmi::sensor::Info. Give it 122 // an empty string. For those function using non-threshold sensors, 123 // the bus service name will be retrieved in an alternative way. 124 boost::container::flat_map<std::string, std::vector<std::string>> 125 connectionMap{ 126 {"", {sensor.second.propertyInterfaces.begin()->first}}}; 127 sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap); 128 } 129 } 130 131 #endif 132 133 // Error if searching for sensors failed. 134 if (!sensorRez) 135 { 136 return sensorUpdatedIndex; 137 } 138 139 // Add VR control as optional search path. 140 (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces); 141 142 subtree = sensorTreePtr; 143 sensorUpdatedIndex++; 144 // The SDR is being regenerated, wipe the old stats 145 sdrStatsTable.wipeTable(); 146 sdrWriteTable.wipeTable(); 147 return sensorUpdatedIndex; 148 } 149 150 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap) 151 { 152 static std::shared_ptr<SensorNumMap> sensorNumMapPtr; 153 bool sensorNumMapUpated = false; 154 static uint16_t prevSensorUpdatedIndex = 0; 155 std::shared_ptr<SensorSubTree> sensorTree; 156 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree); 157 if (!sensorTree) 158 { 159 return sensorNumMapUpated; 160 } 161 162 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr) 163 { 164 sensorNumMap = sensorNumMapPtr; 165 return sensorNumMapUpated; 166 } 167 prevSensorUpdatedIndex = curSensorUpdatedIndex; 168 169 sensorNumMapPtr = std::make_shared<SensorNumMap>(); 170 171 uint16_t sensorNum = 0; 172 uint16_t sensorIndex = 0; 173 for (const auto& sensor : *sensorTree) 174 { 175 sensorNumMapPtr->insert( 176 SensorNumMap::value_type(sensorNum, sensor.first)); 177 sensorIndex++; 178 if (sensorIndex == maxSensorsPerLUN) 179 { 180 sensorIndex = lun1Sensor0; 181 } 182 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN)) 183 { 184 // Skip assigning LUN 0x2 any sensors 185 sensorIndex = lun3Sensor0; 186 } 187 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN)) 188 { 189 // this is an error, too many IPMI sensors 190 throw std::out_of_range("Maximum number of IPMI sensors exceeded."); 191 } 192 sensorNum = sensorIndex; 193 } 194 sensorNumMap = sensorNumMapPtr; 195 sensorNumMapUpated = true; 196 return sensorNumMapUpated; 197 } 198 } // namespace details 199 200 bool getSensorSubtree(SensorSubTree& subtree) 201 { 202 std::shared_ptr<SensorSubTree> sensorTree; 203 details::getSensorSubtree(sensorTree); 204 if (!sensorTree) 205 { 206 return false; 207 } 208 209 subtree = *sensorTree; 210 return true; 211 } 212 213 #ifdef FEATURE_HYBRID_SENSORS 214 // Static sensors are listed in sensor-gen.cpp. 215 ipmi::sensor::IdInfoMap::const_iterator 216 findStaticSensor(const std::string& path) 217 { 218 return std::find_if( 219 ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(), 220 [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) { 221 return findSensor.second.sensorPath == path; 222 }); 223 } 224 #endif 225 226 std::string getSensorTypeStringFromPath(const std::string& path) 227 { 228 // get sensor type string from path, path is defined as 229 // /xyz/openbmc_project/sensors/<type>/label 230 size_t typeEnd = path.rfind("/"); 231 if (typeEnd == std::string::npos) 232 { 233 return path; 234 } 235 size_t typeStart = path.rfind("/", typeEnd - 1); 236 if (typeStart == std::string::npos) 237 { 238 return path; 239 } 240 // Start at the character after the '/' 241 typeStart++; 242 return path.substr(typeStart, typeEnd - typeStart); 243 } 244 245 uint8_t getSensorTypeFromPath(const std::string& path) 246 { 247 uint8_t sensorType = 0; 248 std::string type = getSensorTypeStringFromPath(path); 249 auto findSensor = sensorTypes.find(type.c_str()); 250 if (findSensor != sensorTypes.end()) 251 { 252 sensorType = 253 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second)); 254 } // else default 0x0 RESERVED 255 256 return sensorType; 257 } 258 259 uint16_t getSensorNumberFromPath(const std::string& path) 260 { 261 std::shared_ptr<SensorNumMap> sensorNumMapPtr; 262 details::getSensorNumMap(sensorNumMapPtr); 263 if (!sensorNumMapPtr) 264 { 265 return invalidSensorNumber; 266 } 267 268 try 269 { 270 return sensorNumMapPtr->right.at(path); 271 } 272 catch (const std::out_of_range& e) 273 { 274 return invalidSensorNumber; 275 } 276 } 277 278 uint8_t getSensorEventTypeFromPath(const std::string& path) 279 { 280 uint8_t sensorEventType = 0; 281 std::string type = getSensorTypeStringFromPath(path); 282 auto findSensor = sensorTypes.find(type.c_str()); 283 if (findSensor != sensorTypes.end()) 284 { 285 sensorEventType = static_cast<uint8_t>( 286 std::get<sensorEventTypeCodes>(findSensor->second)); 287 } 288 289 return sensorEventType; 290 } 291 292 std::string getPathFromSensorNumber(uint16_t sensorNum) 293 { 294 std::shared_ptr<SensorNumMap> sensorNumMapPtr; 295 details::getSensorNumMap(sensorNumMapPtr); 296 if (!sensorNumMapPtr) 297 { 298 return std::string(); 299 } 300 301 try 302 { 303 return sensorNumMapPtr->left.at(sensorNum); 304 } 305 catch (const std::out_of_range& e) 306 { 307 return std::string(); 308 } 309 } 310 311 namespace ipmi 312 { 313 314 std::map<std::string, std::vector<std::string>> 315 getObjectInterfaces(const char* path) 316 { 317 std::map<std::string, std::vector<std::string>> interfacesResponse; 318 std::vector<std::string> interfaces; 319 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 320 321 sdbusplus::message_t getObjectMessage = 322 dbus->new_method_call("xyz.openbmc_project.ObjectMapper", 323 "/xyz/openbmc_project/object_mapper", 324 "xyz.openbmc_project.ObjectMapper", "GetObject"); 325 getObjectMessage.append(path, interfaces); 326 327 try 328 { 329 sdbusplus::message_t response = dbus->call(getObjectMessage); 330 response.read(interfacesResponse); 331 } 332 catch (const std::exception& e) 333 { 334 phosphor::logging::log<phosphor::logging::level::ERR>( 335 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path), 336 phosphor::logging::entry("WHAT=%s", e.what())); 337 } 338 339 return interfacesResponse; 340 } 341 342 std::map<std::string, Value> getEntityManagerProperties(const char* path, 343 const char* interface) 344 { 345 std::map<std::string, Value> properties; 346 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 347 348 sdbusplus::message_t getProperties = 349 dbus->new_method_call("xyz.openbmc_project.EntityManager", path, 350 "org.freedesktop.DBus.Properties", "GetAll"); 351 getProperties.append(interface); 352 353 try 354 { 355 sdbusplus::message_t response = dbus->call(getProperties); 356 response.read(properties); 357 } 358 catch (const std::exception& e) 359 { 360 phosphor::logging::log<phosphor::logging::level::ERR>( 361 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path), 362 phosphor::logging::entry("INTF=%s", interface), 363 phosphor::logging::entry("WHAT=%s", e.what())); 364 } 365 366 return properties; 367 } 368 369 // Fetch the ipmiDecoratorPaths to get the list of dbus objects that 370 // have ipmi decorator to prevent unnessary dbus call to fetch the info 371 std::optional<std::unordered_set<std::string>>& 372 getIpmiDecoratorPaths(const std::optional<ipmi::Context::ptr>& ctx) 373 { 374 static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths; 375 376 if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt) 377 { 378 return ipmiDecoratorPaths; 379 } 380 381 boost::system::error_code ec; 382 std::vector<std::string> paths = 383 (*ctx)->bus->yield_method_call<std::vector<std::string>>( 384 (*ctx)->yield, ec, "xyz.openbmc_project.ObjectMapper", 385 "/xyz/openbmc_project/object_mapper", 386 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 387 int32_t(0), 388 std::array<const char*, 1>{ 389 "xyz.openbmc_project.Inventory.Decorator.Ipmi"}); 390 if (ec) 391 { 392 return ipmiDecoratorPaths; 393 } 394 395 ipmiDecoratorPaths = std::unordered_set<std::string>(paths.begin(), 396 paths.end()); 397 return ipmiDecoratorPaths; 398 } 399 400 const std::string* getSensorConfigurationInterface( 401 const std::map<std::string, std::vector<std::string>>& 402 sensorInterfacesResponse) 403 { 404 auto entityManagerService = 405 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager"); 406 if (entityManagerService == sensorInterfacesResponse.end()) 407 { 408 return nullptr; 409 } 410 411 // Find the fan configuration first (fans can have multiple configuration 412 // interfaces). 413 for (const auto& entry : entityManagerService->second) 414 { 415 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" || 416 entry == "xyz.openbmc_project.Configuration.I2CFan" || 417 entry == "xyz.openbmc_project.Configuration.NuvotonFan") 418 { 419 return &entry; 420 } 421 } 422 423 for (const auto& entry : entityManagerService->second) 424 { 425 if (boost::algorithm::starts_with(entry, 426 "xyz.openbmc_project.Configuration.")) 427 { 428 return &entry; 429 } 430 } 431 432 return nullptr; 433 } 434 435 // Follow Association properties for Sensor back to the Board dbus object to 436 // check for an EntityId and EntityInstance property. 437 void updateIpmiFromAssociation( 438 const std::string& path, 439 const std::unordered_set<std::string>& ipmiDecoratorPaths, 440 const DbusInterfaceMap& sensorMap, uint8_t& entityId, 441 uint8_t& entityInstance) 442 { 443 namespace fs = std::filesystem; 444 445 auto sensorAssociationObject = 446 sensorMap.find("xyz.openbmc_project.Association.Definitions"); 447 if (sensorAssociationObject == sensorMap.end()) 448 { 449 if constexpr (debug) 450 { 451 std::fprintf(stderr, "path=%s, no association interface found\n", 452 path.c_str()); 453 } 454 455 return; 456 } 457 458 auto associationObject = 459 sensorAssociationObject->second.find("Associations"); 460 if (associationObject == sensorAssociationObject->second.end()) 461 { 462 if constexpr (debug) 463 { 464 std::fprintf(stderr, "path=%s, no association records found\n", 465 path.c_str()); 466 } 467 468 return; 469 } 470 471 std::vector<Association> associationValues = 472 std::get<std::vector<Association>>(associationObject->second); 473 474 // loop through the Associations looking for the right one: 475 for (const auto& entry : associationValues) 476 { 477 // forward, reverse, endpoint 478 const std::string& forward = std::get<0>(entry); 479 const std::string& reverse = std::get<1>(entry); 480 const std::string& endpoint = std::get<2>(entry); 481 482 // We only currently concern ourselves with chassis+all_sensors. 483 if (!(forward == "chassis" && reverse == "all_sensors")) 484 { 485 continue; 486 } 487 488 // the endpoint is the board entry provided by 489 // Entity-Manager. so let's grab its properties if it has 490 // the right interface. 491 492 // just try grabbing the properties first. 493 ipmi::PropertyMap::iterator entityIdProp; 494 ipmi::PropertyMap::iterator entityInstanceProp; 495 if (ipmiDecoratorPaths.contains(endpoint)) 496 { 497 std::map<std::string, Value> ipmiProperties = 498 getEntityManagerProperties( 499 endpoint.c_str(), 500 "xyz.openbmc_project.Inventory.Decorator.Ipmi"); 501 502 entityIdProp = ipmiProperties.find("EntityId"); 503 entityInstanceProp = ipmiProperties.find("EntityInstance"); 504 if (entityIdProp != ipmiProperties.end()) 505 { 506 entityId = static_cast<uint8_t>( 507 std::get<uint64_t>(entityIdProp->second)); 508 } 509 if (entityInstanceProp != ipmiProperties.end()) 510 { 511 entityInstance = static_cast<uint8_t>( 512 std::get<uint64_t>(entityInstanceProp->second)); 513 } 514 } 515 516 // Now check the entity-manager entry for this sensor to see 517 // if it has its own value and use that instead. 518 // 519 // In theory, checking this first saves us from checking 520 // both, except in most use-cases identified, there won't be 521 // a per sensor override, so we need to always check both. 522 std::string sensorNameFromPath = fs::path(path).filename(); 523 524 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath; 525 526 // Download the interfaces for the sensor from 527 // Entity-Manager to find the name of the configuration 528 // interface. 529 std::map<std::string, std::vector<std::string>> 530 sensorInterfacesResponse = 531 getObjectInterfaces(sensorConfigPath.c_str()); 532 533 const std::string* configurationInterface = 534 getSensorConfigurationInterface(sensorInterfacesResponse); 535 536 // If there are multi association path settings and only one path exist, 537 // we need to continue if cannot find configuration interface for this 538 // sensor. 539 if (!configurationInterface) 540 { 541 continue; 542 } 543 544 // We found a configuration interface. 545 std::map<std::string, Value> configurationProperties = 546 getEntityManagerProperties(sensorConfigPath.c_str(), 547 configurationInterface->c_str()); 548 549 entityIdProp = configurationProperties.find("EntityId"); 550 entityInstanceProp = configurationProperties.find("EntityInstance"); 551 if (entityIdProp != configurationProperties.end()) 552 { 553 entityId = 554 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second)); 555 } 556 if (entityInstanceProp != configurationProperties.end()) 557 { 558 entityInstance = static_cast<uint8_t>( 559 std::get<uint64_t>(entityInstanceProp->second)); 560 } 561 562 // stop searching Association records. 563 break; 564 } // end for Association vectors. 565 566 if constexpr (debug) 567 { 568 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n", 569 path.c_str(), entityId, entityInstance); 570 } 571 } 572 573 } // namespace ipmi 574