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