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