1 /* 2 // Copyright (c) 2019 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 #include "xyz/openbmc_project/Common/error.hpp" 17 18 #include <fstream> 19 #include <ipmid/api.hpp> 20 #include <ipmid/utils.hpp> 21 #include <nlohmann/json.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <regex> 24 #include <xyz/openbmc_project/Software/Activation/server.hpp> 25 #include <xyz/openbmc_project/Software/Version/server.hpp> 26 #include <xyz/openbmc_project/State/BMC/server.hpp> 27 28 namespace ipmi 29 { 30 31 static void registerAPPFunctions() __attribute__((constructor)); 32 33 namespace Log = phosphor::logging; 34 namespace Error = sdbusplus::xyz::openbmc_project::Common::Error; 35 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version; 36 using Activation = 37 sdbusplus::xyz::openbmc_project::Software::server::Activation; 38 using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC; 39 40 constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC"; 41 constexpr auto bmc_state_property = "CurrentBMCState"; 42 43 namespace 44 { 45 static constexpr auto redundancyIntf = 46 "xyz.openbmc_project.Software.RedundancyPriority"; 47 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; 48 static constexpr auto activationIntf = 49 "xyz.openbmc_project.Software.Activation"; 50 static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; 51 52 bool getCurrentBmcState() 53 { 54 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 55 56 // Get the Inventory object implementing the BMC interface 57 ipmi::DbusObjectInfo bmcObject = 58 ipmi::getDbusObject(bus, bmc_state_interface); 59 auto variant = 60 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first, 61 bmc_state_interface, bmc_state_property); 62 63 return std::holds_alternative<std::string>(variant) && 64 BMC::convertBMCStateFromString(std::get<std::string>(variant)) == 65 BMC::BMCState::Ready; 66 } 67 68 bool getCurrentBmcStateWithFallback(const bool fallbackAvailability) 69 { 70 try 71 { 72 return getCurrentBmcState(); 73 } 74 catch (...) 75 { 76 // Nothing provided the BMC interface, therefore return whatever was 77 // configured as the default. 78 return fallbackAvailability; 79 } 80 } 81 82 /** 83 * @brief Returns the Version info from primary software object 84 * 85 * Get the Version info from the active s/w object which is having high 86 * "Priority" value(a smaller number is a higher priority) and "Purpose" 87 * is "BMC" from the list of all s/w objects those are implementing 88 * RedundancyPriority interface from the given softwareRoot path. 89 * 90 * @return On success returns the Version info from primary software object. 91 * 92 */ 93 std::string getActiveSoftwareVersionInfo() 94 { 95 auto busp = getSdBus(); 96 97 std::string revision{}; 98 ipmi::ObjectTree objectTree; 99 try 100 { 101 objectTree = 102 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf); 103 } 104 catch (sdbusplus::exception::SdBusError& e) 105 { 106 Log::log<Log::level::ERR>("Failed to fetch redundancy object from dbus", 107 Log::entry("INTERFACE=%s", redundancyIntf), 108 Log::entry("ERRMSG=%s", e.what())); 109 } 110 111 auto objectFound = false; 112 for (auto& softObject : objectTree) 113 { 114 auto service = 115 ipmi::getService(*busp, redundancyIntf, softObject.first); 116 auto objValueTree = 117 ipmi::getManagedObjects(*busp, service, softwareRoot); 118 119 auto minPriority = 0xFF; 120 for (const auto& objIter : objValueTree) 121 { 122 try 123 { 124 auto& intfMap = objIter.second; 125 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); 126 auto& versionProps = intfMap.at(versionIntf); 127 auto& activationProps = intfMap.at(activationIntf); 128 auto priority = 129 std::get<uint8_t>(redundancyPriorityProps.at("Priority")); 130 auto purpose = 131 std::get<std::string>(versionProps.at("Purpose")); 132 auto activation = 133 std::get<std::string>(activationProps.at("Activation")); 134 auto version = 135 std::get<std::string>(versionProps.at("Version")); 136 if ((Version::convertVersionPurposeFromString(purpose) == 137 Version::VersionPurpose::BMC) && 138 (Activation::convertActivationsFromString(activation) == 139 Activation::Activations::Active)) 140 { 141 if (priority < minPriority) 142 { 143 minPriority = priority; 144 objectFound = true; 145 revision = std::move(version); 146 } 147 } 148 } 149 catch (const std::exception& e) 150 { 151 Log::log<Log::level::ERR>(e.what()); 152 } 153 } 154 } 155 156 if (!objectFound) 157 { 158 Log::log<Log::level::ERR>("Could not find an BMC software object"); 159 } 160 161 return revision; 162 } 163 164 typedef struct 165 { 166 std::string platform; 167 uint8_t major; 168 uint8_t minor; 169 uint32_t buildNo; 170 std::string openbmcHash; 171 std::string metaHash; 172 } MetaRevision; 173 174 // Support both 2 solutions: 175 // 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997 176 // openbmcTag 2.7.0-dev 177 // BuildNo 533 178 // openbmcHash 14dc00e79 179 // MetaHasg 5e7d997 180 // 181 // 2.New solution wht-0.2-3-gab3500-38384ac 182 // IdStr wht 183 // Major 0 184 // Minor 2 185 // buildNo 3 186 // MetaHash ab3500 187 // openbmcHash 38384ac 188 std::optional<MetaRevision> convertIntelVersion(std::string& s) 189 { 190 std::smatch results; 191 MetaRevision rev; 192 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)"); 193 constexpr size_t matchedPhosphor = 6; 194 if (std::regex_match(s, results, pattern1)) 195 { 196 if (results.size() == matchedPhosphor) 197 { 198 rev.platform = "whtref"; 199 rev.major = static_cast<uint8_t>(std::stoi(results[1])); 200 rev.minor = static_cast<uint8_t>(std::stoi(results[2])); 201 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3])); 202 rev.openbmcHash = results[4]; 203 rev.metaHash = results[5]; 204 std::string versionString = 205 rev.platform + ":" + std::to_string(rev.major) + ":" + 206 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) + 207 ":" + rev.openbmcHash + ":" + rev.metaHash; 208 Log::log<Log::level::INFO>( 209 "Get BMC version", 210 Log::entry("VERSION=%s", versionString.c_str())); 211 return rev; 212 } 213 } 214 constexpr size_t matchedIntel = 7; 215 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)"); 216 if (std::regex_match(s, results, pattern2)) 217 { 218 if (results.size() == matchedIntel) 219 { 220 rev.platform = results[1]; 221 rev.major = static_cast<uint8_t>(std::stoi(results[2])); 222 rev.minor = static_cast<uint8_t>(std::stoi(results[3])); 223 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4])); 224 rev.openbmcHash = results[6]; 225 rev.metaHash = results[5]; 226 std::string versionString = 227 rev.platform + ":" + std::to_string(rev.major) + ":" + 228 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) + 229 ":" + rev.openbmcHash + ":" + rev.metaHash; 230 Log::log<Log::level::INFO>( 231 "Get BMC version", 232 Log::entry("VERSION=%s", versionString.c_str())); 233 return rev; 234 } 235 } 236 237 return std::nullopt; 238 } 239 } // namespace 240 RspType<uint8_t, // Device ID 241 uint8_t, // Device Revision 242 uint8_t, // Firmware Revision Major 243 uint8_t, // Firmware Revision minor 244 uint8_t, // IPMI version 245 uint8_t, // Additional device support 246 uint24_t, // MFG ID 247 uint16_t, // Product ID 248 uint32_t // AUX info 249 > 250 ipmiAppGetDeviceId() 251 { 252 static struct 253 { 254 uint8_t id; 255 uint8_t revision; 256 uint8_t fw[2]; 257 uint8_t ipmiVer; 258 uint8_t addnDevSupport; 259 uint24_t manufId; 260 uint16_t prodId; 261 uint32_t aux; 262 } devId; 263 static bool dev_id_initialized = false; 264 static bool defaultActivationSetting = true; 265 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 266 constexpr auto ipmiDevIdStateShift = 7; 267 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); 268 269 if (!dev_id_initialized) 270 { 271 std::optional<MetaRevision> rev; 272 try 273 { 274 auto version = getActiveSoftwareVersionInfo(); 275 rev = convertIntelVersion(version); 276 } 277 catch (const std::exception& e) 278 { 279 Log::log<Log::level::ERR>("Failed to get active version info", 280 Log::entry("ERROR=%s", e.what())); 281 } 282 283 if (rev.has_value()) 284 { 285 // bit7 identifies if the device is available 286 // 0=normal operation 287 // 1=device firmware, SDR update, 288 // or self-initialization in progress. 289 // The availability may change in run time, so mask here 290 // and initialize later. 291 MetaRevision revision = rev.value(); 292 devId.fw[0] = revision.major & ipmiDevIdFw1Mask; 293 294 revision.minor = (revision.minor > 99 ? 99 : revision.minor); 295 devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16; 296 try 297 { 298 uint32_t hash = std::stoul(revision.metaHash, 0, 16); 299 hash = ((hash & 0xff000000) >> 24) | 300 ((hash & 0x00FF0000) >> 8) | ((hash & 0x0000FF00) << 8) | 301 ((hash & 0xFF) << 24); 302 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00); 303 } 304 catch (const std::exception& e) 305 { 306 Log::log<Log::level::ERR>("Failed to convert git hash", 307 Log::entry("ERROR=%s", e.what())); 308 } 309 } 310 311 // IPMI Spec version 2.0 312 devId.ipmiVer = 2; 313 314 std::ifstream devIdFile(filename); 315 if (devIdFile.is_open()) 316 { 317 auto data = nlohmann::json::parse(devIdFile, nullptr, false); 318 if (!data.is_discarded()) 319 { 320 devId.id = data.value("id", 0); 321 devId.revision = data.value("revision", 0); 322 devId.addnDevSupport = data.value("addn_dev_support", 0); 323 devId.manufId = data.value("manuf_id", 0); 324 325 try 326 { 327 auto busp = getSdBus(); 328 const ipmi::DbusObjectInfo& object = ipmi::getDbusObject( 329 *busp, "xyz.openbmc_project.Inventory.Item.Board", 330 "/xyz/openbmc_project/inventory/system/board/", 331 "Baseboard"); 332 const ipmi::Value& propValue = ipmi::getDbusProperty( 333 *busp, object.second, object.first, 334 "xyz.openbmc_project.Inventory.Item.Board", 335 "ProductId"); 336 devId.prodId = 337 static_cast<uint8_t>(std::get<uint64_t>(propValue)); 338 } 339 catch (std::exception& e) 340 { 341 devId.prodId = data.value("prod_id", 0); 342 } 343 344 // Set the availablitity of the BMC. 345 defaultActivationSetting = data.value("availability", true); 346 347 // Don't read the file every time if successful 348 dev_id_initialized = true; 349 } 350 else 351 { 352 Log::log<Log::level::ERR>("Device ID JSON parser failure"); 353 return ipmi::responseUnspecifiedError(); 354 } 355 } 356 else 357 { 358 Log::log<Log::level::ERR>("Device ID file not found"); 359 return ipmi::responseUnspecifiedError(); 360 } 361 } 362 363 // Set availability to the actual current BMC state 364 devId.fw[0] &= ipmiDevIdFw1Mask; 365 if (!getCurrentBmcStateWithFallback(defaultActivationSetting)) 366 { 367 devId.fw[0] |= (1 << ipmiDevIdStateShift); 368 } 369 370 return ipmi::responseSuccess( 371 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer, 372 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux); 373 } 374 375 static void registerAPPFunctions(void) 376 { 377 Log::log<Log::level::INFO>("Registering App commands"); 378 // <Get Device ID> 379 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User, 380 ipmiAppGetDeviceId); 381 } 382 383 } // namespace ipmi 384