Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 | |
Chen Yugang | 7a04f3a | 2019-10-08 11:12:35 +0800 | [diff] [blame] | 18 | #include <appcommands.hpp> |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 | |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 | } |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 80 | |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 81 | /** |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 82 | * @brief Returns the Version info from primary software object |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 | * |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 89 | * @return On success returns the Version info from primary software object. |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 | { |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 157 | Log::log<Log::level::ERR>("Could not find an BMC software object"); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | return revision; |
| 161 | } |
| 162 | |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 167 | // openbmcHash 14dc00e79 |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 168 | // MetaHasg 5e7d997 |
| 169 | // |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 170 | // 2.New solution wht-0.2-3-gab3500-38384ac |
| 171 | // IdStr wht |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 172 | // Major 0 |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 173 | // Minor 2 |
| 174 | // buildNo 3 |
| 175 | // MetaHash ab3500 |
| 176 | // openbmcHash 38384ac |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 177 | std::optional<MetaRevision> convertIntelVersion(std::string& s) |
| 178 | { |
| 179 | std::smatch results; |
| 180 | MetaRevision rev; |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 181 | std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)"); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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]; |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 193 | std::string versionString = |
| 194 | rev.platform + ":" + std::to_string(rev.major) + ":" + |
| 195 | std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) + |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 196 | ":" + rev.openbmcHash + ":" + rev.metaHash; |
| 197 | Log::log<Log::level::INFO>( |
| 198 | "Get BMC version", |
| 199 | Log::entry("VERSION=%s", versionString.c_str())); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 200 | return rev; |
| 201 | } |
| 202 | } |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 203 | constexpr size_t matchedIntel = 7; |
| 204 | std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)"); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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])); |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 213 | rev.openbmcHash = results[6]; |
| 214 | rev.metaHash = results[5]; |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 215 | std::string versionString = |
| 216 | rev.platform + ":" + std::to_string(rev.major) + ":" + |
| 217 | std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) + |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 218 | ":" + rev.openbmcHash + ":" + rev.metaHash; |
| 219 | Log::log<Log::level::INFO>( |
| 220 | "Get BMC version", |
| 221 | Log::entry("VERSION=%s", versionString.c_str())); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 222 | return rev; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | return std::nullopt; |
| 227 | } |
Chen Yugang | 7a04f3a | 2019-10-08 11:12:35 +0800 | [diff] [blame] | 228 | |
Vernon Mauery | 98bbf69 | 2019-09-16 11:14:59 -0700 | [diff] [blame] | 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() |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 240 | { |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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 | { |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 260 | std::optional<MetaRevision> rev; |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 261 | try |
| 262 | { |
| 263 | auto version = getActiveSoftwareVersionInfo(); |
| 264 | rev = convertIntelVersion(version); |
| 265 | } |
| 266 | catch (const std::exception& e) |
| 267 | { |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 268 | Log::log<Log::level::ERR>("Failed to get active version info", |
| 269 | Log::entry("ERROR=%s", e.what())); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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; |
Jia, chunhui | 82d178c | 2019-06-27 16:48:14 +0800 | [diff] [blame] | 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 | } |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 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> |
Vernon Mauery | 98bbf69 | 2019-09-16 11:14:59 -0700 | [diff] [blame] | 368 | registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User, |
| 369 | ipmiAppGetDeviceId); |
Vernon Mauery | ce14e4d | 2019-06-25 11:38:20 -0700 | [diff] [blame] | 370 | } |
| 371 | |
| 372 | } // namespace ipmi |