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; 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 = true; 254 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 255 constexpr auto ipmiDevIdStateShift = 7; 256 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); 257 258 if (!dev_id_initialized) 259 { 260 std::optional<MetaRevision> rev; 261 try 262 { 263 auto version = getActiveSoftwareVersionInfo(); 264 rev = convertIntelVersion(version); 265 } 266 catch (const std::exception& e) 267 { 268 Log::log<Log::level::ERR>("Failed to get active version info", 269 Log::entry("ERROR=%s", e.what())); 270 } 271 272 if (rev.has_value()) 273 { 274 // bit7 identifies if the device is available 275 // 0=normal operation 276 // 1=device firmware, SDR update, 277 // or self-initialization in progress. 278 // The availability may change in run time, so mask here 279 // and initialize later. 280 MetaRevision revision = rev.value(); 281 devId.fw[0] = revision.major & ipmiDevIdFw1Mask; 282 283 revision.minor = (revision.minor > 99 ? 99 : revision.minor); 284 devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16; 285 try 286 { 287 uint32_t hash = std::stoul(revision.metaHash, 0, 16); 288 hash = ((hash & 0xff000000) >> 24) | 289 ((hash & 0x00FF0000) >> 8) | ((hash & 0x0000FF00) << 8) | 290 ((hash & 0xFF) << 24); 291 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00); 292 } 293 catch (const std::exception& e) 294 { 295 Log::log<Log::level::ERR>("Failed to convert git hash", 296 Log::entry("ERROR=%s", e.what())); 297 } 298 } 299 300 // IPMI Spec version 2.0 301 devId.ipmiVer = 2; 302 303 std::ifstream devIdFile(filename); 304 if (devIdFile.is_open()) 305 { 306 auto data = nlohmann::json::parse(devIdFile, nullptr, false); 307 if (!data.is_discarded()) 308 { 309 devId.id = data.value("id", 0); 310 devId.revision = data.value("revision", 0); 311 devId.addnDevSupport = data.value("addn_dev_support", 0); 312 devId.manufId = data.value("manuf_id", 0); 313 314 try 315 { 316 auto busp = getSdBus(); 317 const ipmi::DbusObjectInfo& object = ipmi::getDbusObject( 318 *busp, "xyz.openbmc_project.Inventory.Item.Board", 319 "/xyz/openbmc_project/inventory/system/board/", 320 "Baseboard"); 321 const ipmi::Value& propValue = ipmi::getDbusProperty( 322 *busp, object.second, object.first, 323 "xyz.openbmc_project.Inventory.Item.Board", 324 "ProductId"); 325 devId.prodId = 326 static_cast<uint8_t>(std::get<uint64_t>(propValue)); 327 } 328 catch (std::exception& e) 329 { 330 devId.prodId = data.value("prod_id", 0); 331 } 332 333 // Set the availablitity of the BMC. 334 defaultActivationSetting = data.value("availability", true); 335 336 // Don't read the file every time if successful 337 dev_id_initialized = true; 338 } 339 else 340 { 341 Log::log<Log::level::ERR>("Device ID JSON parser failure"); 342 return ipmi::responseUnspecifiedError(); 343 } 344 } 345 else 346 { 347 Log::log<Log::level::ERR>("Device ID file not found"); 348 return ipmi::responseUnspecifiedError(); 349 } 350 } 351 352 // Set availability to the actual current BMC state 353 devId.fw[0] &= ipmiDevIdFw1Mask; 354 if (!getCurrentBmcStateWithFallback(defaultActivationSetting)) 355 { 356 devId.fw[0] |= (1 << ipmiDevIdStateShift); 357 } 358 359 return ipmi::responseSuccess( 360 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer, 361 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux); 362 } 363 364 static void registerAPPFunctions(void) 365 { 366 Log::log<Log::level::INFO>("Registering App commands"); 367 // <Get Device ID> 368 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User, 369 ipmiAppGetDeviceId); 370 } 371 372 } // namespace ipmi 373