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 = 106 lbdUpdateSensorTree("/xyz/openbmc_project/sensors", 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 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 275 return invalidSensorNumber; 276 } 277 } 278 279 uint8_t getSensorEventTypeFromPath(const std::string& path) 280 { 281 uint8_t sensorEventType = 0; 282 std::string type = getSensorTypeStringFromPath(path); 283 auto findSensor = sensorTypes.find(type.c_str()); 284 if (findSensor != sensorTypes.end()) 285 { 286 sensorEventType = static_cast<uint8_t>( 287 std::get<sensorEventTypeCodes>(findSensor->second)); 288 } 289 290 return sensorEventType; 291 } 292 293 std::string getPathFromSensorNumber(uint16_t sensorNum) 294 { 295 std::shared_ptr<SensorNumMap> sensorNumMapPtr; 296 details::getSensorNumMap(sensorNumMapPtr); 297 if (!sensorNumMapPtr) 298 { 299 return std::string(); 300 } 301 302 try 303 { 304 return sensorNumMapPtr->left.at(sensorNum); 305 } 306 catch (const std::out_of_range& e) 307 { 308 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 309 return std::string(); 310 } 311 } 312 313 namespace ipmi 314 { 315 316 std::map<std::string, std::vector<std::string>> 317 getObjectInterfaces(const char* path) 318 { 319 std::map<std::string, std::vector<std::string>> interfacesResponse; 320 std::vector<std::string> interfaces; 321 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 322 323 sdbusplus::message_t getObjectMessage = 324 dbus->new_method_call("xyz.openbmc_project.ObjectMapper", 325 "/xyz/openbmc_project/object_mapper", 326 "xyz.openbmc_project.ObjectMapper", "GetObject"); 327 getObjectMessage.append(path, interfaces); 328 329 try 330 { 331 sdbusplus::message_t response = dbus->call(getObjectMessage); 332 response.read(interfacesResponse); 333 } 334 catch (const std::exception& e) 335 { 336 phosphor::logging::log<phosphor::logging::level::ERR>( 337 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path), 338 phosphor::logging::entry("WHAT=%s", e.what())); 339 } 340 341 return interfacesResponse; 342 } 343 344 std::map<std::string, Value> getEntityManagerProperties(const char* path, 345 const char* interface) 346 { 347 std::map<std::string, Value> properties; 348 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 349 350 sdbusplus::message_t getProperties = 351 dbus->new_method_call("xyz.openbmc_project.EntityManager", path, 352 "org.freedesktop.DBus.Properties", "GetAll"); 353 getProperties.append(interface); 354 355 try 356 { 357 sdbusplus::message_t response = dbus->call(getProperties); 358 response.read(properties); 359 } 360 catch (const std::exception& e) 361 { 362 phosphor::logging::log<phosphor::logging::level::ERR>( 363 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path), 364 phosphor::logging::entry("INTF=%s", interface), 365 phosphor::logging::entry("WHAT=%s", e.what())); 366 } 367 368 return properties; 369 } 370 371 // Fetch the ipmiDecoratorPaths to get the list of dbus objects that 372 // have ipmi decorator to prevent unnessary dbus call to fetch the info 373 std::optional<std::unordered_set<std::string>>& 374 getIpmiDecoratorPaths(const std::optional<ipmi::Context::ptr>& ctx) 375 { 376 static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths; 377 378 if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt) 379 { 380 return ipmiDecoratorPaths; 381 } 382 383 boost::system::error_code ec; 384 std::vector<std::string> paths = 385 (*ctx)->bus->yield_method_call<std::vector<std::string>>( 386 (*ctx)->yield, ec, "xyz.openbmc_project.ObjectMapper", 387 "/xyz/openbmc_project/object_mapper", 388 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 389 int32_t(0), 390 std::array<const char*, 1>{ 391 "xyz.openbmc_project.Inventory.Decorator.Ipmi"}); 392 if (ec) 393 { 394 return ipmiDecoratorPaths; 395 } 396 397 ipmiDecoratorPaths = 398 std::unordered_set<std::string>(paths.begin(), paths.end()); 399 return ipmiDecoratorPaths; 400 } 401 402 const std::string* getSensorConfigurationInterface( 403 const std::map<std::string, std::vector<std::string>>& 404 sensorInterfacesResponse) 405 { 406 auto entityManagerService = 407 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager"); 408 if (entityManagerService == sensorInterfacesResponse.end()) 409 { 410 return nullptr; 411 } 412 413 // Find the fan configuration first (fans can have multiple configuration 414 // interfaces). 415 for (const auto& entry : entityManagerService->second) 416 { 417 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" || 418 entry == "xyz.openbmc_project.Configuration.I2CFan" || 419 entry == "xyz.openbmc_project.Configuration.NuvotonFan") 420 { 421 return &entry; 422 } 423 } 424 425 for (const auto& entry : entityManagerService->second) 426 { 427 if (boost::algorithm::starts_with(entry, 428 "xyz.openbmc_project.Configuration.")) 429 { 430 return &entry; 431 } 432 } 433 434 return nullptr; 435 } 436 437 // Follow Association properties for Sensor back to the Board dbus object to 438 // check for an EntityId and EntityInstance property. 439 void updateIpmiFromAssociation( 440 const std::string& path, 441 const std::unordered_set<std::string>& ipmiDecoratorPaths, 442 const DbusInterfaceMap& sensorMap, uint8_t& entityId, 443 uint8_t& entityInstance) 444 { 445 namespace fs = std::filesystem; 446 447 auto sensorAssociationObject = 448 sensorMap.find("xyz.openbmc_project.Association.Definitions"); 449 if (sensorAssociationObject == sensorMap.end()) 450 { 451 if constexpr (debug) 452 { 453 std::fprintf(stderr, "path=%s, no association interface found\n", 454 path.c_str()); 455 } 456 457 return; 458 } 459 460 auto associationObject = 461 sensorAssociationObject->second.find("Associations"); 462 if (associationObject == sensorAssociationObject->second.end()) 463 { 464 if constexpr (debug) 465 { 466 std::fprintf(stderr, "path=%s, no association records found\n", 467 path.c_str()); 468 } 469 470 return; 471 } 472 473 std::vector<Association> associationValues = 474 std::get<std::vector<Association>>(associationObject->second); 475 476 // loop through the Associations looking for the right one: 477 for (const auto& entry : associationValues) 478 { 479 // forward, reverse, endpoint 480 const std::string& forward = std::get<0>(entry); 481 const std::string& reverse = std::get<1>(entry); 482 const std::string& endpoint = std::get<2>(entry); 483 484 // We only currently concern ourselves with chassis+all_sensors. 485 if (!(forward == "chassis" && reverse == "all_sensors")) 486 { 487 continue; 488 } 489 490 // the endpoint is the board entry provided by 491 // Entity-Manager. so let's grab its properties if it has 492 // the right interface. 493 494 // just try grabbing the properties first. 495 ipmi::PropertyMap::iterator entityIdProp; 496 ipmi::PropertyMap::iterator entityInstanceProp; 497 if (ipmiDecoratorPaths.contains(endpoint)) 498 { 499 std::map<std::string, Value> ipmiProperties = 500 getEntityManagerProperties( 501 endpoint.c_str(), 502 "xyz.openbmc_project.Inventory.Decorator.Ipmi"); 503 504 entityIdProp = ipmiProperties.find("EntityId"); 505 entityInstanceProp = ipmiProperties.find("EntityInstance"); 506 if (entityIdProp != ipmiProperties.end()) 507 { 508 entityId = static_cast<uint8_t>( 509 std::get<uint64_t>(entityIdProp->second)); 510 } 511 if (entityInstanceProp != ipmiProperties.end()) 512 { 513 entityInstance = static_cast<uint8_t>( 514 std::get<uint64_t>(entityInstanceProp->second)); 515 } 516 } 517 518 // Now check the entity-manager entry for this sensor to see 519 // if it has its own value and use that instead. 520 // 521 // In theory, checking this first saves us from checking 522 // both, except in most use-cases identified, there won't be 523 // a per sensor override, so we need to always check both. 524 std::string sensorNameFromPath = fs::path(path).filename(); 525 526 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath; 527 528 // Download the interfaces for the sensor from 529 // Entity-Manager to find the name of the configuration 530 // interface. 531 std::map<std::string, std::vector<std::string>> 532 sensorInterfacesResponse = 533 getObjectInterfaces(sensorConfigPath.c_str()); 534 535 const std::string* configurationInterface = 536 getSensorConfigurationInterface(sensorInterfacesResponse); 537 538 // We didnt' find a configuration interface for this sensor, but we 539 // followed the Association property to get here, so we're done 540 // searching. 541 if (!configurationInterface) 542 { 543 break; 544 } 545 546 // We found a configuration interface. 547 std::map<std::string, Value> configurationProperties = 548 getEntityManagerProperties(sensorConfigPath.c_str(), 549 configurationInterface->c_str()); 550 551 entityIdProp = configurationProperties.find("EntityId"); 552 entityInstanceProp = configurationProperties.find("EntityInstance"); 553 if (entityIdProp != configurationProperties.end()) 554 { 555 entityId = 556 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second)); 557 } 558 if (entityInstanceProp != configurationProperties.end()) 559 { 560 entityInstance = static_cast<uint8_t>( 561 std::get<uint64_t>(entityInstanceProp->second)); 562 } 563 564 // stop searching Association records. 565 break; 566 } // end for Association vectors. 567 568 if constexpr (debug) 569 { 570 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n", 571 path.c_str(), entityId, entityInstance); 572 } 573 } 574 575 } // namespace ipmi 576