Fix "get Device id" command
1. implement convertIntelVersion to get BMC version (Intel versioning)
2. add support for aux version
3. add product id support
Test:
1.tested on reference platform.
product id is correct
2.ipmitool mc info.
aux version field has value. BMC version is none-zero
Change-Id: I06c16563588f2b0461d48043191751d1a7806e98
Signed-off-by: Jia, chunhui <chunhui.jia@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ac3687f..93663c0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -90,7 +90,7 @@
add_library (
zinteloemcmds SHARED src/oemcommands.cpp src/sensorcommands.cpp
- src/storagecommands.cpp src/multinodecommands.cpp src/firmware-update.cpp
+ src/storagecommands.cpp src/multinodecommands.cpp src/firmware-update.cpp src/appcommands.cpp
src/smbioshandler.cpp src/smbiosmdrv2handler.cpp
src/manufacturingcommands.cpp src/bmccontrolservices.cpp
src/bridgingcommands.cpp src/ipmi_to_redfish_hooks.cpp
diff --git a/src/appcommands.cpp b/src/appcommands.cpp
new file mode 100644
index 0000000..39fc7d5
--- /dev/null
+++ b/src/appcommands.cpp
@@ -0,0 +1,375 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <fstream>
+#include <ipmid/api.hpp>
+#include <ipmid/utils.hpp>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+#include <regex>
+#include <xyz/openbmc_project/Software/Activation/server.hpp>
+#include <xyz/openbmc_project/Software/Version/server.hpp>
+#include <xyz/openbmc_project/State/BMC/server.hpp>
+
+namespace ipmi
+{
+
+static void registerAPPFunctions() __attribute__((constructor));
+
+namespace Log = phosphor::logging;
+namespace Error = sdbusplus::xyz::openbmc_project::Common::Error;
+using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
+using Activation =
+ sdbusplus::xyz::openbmc_project::Software::server::Activation;
+using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
+
+constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
+constexpr auto bmc_state_property = "CurrentBMCState";
+
+namespace
+{
+static constexpr auto redundancyIntf =
+ "xyz.openbmc_project.Software.RedundancyPriority";
+static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
+static constexpr auto activationIntf =
+ "xyz.openbmc_project.Software.Activation";
+static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
+
+bool getCurrentBmcState()
+{
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+
+ // Get the Inventory object implementing the BMC interface
+ ipmi::DbusObjectInfo bmcObject =
+ ipmi::getDbusObject(bus, bmc_state_interface);
+ auto variant =
+ ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
+ bmc_state_interface, bmc_state_property);
+
+ return std::holds_alternative<std::string>(variant) &&
+ BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
+ BMC::BMCState::Ready;
+}
+
+bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
+{
+ try
+ {
+ return getCurrentBmcState();
+ }
+ catch (...)
+ {
+ // Nothing provided the BMC interface, therefore return whatever was
+ // configured as the default.
+ return fallbackAvailability;
+ }
+}
+/**
+ * @brief Returns the Version info from primary s/w object
+ *
+ * Get the Version info from the active s/w object which is having high
+ * "Priority" value(a smaller number is a higher priority) and "Purpose"
+ * is "BMC" from the list of all s/w objects those are implementing
+ * RedundancyPriority interface from the given softwareRoot path.
+ *
+ * @return On success returns the Version info from primary s/w object.
+ *
+ */
+std::string getActiveSoftwareVersionInfo()
+{
+ auto busp = getSdBus();
+
+ std::string revision{};
+ ipmi::ObjectTree objectTree;
+ try
+ {
+ objectTree =
+ ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
+ }
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ Log::log<Log::level::ERR>("Failed to fetch redundancy object from dbus",
+ Log::entry("INTERFACE=%s", redundancyIntf),
+ Log::entry("ERRMSG=%s", e.what()));
+ }
+
+ auto objectFound = false;
+ for (auto& softObject : objectTree)
+ {
+ auto service =
+ ipmi::getService(*busp, redundancyIntf, softObject.first);
+ auto objValueTree =
+ ipmi::getManagedObjects(*busp, service, softwareRoot);
+
+ auto minPriority = 0xFF;
+ for (const auto& objIter : objValueTree)
+ {
+ try
+ {
+ auto& intfMap = objIter.second;
+ auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
+ auto& versionProps = intfMap.at(versionIntf);
+ auto& activationProps = intfMap.at(activationIntf);
+ auto priority =
+ std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
+ auto purpose =
+ std::get<std::string>(versionProps.at("Purpose"));
+ auto activation =
+ std::get<std::string>(activationProps.at("Activation"));
+ auto version =
+ std::get<std::string>(versionProps.at("Version"));
+ if ((Version::convertVersionPurposeFromString(purpose) ==
+ Version::VersionPurpose::BMC) &&
+ (Activation::convertActivationsFromString(activation) ==
+ Activation::Activations::Active))
+ {
+ if (priority < minPriority)
+ {
+ minPriority = priority;
+ objectFound = true;
+ revision = std::move(version);
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ Log::log<Log::level::ERR>(e.what());
+ }
+ }
+ }
+
+ if (!objectFound)
+ {
+ Log::log<Log::level::ERR>("Could not found an BMC software Object");
+ }
+
+ return revision;
+}
+
+typedef struct
+{
+ std::string platform;
+ uint8_t major;
+ uint8_t minor;
+ uint32_t buildNo;
+ std::string openbmcHash;
+ std::string metaHash;
+ std::string buildType;
+} MetaRevision;
+
+// Support both 2 solutions:
+// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
+// openbmcTag 2.7.0-dev
+// BuildNo 533
+// openbmcHash g14dc00e79
+// MetaHasg 5e7d997
+//
+// 2.New solution whtref-0.1-45-023125-a1295e-release
+// IdStr whtref
+// Major 0
+// Minor 1
+// buildNo 45
+// openbmcHash 023125
+// MetaHash a1295e
+// BuildType release/CI/<devloperId>
+std::optional<MetaRevision> convertIntelVersion(std::string& s)
+{
+ std::smatch results;
+ MetaRevision rev;
+ std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-(\\w+?)-(\\w+?)");
+ constexpr size_t matchedPhosphor = 6;
+ if (std::regex_match(s, results, pattern1))
+ {
+ if (results.size() == matchedPhosphor)
+ {
+ rev.platform = "whtref";
+ rev.major = static_cast<uint8_t>(std::stoi(results[1]));
+ rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
+ rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
+ rev.openbmcHash = results[4];
+ rev.metaHash = results[5];
+ rev.buildType = "release";
+ std::string versionString =
+ rev.platform + ":" + std::to_string(rev.major) + ":" +
+ std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
+ ":" + rev.openbmcHash + ":" + rev.metaHash + ":" +
+ rev.buildType;
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ versionString.c_str());
+ return rev;
+ }
+ }
+ constexpr size_t matchedIntel = 8;
+ std::regex pattern2(
+ "(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-(\\w+?)-(\\w+?)-(\\w+?)");
+ if (std::regex_match(s, results, pattern2))
+ {
+ if (results.size() == matchedIntel)
+ {
+ rev.platform = results[1];
+ rev.major = static_cast<uint8_t>(std::stoi(results[2]));
+ rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
+ rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
+ rev.openbmcHash = results[5];
+ rev.metaHash = results[6];
+ rev.buildType = results[7];
+ std::string versionString =
+ rev.platform + ":" + std::to_string(rev.major) + ":" +
+ std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
+ ":" + rev.openbmcHash + ":" + rev.metaHash + ":" +
+ rev.buildType;
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ versionString.c_str());
+ return rev;
+ }
+ }
+
+ return std::nullopt;
+}
+} // namespace
+auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
+ uint8_t, // Device Revision
+ uint8_t, // Firmware Revision Major
+ uint8_t, // Firmware Revision minor
+ uint8_t, // IPMI version
+ uint8_t, // Additional device support
+ uint24_t, // MFG ID
+ uint16_t, // Product ID
+ uint32_t // AUX info
+ >
+{
+ std::optional<MetaRevision> rev;
+ static struct
+ {
+ uint8_t id;
+ uint8_t revision;
+ uint8_t fw[2];
+ uint8_t ipmiVer;
+ uint8_t addnDevSupport;
+ uint24_t manufId;
+ uint16_t prodId;
+ uint32_t aux;
+ } devId;
+ static bool dev_id_initialized = false;
+ static bool defaultActivationSetting = true;
+ const char* filename = "/usr/share/ipmi-providers/dev_id.json";
+ constexpr auto ipmiDevIdStateShift = 7;
+ constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
+
+ if (!dev_id_initialized)
+ {
+ try
+ {
+ auto version = getActiveSoftwareVersionInfo();
+ rev = convertIntelVersion(version);
+ }
+ catch (const std::exception& e)
+ {
+ Log::log<Log::level::ERR>(e.what());
+ }
+
+ if (rev.has_value())
+ {
+ // bit7 identifies if the device is available
+ // 0=normal operation
+ // 1=device firmware, SDR update,
+ // or self-initialization in progress.
+ // The availability may change in run time, so mask here
+ // and initialize later.
+ MetaRevision revision = rev.value();
+ devId.fw[0] = revision.major & ipmiDevIdFw1Mask;
+
+ revision.minor = (revision.minor > 99 ? 99 : revision.minor);
+ devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16;
+ devId.aux = revision.buildNo;
+ }
+
+ // IPMI Spec version 2.0
+ devId.ipmiVer = 2;
+
+ std::ifstream devIdFile(filename);
+ if (devIdFile.is_open())
+ {
+ auto data = nlohmann::json::parse(devIdFile, nullptr, false);
+ if (!data.is_discarded())
+ {
+ devId.id = data.value("id", 0);
+ devId.revision = data.value("revision", 0);
+ devId.addnDevSupport = data.value("addn_dev_support", 0);
+ devId.manufId = data.value("manuf_id", 0);
+
+ try
+ {
+ auto busp = getSdBus();
+ const ipmi::DbusObjectInfo& object = ipmi::getDbusObject(
+ *busp, "xyz.openbmc_project.Inventory.Item.Board",
+ "/xyz/openbmc_project/inventory/system/board/",
+ "Baseboard");
+ const ipmi::Value& propValue = ipmi::getDbusProperty(
+ *busp, object.second, object.first,
+ "xyz.openbmc_project.Inventory.Item.Board",
+ "ProductId");
+ devId.prodId =
+ static_cast<uint8_t>(std::get<uint64_t>(propValue));
+ }
+ catch (std::exception& e)
+ {
+ devId.prodId = data.value("prod_id", 0);
+ }
+
+ // Set the availablitity of the BMC.
+ defaultActivationSetting = data.value("availability", true);
+
+ // Don't read the file every time if successful
+ dev_id_initialized = true;
+ }
+ else
+ {
+ Log::log<Log::level::ERR>("Device ID JSON parser failure");
+ return ipmi::responseUnspecifiedError();
+ }
+ }
+ else
+ {
+ Log::log<Log::level::ERR>("Device ID file not found");
+ return ipmi::responseUnspecifiedError();
+ }
+ }
+
+ // Set availability to the actual current BMC state
+ devId.fw[0] &= ipmiDevIdFw1Mask;
+ if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
+ {
+ devId.fw[0] |= (1 << ipmiDevIdStateShift);
+ }
+
+ return ipmi::responseSuccess(
+ devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
+ devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
+}
+
+static void registerAPPFunctions(void)
+{
+ Log::log<Log::level::INFO>("Registering App commands");
+ // <Get Device ID>
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
+ ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
+ ipmiAppGetDeviceId);
+ return;
+}
+
+} // namespace ipmi