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