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