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